Skip to content

Commit

Permalink
IMN-798 Fix voucher lifespan in token-generation readmodel (#1177)
Browse files Browse the repository at this point in the history
Co-authored-by: shuyec <[email protected]>
  • Loading branch information
taglioni-r and shuyec authored Nov 19, 2024
1 parent 1d1987f commit ec0ad3e
Show file tree
Hide file tree
Showing 6 changed files with 516 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ describe("integration tests V1 events", async () => {
])
);
});
it("should update the entry if the incoming version is more recent than existing table entry", async () => {
it("should update the entry if the incoming version is more recent than the existing table entry", async () => {
const agreement: Agreement = {
...getMockAgreement(),
state: agreementState.active,
Expand Down
41 changes: 40 additions & 1 deletion packages/catalog-platformstate-writer/src/consumerServiceV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
readCatalogEntry,
updateDescriptorStateInPlatformStatesEntry,
updateDescriptorStateInTokenGenerationStatesTable,
updateDescriptorVoucherLifespanInPlatformStateEntry,
updateDescriptorVoucherLifespanInTokenGenerationStatesTable,
writeCatalogEntry,
} from "./utils.js";

Expand Down Expand Up @@ -178,6 +180,44 @@ export async function handleMessageV2(
dynamoDBClient
);
})
.with({ type: "EServiceDescriptorQuotasUpdated" }, async (msg) => {
const { eservice, descriptor } = parseEServiceAndDescriptor(
msg.data.eservice,
unsafeBrandId(msg.data.descriptorId),
message.type
);
const primaryKey = makePlatformStatesEServiceDescriptorPK({
eserviceId: eservice.id,
descriptorId: descriptor.id,
});
const catalogEntry = await readCatalogEntry(primaryKey, dynamoDBClient);

if (!catalogEntry || catalogEntry.version > msg.version) {
return Promise.resolve();
} else {
if (
descriptor.voucherLifespan !== catalogEntry.descriptorVoucherLifespan
) {
await updateDescriptorVoucherLifespanInPlatformStateEntry(
dynamoDBClient,
primaryKey,
descriptor.voucherLifespan,
msg.version
);

// token-generation-states
const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({
eserviceId: eservice.id,
descriptorId: descriptor.id,
});
await updateDescriptorVoucherLifespanInTokenGenerationStatesTable(
eserviceId_descriptorId,
descriptor.voucherLifespan,
dynamoDBClient
);
}
}
})
.with(
{ type: "EServiceDeleted" },
{ type: "EServiceAdded" },
Expand All @@ -186,7 +226,6 @@ export async function handleMessageV2(
{ type: "EServiceDescriptorAdded" },
{ type: "EServiceDraftDescriptorDeleted" },
{ type: "EServiceDraftDescriptorUpdated" },
{ type: "EServiceDescriptorQuotasUpdated" },
{ type: "EServiceDescriptorInterfaceAdded" },
{ type: "EServiceDescriptorDocumentAdded" },
{ type: "EServiceDescriptorInterfaceUpdated" },
Expand Down
125 changes: 125 additions & 0 deletions packages/catalog-platformstate-writer/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,39 @@ export const updateDescriptorStateInPlatformStatesEntry = async (
await dynamoDBClient.send(command);
};

export const updateDescriptorVoucherLifespanInPlatformStateEntry = async (
dynamoDBClient: DynamoDBClient,
primaryKey: PlatformStatesEServiceDescriptorPK,
voucherLifespan: number,
version: number
): Promise<void> => {
const input: UpdateItemInput = {
ConditionExpression: "attribute_exists(PK)",
Key: {
PK: {
S: primaryKey,
},
},
ExpressionAttributeValues: {
":newVoucherLifespan": {
N: voucherLifespan.toString(),
},
":newVersion": {
N: version.toString(),
},
":newUpdateAt": {
S: new Date().toISOString(),
},
},
UpdateExpression:
"SET descriptorVoucherLifespan = :newVoucherLifespan, version = :newVersion, updatedAt = :newUpdateAt",
TableName: config.tokenGenerationReadModelTableNamePlatform,
ReturnValues: "NONE",
};
const command = new UpdateItemCommand(input);
await dynamoDBClient.send(command);
};

export const updateDescriptorStateInTokenGenerationStatesTable = async (
eserviceId_descriptorId: GSIPKEServiceIdDescriptorId,
descriptorState: ItemState,
Expand Down Expand Up @@ -217,6 +250,67 @@ export const updateDescriptorStateInTokenGenerationStatesTable = async (
);
};

export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable =
async (
eserviceId_descriptorId: GSIPKEServiceIdDescriptorId,
voucherLifespan: number,
dynamoDBClient: DynamoDBClient
): Promise<void> => {
const runPaginatedQuery = async (
eserviceId_descriptorId: GSIPKEServiceIdDescriptorId,
dynamoDBClient: DynamoDBClient,
exclusiveStartKey?: Record<string, AttributeValue>
): Promise<void> => {
const input: QueryInput = {
TableName: config.tokenGenerationReadModelTableNameTokenGeneration,
IndexName: "Descriptor",
KeyConditionExpression: `GSIPK_eserviceId_descriptorId = :gsiValue`,
ExpressionAttributeValues: {
":gsiValue": { S: eserviceId_descriptorId },
},
ExclusiveStartKey: exclusiveStartKey,
};
const command = new QueryCommand(input);
const data: QueryCommandOutput = await dynamoDBClient.send(command);

if (!data.Items) {
throw genericInternalError(
`Unable to read token state entries: result ${JSON.stringify(data)} `
);
} else {
const unmarshalledItems = data.Items.map((item) => unmarshall(item));

const tokenStateEntries = z
.array(TokenGenerationStatesClientPurposeEntry)
.safeParse(unmarshalledItems);

if (!tokenStateEntries.success) {
throw genericInternalError(
`Unable to parse token state entry item: result ${JSON.stringify(
tokenStateEntries
)} - data ${JSON.stringify(data)} `
);
}

await updateDescriptorVoucherLifespanInTokenGenerationStatesEntries(
voucherLifespan,
dynamoDBClient,
tokenStateEntries.data
);

if (data.LastEvaluatedKey) {
await runPaginatedQuery(
eserviceId_descriptorId,
dynamoDBClient,
data.LastEvaluatedKey
);
}
}
};

await runPaginatedQuery(eserviceId_descriptorId, dynamoDBClient, undefined);
};

const updateDescriptorStateEntriesInTokenGenerationStatesTable = async (
descriptorState: ItemState,
dynamoDBClient: DynamoDBClient,
Expand Down Expand Up @@ -247,3 +341,34 @@ const updateDescriptorStateEntriesInTokenGenerationStatesTable = async (
await dynamoDBClient.send(command);
}
};

const updateDescriptorVoucherLifespanInTokenGenerationStatesEntries = async (
voucherLifespan: number,
dynamoDBClient: DynamoDBClient,
entriesToUpdate: TokenGenerationStatesClientPurposeEntry[]
): Promise<void> => {
for (const entry of entriesToUpdate) {
const input: UpdateItemInput = {
ConditionExpression: "attribute_exists(GSIPK_eserviceId_descriptorId)",
Key: {
PK: {
S: entry.PK,
},
},
ExpressionAttributeValues: {
":newVoucherLifespan": {
N: voucherLifespan.toString(),
},
":newUpdateAt": {
S: new Date().toISOString(),
},
},
UpdateExpression:
"SET descriptorVoucherLifespan = :newVoucherLifespan, updatedAt = :newUpdateAt",
TableName: config.tokenGenerationReadModelTableNameTokenGeneration,
ReturnValues: "NONE",
};
const command = new UpdateItemCommand(input);
await dynamoDBClient.send(command);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { writeTokenStateEntry } from "pagopa-interop-commons-test";
import { handleMessageV1 } from "../src/consumerServiceV1.js";
import { readCatalogEntry, writeCatalogEntry } from "../src/utils.js";
import { config, sleep } from "./utils.js";
import { config } from "./utils.js";
describe("V1 events", async () => {
if (!config) {
fail();
Expand Down Expand Up @@ -124,7 +124,6 @@ describe("V1 events", async () => {
await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient);

await handleMessageV1(message, dynamoDBClient);
await sleep(1000, mockDate);

const primaryKey = makePlatformStatesEServiceDescriptorPK({
eserviceId: eservice.id,
Expand Down Expand Up @@ -169,7 +168,7 @@ describe("V1 events", async () => {
])
);
});
it("(suspended -> published) should update the entry if incoming version is more recent than existing table entry", async () => {
it("(suspended -> published) should update the entry if the incoming version is more recent than the existing table entry", async () => {
const publishedDescriptor: Descriptor = {
...getMockDescriptor(),
audience: ["pagopa.it/test1", "pagopa.it/test2"],
Expand Down Expand Up @@ -244,7 +243,6 @@ describe("V1 events", async () => {
await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient);

await handleMessageV1(message, dynamoDBClient);
await sleep(1000, mockDate);

const retrievedCatalogEntry = await readCatalogEntry(
primaryKey,
Expand Down Expand Up @@ -358,7 +356,6 @@ describe("V1 events", async () => {
await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient);

await handleMessageV1(message, dynamoDBClient);
await sleep(1000, mockDate);

const retrievedCatalogEntry = await readCatalogEntry(
catalogPrimaryKey,
Expand All @@ -381,7 +378,7 @@ describe("V1 events", async () => {
});

describe("(published -> suspended)", () => {
it("should update the entry if msg.version >= existing version", async () => {
it("should update the entry if the incoming version is more recent than the existing table entry", async () => {
const suspendedDescriptor: Descriptor = {
...getMockDescriptor(),
audience: ["pagopa.it/test1", "pagopa.it/test2"],
Expand Down Expand Up @@ -458,7 +455,6 @@ describe("V1 events", async () => {
};
await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient);
await handleMessageV1(message, dynamoDBClient);
await sleep(1000, mockDate);

const retrievedEntry = await readCatalogEntry(
primaryKey,
Expand Down Expand Up @@ -497,7 +493,7 @@ describe("V1 events", async () => {
);
});

it("should do no operation if msg.version < existing version", async () => {
it("should do no operation if the existing table entry is more recent", async () => {
const suspendedDescriptor: Descriptor = {
...getMockDescriptor(),
audience: ["pagopa.it/test1", "pagopa.it/test2"],
Expand Down Expand Up @@ -574,7 +570,6 @@ describe("V1 events", async () => {
await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient);

await handleMessageV1(message, dynamoDBClient);
await sleep(1000, mockDate);

const retrievedEntry = await readCatalogEntry(
primaryKey,
Expand Down Expand Up @@ -716,7 +711,6 @@ describe("V1 events", async () => {
await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient);

await handleMessageV1(message, dynamoDBClient);
await sleep(1000, mockDate);

const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient);
expect(retrievedEntry).toBeUndefined();
Expand Down
Loading

0 comments on commit ec0ad3e

Please sign in to comment.