-
Notifications
You must be signed in to change notification settings - Fork 622
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: ensure old meta field values are returned (#4161)
- Loading branch information
Showing
15 changed files
with
270 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
163 changes: 163 additions & 0 deletions
163
...ages/api-headless-cms/__tests__/contentAPI/contentEntriesDeprecatedOnByMetaFields.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import { SecurityIdentity } from "@webiny/api-security/types"; | ||
import { useTestModelHandler } from "~tests/testHelpers/useTestModelHandler"; | ||
import { getDocumentClient } from "@webiny/project-utils/testing/dynamodb"; | ||
import { PutCommand, QueryCommand, unmarshall } from "@webiny/aws-sdk/client-dynamodb"; | ||
import { CmsGraphQLSchemaPlugin } from "@webiny/api-headless-cms/plugins"; | ||
|
||
const identityA: SecurityIdentity = { id: "a", type: "admin", displayName: "A" }; | ||
|
||
jest.mock("~/graphql/getSchema/generateCacheId", () => { | ||
return { | ||
generateCacheId: () => Date.now() | ||
}; | ||
}); | ||
|
||
describe("Content entries - Entry Meta Fields", () => { | ||
const { manage: manageApiIdentityA, read: readApiIdentityA } = useTestModelHandler({ | ||
identity: identityA | ||
}); | ||
|
||
beforeEach(async () => { | ||
await manageApiIdentityA.setup(); | ||
}); | ||
|
||
test("deprecated 'publishedOn' and 'ownedBy' GraphQL fields should still return values", async () => { | ||
const { data: testEntry } = await manageApiIdentityA.createTestEntry(); | ||
|
||
// Let's directly insert values for deprecated fields. | ||
const client = getDocumentClient(); | ||
|
||
// Not pretty, but this test will be removed anyway, in 5.41.0. | ||
if (process.env.WEBINY_STORAGE_OPS === "ddb") { | ||
const { Items: testEntryDdbRecords } = await client.send( | ||
new QueryCommand({ | ||
TableName: String(process.env.DB_TABLE), | ||
KeyConditionExpression: "PK = :PK AND SK > :SK", | ||
ExpressionAttributeValues: { | ||
":PK": { S: `T#root#L#en-US#CMS#CME#CME#${testEntry.entryId}` }, | ||
":SK": { S: " " } | ||
} | ||
}) | ||
); | ||
|
||
for (const testEntryDdbRecord of testEntryDdbRecords!) { | ||
await client.send( | ||
new PutCommand({ | ||
TableName: process.env.DB_TABLE, | ||
Item: { | ||
...unmarshall(testEntryDdbRecord), | ||
publishedOn: "2021-01-01T00:00:00.000Z", | ||
ownedBy: identityA | ||
} | ||
}) | ||
); | ||
} | ||
} else { | ||
const { Items: testEntryDdbRecords } = await client.send( | ||
new QueryCommand({ | ||
TableName: String(process.env.DB_TABLE), | ||
KeyConditionExpression: "PK = :PK AND SK > :SK", | ||
ExpressionAttributeValues: { | ||
":PK": { S: `T#root#L#en-US#CMS#CME#${testEntry.entryId}` }, | ||
":SK": { S: " " } | ||
} | ||
}) | ||
); | ||
|
||
for (const testEntryDdbRecord of testEntryDdbRecords!) { | ||
await client.send( | ||
new PutCommand({ | ||
TableName: process.env.DB_TABLE, | ||
Item: { | ||
...unmarshall(testEntryDdbRecord), | ||
publishedOn: "2021-01-01T00:00:00.000Z", | ||
ownedBy: identityA | ||
} | ||
}) | ||
); | ||
} | ||
} | ||
|
||
// Ensure values are visible when data is fetched via GraphQL. | ||
{ | ||
const { data: testEntryWithDeprecatedFields } = await manageApiIdentityA.getTestEntry({ | ||
revision: testEntry.id | ||
}); | ||
|
||
expect(testEntryWithDeprecatedFields).toMatchObject({ | ||
publishedOn: "2021-01-01T00:00:00.000Z", | ||
ownedBy: identityA | ||
}); | ||
} | ||
|
||
await manageApiIdentityA.publishTestEntry({ revision: testEntry.id }); | ||
|
||
{ | ||
const { data: testEntryWithDeprecatedFields } = await readApiIdentityA.getTestEntry({ | ||
where: { entryId: testEntry.entryId } | ||
}); | ||
|
||
expect(testEntryWithDeprecatedFields).toMatchObject({ | ||
publishedOn: "2021-01-01T00:00:00.000Z", | ||
ownedBy: identityA | ||
}); | ||
} | ||
}); | ||
|
||
test("deprecated 'publishedOn' and 'ownedBy' GraphQL fields should fall back to new fields if no value is present", async () => { | ||
const { data: testEntry } = await manageApiIdentityA.createTestEntry(); | ||
|
||
const { data: publishedTestEntry } = await manageApiIdentityA.publishTestEntry({ | ||
revision: testEntry.id | ||
}); | ||
|
||
expect(publishedTestEntry).toMatchObject({ | ||
publishedOn: null, | ||
ownedBy: null | ||
}); | ||
|
||
// Ensure values are visible when data is fetched via GraphQL. | ||
const customGqlResolvers = new CmsGraphQLSchemaPlugin({ | ||
resolvers: { | ||
TestEntry: { | ||
publishedOn: entry => { | ||
return entry.lastPublishedOn; | ||
}, | ||
ownedBy: entry => { | ||
return entry.createdBy; | ||
} | ||
} | ||
} | ||
}); | ||
|
||
customGqlResolvers.name = "cms-test-entry-meta-fields"; | ||
|
||
const { manage: manageApiWithGqlResolvers, read: readApiWithGqlResolvers } = | ||
useTestModelHandler({ | ||
identity: identityA, | ||
plugins: [customGqlResolvers] | ||
}); | ||
|
||
const { data: testEntryWithDeprecatedFields } = | ||
await manageApiWithGqlResolvers.getTestEntry({ | ||
revision: testEntry.id | ||
}); | ||
|
||
expect(testEntryWithDeprecatedFields).toMatchObject({ | ||
publishedOn: publishedTestEntry.lastPublishedOn, | ||
ownedBy: publishedTestEntry.createdBy | ||
}); | ||
|
||
const { data: readTestEntryWithDeprecatedFields } = | ||
await readApiWithGqlResolvers.getTestEntry({ | ||
where: { | ||
entryId: testEntry.entryId | ||
} | ||
}); | ||
|
||
expect(readTestEntryWithDeprecatedFields).toMatchObject({ | ||
publishedOn: publishedTestEntry.lastPublishedOn, | ||
ownedBy: publishedTestEntry.createdBy | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
packages/api-headless-cms/src/graphql/getSchema/generateCacheId.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { ApiEndpoint } from "~/types"; | ||
import { Tenant } from "@webiny/api-tenancy/types"; | ||
import { I18NLocale } from "@webiny/api-i18n/types"; | ||
|
||
interface GenerateCacheIdParams { | ||
type: ApiEndpoint; | ||
getTenant: () => Tenant; | ||
getLocale: () => I18NLocale; | ||
} | ||
|
||
export const generateCacheId = (params: GenerateCacheIdParams): string => { | ||
const { getTenant, type, getLocale } = params; | ||
return [`tenant:${getTenant().id}`, `endpoint:${type}`, `locale:${getLocale().code}`].join("#"); | ||
}; |
31 changes: 31 additions & 0 deletions
31
packages/api-headless-cms/src/graphql/getSchema/generateCacheKey.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { CmsModel } from "~/types"; | ||
import crypto from "crypto"; | ||
|
||
interface GenerateCacheKeyParams { | ||
models: Pick<CmsModel, "modelId" | "singularApiName" | "pluralApiName" | "savedOn">[]; | ||
} | ||
|
||
/** | ||
* Method generates cache key based on last model change time. | ||
* Or sets "unknown" - possible when no models in database. | ||
*/ | ||
export const generateCacheKey = async (params: GenerateCacheKeyParams): Promise<string> => { | ||
const { models } = params; | ||
|
||
const keys: string[] = []; | ||
for (const model of models) { | ||
const savedOn = model.savedOn; | ||
const value = | ||
// @ts-expect-error | ||
savedOn instanceof Date || savedOn?.toISOString | ||
? // @ts-expect-error | ||
savedOn.toISOString() | ||
: savedOn || "unknown"; | ||
keys.push(model.modelId, model.singularApiName, model.pluralApiName, value); | ||
} | ||
const key = keys.join("#"); | ||
|
||
const hash = crypto.createHash("sha1"); | ||
hash.update(key); | ||
return hash.digest("hex"); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.