Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IMN-657 GET EService descriptor by ID #712

Merged
merged 34 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a02dc84
Implements get Eservice's descriptor by id
Viktor-K Jul 1, 2024
b6d1ce8
Fix assertion on GetEserivcesDescriptorById
Viktor-K Jul 10, 2024
af35559
Fix types from API clients package
Viktor-K Jul 10, 2024
56a0277
remove wrong changes
Viktor-K Jul 10, 2024
b1ac408
Fix linting
Viktor-K Jul 10, 2024
8f82ffe
Merge branch 'main' into IMN-657-getEserviceDescriptorById
Viktor-K Jul 10, 2024
87effa8
Move converter to properly ApiConverter file
Viktor-K Jul 10, 2024
6b080a5
Fix linting
Viktor-K Jul 10, 2024
107daaa
Create getter help methods in catalog service
Viktor-K Jul 16, 2024
17be40e
Rename converter catalog API converter method
Viktor-K Jul 16, 2024
6006856
Fix var naming
Viktor-K Jul 16, 2024
33b8648
Fix toBffCatalogApiEserviceRiskAnalysis conversion in Bff tuility
Viktor-K Jul 16, 2024
392cb8c
Fix toBffCatalogApiEserviceRiskAnalysis conversion in Bff tuility
Viktor-K Jul 16, 2024
43848ef
uniform tenant client creation
Viktor-K Jul 17, 2024
480d433
Reduce unsafeBrandId usages
Viktor-K Jul 17, 2024
5bb768c
Minor fix rename variable name
Viktor-K Jul 17, 2024
dfba69b
Merge branch 'main' into IMN-657-getEserviceDescriptorById
Viktor-K Jul 17, 2024
6429d36
Fix linting
Viktor-K Jul 17, 2024
9cf1835
Merge branch 'main' into IMN-657-getEserviceDescriptorById
Viktor-K Jul 17, 2024
7dc73a2
Refactorize validators and mapper on catalogService
Viktor-K Jul 22, 2024
846effd
move agreement upgredeable veirification in mapper file
Viktor-K Jul 22, 2024
98a7bad
Move getTenant email to mappers
Viktor-K Jul 22, 2024
748d8c6
Add proper type during risk anlysis converter
Viktor-K Jul 22, 2024
9fe126b
Merge branch 'main' into IMN-657-getEserviceDescriptorById
Viktor-K Jul 23, 2024
9e9d46e
fix linting
Viktor-K Jul 23, 2024
3d974f2
Rename BFF validator
Viktor-K Jul 23, 2024
00e0562
Minor Fix : renaming function and vars
Viktor-K Jul 24, 2024
1b30f5a
Update packages/bff/src/model/modelMappingUtils.ts
Viktor-K Jul 24, 2024
28954da
Fix typo
Viktor-K Jul 24, 2024
d3d4657
Merge branch 'main' into IMN-657-getEserviceDescriptorById
Viktor-K Jul 24, 2024
5ba6040
Rename const variable
Viktor-K Jul 25, 2024
31c5dcd
IMN-656 get producer eservice detail by ID (#730)
Viktor-K Jul 25, 2024
11026a3
Add operative log
Viktor-K Jul 25, 2024
c41d3ec
Merge branch 'main' into IMN-657-getEserviceDescriptorById
Viktor-K Jul 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/api-clients/src/bffApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { QueryParametersByAlias } from "./utils.js";

type BffApi = typeof bffApi.eservicesApi.api;

export type GetCatalogQueryParam = QueryParametersByAlias<
export type BffGetCatalogQueryParam = QueryParametersByAlias<
BffApi,
"getEServicesCatalog"
>;
Expand Down
2 changes: 1 addition & 1 deletion packages/bff/src/model/api/apiConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function toDescriptorWithOnlyAttributes(
}

export function toEserviceCatalogProcessQueryParams(
queryParams: bffApi.GetCatalogQueryParam
queryParams: bffApi.BffGetCatalogQueryParam
): catalogApi.GetCatalogQueryParam {
return {
...queryParams,
Expand Down
219 changes: 219 additions & 0 deletions packages/bff/src/model/api/converters/catalogClientApiConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/* eslint-disable functional/immutable-data */
/* eslint-disable max-params */
import { DescriptorWithOnlyAttributes } from "pagopa-interop-agreement-lifecycle";
import {
agreementApi,
attributeRegistryApi,
bffApi,
catalogApi,
tenantApi,
} from "pagopa-interop-api-clients";
import { EServiceAttribute, unsafeBrandId } from "pagopa-interop-models";
import { attributeNotExists } from "../../domain/errors.js";
import { getTenantEmail, isUpgradable } from "../../modelMappingUtils.js";
import { catalogApiDescriptorState } from "../apiTypes.js";

export function toEserviceCatalogProcessQueryParams(
queryParams: bffApi.BffGetCatalogQueryParam
): catalogApi.GetCatalogQueryParam {
return {
...queryParams,
eservicesIds: [],
name: queryParams.q,
};
}

export function toBffCatalogApiEService(
eservice: catalogApi.EService,
producerTenant: tenantApi.Tenant,
hasCertifiedAttributes: boolean,
isRequesterEqProducer: boolean,
activeDescriptor?: catalogApi.EServiceDescriptor,
agreement?: agreementApi.Agreement
): bffApi.CatalogEService {
const partialEnhancedEservice = {
id: eservice.id,
name: eservice.name,
description: eservice.description,
producer: {
id: eservice.producerId,
name: producerTenant.name,
},
isMine: isRequesterEqProducer,
hasCertifiedAttributes,
};

return {
...partialEnhancedEservice,
...(activeDescriptor
? {
activeDescriptor: {
id: activeDescriptor.id,
version: activeDescriptor.version,
audience: activeDescriptor.audience,
state: activeDescriptor.state,
},
}
: {}),
...(agreement
? {
agreement: {
id: agreement.id,
state: agreement.state,
canBeUpgraded: isUpgradable(eservice, agreement),
},
}
: {}),
};
}

export function toBffCatalogApiDescriptorAttribute(
attributes: attributeRegistryApi.Attribute[],
descriptorAttributes: catalogApi.Attribute[]
): bffApi.DescriptorAttribute[] {
return descriptorAttributes.map((attribute) => {
const foundAttribute = attributes.find((att) => att.id === attribute.id);
if (!foundAttribute) {
throw attributeNotExists(unsafeBrandId(attribute.id));
}

return {
id: attribute.id,
name: foundAttribute.name,
description: foundAttribute.description,
explicitAttributeVerification: attribute.explicitAttributeVerification,
};
});
}

export function toBffCatalogApiDescriptorDoc(
document: catalogApi.EServiceDoc
): bffApi.EServiceDoc {
return {
id: document.id,
name: document.name,
contentType: document.contentType,
prettyName: document.prettyName,
};
}

export function toBffCatalogApiEserviceRiskAnalysis(
riskAnalysis: catalogApi.EServiceRiskAnalysis
): bffApi.EServiceRiskAnalysis {
const answers: bffApi.RiskAnalysisForm["answers"] =
riskAnalysis.riskAnalysisForm.singleAnswers
.concat(
riskAnalysis.riskAnalysisForm.multiAnswers.flatMap((multiAnswer) =>
multiAnswer.values.map((answerValue) => ({
id: multiAnswer.id,
value: answerValue,
key: multiAnswer.key,
}))
)
)
.reduce((answers: bffApi.RiskAnalysisForm["answers"], answer) => {
const key = answer.key;
if (answers[key] && answer.value) {
answers[key] = [...answers[key], answer.value];
} else {
answers[key] = [];
}

return answers;
}, {});

const riskAnalysisForm: bffApi.RiskAnalysisForm = {
riskAnalysisId: riskAnalysis.id,
version: riskAnalysis.riskAnalysisForm.version,
answers,
};

return {
id: riskAnalysis.id,
name: riskAnalysis.name,
createdAt: riskAnalysis.createdAt,
riskAnalysisForm,
};
}

export function toBffCatalogApiProducerDescriptorEService(
eservice: catalogApi.EService,
producer: tenantApi.Tenant
): bffApi.ProducerDescriptorEService {
const producerMail = getTenantEmail(producer);

const notDraftDecriptors: bffApi.CompactDescriptor[] =
eservice.descriptors.filter(
(d) => d.state !== catalogApiDescriptorState.DRAFT
);

const draftDescriptor: bffApi.CompactDescriptor | undefined =
eservice.descriptors.find(
(d) => d.state === catalogApiDescriptorState.DRAFT
);

return {
id: eservice.id,
name: eservice.name,
description: eservice.description,
technology: eservice.technology,
mode: eservice.mode,
mail: producerMail && {
address: producerMail.address,
description: producerMail.description,
},
draftDescriptor,
riskAnalysis: eservice.riskAnalysis.map(
toBffCatalogApiEserviceRiskAnalysis
),
descriptors: notDraftDecriptors,
};
}

export function toEserviceAttribute(
attributes: catalogApi.Attribute[]
): EServiceAttribute[] {
return attributes.map((attribute) => ({
...attribute,
id: unsafeBrandId(attribute.id),
}));
}

export function toDescriptorWithOnlyAttributes(
descriptor: catalogApi.EServiceDescriptor
): DescriptorWithOnlyAttributes {
return {
...descriptor,
attributes: {
certified: descriptor.attributes.certified.map(toEserviceAttribute),
declared: descriptor.attributes.declared.map(toEserviceAttribute),
verified: descriptor.attributes.verified.map(toEserviceAttribute),
},
};
}
Comment on lines +173 to +193
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are using runtime resources to change something at type level, I know the performance impact could be negligible but I saw this in a lot of part of the monorepo and I'm wondering if it is worth discussing.
What do you think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Carminepo2 good point 👍🏻
unfortunately branded type usages requires mapping like this, so we need to choose what we want encourage. Id as string are very error prone when they are function's parameter, branded type is a sort of guard to avoid bugs, meanwhile every time we convert models it's necessary this wrapping...
I suggest to discuss a possible policy offline.


export function toBffCatalogApiDescriptorAttributes(
attributes: attributeRegistryApi.Attribute[],
descriptor: catalogApi.EServiceDescriptor
): bffApi.DescriptorAttributes {
return {
certified: [
toBffCatalogApiDescriptorAttribute(
attributes,
descriptor.attributes.certified.flat()
),
],
declared: [
toBffCatalogApiDescriptorAttribute(
attributes,
descriptor.attributes.declared.flat()
),
],
verified: [
toBffCatalogApiDescriptorAttribute(
attributes,
descriptor.attributes.verified.flat()
),
],
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { TenantWithOnlyAttributes } from "pagopa-interop-agreement-lifecycle";
import { tenantApi } from "pagopa-interop-api-clients";
import {
CertifiedTenantAttribute,
DeclaredTenantAttribute,
TenantAttribute,
VerifiedTenantAttribute,
tenantAttributeType,
unsafeBrandId,
} from "pagopa-interop-models";

export function toTenantAttribute(
att: tenantApi.TenantAttribute
): TenantAttribute[] {
const certified: CertifiedTenantAttribute | undefined = att.certified && {
id: unsafeBrandId(att.certified.id),
type: tenantAttributeType.CERTIFIED,
revocationTimestamp: att.certified.revocationTimestamp
? new Date(att.certified.revocationTimestamp)
: undefined,
assignmentTimestamp: new Date(att.certified.assignmentTimestamp),
};

const verified: VerifiedTenantAttribute | undefined = att.verified && {
id: unsafeBrandId(att.verified.id),
type: tenantAttributeType.VERIFIED,
assignmentTimestamp: new Date(att.verified.assignmentTimestamp),
verifiedBy: att.verified.verifiedBy.map((v) => ({
id: v.id,
verificationDate: new Date(v.verificationDate),
expirationDate: v.expirationDate ? new Date(v.expirationDate) : undefined,
extensionDate: v.extensionDate ? new Date(v.extensionDate) : undefined,
})),
revokedBy: att.verified.revokedBy.map((r) => ({
id: r.id,
verificationDate: new Date(r.verificationDate),
revocationDate: new Date(r.revocationDate),
expirationDate: r.expirationDate ? new Date(r.expirationDate) : undefined,
extensionDate: r.extensionDate ? new Date(r.extensionDate) : undefined,
})),
};

const declared: DeclaredTenantAttribute | undefined = att.declared && {
id: unsafeBrandId(att.declared.id),
type: tenantAttributeType.DECLARED,
assignmentTimestamp: new Date(att.declared.assignmentTimestamp),
revocationTimestamp: att.declared.revocationTimestamp
? new Date(att.declared.revocationTimestamp)
: undefined,
};

return [certified, verified, declared].filter(
(a): a is TenantAttribute => !!a
);
}

export function toTenantWithOnlyAttributes(
tenant: tenantApi.Tenant
): TenantWithOnlyAttributes {
return {
...tenant,
attributes: tenant.attributes.map(toTenantAttribute).flat(),
};
}
44 changes: 39 additions & 5 deletions packages/bff/src/model/domain/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ import {
ApiError,
PurposeId,
makeApiProblemBuilder,
AttributeId,
} from "pagopa-interop-models";

export const errorCodes = {
purposeNotFound: "0001",
missingClaim: "0002",
tenantLoginNotAllowed: "0003",
tokenVerificationFailed: "0004",
userNotFound: "0005",
selfcareEntityNotFilled: "0006",
userNotFound: "0002",
selfcareEntityNotFilled: "0003",
descriptorNotFound: "0004",
attributeNotExists: "0005",
invalidEserviceRequester: "0006",
missingClaim: "0007",
tenantLoginNotAllowed: "0008",
tokenVerificationFailed: "0009",
};

export type ErrorCodes = keyof typeof errorCodes;
Expand Down Expand Up @@ -47,6 +51,36 @@ export function purposeNotFound(purposeId: PurposeId): ApiError<ErrorCodes> {
});
}

export function invalidEServiceRequester(
eServiceId: string,
requesterId: string
): ApiError<ErrorCodes> {
return new ApiError({
detail: `EService ${eServiceId} does not belong to producer ${requesterId}`,
code: "invalidEserviceRequester",
title: `Invalid eservice requester`,
});
}

export function eserviceDescriptorNotFound(
eServiceId: string,
descriptorId: string
): ApiError<ErrorCodes> {
return new ApiError({
detail: `Descriptor ${descriptorId} not found in Eservice ${eServiceId}`,
code: "descriptorNotFound",
title: `Descriptor not found`,
});
}

export function attributeNotExists(id: AttributeId): ApiError<ErrorCodes> {
return new ApiError({
detail: `Attribute ${id} does not exist in the attribute registry`,
code: "attributeNotExists",
title: "Attribute not exists",
});
}

export function missingClaim(claimName: string): ApiError<ErrorCodes> {
return new ApiError({
detail: `Claim ${claimName} has not been passed`,
Expand Down
Loading