@powerhousedao/service-offering 1.0.0-dev.10 → 1.0.0-dev.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/document-models/subscription-instance/v1/src/reducers/metrics.d.ts.map +1 -1
- package/dist/document-models/subscription-instance/v1/src/reducers/metrics.js +7 -6
- package/dist/document-models/subscription-instance/v1/src/reducers/utils.d.ts +7 -0
- package/dist/document-models/subscription-instance/v1/src/reducers/utils.d.ts.map +1 -0
- package/dist/document-models/subscription-instance/v1/src/reducers/utils.js +15 -0
- package/dist/document-models/subscription-instance/v1/tests/metrics.test.js +88 -1
- package/dist/subgraphs/resources-services/resolvers.d.ts.map +1 -1
- package/dist/subgraphs/resources-services/resolvers.js +32 -11
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../../../../../document-models/subscription-instance/v1/src/reducers/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qCAAqC,EAAE,MAAM,0EAA0E,CAAC;
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../../../../../document-models/subscription-instance/v1/src/reducers/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qCAAqC,EAAE,MAAM,0EAA0E,CAAC;AAgBtI,eAAO,MAAM,qCAAqC,EAAE,qCAsHjD,CAAC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { findService } from "./utils.js";
|
|
1
2
|
import { AddServiceMetricServiceNotFoundError, UpdateMetricServiceNotFoundError, UpdateMetricNotFoundError, UpdateMetricUsageServiceNotFoundError, UpdateMetricUsageNotFoundError, RemoveServiceMetricServiceNotFoundError, RemoveServiceMetricNotFoundError, IncrementMetricUsageServiceNotFoundError, IncrementMetricUsageNotFoundError, DecrementMetricUsageServiceNotFoundError, DecrementMetricUsageNotFoundError, } from "../../gen/metrics/error.js";
|
|
2
3
|
export const subscriptionInstanceMetricsOperations = {
|
|
3
4
|
addServiceMetricOperation(state, action) {
|
|
4
|
-
const svc = state
|
|
5
|
+
const svc = findService(state, action.input.serviceId);
|
|
5
6
|
if (!svc) {
|
|
6
7
|
throw new AddServiceMetricServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);
|
|
7
8
|
}
|
|
@@ -30,7 +31,7 @@ export const subscriptionInstanceMetricsOperations = {
|
|
|
30
31
|
});
|
|
31
32
|
},
|
|
32
33
|
updateMetricOperation(state, action) {
|
|
33
|
-
const svc = state
|
|
34
|
+
const svc = findService(state, action.input.serviceId);
|
|
34
35
|
if (!svc) {
|
|
35
36
|
throw new UpdateMetricServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);
|
|
36
37
|
}
|
|
@@ -50,7 +51,7 @@ export const subscriptionInstanceMetricsOperations = {
|
|
|
50
51
|
metric.nextUsageReset = action.input.nextUsageReset || null;
|
|
51
52
|
},
|
|
52
53
|
updateMetricUsageOperation(state, action) {
|
|
53
|
-
const svc = state
|
|
54
|
+
const svc = findService(state, action.input.serviceId);
|
|
54
55
|
if (!svc) {
|
|
55
56
|
throw new UpdateMetricUsageServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);
|
|
56
57
|
}
|
|
@@ -61,7 +62,7 @@ export const subscriptionInstanceMetricsOperations = {
|
|
|
61
62
|
metric.currentUsage = action.input.currentUsage;
|
|
62
63
|
},
|
|
63
64
|
removeServiceMetricOperation(state, action) {
|
|
64
|
-
const svc = state
|
|
65
|
+
const svc = findService(state, action.input.serviceId);
|
|
65
66
|
if (!svc) {
|
|
66
67
|
throw new RemoveServiceMetricServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);
|
|
67
68
|
}
|
|
@@ -72,7 +73,7 @@ export const subscriptionInstanceMetricsOperations = {
|
|
|
72
73
|
svc.metrics.splice(index, 1);
|
|
73
74
|
},
|
|
74
75
|
incrementMetricUsageOperation(state, action) {
|
|
75
|
-
const svc = state
|
|
76
|
+
const svc = findService(state, action.input.serviceId);
|
|
76
77
|
if (!svc) {
|
|
77
78
|
throw new IncrementMetricUsageServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);
|
|
78
79
|
}
|
|
@@ -83,7 +84,7 @@ export const subscriptionInstanceMetricsOperations = {
|
|
|
83
84
|
metric.currentUsage += action.input.incrementBy;
|
|
84
85
|
},
|
|
85
86
|
decrementMetricUsageOperation(state, action) {
|
|
86
|
-
const svc = state
|
|
87
|
+
const svc = findService(state, action.input.serviceId);
|
|
87
88
|
if (!svc) {
|
|
88
89
|
throw new DecrementMetricUsageServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);
|
|
89
90
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SubscriptionInstanceState, Service } from "../../gen/schema/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Finds a service by ID across both top-level standalone services
|
|
4
|
+
* and services nested inside service groups.
|
|
5
|
+
*/
|
|
6
|
+
export declare function findService(state: SubscriptionInstanceState, serviceId: string): Service | undefined;
|
|
7
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../../document-models/subscription-instance/v1/src/reducers/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,OAAO,EACR,MAAM,2BAA2B,CAAC;AAEnC;;;GAGG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,yBAAyB,EAChC,SAAS,EAAE,MAAM,GAChB,OAAO,GAAG,SAAS,CAQrB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds a service by ID across both top-level standalone services
|
|
3
|
+
* and services nested inside service groups.
|
|
4
|
+
*/
|
|
5
|
+
export function findService(state, serviceId) {
|
|
6
|
+
const standalone = state.services.find((s) => s.id === serviceId);
|
|
7
|
+
if (standalone)
|
|
8
|
+
return standalone;
|
|
9
|
+
for (const group of state.serviceGroups) {
|
|
10
|
+
const grouped = group.services.find((s) => s.id === serviceId);
|
|
11
|
+
if (grouped)
|
|
12
|
+
return grouped;
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
import { generateMock } from "@powerhousedao/common/utils";
|
|
2
|
+
import { generateId } from "document-model/core";
|
|
2
3
|
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { reducer, utils, isSubscriptionInstanceDocument, addServiceMetric, updateMetric, updateMetricUsage, removeServiceMetric, incrementMetricUsage, decrementMetricUsage, AddServiceMetricInputSchema, UpdateMetricInputSchema, UpdateMetricUsageInputSchema, RemoveServiceMetricInputSchema, IncrementMetricUsageInputSchema, DecrementMetricUsageInputSchema, } from "@powerhousedao/service-offering/document-models/subscription-instance/v1";
|
|
4
|
+
import { reducer, utils, isSubscriptionInstanceDocument, initializeSubscription, addServiceMetric, updateMetric, updateMetricUsage, removeServiceMetric, incrementMetricUsage, decrementMetricUsage, AddServiceMetricInputSchema, UpdateMetricInputSchema, UpdateMetricUsageInputSchema, RemoveServiceMetricInputSchema, IncrementMetricUsageInputSchema, DecrementMetricUsageInputSchema, } from "@powerhousedao/service-offering/document-models/subscription-instance/v1";
|
|
5
|
+
/** Creates a document with a service group containing a service with a metric. */
|
|
6
|
+
function createDocWithServiceGroup() {
|
|
7
|
+
const serviceId = generateId();
|
|
8
|
+
const metricId = generateId();
|
|
9
|
+
const groupId = generateId();
|
|
10
|
+
let doc = utils.createDocument();
|
|
11
|
+
doc = reducer(doc, initializeSubscription({
|
|
12
|
+
createdAt: new Date().toISOString(),
|
|
13
|
+
customerName: "Test",
|
|
14
|
+
selectedBillingCycle: "MONTHLY",
|
|
15
|
+
globalCurrency: "USD",
|
|
16
|
+
autoRenew: true,
|
|
17
|
+
serviceGroups: [
|
|
18
|
+
{
|
|
19
|
+
id: groupId,
|
|
20
|
+
name: "Test Group",
|
|
21
|
+
optional: false,
|
|
22
|
+
services: [
|
|
23
|
+
{
|
|
24
|
+
id: serviceId,
|
|
25
|
+
name: "Test Service",
|
|
26
|
+
metrics: [
|
|
27
|
+
{
|
|
28
|
+
id: metricId,
|
|
29
|
+
name: "API Calls",
|
|
30
|
+
unitName: "calls",
|
|
31
|
+
currentUsage: 100,
|
|
32
|
+
freeLimit: 50,
|
|
33
|
+
paidLimit: 200,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
}));
|
|
41
|
+
return { doc, serviceId, metricId, groupId };
|
|
42
|
+
}
|
|
4
43
|
describe("MetricsOperations", () => {
|
|
5
44
|
it("should handle addServiceMetric operation", () => {
|
|
6
45
|
const document = utils.createDocument();
|
|
@@ -62,4 +101,52 @@ describe("MetricsOperations", () => {
|
|
|
62
101
|
expect(updatedDocument.operations.global[0].action.input).toStrictEqual(input);
|
|
63
102
|
expect(updatedDocument.operations.global[0].index).toEqual(0);
|
|
64
103
|
});
|
|
104
|
+
describe("service group metric operations", () => {
|
|
105
|
+
it("should increment metric usage for a service inside a service group", () => {
|
|
106
|
+
const { doc, serviceId, metricId } = createDocWithServiceGroup();
|
|
107
|
+
const updatedDoc = reducer(doc, incrementMetricUsage({
|
|
108
|
+
serviceId,
|
|
109
|
+
metricId,
|
|
110
|
+
incrementBy: 10,
|
|
111
|
+
currentTime: new Date().toISOString(),
|
|
112
|
+
}));
|
|
113
|
+
const metric = updatedDoc.state.global.serviceGroups[0].services[0].metrics[0];
|
|
114
|
+
expect(metric.currentUsage).toBe(110);
|
|
115
|
+
expect(updatedDoc.operations.global[1].error).toBeUndefined();
|
|
116
|
+
});
|
|
117
|
+
it("should decrement metric usage for a service inside a service group", () => {
|
|
118
|
+
const { doc, serviceId, metricId } = createDocWithServiceGroup();
|
|
119
|
+
const updatedDoc = reducer(doc, decrementMetricUsage({
|
|
120
|
+
serviceId,
|
|
121
|
+
metricId,
|
|
122
|
+
decrementBy: 25,
|
|
123
|
+
currentTime: new Date().toISOString(),
|
|
124
|
+
}));
|
|
125
|
+
const metric = updatedDoc.state.global.serviceGroups[0].services[0].metrics[0];
|
|
126
|
+
expect(metric.currentUsage).toBe(75);
|
|
127
|
+
expect(updatedDoc.operations.global[1].error).toBeUndefined();
|
|
128
|
+
});
|
|
129
|
+
it("should set metric usage for a service inside a service group", () => {
|
|
130
|
+
const { doc, serviceId, metricId } = createDocWithServiceGroup();
|
|
131
|
+
const updatedDoc = reducer(doc, updateMetricUsage({
|
|
132
|
+
serviceId,
|
|
133
|
+
metricId,
|
|
134
|
+
currentUsage: 999,
|
|
135
|
+
currentTime: new Date().toISOString(),
|
|
136
|
+
}));
|
|
137
|
+
const metric = updatedDoc.state.global.serviceGroups[0].services[0].metrics[0];
|
|
138
|
+
expect(metric.currentUsage).toBe(999);
|
|
139
|
+
expect(updatedDoc.operations.global[1].error).toBeUndefined();
|
|
140
|
+
});
|
|
141
|
+
it("should return error for non-existent service in service group", () => {
|
|
142
|
+
const { doc, metricId } = createDocWithServiceGroup();
|
|
143
|
+
const updatedDoc = reducer(doc, incrementMetricUsage({
|
|
144
|
+
serviceId: "non-existent-id",
|
|
145
|
+
metricId,
|
|
146
|
+
incrementBy: 1,
|
|
147
|
+
currentTime: new Date().toISOString(),
|
|
148
|
+
}));
|
|
149
|
+
expect(updatedDoc.operations.global[1].error).toBe("Service with ID non-existent-id not found");
|
|
150
|
+
});
|
|
151
|
+
});
|
|
65
152
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/resources-services/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAyD/D,eAAO,MAAM,YAAY,GACvB,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/resources-services/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAyD/D,eAAO,MAAM,YAAY,GACvB,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CA6gBxB,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getUserSelectionPriceBreakdown, } from "../../document-models/service-offering/v1/src/utils.js";
|
|
2
|
-
import { createAction } from "document-model/core";
|
|
3
|
-
import { addFile, driveCreateDocument } from "document-drive";
|
|
2
|
+
import { createAction, generateId } from "document-model/core";
|
|
3
|
+
import { addFile, addFolder, driveCreateDocument } from "document-drive";
|
|
4
4
|
import { ResourceInstance } from "../../document-models/resource-instance/v1/module.js";
|
|
5
5
|
import { SubscriptionInstance } from "../../document-models/subscription-instance/v1/module.js";
|
|
6
6
|
import { mapOfferingToSubscription } from "../../editors/subscription-instance-editor/components/mapOfferingToSubscription.js";
|
|
@@ -269,26 +269,29 @@ export const getResolvers = (subgraph) => {
|
|
|
269
269
|
const builderProfileDoc = await reactorClient.createEmpty("powerhouse/builder-profile", { parentIdentifier: driveId });
|
|
270
270
|
const resourceInstanceDoc = await reactorClient.createEmpty("powerhouse/resource-instance", { parentIdentifier: driveId });
|
|
271
271
|
const subscriptionInstanceDoc = await reactorClient.createEmpty("powerhouse/subscription-instance", { parentIdentifier: driveId });
|
|
272
|
-
//
|
|
273
|
-
const
|
|
272
|
+
// create "Service Subscriptions" folder and organize files in team drive
|
|
273
|
+
const teamServiceSubsFolderId = generateId();
|
|
274
274
|
await reactorClient.execute(driveId, "main", [
|
|
275
|
+
addFolder({
|
|
276
|
+
id: teamServiceSubsFolderId,
|
|
277
|
+
name: "Service Subscriptions",
|
|
278
|
+
}),
|
|
275
279
|
addFile({
|
|
276
280
|
documentType: "powerhouse/builder-profile",
|
|
277
281
|
id: builderProfileDoc.header.id,
|
|
278
282
|
name: `${parsedTeamName} Builder Profile`,
|
|
279
|
-
parentFolder: teamRootFolder,
|
|
280
283
|
}),
|
|
281
284
|
addFile({
|
|
282
285
|
documentType: "powerhouse/resource-instance",
|
|
283
286
|
id: resourceInstanceDoc.header.id,
|
|
284
287
|
name: `${parsedTeamName} Resource Instance`,
|
|
285
|
-
parentFolder:
|
|
288
|
+
parentFolder: teamServiceSubsFolderId,
|
|
286
289
|
}),
|
|
287
290
|
addFile({
|
|
288
291
|
documentType: "powerhouse/subscription-instance",
|
|
289
292
|
id: subscriptionInstanceDoc.header.id,
|
|
290
293
|
name: `${parsedTeamName} Subscription Instance`,
|
|
291
|
-
parentFolder:
|
|
294
|
+
parentFolder: teamServiceSubsFolderId,
|
|
292
295
|
}),
|
|
293
296
|
]);
|
|
294
297
|
// update builder profile
|
|
@@ -307,26 +310,44 @@ export const getResolvers = (subgraph) => {
|
|
|
307
310
|
if (!operatorProfileId) {
|
|
308
311
|
throw new Error(`Operator profile not found for drive ${operatorDrive.header.id}`);
|
|
309
312
|
}
|
|
310
|
-
|
|
313
|
+
// find or create "Service Subscriptions" folder in the operator drive
|
|
314
|
+
let serviceSubscriptionsFolderId = operatorDrive.state.global.nodes.find((node) => node.kind === "folder" &&
|
|
315
|
+
node.name === "Service Subscriptions")?.id;
|
|
316
|
+
if (!serviceSubscriptionsFolderId) {
|
|
317
|
+
serviceSubscriptionsFolderId = generateId();
|
|
318
|
+
await reactorClient.execute(operatorDrive.header.id, "main", [
|
|
319
|
+
addFolder({
|
|
320
|
+
id: serviceSubscriptionsFolderId,
|
|
321
|
+
name: "Service Subscriptions",
|
|
322
|
+
}),
|
|
323
|
+
]);
|
|
324
|
+
}
|
|
325
|
+
// create a team folder inside "Service Subscriptions" for this team's docs
|
|
326
|
+
const teamFolderId = generateId();
|
|
311
327
|
// add reactor-level relationships so Connect syncs the child documents
|
|
312
328
|
// (createEmpty guarantees CREATE_DOCUMENT is persisted before this runs)
|
|
313
329
|
await reactorClient.addChildren(operatorDrive.header.id, [
|
|
314
330
|
resourceInstanceDoc.header.id,
|
|
315
331
|
subscriptionInstanceDoc.header.id,
|
|
316
332
|
]);
|
|
317
|
-
// add file references to operator drive
|
|
333
|
+
// add team folder and file references to operator drive
|
|
318
334
|
await reactorClient.execute(operatorDrive.header.id, "main", [
|
|
335
|
+
addFolder({
|
|
336
|
+
id: teamFolderId,
|
|
337
|
+
name: teamName,
|
|
338
|
+
parentFolder: serviceSubscriptionsFolderId,
|
|
339
|
+
}),
|
|
319
340
|
addFile({
|
|
320
341
|
documentType: "powerhouse/resource-instance",
|
|
321
342
|
id: resourceInstanceDoc.header.id,
|
|
322
343
|
name: `${parsedTeamName} Resource Instance`,
|
|
323
|
-
parentFolder:
|
|
344
|
+
parentFolder: teamFolderId,
|
|
324
345
|
}),
|
|
325
346
|
addFile({
|
|
326
347
|
documentType: "powerhouse/subscription-instance",
|
|
327
348
|
id: subscriptionInstanceDoc.header.id,
|
|
328
349
|
name: `${parsedTeamName} Subscription Instance`,
|
|
329
|
-
parentFolder:
|
|
350
|
+
parentFolder: teamFolderId,
|
|
330
351
|
}),
|
|
331
352
|
]);
|
|
332
353
|
// populate documents after all files are added to both drives
|