Skip to content

Commit

Permalink
Collections 0.4 (#811)
Browse files Browse the repository at this point in the history
* collections: fix some issues on delete

* collections: 0.4
  • Loading branch information
josephjclark authored Nov 1, 2024
1 parent 2edbb05 commit ed4f006
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 65 deletions.
6 changes: 6 additions & 0 deletions packages/collections/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @openfn/language-collections

## 0.4.0

### Minor Changes

- Fixed support for delete

## 0.3.0

### Minor Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/collections/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openfn/language-collections",
"version": "0.3.0",
"version": "0.4.0",
"description": "OpenFn collections adaptor",
"type": "module",
"exports": {
Expand Down
37 changes: 21 additions & 16 deletions packages/collections/src/collections.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,21 +190,27 @@ export function set(name, keyGen, values) {
*/
export function remove(name, query = {}, options = {}) {
return async state => {
const [resolvedName, resolvedQuery] = expandReferences(state, name, query);

let [resolvedName, resolvedQuery] = expandReferences(state, name, query);
if (typeof resolvedQuery === 'string') {
resolvedQuery = { key: resolvedQuery };
}
const { key, ...rest } = expandQuery(resolvedQuery);

const response = await request(
state,
getClient(state),
`${resolvedName}/${key}`,
{
method: 'DELETE',
query: rest,
}
);
let q;
let path = resolvedName;
if (key.match(/\*/) || Object.keys(rest).length) {
// request many
q = resolvedQuery;
} else {
// request one
path = `${resolvedName}/${key}`;
}
const response = await request(state, getClient(state), path, {
method: 'DELETE',
query: q,
});
const result = await response.body.json();
console.log(`Set ${result.upserts} values in collection "${name}"`);
console.log(`Removed ${result.deleted} values in collection "${name}"`);

return state;
};
Expand Down Expand Up @@ -246,14 +252,13 @@ export function each(name, query = {}, callback = () => {}) {
const response = await request(state, getClient(state), resolvedName, {
query: q,
});
console.log(`each response`, response.statusCode)

const cursor = await streamResponse(response, async ({ value, key }) => {
await callback(state, JSON.parse(value), key);
});

state.data = {
cursor
cursor,
};

return state;
Expand Down Expand Up @@ -287,7 +292,7 @@ export const streamResponse = async (response, onValue) => {
if (next.value.value === 'cursor') {
const strValue = await waitFor('stringChunk', 'nullValue');
if (strValue.name === 'nullValue') {
continue
continue;
}
cursor = strValue.value.value;
}
Expand Down Expand Up @@ -328,7 +333,7 @@ export const streamResponse = async (response, onValue) => {
isInsideItems = false;
}
}
return cursor
return cursor;
};

export const expandQuery = query => {
Expand Down
17 changes: 14 additions & 3 deletions packages/collections/src/mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,16 @@ export function API() {
}
const col = collections[name];

delete col[key];
const regex = new RegExp(key.replace('*', '(.*)'));
const removed = [];
for (const key in col) {
if (regex.test(key)) {
delete col[key];
removed.push(key);
}
}

return [key];
return removed;
};

const api = {
Expand Down Expand Up @@ -206,7 +213,11 @@ export function createServer(url = 'https://app.openfn.org') {
}

try {
const { name, key } = parsePath(req.path);
let { name, key } = parsePath(req.path);
if (!key) {
const params = new URLSearchParams(req.query || req.path.split('?')[1]);
key = params.get('key') ?? '*';
}

const keys = api.remove(name, key);

Expand Down
107 changes: 62 additions & 45 deletions packages/collections/test/Adaptor.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from 'chai';
import { MockAgent} from 'undici';
import { MockAgent } from 'undici';
import { createServer } from '../src/mock.js';
import { setMockClient, streamResponse } from '../src/collections.js';
import * as collections from '../src/collections.js';
Expand Down Expand Up @@ -128,7 +128,7 @@ describe('each', () => {

await collections.each(COLLECTION, '*')(state);

expect(state.data.cursor).to.equal('xxx')
expect(state.data.cursor).to.equal('xxx');
});
});

Expand Down Expand Up @@ -204,9 +204,9 @@ describe('get', () => {
['b-2', { id: 'b' }],
['c-3', { id: 'c' }],
]);

const result = await collections.get(COLLECTION, 'b*')(state);

// TODO this is a dummy mock cursor
expect(result.data.cursor).to.eql('xxx');
});
Expand Down Expand Up @@ -316,6 +316,19 @@ describe('remove', () => {
const result = api.byKey(COLLECTION, 'x');
expect(result).to.be.undefined;
});

it('should remove several items', async () => {
const { state } = init();
api.upsert(COLLECTION, 'x', { id: 'x' });
api.upsert(COLLECTION, 'y', { id: 'y' });

await collections.remove(COLLECTION, '*')(state);

const x = api.byKey(COLLECTION, 'x');
expect(x).to.be.undefined;
const y = api.byKey(COLLECTION, 'y');
expect(y).to.be.undefined;
});
});

