Skip to content

Commit

Permalink
feat: add ability's querying
Browse files Browse the repository at this point in the history
  • Loading branch information
favna committed Nov 4, 2019
1 parent db7c96b commit 91bad53
Show file tree
Hide file tree
Showing 10 changed files with 240 additions and 44 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"test": "jest",
"build": "tsc",
"watch": "tsc -w",
"dev": "nodemon --exec ts-node src/index.ts",
"dev": "nodemon dist/index.js",
"commit": "git-cz",
"cz": "git-cz",
"up": "yarn upgrade-interactive --latest",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {IsBoolean, Max, Min} from 'class-validator';
import {ArgsType, Field, Int} from 'type-graphql';

@ArgsType()
export default class PokemonPaginatedArgs {
export default class PaginatedArgs {
@Field(() => Int, {defaultValue: 0, description: 'Sets the offset where to start'})
@Min(0)
skip: number;
Expand All @@ -16,6 +16,6 @@ export default class PokemonPaginatedArgs {
@IsBoolean()
reverse = false;

@Field(() => String, { description: 'The pokemon to search for' })
pokemon: string;
@Field(() => String, { description: 'The data to look up' })
query: string;
}
1 change: 1 addition & 0 deletions src/assets/pokedex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17128,6 +17128,7 @@ const pokedex = new Collection<string, Pokemon.DexEntry>(
'type null',
{
species: 'type null',
baseForme: 'Type: Null',
num: 772,
types: [ 'Normal' ],
gender: 'N',
Expand Down
3 changes: 2 additions & 1 deletion src/graphql/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import express from 'express';
import {ApolloServer} from 'apollo-server-express';
import {buildSchemaSync} from 'type-graphql';
import DexResolver from '../resolvers/DexResolver';
import AbilityResolver from '../resolvers/AbilityResolver';

const gqlServer = () => {
const app = express();
const schema = buildSchemaSync({resolvers: [ DexResolver ]});
const schema = buildSchemaSync({resolvers: [ DexResolver, AbilityResolver ]});

const apolloServer = new ApolloServer({
schema,
Expand Down
91 changes: 91 additions & 0 deletions src/resolvers/AbilityResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {Arg, Args, Query, Resolver} from 'type-graphql';
import AbilityEntry from '../structures/AbilityEntry';
import AbilityService from '../services/AbilityService';
import {GraphQLJSONObject} from 'graphql-type-json';
import PaginatedArgs from '../arguments/PaginatedArgs';

@Resolver(AbilityEntry)
export default class AbilityResolver {
private abilityService: AbilityService;

constructor() {
this.abilityService = new AbilityService();
}

@Query(() => GraphQLJSONObject, {
description: [
'Gets details on a single ability based on a fuzzy search.',
'You can supply skip and take to paginate the fuzzy search and reverse to show the least likely matched on top.',
'Reversal is applied before pagination!'
].join(''),
})
async getAbilityDetailsByFuzzy(@Args() {
query, skip, take, reverse,
}: PaginatedArgs) {
const entry = this.abilityService.findByName(query);

if (!entry) {
const fuzzyEntry = this.abilityService.findByFuzzy({
query, skip, take, reverse,
});
if (fuzzyEntry === undefined) {
throw new Error(`Failed to get data for Pokemon: ${query}`);
}
query = fuzzyEntry[0].name;
}

const detailsEntry = this.abilityService.findByNameWithDetails(query);
if (detailsEntry === undefined) {
throw new Error(`Failed to get data for Pokemon: ${query}`);
}

return detailsEntry;
}

@Query(() => GraphQLJSONObject, {
description: [
'Gets details on a single ability based on an exact name match.'
].join(''),
})
async getAbilityDetailsByName(@Arg('ability') ability: string) {
const entry = this.abilityService.findByNameWithDetails(ability);

if (entry === undefined) {
throw new Error(`Failed to get data for Pokemon: ${ability}`);
}

return entry;
}

@Query(() => GraphQLJSONObject, {
description: [
'Gets entries of multiple ability based on a fuzzy search.',
'You can supply skip and take to limit the amount of flavour texts to return and reverse to show latest games on top.',
'Reversal is applied before pagination!'
].join(''),
})
getAbilityByFuzzy(@Args() {
query, skip, take, reverse,
}: PaginatedArgs) {
const abilityEntries = this.abilityService.findByFuzzy({
query, skip, take, reverse,
});

if (abilityEntries === undefined) {
throw new Error(`Failed to get data for ability: ${name}`);
}

return abilityEntries;
}

@Query(() => GraphQLJSONObject, {description: 'Gets details on a single ability based on name.'})
getAbilityByName(@Arg('name') name: string) {
const abilityEntry = this.abilityService.findByName(name);

if (abilityEntry === undefined) {
throw new Error(`Failed to get data for ability: ${name}`);
}

return abilityEntry;
}
}
38 changes: 19 additions & 19 deletions src/resolvers/DexResolver.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Arg, Args, Query, Resolver } from 'type-graphql';
import DexService from '../services/DexService';
import DetailsEntry from '../structures/DetailsEntry';
import PokemonPaginatedArgs from '../arguments/PokemonPaginatedArgs';
import PaginatedArgs from '../arguments/PaginatedArgs';
import { GraphQLJSONObject } from 'graphql-type-json';

@Resolver(DetailsEntry)
Expand All @@ -20,13 +20,13 @@ export default class DexResolver {
].join(''),
})
async getPokemonDetails(@Args() {
pokemon, skip, take, reverse,
}: PokemonPaginatedArgs) {
const detailsEntry = this.dexService.fetchPokemonDetails({
pokemon, skip, take, reverse,
query, skip, take, reverse,
}: PaginatedArgs) {
const detailsEntry = this.dexService.findBySpeciesWithDetails({
query, skip, take, reverse,
});
if (detailsEntry === undefined) {
throw new Error(`Failed to get data for Pokemon: ${pokemon}`);
throw new Error(`Failed to get data for Pokemon: ${query}`);
}

return detailsEntry;
Expand All @@ -40,25 +40,25 @@ export default class DexResolver {
].join(''),
})
async getPokemonDetailsByFuzzy(@Args() {
pokemon, skip, take, reverse,
}: PokemonPaginatedArgs) {
const entry = this.dexService.findBySpecies(pokemon);
query, skip, take, reverse,
}: PaginatedArgs) {
const entry = this.dexService.findBySpecies(query);

if (!entry) {
const fuzzyEntry = this.dexService.findByFuzzy({
pokemon, skip, take, reverse,
query, skip, take, reverse,
});
if (fuzzyEntry === undefined) {
throw new Error(`Failed to get data for Pokemon: ${pokemon}`);
throw new Error(`Failed to get data for Pokemon: ${query}`);
}
pokemon = fuzzyEntry[0].species;
query = fuzzyEntry[0].species;
}

const detailsEntry = this.dexService.fetchPokemonDetails({
pokemon, skip, take, reverse,
const detailsEntry = this.dexService.findBySpeciesWithDetails({
query, skip, take, reverse,
});
if (detailsEntry === undefined) {
throw new Error(`Failed to get data for Pokemon: ${pokemon}`);
throw new Error(`Failed to get data for Pokemon: ${query}`);
}

return detailsEntry;
Expand All @@ -72,13 +72,13 @@ export default class DexResolver {
].join(''),
})
getDexEntries(@Args() {
pokemon, skip, take, reverse,
}: PokemonPaginatedArgs) {
query, skip, take, reverse,
}: PaginatedArgs) {
const dexEntries = this.dexService.findByFuzzy({
pokemon, skip, take, reverse,
query, skip, take, reverse,
});
if (dexEntries === undefined) {
throw new Error(`Failed to get data for Pokemon: ${pokemon}`);
throw new Error(`Failed to get data for Pokemon: ${query}`);
}

return dexEntries;
Expand Down
56 changes: 56 additions & 0 deletions src/services/AbilityService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {Arg, Args} from 'type-graphql';
import abilities from '../assets/abilities';
import PaginatedArgs from '../arguments/PaginatedArgs';
import {GraphQLJSONObject} from 'graphql-type-json';
import {SimpleFuseOptions} from '../typings/common';
import AbilityEntry from '../structures/AbilityEntry';
import FuzzySearch from '../utils/FuzzySearch';
import {abilityAliases} from '../assets/aliases';
import {sentencecase} from '../utils/util';

export default class AbilityService {
public findByName(@Arg('name') name: string) {
return abilities.get(name);
}

public findByFuzzy(@Args() {
query, skip, take, reverse,
}: PaginatedArgs, @Arg('fuseOptions', () => GraphQLJSONObject) fuseOptions?: SimpleFuseOptions) {
const fuzzyAbility = new FuzzySearch(abilities, [ 'name', 'num' ], {threshold: 0.3, ...fuseOptions});

let fuzzyResult = fuzzyAbility.run(query);

if (!fuzzyResult.length) {
const fuzzyAliasResult = new FuzzySearch(abilityAliases, [ 'alias', 'ability' ], {threshold: 0.4}).run(query);

if (fuzzyAliasResult.length) {
fuzzyResult = fuzzyAbility.run(fuzzyAliasResult[0].ability);
}
}

if (reverse) {
fuzzyResult.reverse();
}

return fuzzyResult.slice(skip, skip + take);
}

public findByNameWithDetails(@Arg('ability') ability: string) {
const abilityData = this.findByName(ability);

if (!abilityData) {
throw new Error(`No ability found for ${ability}`);
}

const abilityEntry = new AbilityEntry();
abilityEntry.desc = abilityData.desc;
abilityEntry.name = abilityData.name;
abilityEntry.num = abilityData.num;
abilityEntry.shortDesc = abilityData.shortDesc;
abilityEntry.bulbapediaPage = `https://bulbapedia.bulbagarden.net/wiki/${sentencecase(abilityData.name.replace(/ /g, '_'))}_(Ability)`;
abilityEntry.serebiiPage = `https://www.serebii.net/abilitydex/${abilityData.name.replace(/ /g, '_').toLowerCase()}.shtml`;
abilityEntry.smogonPage = `https://www.smogon.com/dex/sm/abilities/${abilityData.name.toLowerCase().replace(/ /g, '_')}`;

return abilityEntry;
}
}
Loading

0 comments on commit 91bad53

Please sign in to comment.