Skip to content

Commit

Permalink
Merge pull request #28 from LearningProcesss/27-feature-request-search
Browse files Browse the repository at this point in the history
feat: add search api client
  • Loading branch information
LearningProcesss authored Jun 18, 2022
2 parents 50b86d5 + 7c5a963 commit 3adb312
Show file tree
Hide file tree
Showing 28 changed files with 578 additions and 52 deletions.
12 changes: 3 additions & 9 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/javascript-node/.devcontainer/base.Dockerfile

# [Choice] Node.js version: 14, 12, 10
# ARG VARIANT="14-buster"
# FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
ARG VARIANT=16-bullseye
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}

# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
Expand All @@ -14,7 +12,3 @@

# [Optional] Uncomment if you want to install more global node modules
# RUN su node -c "npm install -g <your-package-list-here>"


FROM node:16-alpine
RUN apk update && apk add bash
55 changes: 55 additions & 0 deletions .devcontainer/base.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
ARG VARIANT=16-bullseye
FROM node:${VARIANT}

# [Option] Install zsh
ARG INSTALL_ZSH="true"
# [Option] Upgrade OS packages to their latest versions
ARG UPGRADE_PACKAGES="true"

# Install needed packages, yarn, nvm and setup non-root user. Use a separate RUN statement to add your own dependencies.
ARG USERNAME=node
ARG USER_UID=1000
ARG USER_GID=$USER_UID
ARG NPM_GLOBAL=/usr/local/share/npm-global
ENV NVM_DIR=/usr/local/share/nvm
ENV NVM_SYMLINK_CURRENT=true \
PATH=${NPM_GLOBAL}/bin:${NVM_DIR}/current/bin:${PATH}
COPY library-scripts/*.sh library-scripts/*.env /tmp/library-scripts/
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# Remove imagemagick due to https://security-tracker.debian.org/tracker/CVE-2019-10131
&& apt-get purge -y imagemagick imagemagick-6-common \
# Install common packages, non-root user, update yarn and install nvm
&& bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" "true" "true" \
# Install yarn, nvm
&& rm -rf /opt/yarn-* /usr/local/bin/yarn /usr/local/bin/yarnpkg \
&& bash /tmp/library-scripts/node-debian.sh "${NVM_DIR}" "none" "${USERNAME}" \
# Configure global npm install location, use group to adapt to UID/GID changes
&& if ! cat /etc/group | grep -e "^npm:" > /dev/null 2>&1; then groupadd -r npm; fi \
&& usermod -a -G npm ${USERNAME} \
&& umask 0002 \
&& mkdir -p ${NPM_GLOBAL} \
&& touch /usr/local/etc/npmrc \
&& chown ${USERNAME}:npm ${NPM_GLOBAL} /usr/local/etc/npmrc \
&& chmod g+s ${NPM_GLOBAL} \
&& npm config -g set prefix ${NPM_GLOBAL} \
&& sudo -u ${USERNAME} npm config -g set prefix ${NPM_GLOBAL} \
# Install eslint
&& su ${USERNAME} -c "umask 0002 && npm install -g eslint" \
&& npm cache clean --force > /dev/null 2>&1 \
# Install python-is-python3 on bullseye to prevent node-gyp regressions
&& . /etc/os-release \
&& if [ "${VERSION_CODENAME}" = "bullseye" ]; then apt-get -y install --no-install-recommends python-is-python3; fi \
# Clean up
&& apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /root/.gnupg /tmp/library-scripts

# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>

# [Optional] Uncomment if you want to install an additional version of node using nvm
# ARG EXTRA_NODE_VERSION=10
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"

# [Optional] Uncomment if you want to install more global node modules
# RUN su node -c "npm install -g <your-package-list-here>""
28 changes: 15 additions & 13 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/javascript-node
// https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/javascript-node
{
"name": "Node.js",
"build": {
"dockerfile": "Dockerfile",
// Update 'VARIANT' to pick a Node version: 10, 12, 14
"args": { "VARIANT": "14" }
// Update 'VARIANT' to pick a Node version: 18, 16, 14.
// Append -bullseye or -buster to pin to an OS version.
// Use -bullseye variants on local arm64/Apple Silicon.
"args": { "VARIANT": "18" }
},

// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"dbaeumer.vscode-eslint"
]
}
},

// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"dbaeumer.vscode-eslint",
"humao.rest-client"
],

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "yarn install",

// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "node"
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@
"scripts": {
"start": "echo TODO!",
"build": "yarn run build:ts",
"build:ts": "rm -r -f dist && mkdir dist && yarn run build:ts:cjs && yarn run build:ts:esm && (sleep 5 && yarn run build:ts:post)",
"build:ts": "rm -r -f dist && mkdir dist && yarn run build:ts:cjs && yarn run build:ts:esm && yarn run build:reflection && (sleep 5 && yarn run build:ts:post)",
"build:ts:cjs": "rm -r -f cjs && tsc -p tsconfig.cjs.json --outdir cjs",
"build:ts:esm": "rm -r -f esm && tsc -p tsconfig.esm.json --outdir esm",
"build:ts:post": "cp -r cjs dist && cp -r esm dist && rm -r -f cjs && rm -r -f esm",
"build:reflection": "npx ts-node test/unit/utils/reflection/ReflectionRunner.ts",
"test": "jest --verbose",
"lint": "echo TODO!",
"prepare": "yarn run build:ts",
Expand Down
14 changes: 13 additions & 1 deletion rest.http
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,16 @@ GET https://www.boardgamegeek.com/xmlapi2/plays?username=mattiabanned HTTP/1.1

### thread

GET https://www.boardgamegeek.com/xmlapi2/thread?id=1082079 HTTP/1.1
GET https://www.boardgamegeek.com/xmlapi2/thread?id=1082079 HTTP/1.1

### search

GET https://www.boardgamegeek.com/xmlapi2/search?query=34294&type=boardgame

### search

GET https://www.boardgamegeek.com/xmlapi2/search?query=gloom&type=boardgame

### search

GET https://www.boardgamegeek.com/xmlapi2/search?query=gloom
34 changes: 34 additions & 0 deletions src/client/concrete/BggSearchClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { IDtoParser, BggSearchDto } from "../../dto";
import { IFetcher } from "../../fetcher";
import { IQueryBuilder } from "../../query";
import { ISearchRequest } from "../../request";
import { IResponseParser } from "../../responseparser";
import { IBggSearchClient } from "../interface/IBggClients";

export class BggSearchClient implements IBggSearchClient {
resource: string;
builder: IQueryBuilder<ISearchRequest>;
fetcher: IFetcher<string, string>;
responseParser: IResponseParser<string, any>;
dtoParser: IDtoParser<BggSearchDto>;
constructor(
builder: IQueryBuilder<ISearchRequest>,
fetcher: IFetcher<string, string>,
responseParser: IResponseParser<string, any>,
dtoParser: IDtoParser<BggSearchDto>
) {
this.resource = "https://www.boardgamegeek.com/xmlapi2/search";
this.builder = builder;
this.fetcher = fetcher;
this.responseParser = responseParser;
this.dtoParser = dtoParser;
}

async query(request: ISearchRequest): Promise<BggSearchDto[]> {
const querystring = this.builder.build(request);
const xml = await this.fetcher.doFetch(`${this.resource}?${querystring}`);
const jsonData = await this.responseParser.parseResponse(xml);
return await this.dtoParser.jsonToDto(jsonData);
}

}
3 changes: 2 additions & 1 deletion src/client/concrete/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export { BggThreadClient } from './BggThreadClient';
export { BggUserClient } from './BggUserClient';
export { BggGuildClient } from './BggGuildClient';
export { BggPlayClient } from './BggPlaysClient';
export { BggCollectionClient } from './BggCollectionClient';
export { BggCollectionClient } from './BggCollectionClient';
export { BggSearchClient } from './BggSearchClient';
7 changes: 4 additions & 3 deletions src/client/interface/IBggClients.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BggFamilyDto, BggThingDto, BggForumlistDto, BggForumDto, BggThreadDto, BggUserDto, BggGuildDto, BggPlayDto, BggCollectionDto } from "../../dto";
import { IThingRequest, IFamilyRequest, IForumlistRequest, IForumRequest, IThreadRequest, IUserRequest, IGuildRequest, IPlaysRequest, ICollectionRequest } from '../../request';
import { BggFamilyDto, BggThingDto, BggForumlistDto, BggForumDto, BggThreadDto, BggUserDto, BggGuildDto, BggPlayDto, BggCollectionDto, BggSearchDto } from "../../dto";
import { IThingRequest, IFamilyRequest, IForumlistRequest, IForumRequest, IThreadRequest, IUserRequest, IGuildRequest, IPlaysRequest, ICollectionRequest, ISearchRequest } from '../../request';
import { IBggClient } from "./IBggClient";

export type IBggThingClient = IBggClient<IThingRequest, BggThingDto>;
Expand All @@ -10,4 +10,5 @@ export type IBggThreadClient = IBggClient<IThreadRequest, BggThreadDto>;
export type IBggUserClient = IBggClient<IUserRequest, BggUserDto>;
export type IBggGuildClient = IBggClient<IGuildRequest, BggGuildDto>;
export type IBggPlaysClient = IBggClient<IPlaysRequest, BggPlayDto>;
export type IBggCollectionClient = IBggClient<ICollectionRequest, BggCollectionDto>;
export type IBggCollectionClient = IBggClient<ICollectionRequest, BggCollectionDto>;
export type IBggSearchClient = IBggClient<ISearchRequest, BggSearchDto>;
2 changes: 1 addition & 1 deletion src/client/interface/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { IBggClient } from './IBggClient';
export { IBggThingClient, IBggFamilyClient, IBggForumlistClient, IBggForumClient, IBggThreadClient, IBggUserClient, IBggGuildClient, IBggPlaysClient, IBggCollectionClient } from './IBggClients';
export { IBggThingClient, IBggFamilyClient, IBggForumlistClient, IBggForumClient, IBggThreadClient, IBggUserClient, IBggGuildClient, IBggPlaysClient, IBggCollectionClient, IBggSearchClient } from './IBggClients';
20 changes: 20 additions & 0 deletions src/dto/concrete/BggSearchDto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { JsonAlias, JsonClassType, JsonIgnoreProperties, JsonManagedReference, JsonProperty } from "jackson-js";
import { IBggDto } from "../interface";
import { BggSearchItemDto } from "./subdto";

@JsonIgnoreProperties({ value: ['@_termsofuse'] })
export class BggSearchDto implements IBggDto {

id!: number;

@JsonProperty()
@JsonClassType({ type: () => [Number] })
@JsonAlias({ values: ["@_total"] })
total!: number;

@JsonProperty()
@JsonClassType({ type: () => [Array, [BggSearchItemDto]] })
@JsonAlias({ values: ["item"] })
@JsonManagedReference()
items!: BggSearchItemDto[];
}
3 changes: 2 additions & 1 deletion src/dto/concrete/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export { BggThreadDto } from './BggThreadDto';
export { BggUserDto } from './BggUserDto';
export { BggGuildDto } from './BggGuildDto';
export { BggPlayDto } from './BggPlayDto';
export { BggCollectionDto } from './BggCollectionDto';
export { BggCollectionDto } from './BggCollectionDto';
export { BggSearchDto } from './BggSearchDto';
28 changes: 28 additions & 0 deletions src/dto/concrete/subdto/BggSearchItemDto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { JsonAlias, JsonClassType, JsonDeserialize, JsonProperty } from "jackson-js";


export class BggSearchItemDto {
@JsonProperty()
@JsonClassType({ type: () => [Number] })
@JsonAlias({ values: ["@_id"] })
id!: number;

@JsonProperty()
@JsonClassType({ type: () => [String] })
@JsonAlias({ values: ["@_type"] })
type!: string;

@JsonProperty()
@JsonClassType({ type: () => [String] })
@JsonDeserialize({
using: (value: []) => value.map(item => item['@_value'])[0]
})
name!: string;

@JsonProperty()
@JsonClassType({ type: () => [Number] })
@JsonDeserialize({
using: (value: []) => value.map(item => item['@_value'])[0]
})
yearpublished!: number;
}
3 changes: 2 additions & 1 deletion src/dto/concrete/subdto/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export { BggThingMarketlistingsDto } from './BggThingMarketlistingsDto';
export { BggLinkDto } from './BggLinkDto';
export { BggPlaysPlayDto } from './BggPlaySubDtos';
export { BggCollectionItemDto } from './BggCollectionItemDto';
export { BggPollDto } from './BggPollDto';
export { BggPollDto } from './BggPollDto';
export { BggSearchItemDto } from './BggSearchItemDto';
20 changes: 20 additions & 0 deletions src/dto/dtoparser/concrete/BggSearchDtoParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { JsonParser } from "jackson-js";
import { BggSearchDto } from "../../concrete";
import { IDtoParser } from "../interface";

export class BggSearchDtoParser implements IDtoParser<BggSearchDto> {
parser: JsonParser<BggSearchDto>;
constructor() {
this.parser = new JsonParser<BggSearchDto>();
}
jsonToDto(jsonData: any): Promise<BggSearchDto[]> {
return new Promise<BggSearchDto[]>((resolve) => {
resolve(
this.parser.transform(jsonData.items, {
mainCreator: () => [Array, [BggSearchDto]]
})
);
});
}

}
3 changes: 2 additions & 1 deletion src/dto/dtoparser/concrete/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export { BggThreadDtoParser } from './BggThreadDtoParser';
export { BggUserDtoParser } from './BggUserDtoParser';
export { BggGuildDtoParser } from './BggGuildDtoParser';
export { BggPlayDtoParser } from './BggPlaysDtoParser';
export { BggCollectionDtoParser } from './BggCollectionDtoParser';
export { BggCollectionDtoParser } from './BggCollectionDtoParser';
export { BggSearchDtoParser } from './BggSearchDtoParser';
8 changes: 5 additions & 3 deletions src/master/BggClient.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { IBggThingClient, IBggFamilyClient, IBggForumlistClient, IBggForumClient, BggThingClient, BggFamilyClient, BggForumlistClient, BggForumClient, IBggThreadClient, BggThreadClient, BggUserClient, IBggUserClient, IBggGuildClient, BggGuildClient, IBggPlaysClient, BggPlayClient, IBggCollectionClient, BggCollectionClient } from "../client";
import { BggFamilyDtoParser, BggThingDtoParser, BggForumlistDtoParser, BggForumDtoParser, BggThreadDtoParser, BggUserDtoParser, BggGuildDtoParser, BggPlayDtoParser, BggCollectionDtoParser } from "../dto";
import { IBggThingClient, IBggFamilyClient, IBggForumlistClient, IBggForumClient, BggThingClient, BggFamilyClient, BggForumlistClient, BggForumClient, IBggThreadClient, BggThreadClient, BggUserClient, IBggUserClient, IBggGuildClient, BggGuildClient, IBggPlaysClient, BggPlayClient, IBggCollectionClient, BggCollectionClient, IBggSearchClient, BggSearchClient } from "../client";
import { BggFamilyDtoParser, BggThingDtoParser, BggForumlistDtoParser, BggForumDtoParser, BggThreadDtoParser, BggUserDtoParser, BggGuildDtoParser, BggPlayDtoParser, BggCollectionDtoParser, BggSearchDtoParser } from "../dto";
import { TextFetcher } from "../fetcher";
import { GenericQueryBuilder } from "../query";
import { IFamilyRequest, IForumlistRequest, IThingRequest, IForumRequest, IThreadRequest, IUserRequest, IGuildRequest, IPlaysRequest, ICollectionRequest } from "../request";
import { IFamilyRequest, IForumlistRequest, IThingRequest, IForumRequest, IThreadRequest, IUserRequest, IGuildRequest, IPlaysRequest, ICollectionRequest, ISearchRequest } from "../request";
import { XmlResponseParser } from "../responseparser";

/**
Expand All @@ -22,6 +22,7 @@ export class BggClient {
readonly guild: Omit<IBggGuildClient, "builder" | "fetcher" | "responseParser" | "resource" | "dtoParser">;
readonly play: Omit<IBggPlaysClient, "builder" | "fetcher" | "responseParser" | "resource" | "dtoParser">;
readonly collection: Omit<IBggCollectionClient, "builder" | "fetcher" | "responseParser" | "resource" | "dtoParser">;
readonly search: Omit<IBggSearchClient, "builder" | "fetcher" | "responseParser" | "resource" | "dtoParser">;

private constructor() {
this.thing = new BggThingClient(new GenericQueryBuilder<IThingRequest>(), new TextFetcher(), new XmlResponseParser(), new BggThingDtoParser());
Expand All @@ -33,6 +34,7 @@ export class BggClient {
this.guild = new BggGuildClient(new GenericQueryBuilder<IGuildRequest>(), new TextFetcher(), new XmlResponseParser(), new BggGuildDtoParser());
this.play = new BggPlayClient(new GenericQueryBuilder<IPlaysRequest>(), new TextFetcher(), new XmlResponseParser(), new BggPlayDtoParser());
this.collection = new BggCollectionClient(new GenericQueryBuilder<ICollectionRequest>(), new TextFetcher(), new XmlResponseParser(), new BggCollectionDtoParser());
this.search = new BggSearchClient(new GenericQueryBuilder<ISearchRequest>(), new TextFetcher(), new XmlResponseParser(), new BggSearchDtoParser());
}

static Create(): BggClient {
Expand Down
24 changes: 24 additions & 0 deletions src/request/interface/IRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,4 +468,28 @@ export interface ICollectionRequest extends IRequest {
* @memberof ICollectionRequest
*/
modifiedsince?: string;
}

