Skip to content

Commit

Permalink
GH-21 - Config API
Browse files Browse the repository at this point in the history
* New `/config` route for GET & PUT
* Config API supports any key depth
* Added fullKey property to daCtx

Resolves: GH-21
  • Loading branch information
auniverseaway committed Feb 17, 2024
1 parent 02f7188 commit c51fad7
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 19 deletions.
1 change: 0 additions & 1 deletion .github/workflows/email-bot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ on:
branches:
- main


jobs:
action:
if: github.event.pull_request.merged == true
Expand Down
4 changes: 2 additions & 2 deletions src/handlers/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/
import { getSource } from '../routes/source.js';
import getList from '../routes/list.js';
import { getProperties } from '../routes/properties.js';
import { getConfig } from '../routes/config.js';

function get404() {
return { body: '', status: 404 };
Expand All @@ -30,7 +30,7 @@ export default async function getHandler({ env, daCtx }) {

if (path.startsWith('/source')) return getSource({ env, daCtx });
if (path.startsWith('/list')) return getList({ env, daCtx });
if (path.startsWith('/properties')) return getProperties();
if (path.startsWith('/config')) return getConfig({ env, daCtx });

return undefined;
}
4 changes: 2 additions & 2 deletions src/handlers/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
* governing permissions and limitations under the License.
*/
import { postSource } from '../routes/source.js';
import { postProperties } from '../routes/properties.js';
import { postConfig } from '../routes/config.js';

export default async function postHandler({ req, env, daCtx }) {
const { path } = daCtx;

if (path.startsWith('/source')) return postSource({ req, env, daCtx });
if (path.startsWith('/properties')) return postProperties({ req, env, daCtx });
if (path.startsWith('/config')) return postConfig({ req, env, daCtx });

return undefined;
}
22 changes: 22 additions & 0 deletions src/routes/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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 putKv from '../storage/kv/put.js';
import getKv from '../storage/kv/get.js';

export function postConfig({ req, env, daCtx }) {
return putKv(req, env, daCtx);
}

export function getConfig({ env, daCtx }) {
return getKv(env, daCtx);
}
17 changes: 17 additions & 0 deletions src/storage/kv/get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* 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.
*/

export default async function getKv(env, daCtx) {
const body = await env.DA_CONFIG.get(daCtx.fullKey);
if (body) return { body, status: 200 };
return { body: JSON.stringify({ error: 'not found' }), status: 404 };
}
41 changes: 41 additions & 0 deletions src/storage/kv/put.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.
*/

async function save(env, key, string) {
let body;
let status;
try {
// Parse it to at least validate its json
JSON.parse(string);
// Put it (seems to not return a response)
await env.DA_CONFIG.put(key, string);
// Validate the content is there
body = await env.DA_CONFIG.get(key);
status = 201;
} catch {
body = JSON.stringify({ error: 'Couldn\'t parse or save config.' });
status = 400;
}
return { body, status };
}

export default async function putKv(req, env, daCtx) {
try {
const formData = await req.formData();
const config = formData.get('config');
if (config) return save(env, daCtx.fullKey, config);
} catch {
// eslint-disable-next-line no-console
console.log('No form data');
}
return { body: JSON.stringify({ error: 'No config or form data.' }), status: 400 };
}
19 changes: 13 additions & 6 deletions src/utils/daCtx.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,18 @@ export default async function getDaCtx(req, env) {
const sanitized = lower.endsWith('/') ? lower.slice(0, -1) : lower;

// Get base details
const [api, org, ...parts] = sanitized.split('/');
const split = sanitized.split('/');
const api = split.shift();
const fullKey = split.join('/');
const [org, ...parts] = split;

// Set base details
const daCtx = {
path: pathname, api, org, users,
path: pathname,
api,
org,
users,
fullKey,
};

// Get org properties
Expand All @@ -53,10 +60,10 @@ export default async function getDaCtx(req, env) {
[daCtx.site] = path;

// Handle folders and files under a site
const split = daCtx.filename.split('.');
daCtx.isFile = split.length > 1;
if (daCtx.isFile) daCtx.ext = split.pop();
daCtx.name = split.join('.');
const fileSplit = daCtx.filename.split('.');
daCtx.isFile = fileSplit.length > 1;
if (daCtx.isFile) daCtx.ext = fileSplit.pop();
daCtx.name = fileSplit.join('.');

// Set keys
daCtx.key = keyBase;
Expand Down
73 changes: 73 additions & 0 deletions test/storage/kv/kv.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import assert from 'assert';

import getKv from '../../../src/storage/kv/get.js';
import putKv from '../../../src/storage/kv/put.js';

const MOCK_CONFIG = '{"mock":"data"}';

describe('KV storage', () => {
describe('Get success', async () => {
const env = {
DA_CONFIG: {
get: () => { return MOCK_CONFIG },
}
};
const daCtx = { fullKey: 'adobe/geometrixx' };

const resp = await getKv(env, daCtx);
assert.strictEqual(resp.body, MOCK_CONFIG);
assert.strictEqual(resp.status, 200);
});

describe('Get not found', async () => {
const env = { DA_CONFIG: { get: () => { return null } } };
const daCtx = { fullKey: 'adobe/geometrixx' };

const resp = await getKv(env, daCtx);
assert.strictEqual(resp.body, '{"error":"not found"}');
assert.strictEqual(resp.status, 404);
});

describe('Put success', async () => {
const formData = new FormData();
formData.append('config', MOCK_CONFIG);

const req = { formData: () => { return formData; } };
const env = {
DA_CONFIG: {
put: () => { return undefined },
get: () => { return MOCK_CONFIG },
}
};
const daCtx = { fullKey: 'adobe/geometrixx' };
const resp = await putKv(req, env, daCtx);
assert.strictEqual(resp.body, MOCK_CONFIG);
assert.strictEqual(resp.status, 201);
});

describe('Put without form data', async () => {
const req = { formData: () => { return null; } };
const env = {};
const daCtx = { fullKey: 'adobe/geometrixx' };
const resp = await putKv(req, env, daCtx);
assert.strictEqual(resp.body, '{"error":"No config or form data."}');
assert.strictEqual(resp.status, 400);
});

describe('Put with malformed config', async () => {
const formData = new FormData();
formData.append('config', 'abc');

const req = { formData: () => { return formData; } };
const env = {
DA_CONFIG: {
put: () => { return undefined },
get: () => { return MOCK_CONFIG },
}
};
const daCtx = { fullKey: 'adobe/geometrixx' };
const resp = await putKv(req, env, daCtx);
assert.strictEqual(resp.body, '{"error":"Couldn\'t parse or save config."}');
assert.strictEqual(resp.status, 400);
});
});
18 changes: 10 additions & 8 deletions wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ main = "src/index.js"
compatibility_date = "2023-10-30"

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

[env.dev]
vars = { ENVIRONMENT = "dev" }
kv_namespaces = [
{ binding = "DA_AUTH", id = "21693f3b20f54fcbb850ddc8947335ba" }
{ binding = "DA_AUTH", id = "d6217b7c63ef40889583ba5c080c3908" },
{ binding = "DA_CONFIG", id = "feb8618620bb4ca3a866f1c71adbe8ef" }
]

[env.stage]
vars = { ENVIRONMENT = "stage" }
kv_namespaces = [
{ binding = "DA_AUTH", id = "21693f3b20f54fcbb850ddc8947335ba" }
{ binding = "DA_AUTH", id = "21693f3b20f54fcbb850ddc8947335ba" },
{ binding = "DA_CONFIG", id = "c44cb8dc69f041dc87c5aaef41b97df9" }
]

[env.dev]
vars = { ENVIRONMENT = "dev" }
kv_namespaces = [
{ binding = "DA_AUTH", id = "21693f3b20f54fcbb850ddc8947335ba" },
{ binding = "DA_CONFIG", id = "c44cb8dc69f041dc87c5aaef41b97df9" }
]

0 comments on commit c51fad7

Please sign in to comment.