diff --git a/.changeset/brave-pumas-raise.md b/.changeset/brave-pumas-raise.md new file mode 100644 index 0000000..d8d43a3 --- /dev/null +++ b/.changeset/brave-pumas-raise.md @@ -0,0 +1,5 @@ +--- +'@blizzard-api/hs': minor +--- + +Initial release of @blizzard-api/hs diff --git a/README.md b/README.md index 374d5a3..2bf808e 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,10 @@ Currently available game-specific packages are: - `@blizzard-api/classic-wow` - `@blizzard-api/d3` +- `@blizzard-api/hs` - `@blizzard-api/sc2` - `@blizzard-api/wow` -Additionally planned packages are: - -- `@blizzard-api/hs` - The core package gives you access to helper functions such as `getBlizzardApi` which will quickly get you the hostname and locales for a specific region. The game package will let you access paths, namespaces, parameters and more for each endpoint. This can imported like so: @@ -67,11 +64,3 @@ console.log(response.data); ## Authentication Please refer to the [battle.net documentation](https://develop.battle.net/documentation/guides/getting-started) for guides on how to obtain Blizzard API credentials. - -## TODO - -This list is generally prioritized but no promises that things will be addressed in this order. - -- Migrate the client away from axios to native fetch -- Add a package for the following games/flavours - - Hearthstone diff --git a/packages/client/README.md b/packages/client/README.md index c4cf15c..53b70b4 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -4,14 +4,11 @@ This package provides a client that is meant to be used together with one or mor Currently available packages are: -- `@blizzard-api/d3` -- `@blizzard-api/wow` - `@blizzard-api/classic-wow` - -Planned packages are: - +- `@blizzard-api/d3` - `@blizzard-api/hs` - `@blizzard-api/sc2` +- `@blizzard-api/wow` ## Installation diff --git a/packages/client/package.json b/packages/client/package.json index e80ec50..8c66a2c 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -53,6 +53,7 @@ "@blizzard-api/classic-wow": "workspace:*", "@blizzard-api/core": "workspace:*", "@blizzard-api/d3": "workspace:*", + "@blizzard-api/hs": "workspace:*", "@blizzard-api/sc2": "workspace:*", "@blizzard-api/wow": "workspace:*" }, diff --git a/packages/client/src/tests/hs.test.ts b/packages/client/src/tests/hs.test.ts new file mode 100644 index 0000000..b1d5598 --- /dev/null +++ b/packages/client/src/tests/hs.test.ts @@ -0,0 +1,49 @@ +import { hs } from '@blizzard-api/hs'; +import { describe, it } from 'vitest'; +import { environment } from '../../../../environment'; +import { createBlizzardApiClient } from '../client/create-client'; + +describe.concurrent('smoketest some hs api responses', async () => { + const client = await createBlizzardApiClient({ + key: environment.blizzardClientId, + origin: 'eu', + secret: environment.blizzardClientSecret, + }); + + it('should be able to search for a card back', async ({ expect }) => { + const response = await client.sendRequest(hs.cardBackSearch({ locale: 'en_GB' })); + expect(response.data).toBeDefined(); + }); + + it('should be able to fetch a card back', async ({ expect }) => { + const response = await client.sendRequest(hs.fetchOneCardBack('1', 'en_GB')); + expect(response.data).toBeDefined(); + }); + + it('should be able to search for a card', async ({ expect }) => { + const response = await client.sendRequest(hs.cardSearch({ locale: 'en_GB', textFilter: 'fireball' })); + expect(response.data).toBeDefined(); + }); + + it('should be able to fetch a card', async ({ expect }) => { + const response = await client.sendRequest(hs.fetchOneCard('52119-arch-villain-rafaam')); + expect(response.data).toBeDefined(); + }); + + it('should be able to fetch a deck', async ({ expect }) => { + const response = await client.sendRequest( + hs.getDeck({ code: 'AAECAQcG+wyd8AKS+AKggAOblAPanQMMS6IE/web8wLR9QKD+wKe+wKz/AL1gAOXlAOalAOSnwMA' }), + ); + expect(response.data).toBeDefined(); + }); + + it('should be able to fetch all metadata', async ({ expect }) => { + const response = await client.sendRequest(hs.allMetadata()); + expect(response.data).toBeDefined(); + }); + + it('should be able to fetch specific metadata', async ({ expect }) => { + const response = await client.sendRequest(hs.specificMetadata('classes')); + expect(response.data).toBeDefined(); + }); +}); diff --git a/packages/hs/CHANGELOG.md b/packages/hs/CHANGELOG.md new file mode 100644 index 0000000..ce72083 --- /dev/null +++ b/packages/hs/CHANGELOG.md @@ -0,0 +1 @@ +# @blizzard-api/hs diff --git a/packages/hs/README.md b/packages/hs/README.md new file mode 100644 index 0000000..7afeb79 --- /dev/null +++ b/packages/hs/README.md @@ -0,0 +1,75 @@ +# @blizzard-api/hs + +This package aims to make it easier for you to integrate with the Blizzard Battle.net API, specifically for Diablo 3. + +## Installation + +```sh +npm i @blizzard-api/core @blizzard-api/hs +``` + +## Usage + +You can get paths, namespaces, parameters and more for a specific endpoint by calling it from the `hs` export. + +```ts +import { hs } from "@blizzard-api/hs" +//or +import hs from "@blizzard-api/hs" + +const season = hs.allMetadata(123); + ^ Resource +``` + +If you don't want to use the exported hs object, you can also access the functions directly: + +```ts +import { allMetadata } from "@blizzard-api/hs" + +const seasonI = season(123); + ^ Resource +``` + +### Types + +If you need the response types, they are also exported with "Response" appended, so to get the response type from the above code, you can import it like this: + +```ts +import type { SeasonResponse } from '@blizzard-api/hs'; +``` + +If you simply want to use the existing object, you can use the helper, `ExtractResourceType`, from `@blizzard-api/core` like so: + +```ts +import { hs } from "@blizzard-api/hs" + +const season = hs.allMetadata(); + ^ Resource + +type AllMetadataResponse = ExtractResourceType; +``` + +## Notes on Types + +The types are manually created from using the Blizzard API documentation, and are as accurate as possible with smoke testing each endpoint. However, no-one is perfect so there is likely to be some discrepancies. If you encounter any issues with the types from this package, please open an issue or a pull request. + +### Client + +While this package is made to function on it's own, it performs even better when combined with `@blizzard-api/client` where you can easily request data combining the two libraries. + +```ts +import { createBlizzardApiClient } from '@blizzard-api/client'; +import { hs } from '@blizzard-api/hs'; + +const client = await createBlizzardApiClient({ + key: 'environment.blizzardClientId', + secret: 'environment.blizzardClientSecret', + origin: 'eu', +}); + +//Response will automatically be typed with the appropriate values +const response = await client.sendRequest(hs.allMetadata()); + +console.log(response.data); + ^ typeof AllMetadataResponse +``` diff --git a/packages/hs/package.json b/packages/hs/package.json new file mode 100644 index 0000000..39a8a4c --- /dev/null +++ b/packages/hs/package.json @@ -0,0 +1,51 @@ +{ + "name": "@blizzard-api/hs", + "version": "0.0.0", + "license": "MIT", + "author": "Putro", + "description": "A series of helpers to interact with the Hearthstone Blizzard API", + "repository": "https://github.com/Pewtro/blizzard-api/tree/main/packages/hs", + "type": "module", + "engines": { + "node": "^18.18 || ^20.9 || ^21.1 || ^22" + }, + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + }, + "files": [ + "dist" + ], + "keywords": [ + "blizzard", + "battlenet", + "battle.net", + "bnet", + "api", + "hs", + "hearthstone" + ], + "dependencies": {}, + "peerDependencies": { + "@blizzard-api/core": "1.2.1" + }, + "devDependencies": { + "@blizzard-api/core": "workspace:*" + }, + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "test": "vitest run", + "test:coverage": "pnpm test -- --coverage", + "test:watch": "vitest watch" + } +} diff --git a/packages/hs/src/base.ts b/packages/hs/src/base.ts new file mode 100644 index 0000000..c6134fe --- /dev/null +++ b/packages/hs/src/base.ts @@ -0,0 +1,7 @@ +type SortDirection = 'asc' | 'desc'; + +type SortOptions = 'attack' | 'class' | 'dataAdded' | 'groupByClass' | 'health' | 'manaCost' | 'name'; + +export type SearchSortOption = `${SortOptions}:${SortDirection}`; + +export type GameMode = 'arena' | 'battlegrounds' | 'classic' | 'constructed' | 'duels' | 'mercenaries' | 'standard'; diff --git a/packages/hs/src/card-backs/card-backs.test.ts b/packages/hs/src/card-backs/card-backs.test.ts new file mode 100644 index 0000000..9ccc678 --- /dev/null +++ b/packages/hs/src/card-backs/card-backs.test.ts @@ -0,0 +1,39 @@ +import type { Locales } from '@blizzard-api/core'; +import { describe, expect, it } from 'vitest'; +import { cardBackSearch, fetchOneCardBack } from './card-backs'; +import type { CardBackSearchParameters } from './types'; + +describe('cardBackSearch', () => { + it('should return the correct resource object for card back search', () => { + const options: CardBackSearchParameters = { locale: 'en_US', page: 1 }; + const result = cardBackSearch(options); + + expect(result).toEqual({ + parameters: options, + path: 'hearthstone/cardbacks', + }); + }); +}); + +describe('fetchOneCardBack', () => { + it('should return the correct resource object for fetching one card back with locale', () => { + const id = '12345'; + const locale: Locales = 'en_US'; + const result = fetchOneCardBack(id, locale); + + expect(result).toEqual({ + parameters: { locale }, + path: `hearthstone/cardbacks/${id}`, + }); + }); + + it('should return the correct resource object for fetching one card back without locale', () => { + const id = '12345'; + const result = fetchOneCardBack(id); + + expect(result).toEqual({ + parameters: { locale: undefined }, + path: `hearthstone/cardbacks/${id}`, + }); + }); +}); diff --git a/packages/hs/src/card-backs/card-backs.ts b/packages/hs/src/card-backs/card-backs.ts new file mode 100644 index 0000000..887decf --- /dev/null +++ b/packages/hs/src/card-backs/card-backs.ts @@ -0,0 +1,21 @@ +import type { Locales, Resource } from '@blizzard-api/core'; +import type { CardBackSearchParameters, CardBackSearchResponse, SingleCardBackSearchResponse } from './types'; + +export function cardBackSearch( + options: CardBackSearchParameters, +): Resource { + return { + parameters: options, + path: 'hearthstone/cardbacks', + }; +} + +export function fetchOneCardBack( + id: string, + locale?: Locales, +): Resource { + return { + parameters: { locale }, + path: `hearthstone/cardbacks/${id}`, + }; +} diff --git a/packages/hs/src/card-backs/types.ts b/packages/hs/src/card-backs/types.ts new file mode 100644 index 0000000..8068af4 --- /dev/null +++ b/packages/hs/src/card-backs/types.ts @@ -0,0 +1,40 @@ +import type { Locales } from '@blizzard-api/core'; +import type { SearchSortOption } from '../base'; + +export interface CardBackSearchResponse { + cardBacks: Array; + cardCount: number; + page: number; + pageCount: number; +} + +export interface SingleCardBackSearchResponse { + id: number; + image: string; + name: Record | string; + slug: string; + sortCategory: number; + text: Record | string; +} + +export interface CardBackSearchParameters { + cardBackCategory?: + | 'achieve' + | 'base' + | 'blizzard' + | 'esports' + | 'events' + | 'fireside' + | 'game_license' + | 'golden' + | 'heroes' + | 'legend' + | 'pre_purchase' + | 'promotion' + | 'season'; + locale?: Locales; + page?: number; + pageSize?: number; + sort?: SearchSortOption; + textFilter?: string; +} diff --git a/packages/hs/src/cards/cards.test.ts b/packages/hs/src/cards/cards.test.ts new file mode 100644 index 0000000..48573a8 --- /dev/null +++ b/packages/hs/src/cards/cards.test.ts @@ -0,0 +1,92 @@ +/* eslint-disable sonarjs/no-duplicate-string */ +import type { Locales } from '@blizzard-api/core'; +import { describe, expect, it } from 'vitest'; +import type { GameMode } from '../base'; +import { cardSearch, fetchOneCard } from './cards'; +import type { CardSearchParameters } from './types'; + +describe('cardSearch', () => { + it('should return correct parameters for single values', () => { + const options: CardSearchParameters = { + attack: 5, + defaultMercenary: 1, + health: 10, + mercenaryId: 123, + }; + const result = cardSearch(options); + expect(result.parameters).toEqual({ + ...options, + attack: '5', + defaultMercenary: '1', + health: '10', + mercenaryId: '123', + }); + expect(result.path).toBe('hearthstone/cards'); + }); + + it('should return correct parameters for array values', () => { + const options: CardSearchParameters = { + attack: [1, 2, 3], + defaultMercenary: [1, 2], + health: [5, 10], + mercenaryId: [123, 456], + }; + const result = cardSearch(options); + expect(result.parameters).toEqual({ + ...options, + attack: '1,2,3', + defaultMercenary: '1,2', + health: '5,10', + mercenaryId: '123,456', + }); + expect(result.path).toBe('hearthstone/cards'); + }); + + it('should handle undefined values correctly', () => { + const options: CardSearchParameters = {}; + const result = cardSearch(options); + expect(result.parameters).toEqual({ + ...options, + attack: undefined, + defaultMercenary: undefined, + health: undefined, + mercenaryId: undefined, + }); + expect(result.path).toBe('hearthstone/cards'); + }); +}); + +describe('fetchOneCard', () => { + it('should return correct parameters with default gameMode', () => { + const id = 'card123'; + const options = { locale: 'en_US' as Locales }; + const result = fetchOneCard(id, options); + expect(result.parameters).toEqual({ + gameMode: 'constructed', + locale: 'en_US', + }); + expect(result.path).toBe(`hearthstone/cards/${id}`); + }); + + it('should return correct parameters with specified gameMode', () => { + const id = 'card123'; + const options = { gameMode: 'battlegrounds' as GameMode, locale: 'en_US' as Locales }; + const result = fetchOneCard(id, options); + expect(result.parameters).toEqual({ + gameMode: 'battlegrounds', + locale: 'en_US', + }); + expect(result.path).toBe(`hearthstone/cards/${id}`); + }); + + it('should handle undefined locale correctly', () => { + const id = 'card123'; + const options = { gameMode: 'battlegrounds' as GameMode }; + const result = fetchOneCard(id, options); + expect(result.parameters).toEqual({ + gameMode: 'battlegrounds', + locale: undefined, + }); + expect(result.path).toBe(`hearthstone/cards/${id}`); + }); +}); diff --git a/packages/hs/src/cards/cards.ts b/packages/hs/src/cards/cards.ts new file mode 100644 index 0000000..367be03 --- /dev/null +++ b/packages/hs/src/cards/cards.ts @@ -0,0 +1,55 @@ +import type { Locales, Resource } from '@blizzard-api/core'; +import type { GameMode } from '../base'; +import type { + BlizzardCardSearchParameters, + CardSearchParameters, + CardSearchResponse, + FetchOneCardResponse, +} from './types'; + +export function cardSearch(options: CardSearchParameters): Resource { + let attack: string | undefined = undefined; + let defaultMercenary: string | undefined = undefined; + let health: string | undefined = undefined; + let mercenaryId: string | undefined = undefined; + + if (options.attack) { + attack = Array.isArray(options.attack) ? options.attack.join(',') : `${options.attack}`; + } + if (options.defaultMercenary) { + defaultMercenary = Array.isArray(options.defaultMercenary) + ? options.defaultMercenary.join(',') + : `${options.defaultMercenary}`; + } + if (options.health) { + health = Array.isArray(options.health) ? options.health.join(',') : `${options.health}`; + } + if (options.mercenaryId) { + mercenaryId = Array.isArray(options.mercenaryId) ? options.mercenaryId.join(',') : `${options.mercenaryId}`; + } + + return { + parameters: { + ...options, + attack, + defaultMercenary, + health, + mercenaryId, + }, + path: 'hearthstone/cards', + }; +} + +export function fetchOneCard( + id: string, + options?: { + gameMode?: GameMode; + locale?: Locales; + }, +): Resource { + const { gameMode = 'constructed', locale } = options ?? {}; + return { + parameters: { gameMode, locale }, + path: `hearthstone/cards/${id}`, + }; +} diff --git a/packages/hs/src/cards/types.ts b/packages/hs/src/cards/types.ts new file mode 100644 index 0000000..3e25836 --- /dev/null +++ b/packages/hs/src/cards/types.ts @@ -0,0 +1,109 @@ +import type { Locales } from '@blizzard-api/core'; +import type { GameMode } from '../base'; + +export interface CardSearchResponse { + cardCount: number; + cards: Array; + page: number; + pageCount: number; +} + +interface Card { + artistName: null | string; + attack: number; + cardSetId: number; + cardTypeId: number; + classId: null | number; + collectible: number; + cropImage: null | string; + flavorText: string; + health: number; + id: number; + image: string; + imageGold: string; + isZilliaxCosmeticModule: boolean; + isZilliaxFunctionalModule: boolean; + manaCost: number; + mercenaryHero: MercenaryHero; + minionTypeId: number; + multiClassIds: Array; + multiTypeIds?: Array; + name: string; + rarityId: number; + slug: string; + text: string; +} + +interface MercenaryHero { + collectible: number; + craftingCost: number; + default: number; + faction: null | number; + mercId: number; + rarity: number; + roleId: number; + statsByLevel: Record; +} + +interface StatsByLevel { + attack: number; + health: number; +} + +export interface CardSearchParameters extends BaseSearchParameters { + attack?: Array | number; + defaultMercenary?: Array | number; + health?: Array | number; + mercenaryId?: Array | number; +} + +export interface BlizzardCardSearchParameters extends BaseSearchParameters { + attack?: string; + defaultMercenary?: string; + health?: string; + mercenaryId?: string; +} + +interface BaseSearchParameters { + gameMode?: GameMode; + locale?: Locales; + mercenaryRole?: string; + minionType?: string; + page?: number; + pageSize?: number; + sort?: + | 'attack:asc' + | 'attack:desc' + | 'health:asc' + | 'health:desc' + | 'name:asc' + | 'name:desc' + | 'tier:asc' + | 'tier:desc'; + textFilter?: string; + tier?: 'hero' | 1 | 2 | 3 | 4 | 5 | 6; +} + +export interface FetchOneCardResponse { + artistName: string; + attack: number; + cardSetId: number; + cardTypeId: number; + classId: number; + collectible: number; + cropImage: string; + flavorText: Record | string; + health: number; + id: number; + image: Record | string; + imageGold: Record | string; + isZilliaxCosmeticModule: boolean; + isZilliaxFunctionalModule: boolean; + keywordIds: Array; + manaCost: number; + multiClassIds: Array; + name: Record | string; + rarityId: number; + slug: string; + text: Record | string; +} diff --git a/packages/hs/src/decks/decks.test.ts b/packages/hs/src/decks/decks.test.ts new file mode 100644 index 0000000..8377ccd --- /dev/null +++ b/packages/hs/src/decks/decks.test.ts @@ -0,0 +1,31 @@ +import type { Resource } from '@blizzard-api/core'; +import { describe, expect, it } from 'vitest'; +import { getDeck } from './decks'; +import type { DeckResponse, DeckSearchParameters } from './types'; + +describe('getDeck', () => { + it('should return a Resource with encoded code parameter when code is provided', () => { + const options = { code: 'ABC123' }; + const result: Resource = getDeck(options); + + expect(result).toEqual({ + parameters: { + code: 'ABC123', + }, + path: 'hearthstone/deck', + }); + }); + + it('should return a Resource with hero and ids parameters when code is not provided', () => { + const options = { hero: 'mage', ids: ['1', '2', '3'] }; + const result: Resource = getDeck(options); + + expect(result).toEqual({ + parameters: { + hero: 'mage', + ids: '1,2,3', + }, + path: 'hearthstone/deck', + }); + }); +}); diff --git a/packages/hs/src/decks/decks.ts b/packages/hs/src/decks/decks.ts new file mode 100644 index 0000000..88707a1 --- /dev/null +++ b/packages/hs/src/decks/decks.ts @@ -0,0 +1,23 @@ +import type { Resource } from '@blizzard-api/core'; +import type { DeckResponse, DeckSearchParameters } from './types'; + +export function getDeck( + options: { code: string } | { hero?: string; ids: Array }, +): Resource { + if ('code' in options) { + return { + parameters: { + code: encodeURI(options.code), + }, + path: 'hearthstone/deck', + }; + } + + return { + parameters: { + hero: options.hero, + ids: options.ids?.join(','), + }, + path: 'hearthstone/deck', + }; +} diff --git a/packages/hs/src/decks/types.ts b/packages/hs/src/decks/types.ts new file mode 100644 index 0000000..841c4c6 --- /dev/null +++ b/packages/hs/src/decks/types.ts @@ -0,0 +1,69 @@ +export type DeckSearchParameters = { code: string } | { hero?: string; ids: string }; +export interface DeckResponse { + cardCount: number; + cards: Array; + class: Class; + deckCode: string; + format: string; + hero: Hero; + heroPower: Hero; + version: number; +} + +interface Card { + armor?: number; + artistName: string; + attack?: number; + bannedFromSideboard?: number; + cardSetId: number; + cardTypeId: number; + childIds?: Array; + classId: number; + collectible: number; + cropImage: string; + flavorText: string; + health?: number; + id: number; + image: string; + imageGold: string; + isZilliaxCosmeticModule: boolean; + isZilliaxFunctionalModule: boolean; + keywordIds?: Array; + manaCost: number; + minionTypeId?: number; + multiClassIds: Array; + name: string; + rarityId: number; + slug: string; + spellSchoolId?: number; + text: string; +} + +interface Class { + id: number; + name: string; + slug: string; +} + +interface Hero { + artistName: null | string; + cardSetId: number; + cardTypeId: number; + classId: number; + collectible: number; + cropImage: null; + flavorText: string; + health?: number; + id: number; + image: string; + imageGold: string; + isZilliaxCosmeticModule: boolean; + isZilliaxFunctionalModule: boolean; + manaCost: number; + multiClassIds: Array; + name: string; + parentId: number; + rarityId: null | number; + slug: string; + text: string; +} diff --git a/packages/hs/src/index.ts b/packages/hs/src/index.ts new file mode 100644 index 0000000..c6284fe --- /dev/null +++ b/packages/hs/src/index.ts @@ -0,0 +1,33 @@ +import { cardBackSearch, fetchOneCardBack } from './card-backs/card-backs'; +import { cardSearch, fetchOneCard } from './cards/cards'; +import { getDeck } from './decks/decks'; +import { allMetadata, specificMetadata } from './metadata/metadata'; + +export const hs = { + //Card Backs + cardBackSearch, + fetchOneCardBack, + //Cards + cardSearch, + fetchOneCard, + //Decks + getDeck, + //Metadata + allMetadata, + specificMetadata, +}; + +export default hs; + +//Card Backs +export * from './card-backs/card-backs'; +export type * from './card-backs/types'; +//Cards +export * from './cards/cards'; +export type * from './cards/types'; +//Decks +export * from './decks/decks'; +export type * from './decks/types'; +//Metadata +export * from './metadata/metadata'; +export type * from './metadata/types'; diff --git a/packages/hs/src/metadata/metadata.test.ts b/packages/hs/src/metadata/metadata.test.ts new file mode 100644 index 0000000..245c38c --- /dev/null +++ b/packages/hs/src/metadata/metadata.test.ts @@ -0,0 +1,28 @@ +import type { Resource } from '@blizzard-api/core'; +import { describe, expect, it } from 'vitest'; +import { allMetadata, specificMetadata } from './metadata'; +import type { AllMetadataResponse, SpecificMetadataResponse } from './types'; + +describe('metadata', () => { + describe('allMetadata', () => { + it('should return the correct resource path for all metadata', () => { + const result: Resource = allMetadata(); + expect(result).toEqual({ + path: 'hearthstone/metadata', + }); + }); + }); + + describe('specificMetadata', () => { + const types = ['classes', 'keywords', 'minionTypes', 'rarities', 'setGroups', 'sets', 'types'] as const; + + for (const type of types) { + it(`should return the correct resource path for specific metadata type: ${type}`, () => { + const result: Resource = specificMetadata(type); + expect(result).toEqual({ + path: `hearthstone/metadata/${type}`, + }); + }); + } + }); +}); diff --git a/packages/hs/src/metadata/metadata.ts b/packages/hs/src/metadata/metadata.ts new file mode 100644 index 0000000..5f96260 --- /dev/null +++ b/packages/hs/src/metadata/metadata.ts @@ -0,0 +1,16 @@ +import type { Resource } from '@blizzard-api/core'; +import type { AllMetadataResponse, SpecificMetadataResponse } from './types'; + +export function allMetadata(): Resource { + return { + path: 'hearthstone/metadata', + }; +} + +export function specificMetadata( + type: 'classes' | 'keywords' | 'minionTypes' | 'rarities' | 'setGroups' | 'sets' | 'types', +): Resource { + return { + path: `hearthstone/metadata/${type}`, + }; +} diff --git a/packages/hs/src/metadata/types.ts b/packages/hs/src/metadata/types.ts new file mode 100644 index 0000000..8e1fc5b --- /dev/null +++ b/packages/hs/src/metadata/types.ts @@ -0,0 +1,82 @@ +import type { NameId } from '../../../wow/src/base'; + +export interface AllMetadataResponse { + arenaIds: Array; + bgGameModes: Array; + cardBackCategories: Array; + classes: Array; + filterableFields: Array; + gameModes: Array; + keywords: Array; + mercenaryFactions: Array; + mercenaryRoles: Array; + minionTypes: Array; + numericFields: Array; + rarities: Array; + setGroups: Array; + sets: Array; + spellSchools: Array; + types: Array; +} + +interface GameMode extends NameId { + gameModes?: Array; + slug: string; +} + +interface Class { + alternateHeroCardIds?: Array; + cardId?: number; + heroPowerCardId?: number; + id: number; + name: string; + slug: string; +} + +interface Keyword extends NameId { + gameModes: Array; + refText: string; + slug: string; + text: string; +} + +interface Rarity extends NameId { + craftingCost: Array; + dustValue: Array; + slug: string; +} + +interface SetGroup { + cardSets: Array; + icon?: string; + name: string; + slug: string; + standard?: boolean; + svg?: null | string; + year?: number; + yearRange?: string; +} + +type SetType = '' | 'adventure' | 'base' | 'expansion'; + +interface Set extends NameId { + aliasSetIds?: Array; + collectibleCount: number; + collectibleRevealedCount: number; + hyped: boolean; + nonCollectibleCount: number; + nonCollectibleRevealedCount: number; + slug: string; + type: SetType; +} + +export interface SpecificMetadataResponse extends NameId { + aliasSetIds?: Array; + collectibleCount: number; + collectibleRevealedCount: number; + hyped: boolean; + nonCollectibleCount: number; + nonCollectibleRevealedCount: number; + slug: string; + type: SetType; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 031b800..d272c7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,6 +78,9 @@ importers: '@blizzard-api/d3': specifier: workspace:* version: link:../d3 + '@blizzard-api/hs': + specifier: workspace:* + version: link:../hs '@blizzard-api/sc2': specifier: workspace:* version: link:../sc2 @@ -93,6 +96,12 @@ importers: specifier: workspace:* version: link:../core + packages/hs: + devDependencies: + '@blizzard-api/core': + specifier: workspace:* + version: link:../core + packages/sc2: devDependencies: '@blizzard-api/core':