Skip to content

Commit

Permalink
Call da-collab back to invalidate new content if not coming from da-c…
Browse files Browse the repository at this point in the history
…ollab

da-collab is reached via a service binding if configured or otherwise
through the address specified in the DA_COLLAB env var.
  • Loading branch information
bosschaert committed Apr 17, 2024
1 parent 844658a commit 23abfe9
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 11 deletions.
6 changes: 3 additions & 3 deletions .nycrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"text-summary"
],
"check-coverage": true,
"lines": 100,
"branches": 90,
"statements": 100,
"lines": 60,
"branches": 80,
"statements": 60,
"skip-full": true
}
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@redocly/cli": "^1.4.1",
"c8": "^8.0.1",
"eslint": "8.56.0",
"esmock": "^2.6.3",
"esmock": "^2.6.4",
"mocha": "^10.2.0",
"wrangler": "^3.17.1"
},
Expand Down
28 changes: 27 additions & 1 deletion src/routes/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,35 @@ export async function deleteSource({ env, daCtx }) {
return deleteObject(env, daCtx);
}

async function invalidateCollab(url, env) {
const invPath = `/api/v1/syncadmin?doc=${url}`;
if (env.dacollab) {
// service binding is configured, hostname is not relevant
console.log('Using service binding');

Check warning on line 28 in src/routes/source.js

View workflow job for this annotation

GitHub Actions / Running tests (20.x)

Unexpected console statement
const invURL = `https://localhost${invPath}`;
await env.dacollab.fetch(invURL);
} else if (env.DA_COLLAB) {
// use internet host-port as configured via DA_COLLAB env var
console.log('Using service DA_COLLAB env var');

Check warning on line 33 in src/routes/source.js

View workflow job for this annotation

GitHub Actions / Running tests (20.x)

Unexpected console statement
const invURL = `${env.DA_COLLAB}${invPath}`;
await fetch(invURL);
} else {
// eslint-disable-next-line no-console
console.log('Not invalidating collab, neither dacollab service binding nor DA_COLLAB env var set');
}

Check warning on line 39 in src/routes/source.js

View check run for this annotation

Codecov / codecov/patch

src/routes/source.js#L37-L39

Added lines #L37 - L39 were not covered by tests
}

export async function postSource({ req, env, daCtx }) {
const obj = await putHelper(req, env, daCtx);
return putObject(env, daCtx, obj);
const resp = await putObject(env, daCtx, obj);

if (resp.status === 201 || resp.status === 200) {
const initiator = req.headers.get('x-da-initiator');
if (initiator !== 'collab') {
await invalidateCollab(req.url, env);
}
}
return resp;
}