describe('utils', () => {
Expand Down Expand Up @@ -366,96 +379,100 @@ describe('streamResponse', () => {
it('should stream a response with an item and cursor', async () => {
client.intercept({ path: '/collections/my-collection' }).reply(200, {
cursor: 'b',
items: [{
key: 'a',
value: "str"
}],
items: [
{
key: 'a',
value: 'str',
},
],
});


const response = await client.request({
method: 'GET',
path: '/collections/my-collection'
path: '/collections/my-collection',
});

let callbackValue;
const cursor = await streamResponse(response, ({ key, value }) => {
callbackValue = value;
})
});

expect(callbackValue).to.eql('str')
expect(cursor).to.equal('b')
})
expect(callbackValue).to.eql('str');
expect(cursor).to.equal('b');
});

it('should stream a response with an item and null cursor', async () => {
client.intercept({ path: '/collections/my-collection' }).reply(200, {
cursor: null,
items: [{
key: 'a',
value: "str"
}],
items: [
{
key: 'a',
value: 'str',
},
],
});


const response = await client.request({
method: 'GET',
path: '/collections/my-collection'
path: '/collections/my-collection',
});

let callbackValue;
const cursor = await streamResponse(response, ({ key, value }) => {
callbackValue = value;
})
});

expect(callbackValue).to.eql('str')
expect(cursor).to.equal(null)
})
expect(callbackValue).to.eql('str');
expect(cursor).to.equal(null);
});

it('should handle the cursor key coming last', async () => {
client.intercept({ path: '/collections/my-collection' }).reply(200, {
items: [{
key: 'a',
value: "str"
}],
items: [
{
key: 'a',
value: 'str',
},
],
cursor: 'b',
});


const response = await client.request({
method: 'GET',
path: '/collections/my-collection'
path: '/collections/my-collection',
});

let callbackValue;
const cursor = await streamResponse(response, ({ key, value }) => {
callbackValue = value;
})
});

expect(callbackValue).to.eql('str')
expect(cursor).to.equal('b')
})
expect(callbackValue).to.eql('str');
expect(cursor).to.equal('b');
});

it('should handle key value pairs in a different order', async () => {
client.intercept({ path: '/collections/my-collection' }).reply(200, {
items: [{
value: "str",
key: 'a',
}],
items: [
{
value: 'str',
key: 'a',
},
],
cursor: 'b',
});


const response = await client.request({
method: 'GET',
path: '/collections/my-collection'
path: '/collections/my-collection',
});

let callbackValue;
const cursor = await streamResponse(response, ({ key, value }) => {
callbackValue = value;
})
});

expect(callbackValue).to.eql('str')
expect(cursor).to.equal('b')
})
})
expect(callbackValue).to.eql('str');
expect(cursor).to.equal('b');
});
});
21 changes: 21 additions & 0 deletions packages/collections/test/mock/server.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,27 @@ describe('DELETE', () => {
expect(result).to.be.undefined;
});

it('should remove items by pattern', async () => {
api.createCollection('my-collection');
api.upsert('my-collection', 'x', { id: 'x' });
api.upsert('my-collection', 'y', { id: 'y' });

const response = await request({
method: 'DELETE',
path: 'collections/my-collection',
query: {
key: '*',
},
});

expect(response.statusCode).to.equal(200);

const x = api.byKey('my-collection', 'x');
expect(x).to.be.undefined;
const y = api.byKey('my-collection', 'y');
expect(y).to.be.undefined;
});

it('should return a JSON summary', async () => {
api.createCollection('my-collection');
api.upsert('my-collection', 'x', { id: 'x' });
Expand Down

0 comments on commit ed4f006

Please sign in to comment.