Skip to content
Merged
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
8 changes: 8 additions & 0 deletions integration-tests/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# @openfn/integration-tests-cli

## 1.0.39

### Patch Changes

- Updated dependencies [f1e8d71]
- @openfn/project@0.23.2
- @openfn/lightning-mock@2.5.9

## 1.0.38

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@openfn/integration-tests-cli",
"private": true,
"version": "1.0.38",
"version": "1.0.39",
"description": "CLI integration tests",
"author": "Open Function Group <admin@openfn.org>",
"license": "ISC",
Expand Down
80 changes: 79 additions & 1 deletion integration-tests/cli/test/sync/cross-project.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// the target is made to look exactly like the workspace

import test from 'ava';
import { randomUUID } from 'node:crypto';
import { generateProject } from '@openfn/project';
import createLightningServer from '@openfn/lightning-mock';
import { setup } from './helpers';
Expand All @@ -27,7 +28,7 @@ const code = (server: any, project: any) =>
])
);

// a project's workflows on a server, as { name: uuid }
// get a project's workflows on a server, as { name: uuid }
const uuids = (server: any, project: any) =>
Object.fromEntries(
(
Expand All @@ -37,6 +38,14 @@ const uuids = (server: any, project: any) =>
).map((wf) => [wf.name, wf.id])
);

// get a project's collections on a server, as { name: uuid }
const collectionUuids = (server: any, project: any) =>
Object.fromEntries(
(server.state.projects[project.openfn.uuid].collections as any[]).map(
(c) => [c.name, c.id]
)
);

