From 311a6874a4c2170bf2eacf64801aa0b12641a6a6 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Thu, 5 Oct 2023 10:25:52 -0500 Subject: [PATCH 1/6] Query on status or success --- .../image-thumbnail/src/Diviner/Query.ts | 2 +- .../src/Payload/ImageThumbnailResult.ts | 3 ++- .../packages/diviner/src/Diviner/Diviner.ts | 18 ++++++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/plugins/packages/payload/packages/image-thumbnail/src/Diviner/Query.ts b/packages/plugins/packages/payload/packages/image-thumbnail/src/Diviner/Query.ts index 7a053ae9841..4a912e517d9 100644 --- a/packages/plugins/packages/payload/packages/image-thumbnail/src/Diviner/Query.ts +++ b/packages/plugins/packages/payload/packages/image-thumbnail/src/Diviner/Query.ts @@ -7,6 +7,6 @@ export type ImageThumbnailDivinerQuerySchema = `${ImageThumbnailDivinerSchema}.q export const ImageThumbnailDivinerQuerySchema: ImageThumbnailDivinerQuerySchema = `${ImageThumbnailDivinerSchema}.query` export type ImageThumbnailDivinerQuery = Pick & - Payload<{ status?: boolean; url: string }, ImageThumbnailDivinerQuerySchema> + Payload<{ status?: number; success?: boolean; url: string }, ImageThumbnailDivinerQuerySchema> export const isImageThumbnailDivinerQuery = isPayloadOfSchemaType(ImageThumbnailDivinerQuerySchema) diff --git a/packages/plugins/packages/payload/packages/image-thumbnail/src/Payload/ImageThumbnailResult.ts b/packages/plugins/packages/payload/packages/image-thumbnail/src/Payload/ImageThumbnailResult.ts index c54c5dc5f78..aacb9565f37 100644 --- a/packages/plugins/packages/payload/packages/image-thumbnail/src/Payload/ImageThumbnailResult.ts +++ b/packages/plugins/packages/payload/packages/image-thumbnail/src/Payload/ImageThumbnailResult.ts @@ -7,7 +7,8 @@ export type ImageThumbnailResultIndexSchema = typeof ImageThumbnailResultIndexSc export interface ImageThumbnailResultInfo { sources: string[] - status: boolean + status?: number + success: boolean timestamp: number url: string } diff --git a/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/Diviner.ts b/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/Diviner.ts index ae07c2d920d..a02037be255 100644 --- a/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/Diviner.ts +++ b/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/Diviner.ts @@ -16,6 +16,7 @@ import { ImageThumbnailDivinerParams, ImageThumbnailResult, ImageThumbnailResultIndexSchema, + ImageThumbnailResultInfo, ImageThumbnailSchema, isImageThumbnail, isImageThumbnailDivinerQuery, @@ -37,7 +38,7 @@ type ConfigStore = Extract /** * The fields that will need to be indexed on in the underlying store */ -type QueryableImageThumbnailResultProperties = Extract +type QueryableImageThumbnailResultProperties = Extract /** * The query that will be used to retrieve the results from the underlying store @@ -129,11 +130,11 @@ export class ImageThumbnailDiviner { const { sourceUrl: url } = thumbnailPayload const { timestamp } = timestampPayload - const status = thumbnailPayload.http?.status ? true : false + const status = thumbnailPayload.http?.status + const success = thumbnailPayload.http?.status ? true : false const sources = [boundWitnessHash, thumbnailHash, timestampHash] - const result = new PayloadBuilder({ schema: ImageThumbnailResultIndexSchema }) - .fields({ sources, status, timestamp, url }) - .build() + const fields = status ? { sources, status, success, timestamp, url } : { sources, success, timestamp, url } + const result = new PayloadBuilder({ schema: ImageThumbnailResultIndexSchema }).fields(fields).build() return result }, ) @@ -168,13 +169,14 @@ export class ImageThumbnailDiviner { - const { limit: payloadLimit, offset: payloadOffset, order: payloadOrder, status: payloadStatus, url } = payload + const { limit: payloadLimit, offset: payloadOffset, order: payloadOrder, status: payloadStatus, success: payloadSuccess, url } = payload const limit = payloadLimit ?? 1 const order = payloadOrder ?? 'desc' const offset = payloadOffset ?? 0 - const status = payloadStatus ?? true + const status = payloadStatus ?? 200 + const success = payloadSuccess ?? true const query = new PayloadBuilder({ schema: PayloadDivinerQuerySchema }) - .fields({ limit, offset, order, status, url }) + .fields({ limit, offset, order, status, success, url }) .build() return await diviner.divine([query]) }), From fb4020b0f72ebecdd7451d4f158ff4eb691d3374 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Thu, 5 Oct 2023 10:37:52 -0500 Subject: [PATCH 2/6] Add tests for filter criteria --- .../diviner/src/Diviner/spec/Diviner.spec.ts | 82 +++++++++++++------ 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/spec/Diviner.spec.ts b/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/spec/Diviner.spec.ts index 082f98c538d..4f6cfbc23b6 100644 --- a/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/spec/Diviner.spec.ts +++ b/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/spec/Diviner.spec.ts @@ -15,27 +15,32 @@ describe('ImageThumbnailDiviner', () => { const indexArchivistName = 'indexArchivist' const stateArchivistName = 'stateArchivist' const thumbnailArchivistName = 'thumbnailArchivist' - - const successfulThumbnail = { - _timestamp: 1692812290173, + const sourceUrl = 'https://placekitten.com/200/300' + const thumbnailHttpSuccess = { http: { status: 200, }, schema: 'network.xyo.image.thumbnail', sourceHash: '7f39363514d9d9b958a5a993edeba35cb44f912c7072ed9ddd628728ac0fd681', - sourceUrl: 'https://assets.otherside.xyz/kodas/pfp_full/1200px/3724.webp', - url: 'data:image/png;base64,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', + sourceUrl, + url: 'data:image/png;base64,===', } - const failedThumbnail = { - _hash: 'e6696f95dfb0df7d9f5054d93d2a5d2bb77ed2763d3b8b2581bcde448d9d7757', - _timestamp: 1692812925775, + const thumbnailHttpFail = { http: { ipAddress: '104.17.96.13', status: 429, }, schema: 'network.xyo.image.thumbnail', - sourceUrl: 'https://cloudflare-ipfs.com/ipfs/QmbFBUR8y9Xhwv7nQdiy1Xafyxu3i8BaFThts2UyxM73Ge', + sourceUrl, + } + + const thumbnailWitnessFail = { + http: { + ipAddress: '104.17.96.13', + }, + schema: 'network.xyo.image.thumbnail', + sourceUrl, } let sut: ImageThumbnailDiviner @@ -69,11 +74,24 @@ describe('ImageThumbnailDiviner', () => { }) // Insert previously witnessed payloads into thumbnail archivist - const successfulTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: Date.now() } - const [successfulBoundWitness, successfulPayloads] = await new BoundWitnessBuilder().payloads([successfulThumbnail, successfulTimestamp]).build() - const failedTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: Date.now() } - const [failedBoundWitness, failedPayloads] = await new BoundWitnessBuilder().payloads([failedThumbnail, failedTimestamp]).build() - await thumbnailArchivist.insert([successfulBoundWitness, ...successfulPayloads, failedBoundWitness, ...failedPayloads]) + const httpSuccessTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: Date.now() } + const [httpSuccessBoundWitness, httpSuccessPayloads] = await new BoundWitnessBuilder() + .payloads([thumbnailHttpSuccess, httpSuccessTimestamp]) + .build() + const httpFailTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: Date.now() } + const [httpFailBoundWitness, httpFailPayloads] = await new BoundWitnessBuilder().payloads([thumbnailHttpFail, httpFailTimestamp]).build() + const witnessFailTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: Date.now() } + const [witnessFailBoundWitness, witnessFailPayloads] = await new BoundWitnessBuilder() + .payloads([thumbnailWitnessFail, witnessFailTimestamp]) + .build() + await thumbnailArchivist.insert([ + httpSuccessBoundWitness, + ...httpSuccessPayloads, + httpFailBoundWitness, + ...httpFailPayloads, + witnessFailBoundWitness, + ...witnessFailPayloads, + ]) const thumbnailStore: SearchableStorage = { archivist: thumbnailArchivist.address, @@ -151,19 +169,37 @@ describe('ImageThumbnailDiviner', () => { await delay(1000) }) describe('with no thumbnail for the provided URL', () => { + const url = 'https://does.not.exist.io' + const schema = ImageThumbnailDivinerQuerySchema it('returns nothing', async () => { - const payload: ImageThumbnailDivinerQuery = { schema: ImageThumbnailDivinerQuerySchema, url: 'https://does.not.exist.io' } - const result = await sut.divine([payload]) + const query: ImageThumbnailDivinerQuery = { schema, url } + const result = await sut.divine([query]) expect(result).toBeArrayOfSize(0) }) }) - describe('with successful thumbnail for the provided URL', () => { - it('returns the most recent success', async () => { - const payload: ImageThumbnailDivinerQuery = { schema: ImageThumbnailDivinerQuerySchema, url: successfulThumbnail.sourceUrl } - const result = await sut.divine([payload]) - expect(result).toBeArrayOfSize(1) - const expected = await PayloadHasher.hashAsync(successfulThumbnail) - expect(result[0]?.sources).toContain(expected) + describe('with thumbnails for the provided URL', () => { + const url = sourceUrl + const schema = ImageThumbnailDivinerQuerySchema + describe('with no filter criteria', () => { + it('returns the most recent success', async () => { + const query: ImageThumbnailDivinerQuery = { schema, url } + const result = await sut.divine([query]) + expect(result).toBeArrayOfSize(1) + const expected = await PayloadHasher.hashAsync(thumbnailHttpSuccess) + expect(result[0]?.sources).toContain(expected) + }) + }) + describe('with filter criteria', () => { + describe('for status code', () => { + it.each([thumbnailHttpSuccess, thumbnailHttpFail])('returns the most recent instance of that status code', async (payload) => { + const { status } = payload.http + const query: ImageThumbnailDivinerQuery = { schema, status, url } + const result = await sut.divine([query]) + expect(result).toBeArrayOfSize(1) + const expected = await PayloadHasher.hashAsync(payload) + expect(result[0]?.sources).toContain(expected) + }) + }) }) }) }) From f650d3a8573aa9842b1feebe50c893c5d8f9c549 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Thu, 5 Oct 2023 11:59:07 -0500 Subject: [PATCH 3/6] Only filter on status code if success was not supplied or is true --- .../packages/diviner/src/Diviner/Diviner.ts | 10 ++++++---- .../diviner/src/Diviner/spec/Diviner.spec.ts | 14 +++++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/Diviner.ts b/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/Diviner.ts index a02037be255..e821bdb0572 100644 --- a/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/Diviner.ts +++ b/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/Diviner.ts @@ -173,11 +173,13 @@ export class ImageThumbnailDiviner({ schema: PayloadDivinerQuerySchema }) - .fields({ limit, offset, order, status, success, url }) - .build() + const fields: Partial = { limit, offset, order, success, url } + // Only filter on status code if success was not supplied or is true + if (payloadSuccess !== undefined && payloadSuccess === true) { + fields.status = payloadStatus ?? 200 + } + const query = new PayloadBuilder({ schema: PayloadDivinerQuerySchema }).fields(fields).build() return await diviner.divine([query]) }), ) diff --git a/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/spec/Diviner.spec.ts b/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/spec/Diviner.spec.ts index 4f6cfbc23b6..73f37d94840 100644 --- a/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/spec/Diviner.spec.ts +++ b/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/spec/Diviner.spec.ts @@ -191,7 +191,8 @@ describe('ImageThumbnailDiviner', () => { }) describe('with filter criteria', () => { describe('for status code', () => { - it.each([thumbnailHttpSuccess, thumbnailHttpFail])('returns the most recent instance of that status code', async (payload) => { + const cases = [thumbnailHttpSuccess, thumbnailHttpFail] + it.each(cases)('returns the most recent instance of that status code', async (payload) => { const { status } = payload.http const query: ImageThumbnailDivinerQuery = { schema, status, url } const result = await sut.divine([query]) @@ -200,6 +201,17 @@ describe('ImageThumbnailDiviner', () => { expect(result[0]?.sources).toContain(expected) }) }) + describe('for success', () => { + const cases = [thumbnailHttpFail, thumbnailWitnessFail] + it.each(cases)('returns the most recent instance of that success state', async (payload) => { + const success = (payload?.http as { status?: number })?.status ? true : false + const query: ImageThumbnailDivinerQuery = { schema, success, url } + const result = await sut.divine([query]) + expect(result).toBeArrayOfSize(1) + const expected = await PayloadHasher.hashAsync(payload) + expect(result[0]?.sources).toContain(expected) + }) + }) }) }) }) From b00aea55cccbce3b8b57768705b174493cc00093 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Thu, 5 Oct 2023 12:00:08 -0500 Subject: [PATCH 4/6] Use offset if supplied --- .../payload/packages/memory/src/MemoryPayloadDiviner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/modules/packages/diviner/packages/payload/packages/memory/src/MemoryPayloadDiviner.ts b/packages/modules/packages/diviner/packages/payload/packages/memory/src/MemoryPayloadDiviner.ts index 8eff83003e2..108037e3487 100644 --- a/packages/modules/packages/diviner/packages/payload/packages/memory/src/MemoryPayloadDiviner.ts +++ b/packages/modules/packages/diviner/packages/payload/packages/memory/src/MemoryPayloadDiviner.ts @@ -30,7 +30,7 @@ export class MemoryPayloadDiviner { const allPairs = await Promise.all( From 2feef8dd4c67e2ddd342dbc13ba241312eefe76c Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Thu, 5 Oct 2023 12:04:46 -0500 Subject: [PATCH 5/6] Simplify filtering edge cases --- .../packages/diviner/src/Diviner/Diviner.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/Diviner.ts b/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/Diviner.ts index e821bdb0572..4a7c4ab23bd 100644 --- a/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/Diviner.ts +++ b/packages/plugins/packages/payloadset/packages/image-thumbnail/packages/diviner/src/Diviner/Diviner.ts @@ -175,10 +175,10 @@ export class ImageThumbnailDiviner = { limit, offset, order, success, url } - // Only filter on status code if success was not supplied or is true - if (payloadSuccess !== undefined && payloadSuccess === true) { - fields.status = payloadStatus ?? 200 - } + // Default to filtering on 200 status code if success was not supplied + if (payloadSuccess === undefined) fields.status = payloadStatus ?? 200 + // If success is true and status was supplied, use it + if (success === true && payloadStatus !== undefined) fields.status = payloadStatus const query = new PayloadBuilder({ schema: PayloadDivinerQuerySchema }).fields(fields).build() return await diviner.divine([query]) }), From 23348052a58ea66c3bb13df1b6aeedfad3f43380 Mon Sep 17 00:00:00 2001 From: Joel Carter Date: Thu, 5 Oct 2023 12:09:59 -0500 Subject: [PATCH 6/6] Don't assert exact collection counts --- .../witness/src/lib/spec/getNftCollectionCount.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugins/packages/payloadset/packages/crypto/packages/nft/packages/collection/witness/src/lib/spec/getNftCollectionCount.spec.ts b/packages/plugins/packages/payloadset/packages/crypto/packages/nft/packages/collection/witness/src/lib/spec/getNftCollectionCount.spec.ts index 93240b26de6..071cb8de440 100644 --- a/packages/plugins/packages/payloadset/packages/crypto/packages/nft/packages/collection/witness/src/lib/spec/getNftCollectionCount.spec.ts +++ b/packages/plugins/packages/payloadset/packages/crypto/packages/nft/packages/collection/witness/src/lib/spec/getNftCollectionCount.spec.ts @@ -19,6 +19,6 @@ describeIf(process.env.INFURA_PROJECT_ID)('getNftCollectionCount', () => { it.each(cases)('gets NFTs owned by the address', async (address, chainId, expected) => { const result = await getNftCollectionCount(address, chainId, privateKey) expect(result).toBeNumber() - expect(result).toEqual(expected) + expect(result).toBeGreaterThanOrEqual(expected) }) })