@openfn/project 0.13.1 → 0.14.0

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/index.d.ts CHANGED
@@ -99,7 +99,7 @@ type SerializedProject = Omit<Partial<l.Project>, 'workflows'> & {
99
99
  type SerializedWorkflow = {
100
100
  id: string;
101
101
  name: string;
102
- steps: WithMeta<l.Step[]>;
102
+ steps: WithMeta<Array<l.Job | l.Trigger>>;
103
103
  openfn?: l.ProjectMeta;
104
104
  };
105
105
 
@@ -158,6 +158,11 @@ type CLIMeta = {
158
158
  alias?: string;
159
159
  forked_from?: Record<string, string>;
160
160
  };
161
+ type Credential = {
162
+ uuid: string;
163
+ name: string;
164
+ owner: string;
165
+ };
161
166
  declare class Project {
162
167
  /** Human readable project name. This corresponds to the label in Lightning */
163
168
  name?: string;
@@ -175,7 +180,7 @@ declare class Project {
175
180
  workspace?: Workspace;
176
181
  config: l__default.WorkspaceConfig;
177
182
  collections: any;
178
- credentials: string[];
183
+ credentials: Credential[];
179
184
  sandbox?: SandboxMeta;
180
185
  static from(type: 'project', data: any, options: never): Promise<Project>;
181
186
  static from(type: 'state', data: Provisioner.Project, meta?: Partial<l__default.ProjectMeta>, config?: fromAppStateConfig): Promise<Project>;
@@ -202,6 +207,11 @@ declare class Project {
202
207
  * Returns a map of ids:uuids for everything in the project
203
208
  */
204
209
  getUUIDMap(): UUIDMap;
210
+ /**
211
+ * Find all project credentials referenced in all
212
+ * workflows and return it
213
+ */
214
+ buildCredentialMap(): Credential[];
205
215
  diff(project: Project, workflows?: string[]): WorkflowDiff[];
206
216
  canMergeInto(target: Project): boolean;
207
217
  /**
package/dist/index.js CHANGED
@@ -44,6 +44,14 @@ function jsonToYaml(json) {
44
44
  return yaml.stringify(doc, null, 2);
45
45
  }
46
46
 
47
+ // src/util/get-credential-name.ts
48
+ var DELIMETER = "|";
49
+ var get_credential_name_default = (cred) => `${cred.owner}${DELIMETER}${cred.name}`;
50
+ var parse = (credentialName) => {
51
+ const [owner, name] = credentialName.split("|");
52
+ return { owner, name };
53
+ };
54
+
47
55
  // src/serialize/to-app-state.ts
48
56
  var defaultJobProps = {
49
57
  // TODO why does the provisioner throw if these keys are not set?
@@ -58,6 +66,8 @@ function to_app_state_default(project, options = {}) {
58
66
  env,
59
67
  id,
60
68
  fetched_at,
69
+ alias,
70
+ // shouldn't be written but has been caught in some legacy files
61
71
  ...rest
62
72
  } = project.openfn ?? {};
63
73
  const state = omitBy(
@@ -66,8 +76,17 @@ function to_app_state_default(project, options = {}) {
66
76
  );
67
77
  state.id = uuid ?? randomUUID();
68
78
  Object.assign(state, rest, project.options);
69
- state.project_credentials = project.credentials ?? [];
70
- state.workflows = project.workflows.map(mapWorkflow).reduce((obj, wf) => {
79
+ const credentialsWithUuids = project.credentials?.map((c) => ({
80
+ ...c,
81
+ uuid: c.uuid ?? randomUUID()
82
+ })) ?? [];
83
+ state.project_credentials = credentialsWithUuids.map((c) => ({
84
+ // note the subtle conversion here
85
+ id: c.uuid,
86
+ name: c.name,
87
+ owner: c.owner
88
+ }));
89
+ state.workflows = project.workflows.map((w) => mapWorkflow(w, credentialsWithUuids)).reduce((obj, wf) => {
71
90
  obj[slugify(wf.name ?? wf.id)] = wf;
72
91
  return obj;
73
92
  }, {});
@@ -77,7 +96,7 @@ function to_app_state_default(project, options = {}) {
77
96
  }
78
97
  return state;
79
98
  }
80
- var mapWorkflow = (workflow) => {
99
+ var mapWorkflow = (workflow, credentials = []) => {
81
100
  if (workflow instanceof Workflow_default) {
82
101
  workflow = workflow.toJSON();
83
102
  }
@@ -122,7 +141,21 @@ var mapWorkflow = (workflow) => {
122
141
  node.body = s.expression;
123
142
  }
124
143
  if (typeof s.configuration === "string" && !s.configuration.endsWith(".json")) {
125
- otherOpenFnProps.project_credential_id = s.configuration;
144
+ let projectCredentialId = s.configuration;
145
+ if (projectCredentialId) {
146
+ const mappedCredential = credentials.find((c) => {
147
+ const name = get_credential_name_default(c);
148
+ return name === projectCredentialId;
149
+ });
150
+ if (mappedCredential) {
151
+ projectCredentialId = mappedCredential.uuid;
152
+ } else {
153
+ console.warn(`WARING! Failed to map credential ${projectCredentialId} - Lightning may throw an error.
154
+
155
+ Ensure the credential exists in project.yaml and try again (maybe ensure the credential is attached to the project in the app and run project fetch)`);
156
+ }
157
+ otherOpenFnProps.project_credential_id = projectCredentialId;
158
+ }
126
159
  }
127
160
  Object.assign(node, defaultJobProps, otherOpenFnProps);
128
161
  wfState.jobs[s.id ?? slugify(s.name)] = node;
@@ -175,7 +208,7 @@ var SHORT_HASH_LENGTH = 12;
175
208
  function isDefined(v) {
176
209
  return v !== void 0 && v !== null;
177
210
  }
178
- var parse = (version) => {
211
+ var parse2 = (version) => {
179
212
  if (version.match(":")) {
180
213
  const [source, hash] = version.split(":");
181
214
  return { source, hash };
@@ -183,7 +216,7 @@ var parse = (version) => {
183
216
  return { hash: version };
184
217
  };
185
218
  var match = (a, b) => {
186
- return parse(a).hash === parse(b).hash;
219
+ return parse2(a).hash === parse2(b).hash;
187
220
  };
188
221
  var generateHash = (workflow, { source = "cli", sha = true } = {}) => {
189
222
  const parts = [];
@@ -725,13 +758,18 @@ var from_app_state_default = (state, meta = {}, config = {}) => {
725
758
  name,
726
759
  description,
727
760
  workflows,
728
- project_credentials: credentials,
761
+ project_credentials = [],
729
762
  collections,
730
763
  inserted_at,
731
764
  updated_at,
732
765
  parent_id,
733
766
  ...options
734
767
  } = stateJson;
768
+ const credentials = project_credentials.map((c) => ({
769
+ uuid: c.id,
770
+ name: c.name,
771
+ owner: c.owner
772
+ }));
735
773
  const proj = {
736
774
  name,
737
775
  description: description ?? void 0,
@@ -753,7 +791,9 @@ var from_app_state_default = (state, meta = {}, config = {}) => {
753
791
  parentId: parent_id
754
792
  };
755
793
  }
756
- proj.workflows = Object.values(stateJson.workflows).map(mapWorkflow2);
794
+ proj.workflows = Object.values(stateJson.workflows).map(
795
+ (w) => mapWorkflow2(w, proj.credentials)
796
+ );
757
797
  return new Project(proj, config);
758
798
  };
759
799
  var mapEdge = (edge) => {
@@ -775,7 +815,7 @@ var mapEdge = (edge) => {
775
815
  }
776
816
  return e;
777
817
  };
778
- var mapWorkflow2 = (workflow) => {
818
+ var mapWorkflow2 = (workflow, credentials = []) => {
779
819
  const { jobs, edges, triggers, name, version_history, ...remoteProps } = workflow;
780
820
  const mapped = {
781
821
  name: workflow.name,
@@ -831,7 +871,14 @@ var mapWorkflow2 = (workflow) => {
831
871
  openfn: renameKeys(remoteProps2, { id: "uuid" })
832
872
  };
833
873
  if (project_credential_id) {
834
- s.configuration = project_credential_id;
874
+ const mappedCredential = credentials.find(
875
+ (c) => c.uuid == project_credential_id
876
+ );
877
+ if (mappedCredential) {
878
+ s.configuration = get_credential_name_default(mappedCredential);
879
+ } else {
880
+ s.configuration = project_credential_id;
881
+ }
835
882
  }
836
883
  if (outboundEdges.length) {
837
884
  s.next = outboundEdges.reduce((next, edge) => {
@@ -1415,13 +1462,32 @@ Pass --force to force the merge anyway`
1415
1462
  name: source.name ?? target.name,
1416
1463
  alias: source.alias ?? target.alias,
1417
1464
  description: source.description ?? target.description,
1418
- credentials: source.credentials ?? target.credentials,
1465
+ // when mapping credentials, we prefer the UUIDs on the target
1466
+ credentials: replaceCredentials(
1467
+ source.credentials,
1468
+ target.credentials
1469
+ ),
1419
1470
  collections: source.collections ?? target.collections
1420
1471
  };
1421
1472
  return new Project(
1422
1473
  baseMerge(target, source, ["collections"], assigns)
1423
1474
  );
1424
1475
  }
1476
+ var replaceCredentials = (sourceCreds = [], targetCreds = []) => {
1477
+ const result = [...targetCreds];
1478
+ const targetCredNames = targetCreds.reduce((acc, cred) => {
1479
+ acc[get_credential_name_default(cred)] = true;
1480
+ return acc;
1481
+ }, {});
1482
+ for (const sourceCred of sourceCreds) {
1483
+ const credName = get_credential_name_default(sourceCred);
1484
+ if (!targetCredNames[credName]) {
1485
+ const { uuid, ...credWithoutUuid } = sourceCred;
1486
+ result.push(credWithoutUuid);
1487
+ }
1488
+ }
1489
+ return result;
1490
+ };
1425
1491
 
1426
1492
  // src/util/project-diff.ts
1427
1493
  function diff(a, b, workflows) {
@@ -1582,6 +1648,22 @@ var Project = class _Project {
1582
1648
  }
1583
1649
  return result;
1584
1650
  }
1651
+ /**
1652
+ * Find all project credentials referenced in all
1653
+ * workflows and return it
1654
+ */
1655
+ buildCredentialMap() {
1656
+ const creds = {};
1657
+ for (const wf of this.workflows) {
1658
+ for (const step of wf.steps) {
1659
+ if (typeof step.configuration === "string" && !creds[step.configuration]) {
1660
+ const { name, owner } = parse(step.configuration);
1661
+ creds[step.configuration] = { name, owner };
1662
+ }
1663
+ }
1664
+ }
1665
+ return Object.values(creds);
1666
+ }
1585
1667
  // Compare this project with another and return a list of workflow changes
1586
1668
  diff(project, workflows = []) {
1587
1669
  return diff(this, project, workflows);
@@ -1776,6 +1858,7 @@ var expectedNodeProps = [
1776
1858
  // TODO need to clarify adaptor/adaptors confusion
1777
1859
  "adaptor",
1778
1860
  "adaptors",
1861
+ "configuration",
1779
1862
  "expression",
1780
1863
  "condition",
1781
1864
  "label",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfn/project",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "Read, serialize, replicate and sync OpenFn projects",
5
5
  "type": "module",
6
6
  "exports": {