diff --git a/.github/workflows/branch-deploy.yml b/.github/workflows/branch-deploy.yml index 87e2dae5..01564e7f 100644 --- a/.github/workflows/branch-deploy.yml +++ b/.github/workflows/branch-deploy.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: github/branch-deploy@v9 + - uses: github/branch-deploy@v10 id: branch-deploy with: admins: the-hideout/core-contributors diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 25dd5041..855c92db 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -13,11 +13,12 @@ jobs: runs-on: ubuntu-latest outputs: # set outputs for use in downstream jobs continue: ${{ steps.deployment-check.outputs.continue }} + sha: ${{ steps.deployment-check.outputs.sha }} steps: # https://github.com/github/branch-deploy/blob/d3c24bd92505e623615b75ffdfac5ed5259adbdb/docs/merge-commit-strategy.md - name: deployment check - uses: github/branch-deploy@v9 + uses: github/branch-deploy@v10 id: deployment-check with: merge_deploy_mode: "true" @@ -32,6 +33,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v4 + with: + ref: ${{ needs.deployment-check.outputs.sha }} - name: setup node uses: actions/setup-node@v4 diff --git a/.github/workflows/unlock-on-merge.yml b/.github/workflows/unlock-on-merge.yml index c12844d1..56a7c65e 100644 --- a/.github/workflows/unlock-on-merge.yml +++ b/.github/workflows/unlock-on-merge.yml @@ -16,7 +16,7 @@ jobs: steps: - name: unlock on merge - uses: github/branch-deploy@v9 + uses: github/branch-deploy@v10 id: unlock-on-merge with: unlock_on_merge_mode: "true" # <-- indicates that this is the "Unlock on Merge Mode" workflow diff --git a/datasources/handbook.mjs b/datasources/handbook.mjs new file mode 100644 index 00000000..c35ad00a --- /dev/null +++ b/datasources/handbook.mjs @@ -0,0 +1,100 @@ +import WorkerKV from '../utils/worker-kv.mjs'; + +class HandbookAPI extends WorkerKV { + constructor(dataSource) { + super('handbook_data', dataSource); + //this.gameModes.push('pve'); + } + + async getCategory(context, info, id) { + const { cache } = await this.getCache(context, info); + return cache.ItemCategory[id] || cache.HandbookCategory[id]; + } + + async getTopCategory(context, info, id) { + const cat = await this.getCategory(context, info, id); + if (cat && cat.parent_id) return this.getTopCategory(context, info, cat.parent_id); + return cat; + } + + async getCategories(context, info) { + const { cache } = await this.getCache(context, info); + if (!cache) { + return Promise.reject(new Error('Item cache is empty')); + } + const categories = []; + for (const id in cache.ItemCategory) { + categories.push(cache.ItemCategory[id]); + } + return categories; + } + + async getCategoriesEnum(context, info) { + const cats = await this.getCategories(context, info); + const map = {}; + for (const id in cats) { + map[cats[id].enumName] = cats[id]; + } + return map; + } + + async getHandbookCategory(context, info, id) { + const { cache } = await this.getCache(context, info); + return cache.HandbookCategory[id]; + } + + async getHandbookCategories(context, info) { + const { cache } = await this.getCache(context, info); + if (!cache) { + return Promise.reject(new Error('Item cache is empty')); + } + return Object.values(cache.HandbookCategory); + } + + async getArmorMaterials(context, info) { + const { cache } = await this.getCache(context, info); + return Object.values(cache.ArmorMaterial).sort(); + } + + async getArmorMaterial(context, info, matKey) { + const { cache } = await this.getCache(context, info); + return cache.ArmorMaterial[matKey]; + } + + async getMasterings(context, info) { + const { cache } = await this.getCache(context, info); + return cache.Mastering; + } + + async getMastering(context, info, mastId) { + const { cache } = await this.getCache(context, info); + return cache.Mastering.find(m => m.id === mastId); + } + + async getSkills(context, info) { + const { cache } = await this.getCache(context, info); + return cache.Skill; + } + + async getSkill(context, info, skillId) { + const { cache } = await this.getCache(context, info); + return cache.Skill.find(s => s.id === skillId); + } + + async getPlayerLevels(context, info) { + const { cache } = await this.getCache(context, info); + return cache.PlayerLevel; + } + + async getAllItemProperties(context, info) { + const { cache } = await this.getCache(context, info); + return cache.ItemProperties; + } + + async getItemProperties(context, info, itemId) { + const { cache } = await this.getCache(context, info); + return cache.ItemProperties[itemId]; + } +} + +export default HandbookAPI; diff --git a/datasources/index.mjs b/datasources/index.mjs index 3a7886d9..0b236f36 100644 --- a/datasources/index.mjs +++ b/datasources/index.mjs @@ -1,5 +1,6 @@ import BartersAPI from './barters.mjs'; import CraftsAPI from './crafts.mjs'; +import HandbookAPI from './handbook.mjs'; import HideoutAPI from './hideout.mjs'; import HistoricalPricesAPI from './historical-prices.mjs'; import ArchivedPricesAPI from './archived-prices.mjs'; @@ -23,6 +24,7 @@ class DataSource { this.worker = { barter: new BartersAPI(this), craft: new CraftsAPI(this), + handbook: new HandbookAPI(this), hideout: new HideoutAPI(this), historicalPrice: new HistoricalPricesAPI(this), archivedPrice: new ArchivedPricesAPI(this), diff --git a/datasources/items.mjs b/datasources/items.mjs index aa6522d8..149a7173 100644 --- a/datasources/items.mjs +++ b/datasources/items.mjs @@ -170,7 +170,7 @@ class ItemsAPI extends WorkerKV { if (!items) { items = Object.values(cache.Item); } - const categories = (await this.getCategories(context, info)).filter(cat => names.includes(cat.enumName)); + const categories = (await context.data.worker.handbook.getCategories(context, info)).filter(cat => names.includes(cat.enumName)); return items.filter((item) => { return item.categories.some(catId => categories.some(cat => cat.id === catId)); }); @@ -181,7 +181,7 @@ class ItemsAPI extends WorkerKV { if (!items) { items = Object.values(cache.Item); } - const categories = (await this.getHandbookCategories(context, info)).filter(cat => names.includes(cat.enumName)); + const categories = (await context.data.worker.handbook.getHandbookCategories(context, info)).filter(cat => names.includes(cat.enumName)); return items.filter((item) => { return item.handbookCategories.some(catId => categories.some(cat => cat.id === catId)); }); @@ -216,108 +216,24 @@ class ItemsAPI extends WorkerKV { }); } - async getCategory(context, info, id) { - const { cache } = await this.getCache(context, info); - return cache.ItemCategory[id] || cache.HandbookCategory[id]; - } - - async getTopCategory(context, info, id) { - const cat = await this.getCategory(context, info, id); - if (cat && cat.parent_id) return this.getTopCategory(context, info, cat.parent_id); - return cat; - } - - async getCategories(context, info) { - const { cache } = await this.getCache(context, info); - if (!cache) { - return Promise.reject(new Error('Item cache is empty')); - } - const categories = []; - for (const id in cache.ItemCategory) { - categories.push(cache.ItemCategory[id]); - } - return categories; - } - - async getCategoriesEnum(context, info) { - const cats = await this.getCategories(context, info); - const map = {}; - for (const id in cats) { - map[cats[id].enumName] = cats[id]; - } - return map; - } - - async getHandbookCategory(context, info, id) { - const { cache } = await this.getCache(context, info); - return cache.HandbookCategory[id]; - } - - async getHandbookCategories(context, info) { - const { cache } = await this.getCache(context, info); - if (!cache) { - return Promise.reject(new Error('Item cache is empty')); - } - return Object.values(cache.HandbookCategory); - } - async getFleaMarket(context, info) { const { cache } = await this.getCache(context, info); return cache.FleaMarket; } - async getArmorMaterials(context, info) { - const { cache } = await this.getCache(context, info); - return Object.values(cache.ArmorMaterial).sort(); - } - - async getArmorMaterial(context, info, matKey) { - const { cache } = await this.getCache(context, info); - return cache.ArmorMaterial[matKey]; - } - - async getMasterings(context, info) { - const { cache } = await this.getCache(context, info); - return cache.Mastering; - } - - async getMastering(context, info, mastId) { - const { cache } = await this.getCache(context, info); - return cache.Mastering.find(m => m.id === mastId); - } - - async getSkills(context, info) { - const { cache } = await this.getCache(context, info); - return cache.Skill; - } - - async getSkill(context, info, skillId) { - const { cache } = await this.getCache(context, info); - return cache.Skill.find(s => s.id === skillId); - } - async getAmmoList(context, info) { const allAmmo = await this.getItemsByBsgCategoryId(context, info, '5485a8684bdc2da71d8b4567').then(ammoItems => { // ignore bb return ammoItems.filter(item => item.id !== '6241c316234b593b5676b637'); }); + const itemProperties = await context.data.worker.handbook.getAllItemProperties(context, info); return allAmmo.map(item => { return { ...item, - ...item.properties + ...itemProperties[item.id], }; }); } - - async getPlayerLevels(context, info) { - const { cache } = await this.getCache(context, info); - return cache.PlayerLevel; - } - - async getTypes(context, info) { - const { cache } = await this.getCache(context, info); - return cache.ItemType; - } } export default ItemsAPI; diff --git a/resolvers/hideoutResolver.mjs b/resolvers/hideoutResolver.mjs index 7fc0429b..f5c65589 100644 --- a/resolvers/hideoutResolver.mjs +++ b/resolvers/hideoutResolver.mjs @@ -50,7 +50,7 @@ export default { return context.data.worker.hideout.getLocale(data.name, context, info); }, skill(data, args, context, info) { - return context.data.worker.item.getSkill(context, info, data.name); + return context.data.worker.handbook.getSkill(context, info, data.name); }, }, HideoutModule: { diff --git a/resolvers/itemResolver.mjs b/resolvers/itemResolver.mjs index dcc6c900..dad50e35 100644 --- a/resolvers/itemResolver.mjs +++ b/resolvers/itemResolver.mjs @@ -53,10 +53,10 @@ export default { return context.util.paginate(items, args); }, itemCategories(obj, args, context, info) { - return context.util.paginate(context.data.worker.item.getCategories(context, info), args); + return context.util.paginate(context.data.worker.handbook.getCategories(context, info), args); }, handbookCategories(obj, args, context, info) { - return context.util.paginate(context.data.worker.item.getHandbookCategories(context, info), args); + return context.util.paginate(context.data.worker.handbook.getHandbookCategories(context, info), args); }, itemsByIDs(obj, args, context, info) { return context.data.worker.item.getItemsByIDs(context, info, args.ids, false); @@ -73,6 +73,16 @@ export default { itemsByBsgCategoryId(obj, args, context, info) { return context.data.worker.item.getItemsByBsgCategoryId(context, info, args.bsgCategoryId); }, + async itemPrices(obj, args, context, info) { + const [ + historical, + archived, + ] = await Promise.all([ + context.data.worker.historicalPrice.getByItemId(context, info, args.id, 30), + context.data.worker.archivedPrice.getByItemId(context, info, args.id), + ]); + return context.util.paginate([...archived, ...historical], args); + }, historicalItemPrices(obj, args, context, info) { return context.util.paginate(context.data.worker.historicalPrice.getByItemId(context, info, args.id, args.days), args); }, @@ -80,19 +90,19 @@ export default { return context.util.paginate(context.data.worker.archivedPrice.getByItemId(context, info, args.id), args); }, armorMaterials(obj, args, context, info) { - return context.data.worker.item.getArmorMaterials(context, info); + return context.data.worker.handbook.getArmorMaterials(context, info); }, fleaMarket(obj, args, context, info) { return context.data.worker.item.getFleaMarket(context, info); }, mastering(obj, args, context, info) { - return context.data.worker.item.getMasterings(context, info); + return context.data.worker.handbook.getMasterings(context, info); }, playerLevels(obj, args, context, info) { - return context.data.worker.item.getPlayerLevels(context, info); + return context.data.worker.handbook.getPlayerLevels(context, info); }, skills(obj, args, context, info) { - return context.data.worker.item.getSkills(context, info); + return context.data.worker.handbook.getSkills(context, info); }, }, Item: { @@ -113,25 +123,25 @@ export default { ]; }, bsgCategory(data, args, context, info) { - if (data.bsgCategoryId) return context.data.worker.item.getCategory(context, info, data.bsgCategoryId); + if (data.bsgCategoryId) return context.data.worker.handbook.getCategory(context, info, data.bsgCategoryId); return null; }, category(data, args, context, info) { - if (data.bsgCategoryId) return context.data.worker.item.getCategory(context, info, data.bsgCategoryId); + if (data.bsgCategoryId) return context.data.worker.handbook.getCategory(context, info, data.bsgCategoryId); return null; }, categoryTop(data, args, context, info) { - if (data.bsgCategoryId) return context.data.worker.item.getTopCategory(context, info, data.bsgCategoryId); + if (data.bsgCategoryId) return context.data.worker.handbook.getTopCategory(context, info, data.bsgCategoryId); return null; }, categories(data, args, context, info) { return data.categories.map(id => { - return context.data.worker.item.getCategory(context, info, id); + return context.data.worker.handbook.getCategory(context, info, id); }); }, handbookCategories(data, args, context, info) { return data.handbookCategories.map(id => { - return context.data.worker.item.getCategory(context, info, id); + return context.data.worker.handbook.getCategory(context, info, id); }); }, async conflictingItems(data, args, context, info) { @@ -215,6 +225,9 @@ export default { }, imageLinkFallback(data) { return data.inspectImageLink; + }, + properties(data, args, context, info) { + return context.data.worker.handbook.getItemProperties(context, info, data.id); } }, ItemArmorSlot: { @@ -224,24 +237,22 @@ export default { } }, ItemArmorSlotLocked: { - name(data) { - if (data.name) return data.name; - return data.type; + name(data, args, context, info) { + return context.data.worker.handbook.getLocale(data.name, context, info); }, zones(data, args, context, info) { - return context.data.worker.item.getLocale(data.zones, context, info); + return context.data.worker.handbook.getLocale(data.zones, context, info); }, material(data, args, context, info) { - return context.data.worker.item.getArmorMaterial(context, info, data.armor_material_id); + return context.data.worker.handbook.getArmorMaterial(context, info, data.armor_material_id); }, }, ItemArmorSlotOpen: { - name(data) { - if (data.name) return data.name; - return data.type; + name(data, args, context, info) { + return context.data.worker.handbook.getLocale(data.name, context, info); }, zones(data, args, context, info) { - return context.data.worker.item.getLocale(data.zones, context, info); + return context.data.worker.handbook.getLocale(data.zones, context, info); }, allowedPlates(data, args, context, info) { return data.allowedPlates.map(id => context.data.worker.item.getItem(context, info, id)); @@ -259,25 +270,25 @@ export default { }, ItemCategory: { name(data, args, context, info) { - return context.data.worker.item.getLocale(data.name, context, info); + return context.data.worker.handbook.getLocale(data.name, context, info); }, parent(data, args, context, info) { - if (data.parent_id) return context.data.worker.item.getCategory(context, info, data.parent_id); + if (data.parent_id) return context.data.worker.handbook.getCategory(context, info, data.parent_id); return null; }, children(data, args, context, info) { - return data.child_ids.map(id => context.data.worker.item.getCategory(context, info, id)); + return data.child_ids.map(id => context.data.worker.handbook.getCategory(context, info, id)); } }, ItemFilters: { allowedCategories(data, args, context, info) { - return data.allowedCategories.map(id => context.data.worker.item.getCategory(context, info, id)); + return data.allowedCategories.map(id => context.data.worker.handbook.getCategory(context, info, id)); }, allowedItems(data, args, context, info) { return data.allowedItems.map(id => context.data.worker.item.getItem(context, info, id)); }, excludedCategories(data, args, context, info) { - return data.excludedCategories.map(id => context.data.worker.item.getCategory(context, info, id)); + return data.excludedCategories.map(id => context.data.worker.handbook.getCategory(context, info, id)); }, excludedItems(data, args, context, info) { return data.excludedItems.map(id => context.data.worker.item.getItem(context, info, id)); @@ -296,24 +307,24 @@ export default { }, ItemPropertiesArmor: { armorType(data, args, context, info) { - return context.data.worker.item.getLocale(data.armorType, context, info); + return context.data.worker.handbook.getLocale(data.armorType, context, info); }, material(data, args, context, info) { - return context.data.worker.item.getArmorMaterial(context, info, data.armor_material_id); + return context.data.worker.handbook.getArmorMaterial(context, info, data.armor_material_id); }, zones(data, args, context, info) { - return context.data.worker.item.getLocale(data.zones, context, info); + return context.data.worker.handbook.getLocale(data.zones, context, info); }, }, ItemPropertiesArmorAttachment: { material(data, args, context, info) { - return context.data.worker.item.getArmorMaterial(context, info, data.armor_material_id); + return context.data.worker.handbook.getArmorMaterial(context, info, data.armor_material_id); }, headZones(data, args, context, info) { - return context.data.worker.item.getLocale(data.headZones, context, info); + return context.data.worker.handbook.getLocale(data.headZones, context, info); }, zones(data, args, context, info) { - return context.data.worker.item.getLocale(data.headZones, context, info); + return context.data.worker.handbook.getLocale(data.headZones, context, info); } }, ItemPropertiesBackpack: { @@ -323,13 +334,13 @@ export default { }, ItemPropertiesChestRig: { armorType(data, args, context, info) { - return context.data.worker.item.getLocale(data.armorType, context, info); + return context.data.worker.handbook.getLocale(data.armorType, context, info); }, material(data, args, context, info) { - return context.data.worker.item.getArmorMaterial(context, info, data.armor_material_id); + return context.data.worker.handbook.getArmorMaterial(context, info, data.armor_material_id); }, zones(data, args, context, info) { - return context.data.worker.item.getLocale(data.zones, context, info); + return context.data.worker.handbook.getLocale(data.zones, context, info); }, pouches(data) { return data.grids; @@ -337,18 +348,18 @@ export default { }, ItemPropertiesGlasses: { material(data, args, context, info) { - return context.data.worker.item.getArmorMaterial(context, info, data.armor_material_id); + return context.data.worker.handbook.getArmorMaterial(context, info, data.armor_material_id); }, }, ItemPropertiesHelmet: { armorType(data, args, context, info) { - return context.data.worker.item.getLocale(data.armorType, context, info); + return context.data.worker.handbook.getLocale(data.armorType, context, info); }, material(data, args, context, info) { - return context.data.worker.item.getArmorMaterial(context, info, data.armor_material_id); + return context.data.worker.handbook.getArmorMaterial(context, info, data.armor_material_id); }, headZones(data, args, context, info) { - return context.data.worker.item.getLocale(data.headZones, context, info); + return context.data.worker.handbook.getLocale(data.headZones, context, info); } }, ItemPropertiesMagazine: { @@ -367,7 +378,7 @@ export default { return context.data.worker.item.getItem(context, info, data.default_ammo_id); }, fireModes(data, args, context, info) { - return context.data.worker.item.getLocale(data.fireModes, context, info); + return context.data.worker.handbook.getLocale(data.fireModes, context, info); }, allowedAmmo(data, args, context, info) { return data.allowedAmmo.map(id => context.data.worker.item.getItem(context, info, id)); @@ -382,7 +393,7 @@ export default { }, ItemSlot: { name(data, ags, context, info) { - return context.data.worker.item.getLocale(data.name, context, info); + return context.data.worker.handbook.getLocale(data.name, context, info); } }, ContainedItem: { @@ -396,7 +407,7 @@ export default { }, ArmorMaterial: { name(data, args, context, info) { - return context.data.worker.item.getLocale(data.name, context, info); + return context.data.worker.handbook.getLocale(data.name, context, info); } }, FleaMarket: { @@ -419,18 +430,18 @@ export default { }, Skill: { name(data, args, context, info) { - return context.data.worker.item.getLocale(data.name, context, info); + return context.data.worker.handbook.getLocale(data.name, context, info); } }, StimEffect: { type(data, args, context, info) { - return context.data.worker.item.getLocale(data.type, context, info); + return context.data.worker.handbook.getLocale(data.type, context, info); }, skill(data, args, context, info) { - return context.data.worker.item.getSkill(context, info, data.skillName); + return context.data.worker.handbook.getSkill(context, info, data.skillName); }, skillName(data, args, context, info) { - return context.data.worker.item.getLocale(data.skillName, context, info); + return context.data.worker.handbook.getLocale(data.skillName, context, info); } }, Vendor: { diff --git a/resolvers/taskResolver.mjs b/resolvers/taskResolver.mjs index 11428097..ca9bedee 100644 --- a/resolvers/taskResolver.mjs +++ b/resolvers/taskResolver.mjs @@ -61,7 +61,7 @@ export default { }, SkillLevel: { skill(data, args, context, info) { - return context.data.worker.item.getSkill(context, info, data.name); + return context.data.worker.handbook.getSkill(context, info, data.name); }, name(data, args, context, info) { return context.data.worker.task.getLocale(data.name, context, info); @@ -155,7 +155,7 @@ export default { }, containsCategory(data, args, context, info) { return data.containsCategory.map((cat) => { - return context.data.worker.item.getCategory(context, info, cat.id); + return context.data.worker.handbook.getCategory(context, info, cat.id); }); }, containsOne(data, args, context, info) { diff --git a/schema-static.mjs b/schema-static.mjs index ba3e4def..3edf7ff0 100644 --- a/schema-static.mjs +++ b/schema-static.mjs @@ -1383,6 +1383,7 @@ type Query { historicalItemPrices(id: ID!, days: Int, lang: LanguageCode, gameMode: GameMode, limit: Int, offset: Int): [historicalPricePoint]! item(id: ID, normalizedName: String, lang: LanguageCode, gameMode: GameMode): Item items(ids: [ID], name: String, names: [String], type: ItemType, types: [ItemType], categoryNames: [ItemCategoryName], handbookCategoryNames: [HandbookCategoryName] bsgCategoryId: String, bsgCategoryIds: [String], bsgCategory: String, lang: LanguageCode, gameMode: GameMode, limit: Int, offset: Int): [Item]! + itemPrices(id: ID!, gameMode: GameMode, limit: Int, offset: Int): [historicalPricePoint]! itemCategories(lang: LanguageCode, limit: Int, offset: Int): [ItemCategory]! goonReports(lang: LanguageCode, gameMode: GameMode, limit: Int, ofset: Int): [GoonReport]! handbookCategories(lang: LanguageCode, limit: Int, offset: Int): [ItemCategory]! diff --git a/script/ci/Tarkov.dev.postman_collection.json b/script/ci/Tarkov.dev.postman_collection.json index 869b976b..03fb77ce 100644 --- a/script/ci/Tarkov.dev.postman_collection.json +++ b/script/ci/Tarkov.dev.postman_collection.json @@ -515,7 +515,6 @@ "\r", "pm.test(\"Has correct data\", function () {\r", " pm.expect(pm.response.json().data.historicalItemPrices).to.be.a(\"array\");\r", - " pm.expect(pm.response.json().data.historicalItemPrices.length).to.be.above(0);\r", "});\r", "\r", "pm.test(\"No errors\", function () {\r",