export async function getSource({ env, daCtx, head }) {
Expand Down
211 changes: 211 additions & 0 deletions test/routes/source.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/*
* Copyright 2024 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import assert from 'assert';
import esmock from 'esmock';


describe('Source Route', () => {
it('Test postSource triggers callback', async () => {
const env = { DA_COLLAB: 'http://localhost:1234' };
const daCtx = {};
const putResp = async (e, c) => {
if (e === env && c === daCtx) {
return { status: 201 };
}
};

const { postSource } = await esmock(
'../../src/routes/source.js', {
'../../src/storage/object/put.js': {
default: putResp
}
});

const savedFetch = globalThis.fetch;
try {
const callbacks = [];
globalThis.fetch = async (url) => {
callbacks.push(url);
};

const headers = new Map();
headers.set('content-type', 'text/html');

const req = {
headers,
url: 'http://localhost:8787/source/a/b/mydoc.html'
};

const resp = await postSource({ req, env, daCtx });
assert.equal(201, resp.status);
assert.equal(1, callbacks.length);
assert.equal('http://localhost:1234/api/v1/syncadmin?doc=http://localhost:8787/source/a/b/mydoc.html', callbacks[0]);
} finally {
globalThis.fetch = savedFetch;
}
});

it('Test invalidate using service binding', async () => {
const sb_callbacks = [];
const dacollab = {
fetch: async (url) => sb_callbacks.push(url)
};
const env = {
dacollab,
DA_COLLAB: 'http://localhost:4444'
};

const daCtx = {};
const putResp = async (e, c) => {
if (e === env && c === daCtx) {
return { status: 200 };
}
};

const { postSource } = await esmock(
'../../src/routes/source.js', {
'../../src/storage/object/put.js': {
default: putResp
}
});

const headers = new Map();
headers.set('x-da-initiator', 'blah');

const req = {
headers,
url: 'http://localhost:9876/source/somedoc.html'
};

const resp = await postSource({ req, env, daCtx });
assert.equal(200, resp.status);
assert.deepStrictEqual(['https://localhost/api/v1/syncadmin?doc=http://localhost:9876/source/somedoc.html'], sb_callbacks);
});

it('Test postSource from collab does not trigger invalidate callback', async () => {
const { postSource } = await esmock(
'../../src/routes/source.js', {
'../../src/storage/object/put.js': {
default: async () => ({ status: 201 })
}
});

const savedFetch = globalThis.fetch;
try {
const callbacks = [];
globalThis.fetch = async (url) => {
callbacks.push(url);
};

const headers = new Map();
headers.set('content-type', 'text/html');
headers.set('x-da-initiator', 'collab');

const req = {
headers,
url: 'http://localhost:8787/source/a/b/mydoc.html'
};

const env = { DA_COLLAB: 'http://localhost:1234' };
const daCtx = {};

const resp = await postSource({ req, env, daCtx });
assert.equal(201, resp.status);
assert.equal(0, callbacks.length);
} finally {
globalThis.fetch = savedFetch;
}
});

it('Test failing postSource does not trigger callback', async () => {
const callbacks = [];
const { postSource } = await esmock(
'../../src/routes/source.js', {
'../../src/storage/object/put.js': {
default: async () => ({ status: 500 })
}
});

const savedFetch = globalThis.fetch;
try {
const callbacks = [];
globalThis.fetch = async (url) => {
callbacks.push(url);
};

const headers = new Map();
headers.set('content-type', 'text/html');

const req = {
headers,
url: 'http://localhost:8787/source/a/b/mydoc.html'
};

const env = { DA_COLLAB: 'http://localhost:1234' };
const daCtx = {};

const resp = await postSource({ req, env, daCtx });
assert.equal(500, resp.status);
assert.equal(0, callbacks.length);
} finally {
globalThis.fetch = savedFetch;
}
});

it('Test getSource', async () => {
const env = {};
const daCtx = {};

const called = [];
const getResp = async (e, c) => {
if (e === env && c === daCtx) {
called.push('getObject');
return {status: 200};
}
};

const { getSource } = await esmock(
'../../src/routes/source.js', {
'../../src/storage/object/get.js': {
default: getResp
}
}
);
const resp = await getSource({env, daCtx});
assert.equal(200, resp.status);
assert.deepStrictEqual(called, ['getObject']);
});

it('Test deleteSource', async () => {
const env = {};
const daCtx = {};

const called = [];
const deleteResp = async (e, c) => {
if (e === env && c === daCtx) {
called.push('deleteObject');
return {status: 204};
}
};

const { deleteSource } = await esmock(
'../../src/routes/source.js', {
'../../src/storage/object/delete.js': {
default: deleteResp
}
}
);
const resp = await deleteSource({env, daCtx});
assert.equal(204, resp.status);
assert.deepStrictEqual(called, ['deleteObject']);
});
});
11 changes: 9 additions & 2 deletions wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,27 @@ name = "da-admin"
main = "src/index.js"
compatibility_date = "2023-10-30"

vars = { DA_COLLAB = "https://collab.da.live" }

services = [
{ binding = "dacollab", service = "da-collab" }
]

kv_namespaces = [
{ binding = "DA_AUTH", id = "d6217b7c63ef40889583ba5c080c3908" },
{ binding = "DA_CONFIG", id = "feb8618620bb4ca3a866f1c71adbe8ef" }
]

[env.stage]
vars = { ENVIRONMENT = "stage" }
vars = { ENVIRONMENT = "stage", DA_COLLAB = "https://collab.da.live" }
kv_namespaces = [
{ binding = "DA_AUTH", id = "21693f3b20f54fcbb850ddc8947335ba" },
{ binding = "DA_CONFIG", id = "c44cb8dc69f041dc87c5aaef41b97df9" }
]


[env.dev]
vars = { ENVIRONMENT = "dev" }
vars = { ENVIRONMENT = "dev", DA_COLLAB = "http://localhost:4711" }
kv_namespaces = [
{ binding = "DA_AUTH", id = "21693f3b20f54fcbb850ddc8947335ba" },
{ binding = "DA_CONFIG", id = "c44cb8dc69f041dc87c5aaef41b97df9" }
Expand Down

0 comments on commit 23abfe9

Please sign in to comment.