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

Add contract item reference model #191

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions packages/models/src/config/behaviors/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions packages/models/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down
18 changes: 18 additions & 0 deletions packages/models/src/contract/Contract/Contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -46,8 +47,25 @@ class ContractCommon extends classes(
DataModel<ContractListItemData | ContractData>,
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;
}
}

Expand Down
68 changes: 64 additions & 4 deletions packages/models/src/contract/ContractItem/ContractItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -20,7 +21,7 @@ export class ContractItem extends ReferenceModel {
contractItemId,
);
if (data !== undefined) {
return new ContractItemDetailed(contractId, data);
return new ContractItemDetailed(data);
}
},
);
Expand All @@ -44,11 +45,70 @@ export class ContractItem extends ReferenceModel {
}
}

export class ContractItemDetailed extends classes(
export class ContractItemCommon extends classes(
DataModel<ContractItemData>,
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 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;
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.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",
});
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<ContractItemData>,
ContractItemCommon,
) {
public constructor(data: ContractItemData) {
super([data]);
}
}
Original file line number Diff line number Diff line change
@@ -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<ContractItemReferenceDetailed | undefined> => {
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<ContractItemReferenceDetailed> => {
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<ContractItemReferenceData>,
ContractItemReference,
) {
public constructor(
contractId: string,
contractItemId: string,
data: ContractItemReferenceData,
) {
super([data], [contractId, contractItemId]);
}
}
Original file line number Diff line number Diff line change
@@ -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,
};
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./api.js";
export * from "./types.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ContractItemReferenceData } from "../types.js";

export interface ContractItemReferenceBehaviors {
find: (
contractId: string,
contractItemId: string,
) => Promise<ContractItemReferenceData | undefined>;
}
2 changes: 2 additions & 0 deletions packages/models/src/contract/ContractItemReference/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./ContractItemReference.js";
export * from "./types.js";
4 changes: 4 additions & 0 deletions packages/models/src/contract/ContractItemReference/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type ContractItemReferenceData = {
shortId?: string;
description: string;
};