// The workspace is pulled from cloud (Alpha and Beta). Prod has its own version of
// Alpha, and a workflow of its own (Gamma)
const genProjects = () => ({
Expand Down Expand Up @@ -92,6 +101,75 @@ test('workflows deployed to a different project get new uuids', async (t) => {
t.not(prodUuids.Alpha, cloudUuids.Alpha);
});

test('a collection deployed to a different project keeps the target uuid', async (t) => {
const { run } = initWorkspace(t);

// both projects have a "patients" collection, each with its own uuid
const cloud = generateProject(
'cloud',
['@name Alpha trigger-job(expression="fn(a)")'],
{ openfnUuid: true }
);
cloud.collections = [{ uuid: randomUUID(), name: 'patients' }];
lightning.addProject(cloud.serialize('state', { format: 'json' }));

const prod = generateProject(
'prod',
['@name Alpha trigger-job(expression="fn(old)")'],
{ openfnUuid: true }
);
prod.collections = [{ uuid: randomUUID(), name: 'patients' }];
lightning.addProject(prod.serialize('state', { format: 'json' }));

const cloudUuids = collectionUuids(lightning, cloud);
const prodPatients = collectionUuids(lightning, prod).patients;
t.not(cloudUuids.patients, prodPatients);

await run(`openfn project pull ${cloud.openfn.uuid}`);
await run(`openfn project deploy ${prod.openfn.uuid} --force`);

// matched by name, so prod keeps its own uuid rather than taking cloud's
t.deepEqual(collectionUuids(lightning, prod), { patients: prodPatients });

// and cloud is untouched
t.deepEqual(collectionUuids(lightning, cloud), cloudUuids);
});

test('a collection the workspace does not have is removed from the target on deploy', async (t) => {
const { run } = initWorkspace(t);

const cloud = generateProject(
'cloud',
['@name Alpha trigger-job(expression="fn(a)")'],
{ openfnUuid: true }
);
cloud.collections = [{ uuid: randomUUID(), name: 'patients' }];
lightning.addProject(cloud.serialize('state', { format: 'json' }));

// prod has a collection of its own, which the workspace doesn't know about
const prod = generateProject(
'prod',
['@name Alpha trigger-job(expression="fn(old)")'],
{ openfnUuid: true }
);
prod.collections = [
{ uuid: randomUUID(), name: 'patients' },
{ uuid: randomUUID(), name: 'legacy' },
];
lightning.addProject(prod.serialize('state', { format: 'json' }));

const cloudUuids = collectionUuids(lightning, cloud);

await run(`openfn project pull ${cloud.openfn.uuid}`);
await run(`openfn project deploy ${prod.openfn.uuid} --force`);

// The legacy collection should now be removed from prod
t.deepEqual(Object.keys(collectionUuids(lightning, prod)), ['patients']);

// and cloud is untouched
t.deepEqual(collectionUuids(lightning, cloud), cloudUuids);
});

test('deploy to a different project without a prompt will not remove workflows unless forced', async (t) => {
const { run } = initWorkspace(t);
const { cloud, prod } = genProjects();
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# @openfn/cli

## 1.45.1

### Patch Changes

- f1e8d71: Fix deploying to a different project failing once that project has collections
- Updated dependencies [f1e8d71]
- @openfn/project@0.23.2

## 1.45.0

### Minor Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openfn/cli",
"version": "1.45.0",
"version": "1.45.1",
"description": "CLI devtools for the OpenFn toolchain",
"repository": {
"url": "https://github.com/OpenFn/kit"
Expand Down
7 changes: 7 additions & 0 deletions packages/lightning-mock/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# @openfn/lightning-mock

## 2.5.9

### Patch Changes

- Updated dependencies [f1e8d71]
- @openfn/project@0.23.2

## 2.5.8

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/lightning-mock/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openfn/lightning-mock",
"version": "2.5.8",
"version": "2.5.9",
"private": true,
"description": "A mock Lightning server",
"main": "dist/index.js",
Expand Down
6 changes: 6 additions & 0 deletions packages/project/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @openfn/project

## 0.23.2

### Patch Changes

- f1e8d71: Fix deploying to a different project failing once that project has collections

## 0.23.1

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/project/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openfn/project",
"version": "0.23.1",
"version": "0.23.2",
"description": "Read, serialize, replicate and sync OpenFn projects",
"author": "Open Function Group <admin@openfn.org>",
"license": "ISC",
Expand Down
2 changes: 1 addition & 1 deletion packages/project/src/merge/merge-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export function merge(
source.credentials,
target.credentials
),
collections: source.collections,
collections: mergeCollections(source.collections, target.collections),
channels: mergeChannels(source.channels, target.channels),
}
: {
Expand Down
18 changes: 13 additions & 5 deletions packages/project/test/merge/merge-project.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ test('replace mode: undefined local collections leave the target collections alo
t.deepEqual(result.collections, targetCollections);
});

test('sandbox mode: source collections fully replace target collections', (t) => {
test('sandbox mode: source collections replace target collections, keeping target uuid on a name match', (t) => {
const wf = {
steps: [
{ id: 'x', name: 'X', adaptor: 'common', expression: 'fn(s => s)' },
Expand All @@ -395,17 +395,25 @@ test('sandbox mode: source collections fully replace target collections', (t) =>

const targetCollections = [
{ uuid: 'remote-uuid-1', name: 'target-collection' },
{ uuid: 'remote-uuid-2', name: 'shared-collection' },
];
// the source's own uuids belong to a different project
const sourceCollections = [
{ name: 'source-collection' },
{ uuid: 'source-uuid', name: 'shared-collection' },
];
const sourceCollections = [{ name: 'source-collection' }];

const target = createProject(wf_a, 'a', { collections: targetCollections });
const source = createProject(wf_b, 'b', { collections: sourceCollections });

const result = merge(source, target, { mode: SANDBOX_MERGE });

// b's collections are wholly replaced by a's - sandbox mode does a raw
// swap here, unlike replace mode's name-matching mergeCollections
t.deepEqual(result.collections, sourceCollections);
// a shared collection must keep the target's uuid, or the deploy tries to
// create a duplicate and Lightning rejects it
t.deepEqual(result.collections, [
{ name: 'source-collection', uuid: undefined },
{ name: 'shared-collection', uuid: 'remote-uuid-2' },
]);
});

test('sandbox mode: target channels are preserved when source has none', (t) => {
Expand Down
Loading