From 93542e2c560e2dbee7bd6027de1672eb9ef4041e Mon Sep 17 00:00:00 2001 From: Jonas Lampe Date: Wed, 25 Sep 2024 15:30:22 +0200 Subject: [PATCH 1/3] Extend types of Contract and ContractItemModel --- .../models/src/contract/Contract/Contract.ts | 18 ++++++ .../src/contract/ContractItem/ContractItem.ts | 58 +++++++++++++++++-- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/packages/models/src/contract/Contract/Contract.ts b/packages/models/src/contract/Contract/Contract.ts index 919ddb11..da1fc28a 100644 --- a/packages/models/src/contract/Contract/Contract.ts +++ b/packages/models/src/contract/Contract/Contract.ts @@ -14,6 +14,7 @@ import { ContractListQueryData, ContractListQueryModelData, } from "./types.js"; +import { ContractItem } from "../ContractItem/index.js"; export class Contract extends ReferenceModel { public static ofId(id: string): Contract { @@ -46,8 +47,25 @@ class ContractCommon extends classes( DataModel, Contract, ) { + public readonly id: string; + public readonly data: ContractData; + public readonly baseItem: ContractItem; + public readonly additionalItems: ContractItem[]; + public readonly allItems: ContractItem[]; + public readonly hasTermination: boolean; + public readonly customerId: string; public constructor(data: ContractListItemData | ContractData) { super([data], [data.customerId]); + this.id = data.contractId; + this.customerId = data.customerId; + this.baseItem = ContractItem.ofId(data.contractId, data.baseItem.itemId); + this.additionalItems = + data.additionalItems?.map((item) => { + return ContractItem.ofId(data.contractId, item.itemId); + }) ?? []; + this.allItems = [this.baseItem, ...this.additionalItems]; + this.data = Object.freeze(data); + this.hasTermination = !!data.termination; } } diff --git a/packages/models/src/contract/ContractItem/ContractItem.ts b/packages/models/src/contract/ContractItem/ContractItem.ts index d4fc7729..20a6160a 100644 --- a/packages/models/src/contract/ContractItem/ContractItem.ts +++ b/packages/models/src/contract/ContractItem/ContractItem.ts @@ -4,6 +4,7 @@ import { DataModel, ReferenceModel } from "../../base/index.js"; import { provideReact } from "../../react/index.js"; import { config } from "../../config/config.js"; import assertObjectFound from "../../base/assertObjectFound.js"; +import { Money } from "../../base/Money.js"; export class ContractItem extends ReferenceModel { public static ofId(contractId: string, id: string): ContractItem { @@ -20,7 +21,7 @@ export class ContractItem extends ReferenceModel { contractItemId, ); if (data !== undefined) { - return new ContractItemDetailed(contractId, data); + return new ContractItemDetailed(data); } }, ); @@ -44,11 +45,60 @@ export class ContractItem extends ReferenceModel { } } -export class ContractItemDetailed extends classes( +export class ContractItemCommon extends classes( DataModel, ContractItem, ) { - public constructor(contractId: string, data: ContractItemData) { - super([data], [contractId, data.itemId]); + public readonly id: string; + public readonly data: ContractItemData; + public readonly description: string; + public readonly totalPrice: Money; + public readonly totalYearlyPrice: Money; + public readonly freeTrialDays?: number; + public readonly orderDate?: Date; + public readonly activationDate?: Date; + public readonly nextPossibleDowngradeDate?: Date; + public readonly nextPossibleTerminationDate?: Date; + public readonly nextPossibleUpgradeDate?: Date; + public readonly invoiceStop?: Date; + public constructor(data: ContractItemData) { + super([data]); + this.id = data.itemId; + this.data = Object.freeze(data); + this.description = data.description; + this.totalPrice = Money({ + amount: data.totalPrice.value, + currency: "EUR", + }); + this.totalYearlyPrice = Money({ + amount: this.totalPrice.getAmount() * 12, + currency: "EUR", + }); + this.freeTrialDays = data.freeTrialDays; + this.activationDate = data.activationDate + ? new Date(data.activationDate) + : undefined; + this.nextPossibleDowngradeDate = data.nextPossibleDowngradeDate + ? new Date(data.nextPossibleDowngradeDate) + : undefined; + this.nextPossibleTerminationDate = data.nextPossibleDowngradeDate + ? new Date(data.nextPossibleDowngradeDate) + : undefined; + this.nextPossibleUpgradeDate = data.nextPossibleDowngradeDate + ? new Date(data.nextPossibleDowngradeDate) + : undefined; + this.orderDate = data.orderDate ? new Date(data.orderDate) : undefined; + this.invoiceStop = data.invoiceStop + ? new Date(data.invoiceStop) + : undefined; + } +} + +export class ContractItemDetailed extends classes( + DataModel, + ContractItemCommon, +) { + public constructor(data: ContractItemData) { + super([data]); } } From e338d62fe20be59bd3f58ba7ada85c0d88420c1a Mon Sep 17 00:00:00 2001 From: Jonas Lampe Date: Thu, 26 Sep 2024 10:35:51 +0200 Subject: [PATCH 2/3] Extend types with isDomain, isSSLCertificate, isProSpace, isSpaceServer --- .../models/src/contract/ContractItem/ContractItem.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/models/src/contract/ContractItem/ContractItem.ts b/packages/models/src/contract/ContractItem/ContractItem.ts index 20a6160a..258b2f44 100644 --- a/packages/models/src/contract/ContractItem/ContractItem.ts +++ b/packages/models/src/contract/ContractItem/ContractItem.ts @@ -56,6 +56,10 @@ export class ContractItemCommon extends classes( public readonly totalYearlyPrice: Money; public readonly freeTrialDays?: number; public readonly orderDate?: Date; + public readonly isDomain: boolean; + public readonly isSSLCertificate: boolean; + public readonly isSpaceServer: boolean; + public readonly isProSpace: boolean; public readonly activationDate?: Date; public readonly nextPossibleDowngradeDate?: Date; public readonly nextPossibleTerminationDate?: Date; @@ -66,6 +70,12 @@ export class ContractItemCommon extends classes( this.id = data.itemId; this.data = Object.freeze(data); this.description = data.description; + this.isDomain = data.aggregateReference?.aggregate === "domain"; + this.isSSLCertificate = + data.aggregateReference?.aggregate === "certificate"; + this.isSpaceServer = + data.aggregateReference?.aggregate === "placementgroup"; + this.isProSpace = data.aggregateReference?.aggregate === "project"; this.totalPrice = Money({ amount: data.totalPrice.value, currency: "EUR", From e36533336273d332c9c6eac38cf773ecd7d375fe Mon Sep 17 00:00:00 2001 From: Jonas Lampe Date: Thu, 26 Sep 2024 13:32:11 +0200 Subject: [PATCH 3/3] Add contractItemReferenceModel to apiTools --- packages/models/src/config/behaviors/api.ts | 3 + packages/models/src/config/config.ts | 4 ++ .../ContractItemReference.ts | 67 +++++++++++++++++++ .../ContractItemReference/behaviors/api.ts | 46 +++++++++++++ .../ContractItemReference/behaviors/index.ts | 2 + .../ContractItemReference/behaviors/types.ts | 8 +++ .../contract/ContractItemReference/index.ts | 2 + .../contract/ContractItemReference/types.ts | 4 ++ 8 files changed, 136 insertions(+) create mode 100644 packages/models/src/contract/ContractItemReference/ContractItemReference.ts create mode 100644 packages/models/src/contract/ContractItemReference/behaviors/api.ts create mode 100644 packages/models/src/contract/ContractItemReference/behaviors/index.ts create mode 100644 packages/models/src/contract/ContractItemReference/behaviors/types.ts create mode 100644 packages/models/src/contract/ContractItemReference/index.ts create mode 100644 packages/models/src/contract/ContractItemReference/types.ts diff --git a/packages/models/src/config/behaviors/api.ts b/packages/models/src/config/behaviors/api.ts index 9fbb4efb..d90477d2 100644 --- a/packages/models/src/config/behaviors/api.ts +++ b/packages/models/src/config/behaviors/api.ts @@ -9,6 +9,7 @@ import { addUrlTagToProvideReactCache } from "../../react/asyncResourceInvalidat import { apiArticleBehaviors } from "../../article/Article/behaviors/index.js"; import { apiContractBehaviors } from "../../contract/Contract/behaviors/index.js"; import { apiContractItemBehaviors } from "../../contract/ContractItem/behaviors/index.js"; +import { apiContractItemReferenceBehaviors } from "../../contract/ContractItemReference/behaviors/index.js"; class ApiSetupState { private _client: MittwaldAPIV2Client | undefined; @@ -31,6 +32,8 @@ class ApiSetupState { config.behaviors.appInstallation = apiAppInstallationBehaviors(client); config.behaviors.contract = apiContractBehaviors(client); config.behaviors.contractItem = apiContractItemBehaviors(client); + config.behaviors.contractItemReference = + apiContractItemReferenceBehaviors(client); } public setupWithApiToken(apiToken: string) { diff --git a/packages/models/src/config/config.ts b/packages/models/src/config/config.ts index c20a52c3..dfef0f90 100644 --- a/packages/models/src/config/config.ts +++ b/packages/models/src/config/config.ts @@ -6,12 +6,14 @@ import { ContractBehaviors } from "../contract/Contract/behaviors/index.js"; import { AppInstallationBehaviors } from "../app/AppInstallation/behaviors/index.js"; import { ContractItemBehaviors } from "../contract/ContractItem/behaviors/index.js"; import { ArticleBehaviors } from "../article/Article/behaviors/index.js"; +import { ContractItemReferenceBehaviors } from "../contract/ContractItemReference/behaviors/index.js"; interface Config { defaultPaginationLimit: number; behaviors: { contract: ContractBehaviors; contractItem: ContractItemBehaviors; + contractItemReference: ContractItemReferenceBehaviors; article: ArticleBehaviors; project: ProjectBehaviors; server: ServerBehaviors; @@ -26,6 +28,8 @@ export const config: Config = { behaviors: { contract: undefined as unknown as ContractBehaviors, contractItem: undefined as unknown as ContractItemBehaviors, + contractItemReference: + undefined as unknown as ContractItemReferenceBehaviors, article: undefined as unknown as ArticleBehaviors, project: undefined as unknown as ProjectBehaviors, server: undefined as unknown as ServerBehaviors, diff --git a/packages/models/src/contract/ContractItemReference/ContractItemReference.ts b/packages/models/src/contract/ContractItemReference/ContractItemReference.ts new file mode 100644 index 00000000..5369692f --- /dev/null +++ b/packages/models/src/contract/ContractItemReference/ContractItemReference.ts @@ -0,0 +1,67 @@ +import { classes } from "polytype"; +import { ContractItemReferenceData } from "./types.js"; +import { DataModel, ReferenceModel } from "../../base/index.js"; +import { provideReact } from "../../react/index.js"; +import { config } from "../../config/config.js"; +import assertObjectFound from "../../base/assertObjectFound.js"; + +export class ContractItemReference extends ReferenceModel { + public static ofId( + contractId: string, + contractItemId: string, + ): ContractItemReference { + return new ContractItemReference(contractId, contractItemId); + } + + public static find = provideReact( + async ( + contractId: string, + contractItemId: string, + ): Promise => { + const data = await config.behaviors.contractItemReference.find( + contractId, + contractItemId, + ); + if (data !== undefined) { + return new ContractItemReferenceDetailed( + contractId, + contractItemId, + data, + ); + } + }, + ); + + public static get = provideReact( + async ( + contractId: string, + contractItemId: string, + ): Promise => { + const item = await this.find(contractId, contractItemId); + assertObjectFound(item, this, contractItemId); + return item; + }, + ); + + public readonly contractId: string; + public readonly contractItemId: string; + + public constructor(contractId: string, contractItemId: string) { + super(contractItemId); + this.contractId = contractId; + this.contractItemId = contractItemId; + } +} + +export class ContractItemReferenceDetailed extends classes( + DataModel, + ContractItemReference, +) { + public constructor( + contractId: string, + contractItemId: string, + data: ContractItemReferenceData, + ) { + super([data], [contractId, contractItemId]); + } +} diff --git a/packages/models/src/contract/ContractItemReference/behaviors/api.ts b/packages/models/src/contract/ContractItemReference/behaviors/api.ts new file mode 100644 index 00000000..556b33c6 --- /dev/null +++ b/packages/models/src/contract/ContractItemReference/behaviors/api.ts @@ -0,0 +1,46 @@ +import { MittwaldAPIV2Client } from "@mittwald/api-client"; +import { ContractItemReferenceBehaviors } from "./types.js"; +import { ContractItem } from "../../ContractItem/index.js"; + +export const apiContractItemReferenceBehaviors = ( + client: MittwaldAPIV2Client, +): ContractItemReferenceBehaviors => ({ + find: async (contractId, contractItemId) => { + const contractItem = await ContractItem.find(contractId, contractItemId); + const contractItemData = contractItem?.data; + const aggregateReference = contractItemData?.aggregateReference; + const description = contractItemData?.description ?? ""; + + let shortId: string | undefined; + let itemDescription: string = description; + + if (aggregateReference) { + const refId = aggregateReference.id; + + if (contractItem?.isProSpace) { + const projectResponse = await client.project.getProject({ + projectId: refId, + }); + if (projectResponse.status === 200) { + const projectData = projectResponse.data; + shortId = projectData.serverShortId; + itemDescription = projectData.description ?? description; + } + } else if (contractItem?.isSpaceServer) { + const serverResponse = await client.project.getServer({ + serverId: refId, + }); + if (serverResponse.status === 200) { + const serverData = serverResponse.data; + shortId = serverData.shortId; + itemDescription = serverData.description ?? description; + } + } + } + + return { + shortId, + description: itemDescription, + }; + }, +}); diff --git a/packages/models/src/contract/ContractItemReference/behaviors/index.ts b/packages/models/src/contract/ContractItemReference/behaviors/index.ts new file mode 100644 index 00000000..a7b74f7c --- /dev/null +++ b/packages/models/src/contract/ContractItemReference/behaviors/index.ts @@ -0,0 +1,2 @@ +export * from "./api.js"; +export * from "./types.js"; diff --git a/packages/models/src/contract/ContractItemReference/behaviors/types.ts b/packages/models/src/contract/ContractItemReference/behaviors/types.ts new file mode 100644 index 00000000..ac6d919e --- /dev/null +++ b/packages/models/src/contract/ContractItemReference/behaviors/types.ts @@ -0,0 +1,8 @@ +import { ContractItemReferenceData } from "../types.js"; + +export interface ContractItemReferenceBehaviors { + find: ( + contractId: string, + contractItemId: string, + ) => Promise; +} diff --git a/packages/models/src/contract/ContractItemReference/index.ts b/packages/models/src/contract/ContractItemReference/index.ts new file mode 100644 index 00000000..28c20073 --- /dev/null +++ b/packages/models/src/contract/ContractItemReference/index.ts @@ -0,0 +1,2 @@ +export * from "./ContractItemReference.js"; +export * from "./types.js"; diff --git a/packages/models/src/contract/ContractItemReference/types.ts b/packages/models/src/contract/ContractItemReference/types.ts new file mode 100644 index 00000000..353148bd --- /dev/null +++ b/packages/models/src/contract/ContractItemReference/types.ts @@ -0,0 +1,4 @@ +export type ContractItemReferenceData = { + shortId?: string; + description: string; +};