@prismatic-io/components-duro-plm 0.0.0-stage → 0.0.1

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.
@@ -0,0 +1,1132 @@
1
+ let _prismatic_io_spectral = require("@prismatic-io/spectral");
2
+ let _prismatic_io_spectral_http = require("@prismatic-io/spectral/http");
3
+ let graphql_request = require("graphql-request");
4
+ var connections_default = [(0, _prismatic_io_spectral.connection)({
5
+ key: "duroApiKey",
6
+ display: {
7
+ label: "API Key",
8
+ description: "Authenticate requests using an API key."
9
+ },
10
+ inputs: {
11
+ username: {
12
+ label: "API Key",
13
+ placeholder: "Enter Duro API Key",
14
+ example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0",
15
+ comments: "The API key for the Duro account. Generate an API key in Duro account settings under the Integrations tab.",
16
+ type: "password",
17
+ required: true
18
+ },
19
+ duroEnvironment: {
20
+ label: "Duro Environment",
21
+ placeholder: "Select Duro Environment",
22
+ comments: "The Duro GraphQL endpoint. Select based on data residency requirements: Main (MFG) for standard accounts, ITAR for US government compliance, or EU for European data residency.",
23
+ type: "string",
24
+ required: true,
25
+ model: [
26
+ {
27
+ label: "Main (MFG)",
28
+ value: "https://mfg.duro.app/graphql"
29
+ },
30
+ {
31
+ label: "ITAR",
32
+ value: "https://gov.duro.us/graphql"
33
+ },
34
+ {
35
+ label: "EU",
36
+ value: "https://eu.duro.app/graphql"
37
+ }
38
+ ],
39
+ example: "https://mfg.duro.app/graphql"
40
+ },
41
+ customDuroEnvironment: {
42
+ label: "Custom Duro Environment",
43
+ type: "string",
44
+ required: false,
45
+ placeholder: "Enter custom GraphQL endpoint URL",
46
+ comments: "If provided, this will override the selected Duro Environment. Use for private Duro instances or custom deployments.",
47
+ example: "https://custom-instance.duro.app/graphql"
48
+ }
49
+ }
50
+ })];
51
+ //#endregion
52
+ //#region src/constants.ts
53
+ const GRAPHQL_EXAMPLE = `{
54
+ components(libraryType: GENERAL) {
55
+ connection(
56
+ first: 10
57
+ ) {
58
+ totalCount
59
+ edges {
60
+ cursor
61
+ node {
62
+ id
63
+ name
64
+ created
65
+ lastModified
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }`;
71
+ const LOOK_BACK_DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
72
+ const POLL_RESOURCE_CONFIG = {
73
+ components: {
74
+ query: `
75
+ query PollComponents($first: Int, $after: String, $libraryType: LibraryType, $orderBy: [ComponentsOrderByInput]) {
76
+ components(libraryType: $libraryType, orderBy: $orderBy) {
77
+ connection(first: $first, after: $after) {
78
+ edges {
79
+ cursor
80
+ node {
81
+ id
82
+ name
83
+ created
84
+ lastModified
85
+ }
86
+ }
87
+ pageInfo {
88
+ hasNextPage
89
+ endCursor
90
+ }
91
+ }
92
+ }
93
+ }
94
+ `,
95
+ dataPath: ["components", "connection"],
96
+ buildVariables: (first, after) => ({
97
+ first,
98
+ after,
99
+ libraryType: "GENERAL",
100
+ orderBy: [{ lastModified: "desc" }]
101
+ }),
102
+ earlyStop: true
103
+ },
104
+ changeOrders: {
105
+ query: `
106
+ query PollChangeOrders($first: Int, $after: String, $orderBy: [ChangeOrdersOrderByInput]) {
107
+ changeOrders(orderBy: $orderBy) {
108
+ connection(first: $first, after: $after) {
109
+ edges {
110
+ cursor
111
+ node {
112
+ id
113
+ name
114
+ status
115
+ type
116
+ created
117
+ lastModified
118
+ }
119
+ }
120
+ pageInfo {
121
+ hasNextPage
122
+ endCursor
123
+ }
124
+ }
125
+ }
126
+ }
127
+ `,
128
+ dataPath: ["changeOrders", "connection"],
129
+ buildVariables: (first, after) => ({
130
+ first,
131
+ after,
132
+ orderBy: [{ lastModified: "desc" }]
133
+ }),
134
+ earlyStop: true
135
+ }
136
+ };
137
+ //#endregion
138
+ //#region src/util/index.ts
139
+ const validateConnection = (connection) => {
140
+ if (!connections_default.map((c) => c.key).includes(connection.key)) throw new _prismatic_io_spectral.ConnectionError(connection, `Unsupported connection ${connection.key}.`);
141
+ };
142
+ const cleanStringInput = (value) => value ? _prismatic_io_spectral.util.types.toString(value) : void 0;
143
+ const cleanNumberInput = (value) => value ? _prismatic_io_spectral.util.types.toNumber(value) : void 0;
144
+ const getCredentials = (connection) => {
145
+ const url = _prismatic_io_spectral.util.types.toString(connection.fields.customDuroEnvironment).trim() || _prismatic_io_spectral.util.types.toString(connection.fields.duroEnvironment);
146
+ return {
147
+ apiKey: _prismatic_io_spectral.util.types.toString(connection.fields.username),
148
+ url
149
+ };
150
+ };
151
+ const cleanCodeInput = (value) => value ? _prismatic_io_spectral.util.types.toObject(value) : {};
152
+ const cleanKeyValueListInput = (value) => {
153
+ if (Array.isArray(value)) return _prismatic_io_spectral.util.types.keyValPairListToObject(value);
154
+ return {};
155
+ };
156
+ const parseJsonArray = (value) => {
157
+ if (value) {
158
+ const json = _prismatic_io_spectral.util.types.toObject(value);
159
+ if (!Array.isArray(json)) throw new Error("Expected a JSON array");
160
+ return json;
161
+ }
162
+ return [];
163
+ };
164
+ const mapComponentEdges = (edges) => edges.map((edge) => edge.node);
165
+ const getComponentsList = async (client, query, first, libraryType) => {
166
+ const variables = {
167
+ first,
168
+ libraryType
169
+ };
170
+ const edges = (await client.request(query, variables))?.components?.connection?.edges ?? [];
171
+ return mapComponentEdges(edges);
172
+ };
173
+ const toResourceLabel = (key) => key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (char) => char.toUpperCase());
174
+ const pollResourceModel = Object.entries(POLL_RESOURCE_CONFIG).map(([value]) => ({
175
+ label: toResourceLabel(value),
176
+ value
177
+ }));
178
+ const readConnection = (data, dataPath) => {
179
+ let current = data;
180
+ for (const key of dataPath) if (current && typeof current === "object") current = current[key];
181
+ else return;
182
+ return current;
183
+ };
184
+ const fetchAllSince = async (client, resourceType, lastPolledAt, options = {}) => {
185
+ const config = POLL_RESOURCE_CONFIG[resourceType];
186
+ if (!config) throw new Error(`Unsupported resource type: ${resourceType}`);
187
+ const { debug, logger } = options;
188
+ const lastPolledAtDate = new Date(lastPolledAt);
189
+ const records = [];
190
+ let after = options.after;
191
+ let page = 0;
192
+ while (page < 50) {
193
+ page += 1;
194
+ const variables = config.buildVariables(100, after);
195
+ const data = await client.request(config.query, variables);
196
+ const connection = readConnection(data, config.dataPath);
197
+ const edges = connection?.edges ?? [];
198
+ let stop = false;
199
+ for (const edge of edges) {
200
+ const node = edge?.node;
201
+ if (!node) continue;
202
+ records.push(node);
203
+ if (config.earlyStop && typeof node.lastModified === "string" && new Date(node.lastModified) <= lastPolledAtDate) {
204
+ stop = true;
205
+ break;
206
+ }
207
+ }
208
+ if (stop) return {
209
+ records,
210
+ truncated: false
211
+ };
212
+ const pageInfo = connection?.pageInfo;
213
+ if (!pageInfo?.hasNextPage || !pageInfo.endCursor) return {
214
+ records,
215
+ truncated: false
216
+ };
217
+ after = pageInfo.endCursor;
218
+ }
219
+ if (debug && logger) logger.warn(`Reached MAX_POLL_PAGES (50) while polling ${resourceType}; results truncated. The next poll resumes from the held cursor.`);
220
+ return {
221
+ records,
222
+ truncated: true,
223
+ endCursor: after
224
+ };
225
+ };
226
+ const resolvePollingStart = (state, lookBackDate, now) => ({
227
+ since: state?.lastPolledAt ?? (lookBackDate || now),
228
+ isBackfill: state?.backfillActive === true || !state?.lastPolledAt && Boolean(lookBackDate)
229
+ });
230
+ const resolvePollingStream = (state, resourceType, now) => {
231
+ return Boolean(state?.endCursor) && Boolean(state?.streamStartedAt) && state?.pollResourceType === resourceType ? {
232
+ streamStartedAt: state?.streamStartedAt,
233
+ endCursor: state?.endCursor
234
+ } : { streamStartedAt: now };
235
+ };
236
+ const nextPollingState = (round, endCursor) => endCursor ? {
237
+ lastPolledAt: round.lastPolledAt,
238
+ endCursor,
239
+ streamStartedAt: round.stream.streamStartedAt,
240
+ pollResourceType: round.resourceType,
241
+ ...round.isBackfill ? { backfillActive: true } : {}
242
+ } : { lastPolledAt: round.stream.streamStartedAt };
243
+ const lookBackDateClean = (value) => {
244
+ if (value === void 0 || value === null || typeof value === "string" && value.trim() === "") return "";
245
+ const raw = typeof value === "string" ? value.trim() : String(value);
246
+ const match = typeof value === "string" ? raw.match(LOOK_BACK_DATE_PATTERN) : null;
247
+ if (!match) throw new Error(`Look-back Date must be a date in YYYY-MM-DD format. Received: ${raw}`);
248
+ const [, yearStr, monthStr, dayStr] = match;
249
+ const year = Number(yearStr);
250
+ const month = Number(monthStr);
251
+ const day = Number(dayStr);
252
+ const parsed = new Date(Date.UTC(year, month - 1, day));
253
+ if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() !== month - 1 || parsed.getUTCDate() !== day) throw new Error(`Look-back Date must be a date in YYYY-MM-DD format. Received: ${raw}`);
254
+ if (parsed.getTime() > Date.now()) throw new Error(`Look-back Date cannot be a future date. Received: ${raw}`);
255
+ return parsed.toISOString();
256
+ };
257
+ const classifyRecords = (records, lastPolledAt, options) => {
258
+ const lastPolledAtDate = new Date(lastPolledAt);
259
+ const created = [];
260
+ const updated = [];
261
+ for (const record of records) {
262
+ const createdDate = typeof record.created === "string" ? new Date(record.created) : null;
263
+ const lastModifiedDate = typeof record.lastModified === "string" ? new Date(record.lastModified) : null;
264
+ const isNew = createdDate !== null && createdDate > lastPolledAtDate;
265
+ const isUpdated = !isNew && lastModifiedDate !== null && lastModifiedDate > lastPolledAtDate;
266
+ if (isNew && (options.isBackfill || options.showNewRecords)) created.push(record);
267
+ else if (isUpdated && (options.isBackfill || options.showUpdatedRecords)) updated.push(record);
268
+ }
269
+ return {
270
+ created,
271
+ updated
272
+ };
273
+ };
274
+ const resolveRecordChanges = (data) => {
275
+ const changesObject = data ?? {};
276
+ return [...(changesObject.created ?? []).map((record) => ({
277
+ changeType: "created",
278
+ record
279
+ })), ...(changesObject.updated ?? []).map((record) => ({
280
+ changeType: "updated",
281
+ record
282
+ }))];
283
+ };
284
+ //#endregion
285
+ //#region src/client.ts
286
+ const createDuroClient = (connection, debug) => {
287
+ validateConnection(connection);
288
+ const { apiKey, url } = getCredentials(connection);
289
+ return new graphql_request.GraphQLClient(url, {
290
+ headers: { apiToken: apiKey },
291
+ requestMiddleware: (request) => {
292
+ if (debug) console.log(JSON.stringify(request));
293
+ return request;
294
+ },
295
+ responseMiddleware: (response) => {
296
+ if (debug) console.log(JSON.stringify(response));
297
+ return response;
298
+ }
299
+ });
300
+ };
301
+ //#endregion
302
+ //#region src/inputs/common.ts
303
+ const connection = (0, _prismatic_io_spectral.input)({
304
+ label: "Connection",
305
+ type: "connection",
306
+ required: true,
307
+ comments: "The Duro PLM connection to use."
308
+ });
309
+ const first = (0, _prismatic_io_spectral.input)({
310
+ label: "First N Items",
311
+ comments: "The number of items to return.",
312
+ example: "10",
313
+ placeholder: "Enter number of items",
314
+ type: "string",
315
+ required: false,
316
+ clean: cleanNumberInput
317
+ });
318
+ //#endregion
319
+ //#region src/inputs/company.ts
320
+ const listCompanyUsersInputs = { connection };
321
+ //#endregion
322
+ //#region src/examplePayloads.ts
323
+ const getCurrentUserExamplePayload = { data: { userById: {
324
+ lastName: "Smith",
325
+ email: "john.smith@example.com",
326
+ firstName: "John",
327
+ id: "1234567890abcdef12345678",
328
+ primaryCompany: {
329
+ id: "abcdef1234567890abcdef12",
330
+ name: "ExampleCorp"
331
+ }
332
+ } } };
333
+ const listCompanyUsersExamplePayload = { data: {
334
+ id: "abcdef1234567890abcdef12",
335
+ name: "ExampleCorp",
336
+ users: [{
337
+ email: "john.doe@example.com",
338
+ id: "1234567890abcdef12345678",
339
+ firstName: "John",
340
+ lastName: "Doe",
341
+ created: "2024-06-14T14:57:37.227Z",
342
+ role: "ADMINISTRATOR",
343
+ title: "CEO",
344
+ lastLogin: "2024-06-24T16:11:02.889Z"
345
+ }]
346
+ } };
347
+ const getComponentByIdExamplePayload = { data: { componentsByIds: [{
348
+ id: "abcdef1234567890abcdef12",
349
+ cpn: { displayValue: "123-45678" },
350
+ created: "2024-06-14T14:58:29.791Z",
351
+ category: "Integrated Circuit",
352
+ archived: false,
353
+ customSpecs: [],
354
+ description: "A high-performance integrated circuit for advanced applications.",
355
+ documentLinks: [],
356
+ eid: "20-4567-B2",
357
+ family: "IC",
358
+ images: [{
359
+ mime: "JPEG",
360
+ creator: { email: "john.doe@example.com" },
361
+ name: "ic_image.jpg",
362
+ src: "https://example.com/ic_image.jpg"
363
+ }],
364
+ legacyCpn: "123-45678",
365
+ lastModified: "2024-06-14T14:58:30.229Z",
366
+ manufacturers: [],
367
+ name: "High-Performance IC",
368
+ primarySource: {
369
+ dpn: "DP1234",
370
+ distributor: "Distributor Inc.",
371
+ manufacturer: "IC Manufacturer Ltd.",
372
+ minQuantity: 10,
373
+ mpn: "MP1234",
374
+ unitPrice: 5.99
375
+ },
376
+ specs: [],
377
+ status: "PRODUCTION",
378
+ revisionValue: "1",
379
+ workflowState: "Approved",
380
+ vendorId: "V1234",
381
+ creator: {
382
+ id: "1234567890abcdef12345678",
383
+ email: "john.doe@example.com",
384
+ firstName: "John",
385
+ lastName: "Doe"
386
+ },
387
+ revisionHistory: [{
388
+ id: "abcdef1234567890abcdef34",
389
+ cpn: { displayValue: "123-45678" },
390
+ revisionValue: "1"
391
+ }],
392
+ children: [{ component: { id: "abcdef1234567890abcdef56" } }, { component: { id: "abcdef1234567890abcdef78" } }],
393
+ modified: false,
394
+ imageIds: ["abcdef1234567890abcdef90"]
395
+ }] } };
396
+ const createChangeOrderExamplePayload = { data: { createChangeOrder: {
397
+ id: "abcdef1234567890abcdef12",
398
+ con: { displayValue: "ECO-12345" },
399
+ description: "Initial creation of the change order for testing purposes.",
400
+ name: "Initial Change Order",
401
+ status: "DRAFT",
402
+ type: "ECO",
403
+ created: "2024-06-24T22:31:27.808Z",
404
+ creator: {
405
+ email: "john.doe@example.com",
406
+ id: "1234567890abcdef12345678",
407
+ firstName: "John",
408
+ lastName: "Doe"
409
+ }
410
+ } } };
411
+ const listChangeOrdersExamplePayload = { data: { changeOrders: { connection: { edges: [{ node: {
412
+ con: {
413
+ id: "abcdef1234567890abcdef12",
414
+ displayValue: "ECO-12345"
415
+ },
416
+ id: "abcdef1234567890abcdef34",
417
+ name: "PCB Revision for Rev B Release",
418
+ type: "ECO",
419
+ description: "Engineering change order to update PCB layout for Rev B manufacturing release.",
420
+ documentLinks: [],
421
+ status: "DRAFT",
422
+ resolution: "NONE",
423
+ erpOptions: null,
424
+ created: "2024-06-24T22:31:27.808Z",
425
+ creator: {
426
+ email: "john.doe@example.com",
427
+ id: "1234567890abcdef12345678",
428
+ firstName: "John",
429
+ lastName: "Doe"
430
+ },
431
+ approvalType: "FIRST_IN",
432
+ lastModified: "2024-06-24T22:31:27.808Z"
433
+ } }] } } } };
434
+ const listComponentsExamplePayload = { data: [{
435
+ id: "666c5a8828e82100081595b0",
436
+ name: "CONN HEADER 10POS DUAL .05\", Keying Shroud, SMD",
437
+ mass: null,
438
+ created: "2024-06-14T14:58:16.638Z",
439
+ lastModified: "2024-06-14T14:58:18.069Z"
440
+ }] };
441
+ const rawRequestExamplePayload = { data: { componentsByIds: [{
442
+ id: "abcdef1234567890abcdef12",
443
+ name: "High-Performance IC"
444
+ }] } };
445
+ const pollChangesExamplePayload = {
446
+ payload: {
447
+ headers: {},
448
+ queryParameters: {},
449
+ rawBody: {
450
+ data: "",
451
+ contentType: "application/json"
452
+ },
453
+ body: { data: {
454
+ created: [{
455
+ id: "666c5a8828e82100081595b0",
456
+ name: "High-Performance IC",
457
+ created: "2024-06-14T14:58:16.638Z",
458
+ lastModified: "2024-06-14T14:58:16.638Z"
459
+ }],
460
+ updated: [{
461
+ id: "666c5a9528e821000815990e",
462
+ name: "Precision Resistor 10k",
463
+ created: "2024-06-10T09:12:04.118Z",
464
+ lastModified: "2024-06-14T14:58:18.069Z"
465
+ }]
466
+ } },
467
+ pathFragment: "",
468
+ webhookUrls: { "Polling Flow": "https://hooks.example.com/trigger/EXAMPLE" },
469
+ webhookApiKeys: { "Polling Flow": ["example-api-key"] },
470
+ invokeUrl: "https://hooks.example.com/trigger/EXAMPLE",
471
+ executionId: "SW5zdGFuY2VFeGVjdXRpb246MTIzNDU=",
472
+ customer: {
473
+ id: "Q3VzdG9tZXI6MTIzNDU=",
474
+ externalId: "example-customer-external-id",
475
+ name: "Example Customer"
476
+ },
477
+ instance: {
478
+ id: "SW5zdGFuY2U6MTIzNDU=",
479
+ name: "Example Instance"
480
+ },
481
+ user: {
482
+ id: "VXNlcjoxMjM0NQ==",
483
+ externalId: "example-user-external-id",
484
+ name: "Example User",
485
+ email: "user@example.com"
486
+ }
487
+ },
488
+ polledNoChanges: false
489
+ };
490
+ //#endregion
491
+ //#region src/actions/company/index.ts
492
+ var company_default = { listCompanyUsers: (0, _prismatic_io_spectral.action)({
493
+ display: {
494
+ label: "List Company Users",
495
+ description: "Retrieves account information for each user in the company library."
496
+ },
497
+ inputs: listCompanyUsersInputs,
498
+ perform: async (context, { connection }) => {
499
+ const client = createDuroClient(connection, context.debug.enabled);
500
+ const query = graphql_request.gql`
501
+ query UserById {
502
+ userById {
503
+ primaryCompany {
504
+ id
505
+ name
506
+ users {
507
+ email
508
+ id
509
+ firstName
510
+ lastName
511
+ created
512
+ role
513
+ title
514
+ lastLogin
515
+ }
516
+ }
517
+ }
518
+ }
519
+ `;
520
+ return { data: (await client.request(query)).userById.primaryCompany };
521
+ },
522
+ examplePayload: listCompanyUsersExamplePayload
523
+ }) };
524
+ //#endregion
525
+ //#region src/inputs/components.ts
526
+ const libraryType = (0, _prismatic_io_spectral.input)({
527
+ label: "Library Type",
528
+ comments: "The type of library to query. Company (GENERAL) contains shared production components, while Sandbox (PERSONAL) contains personal or test components.",
529
+ example: "GENERAL",
530
+ placeholder: "Select Library Type",
531
+ type: "string",
532
+ required: true,
533
+ clean: cleanStringInput,
534
+ model: [{
535
+ label: "Company",
536
+ value: "GENERAL"
537
+ }, {
538
+ label: "Sandbox",
539
+ value: "PERSONAL"
540
+ }]
541
+ });
542
+ const componentId = (0, _prismatic_io_spectral.input)({
543
+ label: "Component ID",
544
+ comments: "The unique identifier for the component to retrieve.",
545
+ type: "string",
546
+ required: true,
547
+ example: "666c5a9528e821000815990e",
548
+ placeholder: "Enter Component ID",
549
+ clean: cleanStringInput,
550
+ dataSource: "selectComponent"
551
+ });
552
+ const listComponentsInputs = {
553
+ connection,
554
+ libraryType,
555
+ first
556
+ };
557
+ const getComponentByIdInputs = {
558
+ connection,
559
+ componentId
560
+ };
561
+ //#endregion
562
+ //#region src/actions/components/index.ts
563
+ var components_default$1 = {
564
+ getComponentById: (0, _prismatic_io_spectral.action)({
565
+ display: {
566
+ label: "Get Component by ID",
567
+ description: "Get a specific component by a unique identifier"
568
+ },
569
+ inputs: getComponentByIdInputs,
570
+ perform: async (context, { connection, componentId }) => {
571
+ const client = createDuroClient(connection, context.debug.enabled);
572
+ const query = graphql_request.gql`
573
+ query ComponentsByIds($ids: [ID]) {
574
+ componentsByIds(ids: $ids) {
575
+ id
576
+ cpn {
577
+ displayValue
578
+ }
579
+ created
580
+ category
581
+ archived
582
+ customSpecs {
583
+ key
584
+ specId
585
+ value
586
+ }
587
+ description
588
+ documentLinks {
589
+ document {
590
+ src
591
+ name
592
+ mime
593
+ id
594
+ archived
595
+ url
596
+ }
597
+ specs {
598
+ status
599
+ type
600
+ revision
601
+ lastModified
602
+ }
603
+ }
604
+ eid
605
+ family
606
+ images {
607
+ mime
608
+ creator {
609
+ email
610
+ }
611
+ name
612
+ src
613
+ }
614
+ legacyCpn
615
+ lastModified
616
+ manufacturers {
617
+ name
618
+ description
619
+ mpn {
620
+ key
621
+ src
622
+ }
623
+ }
624
+ name
625
+ primarySource {
626
+ dpn
627
+ distributor
628
+ manufacturer
629
+ minQuantity
630
+ mpn
631
+ unitPrice
632
+ }
633
+ specs {
634
+ key
635
+ value
636
+ }
637
+ status
638
+ revisionValue
639
+ workflowState
640
+ vendorId
641
+ creator {
642
+ id
643
+ email
644
+ firstName
645
+ lastName
646
+ }
647
+ revisionHistory {
648
+ id
649
+ cpn {
650
+ displayValue
651
+ }
652
+ revisionValue
653
+ }
654
+ children {
655
+ component {
656
+ id
657
+ }
658
+ }
659
+ modified
660
+ imageIds
661
+ }
662
+ }
663
+ `;
664
+ const variables = { ids: [componentId] };
665
+ return { data: await client.request(query, variables) };
666
+ },
667
+ examplePayload: getComponentByIdExamplePayload
668
+ }),
669
+ listComponents: (0, _prismatic_io_spectral.action)({
670
+ display: {
671
+ label: "List Components",
672
+ description: "Retrieves a list of components."
673
+ },
674
+ inputs: listComponentsInputs,
675
+ perform: async (context, { connection, libraryType, first }) => {
676
+ return { data: await getComponentsList(createDuroClient(connection, context.debug.enabled), graphql_request.gql`
677
+ query Components($first: Int, $libraryType: LibraryType) {
678
+ components(libraryType: $libraryType) {
679
+ connection(first: $first) {
680
+ edges {
681
+ node {
682
+ id
683
+ name
684
+ mass
685
+ created
686
+ lastModified
687
+ }
688
+ }
689
+ }
690
+ }
691
+ }
692
+ `, first ?? 100, libraryType ?? "GENERAL") };
693
+ },
694
+ examplePayload: listComponentsExamplePayload
695
+ })
696
+ };
697
+ const rawRequestInputs = {
698
+ connection,
699
+ query: (0, _prismatic_io_spectral.input)({
700
+ label: "Query or Mutation",
701
+ type: "code",
702
+ required: true,
703
+ language: "graphql",
704
+ comments: "GraphQL query or mutation to execute against the Duro API. See the Duro GraphQL API documentation for available queries and mutations.",
705
+ placeholder: "Enter GraphQL query or mutation",
706
+ default: GRAPHQL_EXAMPLE,
707
+ example: GRAPHQL_EXAMPLE,
708
+ clean: cleanStringInput
709
+ }),
710
+ variables: (0, _prismatic_io_spectral.input)({
711
+ label: "Variables",
712
+ type: "string",
713
+ required: false,
714
+ collection: "keyvaluelist",
715
+ comments: "GraphQL variables to pass to the query or mutation. Each variable should be a key-value pair.",
716
+ example: "{\"userId\": \"12345\", \"status\": \"active\"}",
717
+ placeholder: "Enter key-value pairs",
718
+ clean: cleanKeyValueListInput
719
+ }),
720
+ variablesObject: (0, _prismatic_io_spectral.input)({
721
+ label: "Variables Object",
722
+ type: "code",
723
+ comments: "Alternative way to provide GraphQL variables as a JSON object. Use this instead of the Variables field for complex nested variables.",
724
+ placeholder: "Enter GraphQL variables as JSON",
725
+ example: JSON.stringify({
726
+ key1: "value1",
727
+ key2: "value2"
728
+ }, null, 2),
729
+ language: "json",
730
+ required: false,
731
+ clean: cleanCodeInput
732
+ })
733
+ };
734
+ //#endregion
735
+ //#region src/actions/misc/index.ts
736
+ var misc_default = { rawRequest: (0, _prismatic_io_spectral.action)({
737
+ display: {
738
+ label: "Raw Request",
739
+ description: "Make a generic request to the Duro API"
740
+ },
741
+ inputs: rawRequestInputs,
742
+ perform: async (context, { connection, query, variables, variablesObject }) => {
743
+ return { data: await createDuroClient(connection, context.debug.enabled).request(query ?? "", {
744
+ ...variables,
745
+ ...variablesObject
746
+ }) };
747
+ },
748
+ examplePayload: rawRequestExamplePayload
749
+ }) };
750
+ //#endregion
751
+ //#region src/inputs/orders.ts
752
+ const name = (0, _prismatic_io_spectral.input)({
753
+ label: "Name",
754
+ type: "string",
755
+ comments: "The name of the change order to create.",
756
+ example: "ECO-2024-001: Update Component Materials",
757
+ placeholder: "Enter change order name",
758
+ required: true,
759
+ clean: cleanStringInput
760
+ });
761
+ const description = (0, _prismatic_io_spectral.input)({
762
+ label: "Description",
763
+ type: "text",
764
+ required: false,
765
+ comments: "A detailed description of the change order, including the reason for the change and expected impact.",
766
+ example: "Replace aluminum housing with stainless steel to improve corrosion resistance in marine environments.",
767
+ placeholder: "Enter change order description",
768
+ clean: cleanStringInput
769
+ });
770
+ const type = (0, _prismatic_io_spectral.input)({
771
+ label: "Type",
772
+ type: "string",
773
+ required: true,
774
+ comments: "The type of change order to create. ECO (Engineering Change Order), MCO (Manufacturing Change Order), or DCO (Document Change Order).",
775
+ clean: cleanStringInput,
776
+ default: "ECO",
777
+ placeholder: "Select change order type",
778
+ model: [
779
+ {
780
+ label: "ECO (Engineering Change Order)",
781
+ value: "ECO"
782
+ },
783
+ {
784
+ label: "MCO (Manufacturing Change Order)",
785
+ value: "MCO"
786
+ },
787
+ {
788
+ label: "DCO (Document Change Order)",
789
+ value: "DCO"
790
+ }
791
+ ],
792
+ example: "ECO"
793
+ });
794
+ const orderBy = (0, _prismatic_io_spectral.input)({
795
+ label: "Order By",
796
+ comments: "The field and direction to sort the change orders by. Results will be returned in the specified order.",
797
+ example: "[{\"con\": \"asc\"}]",
798
+ placeholder: "Select sort order",
799
+ type: "string",
800
+ required: false,
801
+ clean: parseJsonArray,
802
+ model: [
803
+ {
804
+ label: "ID: Ascending",
805
+ value: "[{\"con\": \"asc\"}]"
806
+ },
807
+ {
808
+ label: "ID: Descending",
809
+ value: "[{\"con\": \"desc\"}]"
810
+ },
811
+ {
812
+ label: "Last Modified: Ascending",
813
+ value: "[{\"lastModified\": \"asc\"}]"
814
+ },
815
+ {
816
+ label: "Last Modified: Descending",
817
+ value: "[{\"lastModified\": \"desc\"}]"
818
+ },
819
+ {
820
+ label: "Name: Ascending",
821
+ value: "[{\"name\": \"asc\"}]"
822
+ },
823
+ {
824
+ label: "Name: Descending",
825
+ value: "[{\"name\": \"desc\"}]"
826
+ }
827
+ ]
828
+ });
829
+ const createChangeOrderInputs = {
830
+ connection,
831
+ name,
832
+ description,
833
+ type
834
+ };
835
+ const listChangeOrdersInputs = {
836
+ connection,
837
+ orderBy,
838
+ first
839
+ };
840
+ //#endregion
841
+ //#region src/actions/orders/createChangeOrder.ts
842
+ const createChangeOrder = (0, _prismatic_io_spectral.action)({
843
+ display: {
844
+ label: "Create Change Order",
845
+ description: "Creates a draft change order."
846
+ },
847
+ inputs: createChangeOrderInputs,
848
+ perform: async (context, { connection, name, description, type }) => {
849
+ const client = createDuroClient(connection, context.debug.enabled);
850
+ const query = graphql_request.gql`
851
+ mutation CreateChangeOrder($input: CreateChangeOrderInput) {
852
+ createChangeOrder(input: $input) {
853
+ id
854
+ con {
855
+ displayValue
856
+ }
857
+ description
858
+ name
859
+ status
860
+ type
861
+ created
862
+ creator {
863
+ email
864
+ id
865
+ firstName
866
+ lastName
867
+ }
868
+ }
869
+ }
870
+ `;
871
+ const variables = { input: {
872
+ name,
873
+ description,
874
+ type
875
+ } };
876
+ return { data: await client.request(query, variables) };
877
+ },
878
+ examplePayload: createChangeOrderExamplePayload
879
+ });
880
+ //#endregion
881
+ //#region src/actions/orders/index.ts
882
+ var orders_default = {
883
+ listChangeOrders: (0, _prismatic_io_spectral.action)({
884
+ display: {
885
+ label: "List Change Orders",
886
+ description: "Retrieves a list of change orders."
887
+ },
888
+ inputs: listChangeOrdersInputs,
889
+ perform: async (context, { connection, orderBy, first }) => {
890
+ const client = createDuroClient(connection, context.debug.enabled);
891
+ const query = graphql_request.gql`
892
+ query ChangeOrders($first: Int, $orderBy: [ChangeOrdersOrderByInput]) {
893
+ changeOrders(orderBy: $orderBy) {
894
+ connection(first: $first) {
895
+ edges {
896
+ node {
897
+ con {
898
+ id
899
+ displayValue
900
+ }
901
+ id
902
+ name
903
+ type
904
+ description
905
+ documentLinks {
906
+ document {
907
+ id
908
+ size
909
+ src
910
+ name
911
+ url
912
+ status
913
+ created
914
+ }
915
+ }
916
+ status
917
+ resolution
918
+ erpOptions {
919
+ effectivity {
920
+ endDate
921
+ startDate
922
+ }
923
+ itemType {
924
+ value
925
+ }
926
+ }
927
+ created
928
+ creator {
929
+ email
930
+ id
931
+ firstName
932
+ lastName
933
+ }
934
+ approvalType
935
+ lastModified
936
+ }
937
+ }
938
+ }
939
+ }
940
+ }
941
+ `;
942
+ const variables = {
943
+ first: first ?? 100,
944
+ orderBy
945
+ };
946
+ return { data: await client.request(query, variables) };
947
+ },
948
+ examplePayload: listChangeOrdersExamplePayload
949
+ }),
950
+ createChangeOrder
951
+ };
952
+ //#endregion
953
+ //#region src/inputs/users.ts
954
+ const getCurrentUserInputs = { connection };
955
+ //#endregion
956
+ //#region src/actions/users/index.ts
957
+ var users_default = { getCurrentUser: (0, _prismatic_io_spectral.action)({
958
+ display: {
959
+ label: "Get Current User",
960
+ description: "Get information about the currently authenticated user"
961
+ },
962
+ inputs: getCurrentUserInputs,
963
+ perform: async (context, { connection }) => {
964
+ const client = createDuroClient(connection, context.debug.enabled);
965
+ const query = graphql_request.gql`
966
+ query UserById {
967
+ userById {
968
+ lastName
969
+ email
970
+ firstName
971
+ id
972
+ primaryCompany {
973
+ id
974
+ name
975
+ }
976
+ }
977
+ }
978
+ `;
979
+ return { data: await client.request(query) };
980
+ },
981
+ examplePayload: getCurrentUserExamplePayload
982
+ }) };
983
+ //#endregion
984
+ //#region src/actions/index.ts
985
+ var actions_default = {
986
+ ...company_default,
987
+ ...components_default$1,
988
+ ...misc_default,
989
+ ...orders_default,
990
+ ...users_default
991
+ };
992
+ //#endregion
993
+ //#region src/inputs/dataSources.ts
994
+ const selectComponentInputs = listComponentsInputs;
995
+ //#endregion
996
+ //#region src/dataSources/index.ts
997
+ var dataSources_default = { selectComponent: (0, _prismatic_io_spectral.dataSource)({
998
+ display: {
999
+ label: "Select Component",
1000
+ description: "A picklist of components in your library."
1001
+ },
1002
+ inputs: selectComponentInputs,
1003
+ dataSourceType: "picklist",
1004
+ perform: async (_context, { connection, libraryType, first }) => {
1005
+ return { result: (await getComponentsList(createDuroClient(connection, false), graphql_request.gql`
1006
+ query Components($first: Int, $libraryType: LibraryType) {
1007
+ components(libraryType: $libraryType) {
1008
+ connection(first: $first) {
1009
+ edges {
1010
+ node {
1011
+ id
1012
+ name
1013
+ }
1014
+ }
1015
+ }
1016
+ }
1017
+ }
1018
+ `, first ?? 100, libraryType ?? "GENERAL")).map((component) => ({
1019
+ key: component.id,
1020
+ label: component.name
1021
+ })) };
1022
+ }
1023
+ }) };
1024
+ const pollChangesInputs = {
1025
+ connection,
1026
+ pollResourceType: (0, _prismatic_io_spectral.input)({
1027
+ label: "Resource Type",
1028
+ type: "string",
1029
+ required: true,
1030
+ model: pollResourceModel,
1031
+ comments: "The type of resource to poll for changes.",
1032
+ example: "components",
1033
+ placeholder: "Select resource type",
1034
+ clean: _prismatic_io_spectral.util.types.toString
1035
+ }),
1036
+ lookBackDate: (0, _prismatic_io_spectral.input)({
1037
+ label: "Look-back Date",
1038
+ placeholder: "Enter look-back date (YYYY-MM-DD)",
1039
+ type: "string",
1040
+ required: false,
1041
+ comments: "The date the initial sync starts from, in YYYY-MM-DD format. Cannot be a future date. Leave empty to start from the first recurrence with no backfill. When set, the initial sync seeds each record modified on or after this date once, ignoring the visibility filters.",
1042
+ example: "2026-01-01",
1043
+ clean: lookBackDateClean
1044
+ }),
1045
+ showNewRecords: (0, _prismatic_io_spectral.input)({
1046
+ label: "Show New Records",
1047
+ type: "boolean",
1048
+ required: false,
1049
+ default: "true",
1050
+ comments: "When true, newly created records are included in the trigger output.",
1051
+ clean: _prismatic_io_spectral.util.types.toBool
1052
+ }),
1053
+ showUpdatedRecords: (0, _prismatic_io_spectral.input)({
1054
+ label: "Show Updated Records",
1055
+ type: "boolean",
1056
+ required: false,
1057
+ default: "true",
1058
+ comments: "When true, records updated after the last poll are included in the trigger output.",
1059
+ clean: _prismatic_io_spectral.util.types.toBool
1060
+ })
1061
+ };
1062
+ //#endregion
1063
+ //#region src/triggers/index.ts
1064
+ var triggers_default = { pollChangesTrigger: (0, _prismatic_io_spectral.pollingTrigger)({
1065
+ display: {
1066
+ label: "New and Updated Records",
1067
+ description: "Retrieves existing and ongoing components or change orders in Duro. Load history once, check for changes on a schedule, or both."
1068
+ },
1069
+ inputs: pollChangesInputs,
1070
+ examplePayload: pollChangesExamplePayload,
1071
+ triggerResolverSupport: "valid",
1072
+ batchConfig: { batchSize: 50 },
1073
+ triggerResolver: { resolveItems: (_context, { payload }) => resolveRecordChanges(payload.body.data) },
1074
+ perform: async (context, payload, params) => {
1075
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1076
+ const pollState = context.polling.getState();
1077
+ const { since: lastPolledAt, isBackfill } = resolvePollingStart(pollState, params.lookBackDate, now);
1078
+ const stream = resolvePollingStream(pollState, params.pollResourceType, now);
1079
+ if (!POLL_RESOURCE_CONFIG[params.pollResourceType]) throw new Error(`Unsupported resource type: ${params.pollResourceType}`);
1080
+ const { records, truncated, endCursor } = await fetchAllSince(createDuroClient(params.connection, context.debug.enabled), params.pollResourceType, lastPolledAt, {
1081
+ after: stream.endCursor,
1082
+ debug: context.debug.enabled,
1083
+ logger: context.logger
1084
+ });
1085
+ const { created, updated } = classifyRecords(records, lastPolledAt, {
1086
+ ...params,
1087
+ isBackfill
1088
+ });
1089
+ const totalMatched = created.length + updated.length;
1090
+ context.polling.setState(nextPollingState({
1091
+ lastPolledAt,
1092
+ stream,
1093
+ resourceType: params.pollResourceType,
1094
+ isBackfill
1095
+ }, truncated ? endCursor : void 0));
1096
+ if (truncated) context.logger.warn(`Polling truncated at the page cap for ${params.pollResourceType}. Holding cursor at ${lastPolledAt}; the next poll resumes from the stored cursor.`);
1097
+ if (context.debug.enabled) context.logger.debug(`Polled ${params.pollResourceType}: ${records.length} fetched, ${created.length} created, ${updated.length} updated, truncated=${truncated}`);
1098
+ return {
1099
+ payload: {
1100
+ ...payload,
1101
+ body: { data: {
1102
+ created,
1103
+ updated
1104
+ } }
1105
+ },
1106
+ polledNoChanges: totalMatched === 0
1107
+ };
1108
+ }
1109
+ }) };
1110
+ //#endregion
1111
+ //#region src/index.ts
1112
+ var src_default = (0, _prismatic_io_spectral.component)({
1113
+ key: "duro-plm",
1114
+ public: true,
1115
+ documentationUrl: "https://prismatic.io/docs/components/duro-plm/",
1116
+ display: {
1117
+ label: "Duro PLM",
1118
+ description: "Manage products, components, and change orders in Duro PLM.",
1119
+ iconPath: "icon.png",
1120
+ category: "Application Connectors"
1121
+ },
1122
+ hooks: { error: _prismatic_io_spectral_http.handleErrors },
1123
+ actions: actions_default,
1124
+ triggers: triggers_default,
1125
+ connections: connections_default,
1126
+ dataSources: dataSources_default
1127
+ });
1128
+ //#endregion
1129
+ //#region npm-dist/.entry.ts
1130
+ var _entry_default = (0, _prismatic_io_spectral.callableComponent)(src_default);
1131
+ //#endregion
1132
+ module.exports = _entry_default;