From ead1100c29d4c14a51154b57dc2bbe46055b9ce9 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Sun, 3 Dec 2023 14:40:30 -0600 Subject: [PATCH 1/4] Match generic API response type to witness type --- packages/payloadset/packages/api/src/Payload.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/payloadset/packages/api/src/Payload.ts b/packages/payloadset/packages/api/src/Payload.ts index 95c63d2bf..0179d21e6 100644 --- a/packages/payloadset/packages/api/src/Payload.ts +++ b/packages/payloadset/packages/api/src/Payload.ts @@ -1,5 +1,5 @@ import { Hash } from '@xylabs/hex' -import { AsObjectFactory } from '@xyo-network/object' +import { AsObjectFactory, JsonArray, JsonObject } from '@xyo-network/object' import { isPayloadOfSchemaType, Payload } from '@xyo-network/payload-model' export const ApiCallSchema = 'network.xyo.api.call' @@ -38,7 +38,7 @@ export interface HttpMeta { status?: number } -export type ApiCallJsonResult = Payload< +export type ApiCallJsonResult = Payload< { call: Hash contentType: 'application/json' From 6ec21e215b5bbdd61c5d0e282b94ba02f7544f3d Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Sun, 3 Dec 2023 15:08:57 -0600 Subject: [PATCH 2/4] Allow more strongly typed JSON responses --- packages/payloadset/packages/api/src/Payload.ts | 9 +++++++-- packages/payloadset/packages/api/src/Witness.ts | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/payloadset/packages/api/src/Payload.ts b/packages/payloadset/packages/api/src/Payload.ts index 0179d21e6..3b0ca2354 100644 --- a/packages/payloadset/packages/api/src/Payload.ts +++ b/packages/payloadset/packages/api/src/Payload.ts @@ -38,7 +38,9 @@ export interface HttpMeta { status?: number } -export type ApiCallJsonResult = Payload< +export type ApiCallJsonResultType = JsonArray | JsonObject + +export type ApiCallJsonResult = Payload< { call: Hash contentType: 'application/json' @@ -64,7 +66,10 @@ export type ApiCallErrorResult = Payload< ApiCallResultSchema > -export type ApiCallResult = ApiCallBase64Result | ApiCallJsonResult | ApiCallErrorResult +export type ApiCallResult = + | ApiCallBase64Result + | ApiCallJsonResult + | ApiCallErrorResult export const isApiCall = isPayloadOfSchemaType(ApiCallSchema) export const asApiCall = AsObjectFactory.create(isApiCall) diff --git a/packages/payloadset/packages/api/src/Witness.ts b/packages/payloadset/packages/api/src/Witness.ts index a7a88f003..dc5883ef4 100644 --- a/packages/payloadset/packages/api/src/Witness.ts +++ b/packages/payloadset/packages/api/src/Witness.ts @@ -3,7 +3,6 @@ import { Axios, AxiosError, AxiosJson } from '@xylabs/axios' import { Hash } from '@xylabs/hex' import { AbstractWitness } from '@xyo-network/abstract-witness' import { PayloadHasher } from '@xyo-network/hash' -import { JsonArray, JsonObject } from '@xyo-network/object' import { isPayloadOfSchemaType } from '@xyo-network/payload-model' import { WitnessParams } from '@xyo-network/witness-model' import { fromByteArray } from 'base64-js' @@ -16,6 +15,7 @@ import { ApiCallBase64Result, ApiCallErrorResult, ApiCallJsonResult, + ApiCallJsonResultType, ApiCallResult, ApiCallResultSchema, ApiCallSchema, @@ -116,7 +116,7 @@ export class ApiCallWitness(url) + const response = await axios.get(url) if (response.status >= 200 && response.status < 300) { const jsonResult = result as ApiCallJsonResult jsonResult.data = response.data From 2e24e5cefbc0f2e175ba596d076ff2180edc5ad1 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Sun, 3 Dec 2023 15:09:48 -0600 Subject: [PATCH 3/4] Use specific response type in tests --- cspell.json | 1 + .../api/src/spec/opensea.nft-call.spec.ts | 65 +++++++++++++++++-- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/cspell.json b/cspell.json index a8d935375..392450dfc 100644 --- a/cspell.json +++ b/cspell.json @@ -58,6 +58,7 @@ "Merkle", "microether", "milliether", + "offchain", "pako", "payloadset", "Promisable", diff --git a/packages/payloadset/packages/api/src/spec/opensea.nft-call.spec.ts b/packages/payloadset/packages/api/src/spec/opensea.nft-call.spec.ts index b5d0ad09b..0c317ea04 100644 --- a/packages/payloadset/packages/api/src/spec/opensea.nft-call.spec.ts +++ b/packages/payloadset/packages/api/src/spec/opensea.nft-call.spec.ts @@ -1,5 +1,4 @@ /* eslint-disable max-statements */ - import { describeIf } from '@xylabs/jest-helpers' import { HDWallet } from '@xyo-network/account' import { ManifestWrapper, PackageManifestPayload } from '@xyo-network/manifest' @@ -8,7 +7,7 @@ import { isPayloadOfSchemaType } from '@xyo-network/payload-model' import { asSentinelInstance } from '@xyo-network/sentinel-model' import { asWitnessInstance } from '@xyo-network/witness-model' -import { ApiCallJsonResult, ApiCallResult, ApiCallResultSchema, ApiCallSchema, ApiUriTemplateCall } from '../Payload' +import { ApiCallJsonResult, ApiCallResultSchema, ApiCallSchema, ApiUriTemplateCall } from '../Payload' import { ApiCallWitness } from '../Witness' import openseaNftsManifest from './opensea.nft-call.json' @@ -19,6 +18,60 @@ describe('OpenSeaApi', () => { describeIf(apiKey)('report', () => { it('specifying address', async () => { + type OpenSeaNft = { + /* + * Collection slug. A unique string to identify a collection on OpenSea + */ + collection: string + /* + * The unique public blockchain identifier for the contract + */ + contract: string + /** + * @deprecated + */ + created_at: string + /* + * Description of the NFT + */ + description: string + /* + * The NFT's unique identifier within the smart contract (also referred to as token_id) + */ + identifier: string + /* + * Link to the image associated with the NFT + */ + image_url: string + /* + * If the item is currently able to be bought or sold using OpenSea + */ + is_disabled: boolean + /* + * If the item is currently classified as 'Not Safe for Work' by OpenSea as defined in OpenSea's NSFW Policy. + */ + is_nsfw: boolean + /* + * Link to the offchain metadata store + */ + metadata_url: string + /* + * Name of the NFT + */ + name: string + /* + * ERC standard of the token (erc721, erc1155) + */ + token_standard: string + /* + * Last time that the NFT's metadata was updated by OpenSea + */ + updated_at: string + } + type OpenSeaListNftsByAccountResponse = { + next: string + nfts: OpenSeaNft[] + } const mnemonic = 'later puppy sound rebuild rebuild noise ozone amazing hope broccoli crystal grief' const wallet = await HDWallet.fromPhrase(mnemonic) const locator = new ModuleFactoryLocator() @@ -51,11 +104,11 @@ describe('OpenSeaApi', () => { const report = await sentinel?.report([call]) - const apiCallResult = report?.find(isPayloadOfSchemaType(ApiCallResultSchema)) as ApiCallResult | undefined - + const apiCallResult = report?.find(isPayloadOfSchemaType>(ApiCallResultSchema)) + expect(apiCallResult).toBeDefined() expect(apiCallResult?.schema).toBeString() - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect((apiCallResult as ApiCallJsonResult)?.data.nfts).toBeArrayOfSize(1) + expect(apiCallResult?.data.nfts).toBeArrayOfSize(1) + expect(apiCallResult?.data.nfts[0].collection).toBeString() }) }) }) From 9b2d379b3bce89838af1dc8cb89b6371d46e7883 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Sun, 3 Dec 2023 15:15:02 -0600 Subject: [PATCH 4/4] Hoist types --- .../api/src/spec/opensea.nft-call.spec.ts | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/packages/payloadset/packages/api/src/spec/opensea.nft-call.spec.ts b/packages/payloadset/packages/api/src/spec/opensea.nft-call.spec.ts index 0c317ea04..bb01a4f6e 100644 --- a/packages/payloadset/packages/api/src/spec/opensea.nft-call.spec.ts +++ b/packages/payloadset/packages/api/src/spec/opensea.nft-call.spec.ts @@ -17,61 +17,61 @@ describe('OpenSeaApi', () => { const apiKey = process.env.OPENSEA_API_KEY describeIf(apiKey)('report', () => { + type OpenSeaNft = { + /* + * Collection slug. A unique string to identify a collection on OpenSea + */ + collection: string + /* + * The unique public blockchain identifier for the contract + */ + contract: string + /** + * @deprecated + */ + created_at: string + /* + * Description of the NFT + */ + description: string + /* + * The NFT's unique identifier within the smart contract (also referred to as token_id) + */ + identifier: string + /* + * Link to the image associated with the NFT + */ + image_url: string + /* + * If the item is currently able to be bought or sold using OpenSea + */ + is_disabled: boolean + /* + * If the item is currently classified as 'Not Safe for Work' by OpenSea as defined in OpenSea's NSFW Policy. + */ + is_nsfw: boolean + /* + * Link to the offchain metadata store + */ + metadata_url: string + /* + * Name of the NFT + */ + name: string + /* + * ERC standard of the token (erc721, erc1155) + */ + token_standard: string + /* + * Last time that the NFT's metadata was updated by OpenSea + */ + updated_at: string + } + type OpenSeaListNftsByAccountResponse = { + next: string + nfts: OpenSeaNft[] + } it('specifying address', async () => { - type OpenSeaNft = { - /* - * Collection slug. A unique string to identify a collection on OpenSea - */ - collection: string - /* - * The unique public blockchain identifier for the contract - */ - contract: string - /** - * @deprecated - */ - created_at: string - /* - * Description of the NFT - */ - description: string - /* - * The NFT's unique identifier within the smart contract (also referred to as token_id) - */ - identifier: string - /* - * Link to the image associated with the NFT - */ - image_url: string - /* - * If the item is currently able to be bought or sold using OpenSea - */ - is_disabled: boolean - /* - * If the item is currently classified as 'Not Safe for Work' by OpenSea as defined in OpenSea's NSFW Policy. - */ - is_nsfw: boolean - /* - * Link to the offchain metadata store - */ - metadata_url: string - /* - * Name of the NFT - */ - name: string - /* - * ERC standard of the token (erc721, erc1155) - */ - token_standard: string - /* - * Last time that the NFT's metadata was updated by OpenSea - */ - updated_at: string - } - type OpenSeaListNftsByAccountResponse = { - next: string - nfts: OpenSeaNft[] - } const mnemonic = 'later puppy sound rebuild rebuild noise ozone amazing hope broccoli crystal grief' const wallet = await HDWallet.fromPhrase(mnemonic) const locator = new ModuleFactoryLocator()