From 4a9e53b3d7c88e93a5d716c26035299bf9eec309 Mon Sep 17 00:00:00 2001 From: Ken Eucker Date: Thu, 28 Dec 2023 11:31:46 -0800 Subject: [PATCH 1/2] fix(sanity): resolves issues in getting specific game achievements and settings also adds an enum for game settings by key --- examples/node/index.js | 7 ++++--- package-lock.json | 4 ++-- package.json | 2 +- src/client.ts | 36 ++++++++++++++++++++++++++++++++-- src/common/data.ts | 1 + src/common/enums.ts | 4 ++++ src/sanity/getAchievements.ts | 37 +++++++++++++++++++++++++++++------ src/sanity/getSettings.ts | 4 ++-- src/sanity/helpers.ts | 34 ++++++++++++++++++++++++++++++++ 9 files changed, 113 insertions(+), 16 deletions(-) diff --git a/examples/node/index.js b/examples/node/index.js index 1e59322..00409e4 100644 --- a/examples/node/index.js +++ b/examples/node/index.js @@ -101,7 +101,7 @@ const queueTagAsync = async (pre, client, out = false, opts = {}) => { const get10TagsAsync = async (pre, client, out = false, opts = {}) => { opts.limit = opts.limit ? opts.limit : 10 const tags = await client.getTags(undefined, opts).catch(console.error) - log(`${pre} :: successfully retrieved tags data`, tags, out) + log(`${pre} :: successfully retrieved 10 tags data`, tags, out) console.log(tags[0]) return tags @@ -188,7 +188,7 @@ const runTests = async (out = false) => { await get10PlayersAsync("BikeTag", biketagDefaultInstance, out) } - if (false) { + if (bikeTagImgurInstance) { console.log(pretty("Imgur BikeTag Client Instantiated"), imgurInstanceOpts) await getGameAsync("Imgur", bikeTagImgurInstance, out) // await getTag1Async("Imgur", bikeTagImgurInstance, out) @@ -207,7 +207,8 @@ const runTests = async (out = false) => { await getGameAsync("Sanity", bikeTagSanityInstance, out) // await getAllGamesAsync("Sanity", bikeTagSanityInstance, out) await get10PlayersAsync("Sanity", bikeTagSanityInstance, out) - await get1PlayerAsync("Sanity", bikeTagSanityInstance, out) + // await get1PlayerAsync("Sanity", bikeTagSanityInstance, out) + await get10AchievementsAsync("Sanity", bikeTagSanityInstance, out) // await get10AmbassadorsAsync("Sanity", bikeTagSanityInstance, out) // await get10SettingsAsync("Sanity", bikeTagSanityInstance, out) // await get10AchievementsAsync("Sanity", bikeTagSanityInstance, out) diff --git a/package-lock.json b/package-lock.json index 260d05b..71e2d0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "biketag", - "version": "3.2.3", + "version": "3.2.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "biketag", - "version": "3.2.3", + "version": "3.2.4", "license": "AGPL-3.0-or-later", "dependencies": { "@sanity/client": "2.25.1-feature-image-file-input-refactor.150", diff --git a/package.json b/package.json index 9fe3615..a58d85c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "biketag", - "version": "3.2.3", + "version": "3.2.4", "description": "The Javascript client API for BikeTag Games", "main": "./biketag.node.js", "browser": "./biketag.js", diff --git a/src/client.ts b/src/client.ts index c01c2f0..14ac081 100644 --- a/src/client.ts +++ b/src/client.ts @@ -234,6 +234,14 @@ export class BikeTagClient extends EventEmitter { options.slug = options.slug ?? options.game?.toLowerCase() ?? undefined break + case DataTypes.achievement: + options.game = options.game ?? options.slug ?? this.biketagConfig?.game + break + + case DataTypes.setting: + options.game = options.game ?? options.slug ?? this.biketagConfig?.game + break + case DataTypes.player: options.game = options.game ? options.game : this.biketagConfig?.game @@ -1383,10 +1391,22 @@ export class BikeTagClient extends EventEmitter { opts, DataTypes.setting ) - const clientMethod = api.getAchievement + let clientMethod = api.getAchievement /// If the client adapter implements a direct way to retrieve a single setting if (clientMethod) { + switch (options.source) { + case AvailableApis.sanity: + clientMethod = clientMethod.bind({ + getGame: this.getPassthroughApiMethod( + api.getGame, + client, + DataTypes.game + ), + }) + break + } + return clientMethod(client, options).catch((e) => { return { status: HttpStatusCode.InternalServerError, @@ -1426,9 +1446,21 @@ export class BikeTagClient extends EventEmitter { DataTypes.achievement, 'getAchievements' ) - const clientMethod = api.getAchievements + let clientMethod = api.getAchievements if (clientMethod) { + switch (options.source) { + case AvailableApis.sanity: + clientMethod = clientMethod.bind({ + getGame: this.getPassthroughApiMethod( + api.getGame, + client, + DataTypes.game + ), + }) + break + } + return clientMethod(client, options).catch((e) => { return Promise.resolve({ status: HttpStatusCode.InternalServerError, diff --git a/src/common/data.ts b/src/common/data.ts index ce512a8..55ff131 100644 --- a/src/common/data.ts +++ b/src/common/data.ts @@ -1,5 +1,6 @@ import { Tag, Game, Player, Ambassador, Setting, Achievement } from './schema' +/// TODO: make an enum and put into the enums export const cacheKeys = { sanityUrlText: `sanity::`, imageHashText: `hash::`, diff --git a/src/common/enums.ts b/src/common/enums.ts index f43b141..0a39210 100644 --- a/src/common/enums.ts +++ b/src/common/enums.ts @@ -22,6 +22,10 @@ export enum DataTypes { achievement, } +export enum GameSettingsKeys { + achievementsEnabled = 'achievements::enabled', +} + export enum Errors { NotImplemented = 'method not implemented for adapter: ', } diff --git a/src/sanity/getAchievements.ts b/src/sanity/getAchievements.ts index c81acc2..f35ec3a 100644 --- a/src/sanity/getAchievements.ts +++ b/src/sanity/getAchievements.ts @@ -1,8 +1,13 @@ import { SanityClient } from '@sanity/client' import { BikeTagApiResponse } from '../common/types' -import { AvailableApis, HttpStatusCode, DataTypes } from '../common/enums' import { - constructPlayerFromSanityObject, + AvailableApis, + HttpStatusCode, + DataTypes, + GameSettingsKeys, +} from '../common/enums' +import { + constructAchievementFromSanityObject, constructSanityDocumentQuery, constructSanityFieldsQuery, } from './helpers' @@ -22,18 +27,38 @@ export async function getAchievements( const fieldsFilter = payload.fields?.length ? payload.fields : [] const query = constructSanityDocumentQuery( DataTypes[DataTypes.achievement], - payload.game, + // payload.game, // don't include the game here because we are not specifically assigning achievements to a game + undefined, payload.player, payload.slugs, undefined, fields ) - return client.fetch(query, {}).then((achievementsData) => { - const achievements = achievementsData.map((achievement: any) => - constructPlayerFromSanityObject(achievement, fieldsFilter) + return client.fetch(query, {}).then(async (achievementsData) => { + let achievements = achievementsData.map((achievement: any) => + constructAchievementFromSanityObject(achievement, fieldsFilter) ) + if (payload.game?.length) { + const game = (await this.getGame(payload.game)).data + if (game) { + const achievementsEnabled = + game.settings[GameSettingsKeys.achievementsEnabled] + + if (!achievementsEnabled || achievementsEnabled === 'false') { + achievements = [] + } else if (achievementsEnabled && achievementsEnabled !== 'true') { + const enabledAchievements = achievementsEnabled.split(',') + achievements = achievements.filter((a) => + enabledAchievements.includes(a.key) + ) + } else { + // first do nothing + } + } + } + const response = { data: sortAchievements(achievements, payload.sort, payload.limit), status: HttpStatusCode.Found, diff --git a/src/sanity/getSettings.ts b/src/sanity/getSettings.ts index 99065e3..ff87287 100644 --- a/src/sanity/getSettings.ts +++ b/src/sanity/getSettings.ts @@ -2,7 +2,7 @@ import { SanityClient } from '@sanity/client' import { BikeTagApiResponse } from '../common/types' import { AvailableApis, HttpStatusCode, DataTypes } from '../common/enums' import { - constructPlayerFromSanityObject, + constructSettingFromSanityObject, constructSanityDocumentQuery, constructSanityFieldsQuery, } from './helpers' @@ -27,7 +27,7 @@ export async function getSettings( return client.fetch(query, {}).then((settingsData) => { const settings = settingsData.map((setting: any) => - constructPlayerFromSanityObject(setting, fieldsFilter) + constructSettingFromSanityObject(setting, fieldsFilter) ) const response = { diff --git a/src/sanity/helpers.ts b/src/sanity/helpers.ts index baf6f43..093703e 100644 --- a/src/sanity/helpers.ts +++ b/src/sanity/helpers.ts @@ -16,12 +16,14 @@ import { playerDataObjectFields, playerDataArrayFields, createPlayerObject, + createAchievementObject, ambassadorDataReferenceFields, createAmbassadorObject, settingDataFields, ambassadorDataFields, gameDataAssetFields, playerDataAssetFields, + createSettingObject, } from '../common/data' import { DataTypes } from '../common/enums' @@ -393,6 +395,38 @@ export function constructAmbassadorFromSanityObject( return createAmbassadorObject(ambassadorData) } +export function constructSettingFromSanityObject( + data: any, + fields: string[] = [] +): any { + const settingData = fields.length + ? fields.reduce((o: any, f: any) => { + o[f] = data[f] + return o + }, {}) + : data + + settingData.slug = settingData.slug?.current ?? settingData.slug + + return createSettingObject(settingData) +} + +export function constructAchievementFromSanityObject( + data: any, + fields: string[] = [] +): any { + const achievementData = fields.length + ? fields.reduce((o: any, f: any) => { + o[f] = data[f] + return o + }, {}) + : data + + achievementData.slug = achievementData.slug?.current ?? achievementData.slug + + return createAchievementObject(achievementData) +} + export function constructSanityDocumentQuery( docType: string, game?: string, From f0c290eb1021bcc00447acfe925b1d675e96e05d Mon Sep 17 00:00:00 2001 From: Ken Eucker Date: Thu, 28 Dec 2023 12:19:37 -0800 Subject: [PATCH 2/2] fix(sanity): now returning null from getPlayer when a name is passed in and doesn't match a player this bug was found in testing before production --- examples/node/index.js | 11 ++++++----- src/sanity/getPlayers.ts | 8 +++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/examples/node/index.js b/examples/node/index.js index 00409e4..3808d6a 100644 --- a/examples/node/index.js +++ b/examples/node/index.js @@ -130,7 +130,7 @@ const getAllGamesAsync = async (pre, client, out = false, opts = {}) => { const get1PlayerAsync = async (pre, client, out = false, opts = {}) => { opts.limit = opts.limit ? opts.limit : 10 - const testPlayerData = await client.getPlayer('player-test', opts).catch(console.error) + const testPlayerData = await client.getPlayer('Ken', opts).catch(console.error) log(`${pre} :: success fully retrieved player data`, testPlayerData, out) return testPlayerData @@ -188,7 +188,8 @@ const runTests = async (out = false) => { await get10PlayersAsync("BikeTag", biketagDefaultInstance, out) } - if (bikeTagImgurInstance) { + if (false) { + // if (bikeTagImgurInstance) { console.log(pretty("Imgur BikeTag Client Instantiated"), imgurInstanceOpts) await getGameAsync("Imgur", bikeTagImgurInstance, out) // await getTag1Async("Imgur", bikeTagImgurInstance, out) @@ -206,9 +207,9 @@ const runTests = async (out = false) => { // await get10TagsAsync("Sanity", bikeTagSanityInstance, out) await getGameAsync("Sanity", bikeTagSanityInstance, out) // await getAllGamesAsync("Sanity", bikeTagSanityInstance, out) - await get10PlayersAsync("Sanity", bikeTagSanityInstance, out) - // await get1PlayerAsync("Sanity", bikeTagSanityInstance, out) - await get10AchievementsAsync("Sanity", bikeTagSanityInstance, out) + // await get10PlayersAsync("Sanity", bikeTagSanityInstance, out) + await get1PlayerAsync("Sanity", bikeTagSanityInstance, out) + // await get10AchievementsAsync("Sanity", bikeTagSanityInstance, out) // await get10AmbassadorsAsync("Sanity", bikeTagSanityInstance, out) // await get10SettingsAsync("Sanity", bikeTagSanityInstance, out) // await get10AchievementsAsync("Sanity", bikeTagSanityInstance, out) diff --git a/src/sanity/getPlayers.ts b/src/sanity/getPlayers.ts index 10ce995..4f0846d 100644 --- a/src/sanity/getPlayers.ts +++ b/src/sanity/getPlayers.ts @@ -30,10 +30,16 @@ export async function getPlayers( ) return client.fetch(query, {}).then((players) => { - const playersData = players.map((player: any) => + let playersData = players.map((player: any) => constructPlayerFromSanityObject(player, fieldsFilter) ) + if (payload.slugs?.length) { + playersData = playersData.filter((p) => payload.slugs?.includes(p.slug)) + } else if (payload.names?.length) { + playersData = playersData.filter((p) => payload.names?.includes(p.name)) + } + const response = { data: sortPlayers(playersData, payload.sort, payload.limit), status: HttpStatusCode.Found,