Skip to content

Commit

Permalink
Allow enabling service object config IDs to be provided by a client.
Browse files Browse the repository at this point in the history
  • Loading branch information
dlongley committed May 20, 2024
1 parent 973e957 commit efa98ee
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 11 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# bedrock-service-core ChangeLog

## 9.3.0 - 2024-mm-dd

### Added
- Allow service object config IDs to be provided by a client if the
extending service code overrides the default validation schema;
by default, this is still prohibited.

## 9.2.0 - 2024-03-19

### Added
Expand Down
14 changes: 10 additions & 4 deletions lib/http/configs.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,20 @@ export function addRoutes({app, service}) {
});
}

// do not allow client to choose service object ID
delete req.body.id;
const id = getId({routePrefix, localId: await generateRandom()});
// auto-generate service object ID if not given; default behavior is to
// reject via previous JSON schema validation if given, but this can be
// overridden by extending service code (if desired) and then IDs values
// can be checked via `validateConfigFn`
const id = req.body.id ?? getId({
routePrefix, localId: await generateRandom()
});
const config = {id, ...req.body};

// run custom validate config function, if any
if(validateConfigFn) {
const {valid, error} = await validateConfigFn({config, op: 'create'});
const {valid, error} = await validateConfigFn({
req, config, op: 'create'
});
if(!valid) {
throw new BedrockError(
'Configuration validation failed.', 'DataError', {
Expand Down
48 changes: 48 additions & 0 deletions test/mocha/10-http.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ describe('bedrock-service-core HTTP API', () => {
const {id: capabilityAgentId} = capabilityAgent;
result.controller.should.equal(capabilityAgentId);
});
it('fails to create w/client-provided config ID', async () => {
let err;
let result;
try {
// create meter for example service
const {id: meterId} = await helpers.createMeter({
capabilityAgent, serviceName: 'example'
});
result = await helpers.createConfig({
capabilityAgent, meterId, options: {id: 'should-fail'}
});
} catch(e) {
err = e;
}
should.exist(err);
should.not.exist(result);
err.status.should.equal(400);
err.data.details.errors.should.have.length(1);
const [error] = err.data.details.errors;
error.name.should.equal('ValidationError');
error.message.should.contain('should NOT have additional properties');
});
it('fails to create w/incorrect service meter zcap', async () => {
let err;
let result;
Expand Down Expand Up @@ -153,6 +175,32 @@ describe('bedrock-service-core HTTP API', () => {
err.data.message.should.equal(
'A validation error occured in the \'createConfigBody\' validator.');
});
it('creates a config with a client ID', async () => {
let err;
let result;
try {
const {id: meterId} = await helpers.createMeter({
capabilityAgent, serviceName: 'alternative'
});

result = await helpers.createConfig({
capabilityAgent, meterId, servicePath: '/alternatives',
options: {
id: `${mockData.baseUrl}/alternatives/foo`
}
});
} catch(e) {
err = e;
}
assertNoError(err);
should.exist(result);
result.should.have.keys([
'controller', 'id', 'sequence', 'meterId'
]);
result.sequence.should.equal(0);
const {id: capabilityAgentId} = capabilityAgent;
result.controller.should.equal(capabilityAgentId);
});
});

describe('get config', () => {
Expand Down
10 changes: 6 additions & 4 deletions test/mocha/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,20 @@ export async function createMeter({
}

export async function createConfig({
capabilityAgent, ipAllowList, meterId, oauth2 = false
capabilityAgent, ipAllowList, meterId, oauth2 = false, options = {},
servicePath = '/examples'
} = {}) {
if(!meterId) {
// create a meter for the keystore
// create a meter for the config
({id: meterId} = await createMeter({capabilityAgent}));
}

// create service object
const config = {
sequence: 0,
controller: capabilityAgent.id,
meterId
meterId,
...options
};
if(ipAllowList) {
config.ipAllowList = ipAllowList;
Expand All @@ -65,7 +67,7 @@ export async function createConfig({
}

const zcapClient = createZcapClient({capabilityAgent});
const url = `${mockData.baseUrl}/examples`;
const url = `${mockData.baseUrl}${servicePath}`;
const response = await zcapClient.write({url, json: config});
return response.data;
}
Expand Down
3 changes: 2 additions & 1 deletion test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"@digitalbazaar/webkms-client": "^14.1.0",
"c8": "^9.1.0",
"cross-env": "^7.0.3",
"jose": "^4.8.3"
"jose": "^4.8.3",
"klona": "^2.0.6"
},
"c8": {
"excludeNodeModules": false,
Expand Down
21 changes: 19 additions & 2 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/*!
* Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2022-2024 Digital Bazaar, Inc. All rights reserved.
*/
import * as bedrock from '@bedrock/core';
import {createService} from '@bedrock/service-core';
import {createService, schemas} from '@bedrock/service-core';
import {getServiceIdentities} from '@bedrock/app-identity';
import {handlers} from '@bedrock/meter-http';
import {klona} from 'klona';
import {mockData} from './mocha/mock.data.js';
import '@bedrock/https-agent';
import '@bedrock/meter';
Expand Down Expand Up @@ -40,6 +41,22 @@ bedrock.events.on('bedrock.init', async () => {
revocation: 1
}
});

// create `alternative` service that allows the client to provide IDs
const alternativeCreateConfigBody = klona(schemas.createConfigBody);
alternativeCreateConfigBody.properties.id =
schemas.updateConfigBody.properties.id;
await createService({
serviceType: 'alternative',
routePrefix: '/alternatives',
storageCost: {
config: 1,
revocation: 1
},
validation: {
createConfigBody: alternativeCreateConfigBody
}
});
});

// mock oauth2 authz server routes
Expand Down

0 comments on commit efa98ee

Please sign in to comment.