export interface ISearchRequest extends IRequest, IRequestWithType<ThingType> {

/**
* @type {string}
* @memberof ISearchRequest
* @description Returns all types of Items that match SEARCH_QUERY. Spaces in the SEARCH_QUERY are replaced by a +
*/
query: string;

/**
* @type {ThingType[]}
* @memberof ISearchRequest
* @description Return all items that match SEARCH_QUERY of type TYPE. TYPE might be rpgitem, videogame, boardgame, boardgameaccessory or boardgameexpansion. You can return multiple types by listing them separated by commas, e.g. type=TYPE1,TYPE2,TYPE3
*/
type?: ThingType | ThingType[];

/**
* @type {number}
* @memberof ISearchRequest
* @description Limit results to items that match the SEARCH_QUERY exactly
*/
exact?: number;
}
2 changes: 1 addition & 1 deletion src/request/interface/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { IRequest, IThingRequest, IFamilyRequest, IForumlistRequest, IForumRequest, IThreadRequest, IUserRequest, IGuildRequest, IPlaysRequest, ICollectionRequest } from './IRequest';
export { IRequest, IThingRequest, IFamilyRequest, IForumlistRequest, IForumRequest, IThreadRequest, IUserRequest, IGuildRequest, IPlaysRequest, ICollectionRequest, ISearchRequest } from './IRequest';
Loading

0 comments on commit 3adb312

Please sign in to comment.