@powerhousedao/service-offering 0.0.3 → 0.0.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"document-models.d.ts","sourceRoot":"","sources":["../../document-models/document-models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAQ1D,eAAO,MAAM,cAAc,EAAE,mBAAmB,CAAC,GAAG,CAAC,EAOpD,CAAC"}
1
+ {"version":3,"file":"document-models.d.ts","sourceRoot":"","sources":["../../document-models/document-models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAO1D,eAAO,MAAM,cAAc,EAAE,mBAAmB,CAAC,GAAG,CAAC,EAMpD,CAAC"}
@@ -2,13 +2,11 @@ import { Facet } from "./facet/module.js";
2
2
  import { ResourceInstance } from "./resource-instance/module.js";
3
3
  import { ResourceTemplate } from "./resource-template/module.js";
4
4
  import { ServiceOffering } from "./service-offering/module.js";
5
- import { ServiceSubscription } from "./service-subscription/module.js";
6
5
  import { SubscriptionInstance } from "./subscription-instance/module.js";
7
6
  export const documentModels = [
8
7
  Facet,
9
8
  ResourceInstance,
10
9
  ResourceTemplate,
11
10
  ServiceOffering,
12
- ServiceSubscription,
13
11
  SubscriptionInstance,
14
12
  ];
@@ -2,6 +2,5 @@ export { Facet } from "./facet/module.js";
2
2
  export { ResourceInstance } from "./resource-instance/module.js";
3
3
  export { ResourceTemplate } from "./resource-template/module.js";
4
4
  export { ServiceOffering } from "./service-offering/module.js";
5
- export { ServiceSubscription } from "./service-subscription/module.js";
6
5
  export { SubscriptionInstance } from "./subscription-instance/module.js";
7
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../document-models/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../document-models/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC"}
@@ -2,5 +2,4 @@ export { Facet } from "./facet/module.js";
2
2
  export { ResourceInstance } from "./resource-instance/module.js";
3
3
  export { ResourceTemplate } from "./resource-template/module.js";
4
4
  export { ServiceOffering } from "./service-offering/module.js";
5
- export { ServiceSubscription } from "./service-subscription/module.js";
6
5
  export { SubscriptionInstance } from "./subscription-instance/module.js";
@@ -41,10 +41,10 @@ export declare const ResetPeriodSchema: z.ZodEnum<{
41
41
  }>;
42
42
  export declare const SubscriptionStatusSchema: z.ZodEnum<{
43
43
  ACTIVE: "ACTIVE";
44
- PENDING: "PENDING";
45
44
  CANCELLED: "CANCELLED";
46
45
  EXPIRING: "EXPIRING";
47
46
  PAUSED: "PAUSED";
47
+ PENDING: "PENDING";
48
48
  }>;
49
49
  export declare const TierPricingModeSchema: z.ZodEnum<{
50
50
  CALCULATED: "CALCULATED";
@@ -1 +1 @@
1
- {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/resources-services/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAkC5D,eAAO,MAAM,YAAY,GAAI,UAAU,SAAS,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CA6UxE,CAAC"}
1
+ {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/resources-services/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAqC5D,eAAO,MAAM,YAAY,GAAI,UAAU,SAAS,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CA6ZxE,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import {} from "@powerhousedao/reactor-api";
2
2
  import { createAction } from "document-model/core";
3
3
  import { addFile } from "document-drive";
4
- import { ResourceInstance } from "@powerhousedao/service-offering/document-models";
4
+ import { ResourceInstance, SubscriptionInstance, } from "@powerhousedao/service-offering/document-models";
5
5
  export const getResolvers = (subgraph) => {
6
6
  const reactor = subgraph.reactor;
7
7
  return {
@@ -150,15 +150,15 @@ export const getResolvers = (subgraph) => {
150
150
  },
151
151
  },
152
152
  Mutation: {
153
- createResourceInstances: async (_, args) => {
153
+ createProductInstances: async (_, args) => {
154
154
  const { input } = args;
155
- const { resourceTemplateId, name, teamName } = input;
155
+ const { serviceOfferingId, name, teamName } = input;
156
156
  // Validate input
157
- if (!resourceTemplateId) {
157
+ if (!serviceOfferingId) {
158
158
  return {
159
159
  success: false,
160
160
  data: null,
161
- errors: ["Resource template ID is required"],
161
+ errors: ["Service offering ID is required"],
162
162
  };
163
163
  }
164
164
  if (!name) {
@@ -171,6 +171,32 @@ export const getResolvers = (subgraph) => {
171
171
  errors: ["Team name is required"],
172
172
  };
173
173
  }
174
+ // Fetch the service offering to get resourceTemplateId and finalConfiguration
175
+ const serviceOfferingDoc = await reactor.getDocument(serviceOfferingId);
176
+ if (!serviceOfferingDoc) {
177
+ return {
178
+ success: false,
179
+ data: null,
180
+ errors: ["Service offering not found"],
181
+ };
182
+ }
183
+ const serviceOfferingState = serviceOfferingDoc.state.global;
184
+ const resourceTemplateId = serviceOfferingState.resourceTemplateId;
185
+ if (!resourceTemplateId) {
186
+ return {
187
+ success: false,
188
+ data: null,
189
+ errors: ["Service offering has no associated resource template"],
190
+ };
191
+ }
192
+ const finalConfiguration = serviceOfferingState.finalConfiguration;
193
+ if (!finalConfiguration) {
194
+ return {
195
+ success: false,
196
+ data: null,
197
+ errors: ["Service offering has no final configuration"],
198
+ };
199
+ }
174
200
  const parsedTeamName = teamName.toLowerCase().replace(/ /g, "-");
175
201
  const parsedName = name.toLowerCase().replace(/ /g, "-");
176
202
  try {
@@ -184,7 +210,6 @@ export const getResolvers = (subgraph) => {
184
210
  slug: parsedTeamName,
185
211
  preferredEditor: "builder-team-admin",
186
212
  });
187
- teamBuilderAdminDrive.header.id;
188
213
  // create builder-profile doc inside the team-builder-admin drive
189
214
  const builderProfileDoc = await reactor.addDocument("powerhouse/builder-profile");
190
215
  await reactor.addAction(teamBuilderAdminDrive.header.id, addFile({
@@ -194,25 +219,65 @@ export const getResolvers = (subgraph) => {
194
219
  parentFolder: teamBuilderAdminDrive.state.global.nodes?.find((node) => node.kind === "folder")?.parentFolder,
195
220
  }));
196
221
  await reactor.addAction(builderProfileDoc.header.id, createAction("UPDATE_PROFILE", { name }, undefined, undefined, "global"));
197
- // create resource-instance doc inside the team-builder-admin drive
222
+ // create resource-instance and subscription-instance docs
198
223
  const resourceInstanceDoc = await reactor.addDocument("powerhouse/resource-instance");
199
- await reactor.addAction(teamBuilderAdminDrive.header.id, addFile({
200
- documentType: "powerhouse/resource-instance",
201
- id: resourceInstanceDoc.header.id,
202
- name: `${parsedName} Resource Instance`,
203
- parentFolder: teamBuilderAdminDrive.state.global.nodes?.find((node) => node.kind === "folder")?.parentFolder,
204
- }));
205
- await populateResourceInstance(reactor, resourceInstanceDoc.header.id, resourceTemplateId, builderProfileDoc.header.id, name);
206
- // create copy of resource-instance doc inside the operator's drive
224
+ const subscriptionInstanceDoc = await reactor.addDocument("powerhouse/subscription-instance");
225
+ // resolve parent folders for both drives
226
+ const teamParentFolder = teamBuilderAdminDrive.state.global.nodes?.find((node) => node.kind === "folder")?.parentFolder;
207
227
  const operatorDrive = await getOperatorDrive(reactor, resourceTemplateId);
208
228
  if (!operatorDrive) {
209
229
  throw new Error(`Operator drive not found for resource template ${resourceTemplateId}`);
210
230
  }
211
- await reactor.addAction(operatorDrive.header.id, addFile({
212
- documentType: "powerhouse/resource-instance",
213
- id: resourceInstanceDoc.header.id,
214
- name: `${parsedName} Resource Instance`,
215
- parentFolder: operatorDrive.state.global.nodes?.find((node) => node.kind === "folder")?.parentFolder,
231
+ const operatorParentFolder = operatorDrive.state.global.nodes?.find((node) => node.kind === "folder")?.parentFolder;
232
+ // batch add resource-instance and subscription-instance to team-builder-admin drive
233
+ await reactor.addActions(teamBuilderAdminDrive.header.id, [
234
+ addFile({
235
+ documentType: "powerhouse/resource-instance",
236
+ id: resourceInstanceDoc.header.id,
237
+ name: `${parsedName} Resource Instance`,
238
+ parentFolder: teamParentFolder,
239
+ }),
240
+ addFile({
241
+ documentType: "powerhouse/subscription-instance",
242
+ id: subscriptionInstanceDoc.header.id,
243
+ name: `${parsedName} Subscription Instance`,
244
+ parentFolder: teamParentFolder,
245
+ }),
246
+ ]);
247
+ // batch add resource-instance and subscription-instance to operator drive
248
+ await reactor.addActions(operatorDrive.header.id, [
249
+ addFile({
250
+ documentType: "powerhouse/resource-instance",
251
+ id: resourceInstanceDoc.header.id,
252
+ name: `${parsedName} Resource Instance`,
253
+ parentFolder: operatorParentFolder,
254
+ }),
255
+ addFile({
256
+ documentType: "powerhouse/subscription-instance",
257
+ id: subscriptionInstanceDoc.header.id,
258
+ name: `${parsedName} Subscription Instance`,
259
+ parentFolder: operatorParentFolder,
260
+ }),
261
+ ]);
262
+ // populate documents after all files are added to both drives
263
+ await populateResourceInstance(reactor, resourceInstanceDoc.header.id, resourceTemplateId, builderProfileDoc.header.id, name);
264
+ const now = new Date().toISOString();
265
+ // find the selected tier to get its name and pricing details
266
+ const selectedTier = serviceOfferingState.tiers.find((t) => t.id === finalConfiguration.selectedTierId);
267
+ await reactor.addAction(subscriptionInstanceDoc.header.id, SubscriptionInstance.actions.initializeSubscription({
268
+ customerId: builderProfileDoc.header.id,
269
+ customerName: name,
270
+ serviceOfferingId,
271
+ resourceId: resourceInstanceDoc.header.id,
272
+ resourceLabel: name,
273
+ resourceThumbnailUrl: serviceOfferingState.thumbnailUrl,
274
+ tierName: selectedTier?.name || null,
275
+ tierPrice: selectedTier?.pricing?.amount ?? null,
276
+ tierCurrency: selectedTier?.pricing?.currency || null,
277
+ tierPricingMode: selectedTier?.pricingMode || null,
278
+ selectedBillingCycle: finalConfiguration.selectedBillingCycle || null,
279
+ globalCurrency: finalConfiguration.tierCurrency || null,
280
+ createdAt: now,
216
281
  }));
217
282
  return {
218
283
  success: true,
@@ -223,7 +288,7 @@ export const getResolvers = (subgraph) => {
223
288
  };
224
289
  }
225
290
  catch (error) {
226
- console.error("Failed to create resource instance:", error);
291
+ console.error("Failed to create product instances:", error);
227
292
  return {
228
293
  success: false,
229
294
  data: null,
@@ -9,18 +9,18 @@ export const schema = gql `
9
9
  }
10
10
 
11
11
  type Mutation {
12
- createResourceInstances(
13
- input: CreateResourceInstancesInput!
14
- ): CreateResourceInstancesOutput
12
+ createProductInstances(
13
+ input: CreateProductInstancesInput!
14
+ ): CreateProductInstancesOutput
15
15
  }
16
16
 
17
- input CreateResourceInstancesInput {
18
- resourceTemplateId: PHID!
17
+ input CreateProductInstancesInput {
18
+ serviceOfferingId: PHID!
19
19
  name: String!
20
20
  teamName: String!
21
21
  }
22
22
 
23
- type CreateResourceInstancesOutput {
23
+ type CreateProductInstancesOutput {
24
24
  success: Boolean!
25
25
  data: JSONObject
26
26
  errors: [String!]!
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@powerhousedao/service-offering",
3
3
  "description": "service offering document models",
4
- "version": "0.0.3",
4
+ "version": "0.0.5",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
7
7
  "files": [
@@ -1,20 +0,0 @@
1
- import { type RemoteBuilderProfile } from "../utils/graphql-client.js";
2
- interface UseRemoteBuilderProfilesResult {
3
- /** Map of PHID to remote builder profile data */
4
- profileMap: Map<string, RemoteBuilderProfile>;
5
- /** All available remote profiles for selection */
6
- allProfiles: RemoteBuilderProfile[];
7
- /** Whether remote data is currently loading */
8
- isLoading: boolean;
9
- /** Manually refetch all available profiles */
10
- refetchAll: () => Promise<void>;
11
- }
12
- /**
13
- * Hook for fetching builder profiles from remote Switchboard drives.
14
- * Used as a fallback when local drives don't have the builder profile documents.
15
- *
16
- * @param localProfileMap - Map of PHIDs that are already resolved locally (to avoid using remote data for those)
17
- */
18
- export declare function useRemoteBuilderProfiles(localProfileMap: Map<string, unknown>): UseRemoteBuilderProfilesResult;
19
- export {};
20
- //# sourceMappingURL=useRemoteBuilderProfiles.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useRemoteBuilderProfiles.d.ts","sourceRoot":"","sources":["../../../../editors/service-offering-editor/hooks/useRemoteBuilderProfiles.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,4BAA4B,CAAC;AAEpC,UAAU,8BAA8B;IACtC,iDAAiD;IACjD,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC9C,kDAAkD;IAClD,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAC;IACnB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GACpC,8BAA8B,CA8DhC"}
@@ -1,57 +0,0 @@
1
- import { useState, useEffect, useCallback, useRef } from "react";
2
- import { fetchAllRemoteBuilderProfiles, } from "../utils/graphql-client.js";
3
- /**
4
- * Hook for fetching builder profiles from remote Switchboard drives.
5
- * Used as a fallback when local drives don't have the builder profile documents.
6
- *
7
- * @param localProfileMap - Map of PHIDs that are already resolved locally (to avoid using remote data for those)
8
- */
9
- export function useRemoteBuilderProfiles(localProfileMap) {
10
- const [profileMap, setProfileMap] = useState(new Map());
11
- const [allProfiles, setAllProfiles] = useState([]);
12
- const [isLoading, setIsLoading] = useState(false);
13
- // Track if we've already started fetching to avoid duplicate requests
14
- const isFetchingRef = useRef(false);
15
- const hasFetchedRef = useRef(false);
16
- // Fetch all available profiles
17
- const refetchAll = useCallback(async () => {
18
- // Prevent concurrent fetches
19
- if (isFetchingRef.current) {
20
- return;
21
- }
22
- isFetchingRef.current = true;
23
- setIsLoading(true);
24
- try {
25
- const profiles = await fetchAllRemoteBuilderProfiles();
26
- hasFetchedRef.current = true;
27
- setAllProfiles(profiles);
28
- // Build profile map
29
- const newMap = new Map();
30
- profiles.forEach((profile) => {
31
- newMap.set(profile.id, profile);
32
- });
33
- setProfileMap(newMap);
34
- }
35
- catch (error) {
36
- console.warn("[useRemoteBuilderProfiles] Failed to fetch profiles:", error);
37
- }
38
- finally {
39
- setIsLoading(false);
40
- isFetchingRef.current = false;
41
- }
42
- }, []);
43
- // Auto-fetch all profiles on mount
44
- useEffect(() => {
45
- if (!hasFetchedRef.current && !isFetchingRef.current) {
46
- void refetchAll();
47
- }
48
- }, [refetchAll]);
49
- // Filter out profiles that exist locally from the returned allProfiles
50
- const filteredAllProfiles = allProfiles.filter((profile) => !localProfileMap.has(profile.id));
51
- return {
52
- profileMap,
53
- allProfiles: filteredAllProfiles,
54
- isLoading,
55
- refetchAll,
56
- };
57
- }