Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#9065): add cht-datasource to support get person by uuid #9090

Merged
merged 45 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
de6b864
Move cht-script-api to cht-datasource
jkuester May 2, 2024
b429273
Clean up auth.js to fix Sonar
jkuester May 2, 2024
b63981e
Switch cht-datasource to be built with tsc
jkuester May 3, 2024
1701082
Use cht-datasource as TS lib in webapp
jkuester May 3, 2024
1f1961a
Fix up the -watch commands to listen for changes to cht-datasource
jkuester May 3, 2024
eb7e4b2
Clean up index code for cht-datasource
jkuester May 3, 2024
463f002
Tweak up tsconfig options. Generating source maps for debugging and e…
jkuester May 3, 2024
c2dc2df
Add cht-datasource support for getting a person
jkuester May 7, 2024
b00c298
Get ambient types working - tests broken
jkuester May 10, 2024
af16a8c
Make hard separation between local and remote
jkuester May 10, 2024
b508bc9
Take the parallel route where functions are available, but the base d…
jkuester May 13, 2024
c743fb2
Clean up cht-datasource based on Mokhtar's suggestions
jkuester May 13, 2024
149dcde
Clean up cht-datasource based on Mokhtar's suggestions
jkuester May 13, 2024
6b5e32c
Merge remote-tracking branch 'origin/9065_get_contact_by_id' into 906…
jkuester May 13, 2024
0a5e7f6
Refactor cht-datasource to support different access methodologies and…
jkuester May 14, 2024
f4cc564
Merge branch 'refs/heads/master' into 9065_get_contact_by_id
jkuester May 14, 2024
b2b3585
Fix linking issue with @medic/logger
jkuester May 14, 2024
ef8b511
Add cht-datasource unit tests
jkuester May 15, 2024
c87de8a
Fix webapp tests
jkuester May 15, 2024
5196de4
Clean everything up
jkuester May 15, 2024
640abab
Remove unnecessary eslint config
jkuester May 15, 2024
2a53e9d
Add eslint-plugin-jsdoc and require all exported entities have doc
jkuester May 17, 2024
fcd0c21
Update jsdocs to remove redundant documentation and expand other docs…
jkuester May 20, 2024
2039d74
Move Local Data Context code to be in `./local`
jkuester May 20, 2024
e5730b6
Move Remote Data Context code to be in `./remote`
jkuester May 20, 2024
6f8c428
Restructure data context code and add logging
jkuester May 22, 2024
266a9e8
Refactor V1 namespace to v1
jkuester May 22, 2024
6f7e3ff
Merge branch 'refs/heads/master' into 9065_get_contact_by_id
jkuester May 22, 2024
f4174ac
Remove request-promise-native from cht-datasouce since it broke webapp.
jkuester May 22, 2024
871c1ea
Update sentinel to use local data context
jkuester May 22, 2024
5c5469d
Update cht-datasource README to point to TSDoc
jkuester May 22, 2024
c454405
Add some development instructions to the cht-datasource README docs
jkuester May 22, 2024
1b81b78
Include doc on backwards compatibility of the API
jkuester May 22, 2024
d95beaa
Update cht-datasource DataContext to be able to run provided function…
jkuester May 24, 2024
738e4b4
Add support for /api/v1/person/:uuid endpoint
jkuester May 24, 2024
bbeece8
Add integration tests for /api/v1/person/:uuid api
jkuester May 24, 2024
9110233
Clean things up
jkuester May 24, 2024
815f920
Rename CHTScriptApiService to CHTDatasourceService
jkuester Jun 10, 2024
fa3e041
Rename getApi to just get
jkuester Jun 10, 2024
3128337
Rename cht-script-api.service file to be cht-datasource.service
jkuester Jun 10, 2024
d3b9532
Change DataContext.get to DataContext.bind
jkuester Jun 10, 2024
9ab853d
Merge branch 'refs/heads/master' into 9065_get_contact_by_id
jkuester Jun 10, 2024
45411d3
Watch cht-datasource when running dev-api
jkuester Jun 10, 2024
a22df65
Fix sonar issues
jkuester Jun 10, 2024
9dac6fe
Merge branch 'refs/heads/master' into 9065_get_contact_by_id
jkuester Jun 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"api/src/public/login/lib-bowser.js",
"build/**",
"jsdocs/**",
"shared-libs/cht-datasource/dist/**",
"tests/scalability/report*/**",
"tests/scalability/jmeter/**",
"webapp/src/ts/providers/xpath-element-path.provider.ts"
Expand Down
6 changes: 6 additions & 0 deletions .mocharc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const chaiExclude = require('chai-exclude');
const chaiAsPromised = require('chai-as-promised');
const chai = require('chai');

chai.use(chaiExclude);
chai.use(chaiAsPromised);
4 changes: 2 additions & 2 deletions admin/package-lock.json

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

7 changes: 4 additions & 3 deletions admin/src/js/services/auth.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const chtScriptApi = require('@medic/cht-script-api');
const cht = require('@medic/cht-datasource');
const chtDatasource = cht.getDatasource(cht.getRemoteDataContext());

jkuester marked this conversation as resolved.
Show resolved Hide resolved
angular.module('inboxServices').factory('Auth',
function(
Expand Down Expand Up @@ -36,7 +37,7 @@ angular.module('inboxServices').factory('Auth',
return false;
}

return chtScriptApi.v1.hasAnyPermission(permissionsGroupList, userCtx.roles, settings.permissions);
return chtDatasource.v1.hasAnyPermission(permissionsGroupList, userCtx.roles, settings.permissions);
})
.catch(() => false);
};
Expand All @@ -62,7 +63,7 @@ angular.module('inboxServices').factory('Auth',
return false;
}

return chtScriptApi.v1.hasPermissions(permissions, userCtx.roles, settings.permissions);
return chtDatasource.v1.hasPermissions(permissions, userCtx.roles, settings.permissions);
})
.catch(() => false);
};
Expand Down
2 changes: 1 addition & 1 deletion api/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
{
"allowModules": [
"@medic/bulk-docs-utils",
"@medic/cht-script-api",
"@medic/cht-datasource",
"@medic/contact-types-utils",
"@medic/contacts",
"@medic/couch-request",
Expand Down
3 changes: 2 additions & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
},
"scripts": {
"toc": "doctoc --github --maxlevel 2 README.md",
"postinstall": "patch-package"
"postinstall": "patch-package",
"run-watch": "TZ=UTC nodemon --inspect=0.0.0.0:9229 --ignore 'build/static' --ignore 'build/public' --watch ./ --watch '../shared-libs/**/src/**' server.js -- --allow-cors"
},
"dependencies": {
"@medic/logger": "file:../shared-libs/logger",
Expand Down
20 changes: 20 additions & 0 deletions api/src/controllers/person.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const { Person, Qualifier } = require('@medic/cht-datasource');
const ctx = require('../services/data-context');
const serverUtils = require('../server-utils');
const auth = require('../auth');

const getPerson = (qualifier) => ctx.bind(Person.v1.get)(qualifier);

module.exports = {
v1: {
get: serverUtils.doOrError(async (req, res) => {
await auth.check(req, 'can_view_contacts');
const { uuid } = req.params;
const person = await getPerson(Qualifier.byUuid(uuid));
if (!person) {
return serverUtils.error({ status: 404, message: 'Person not found' }, req, res);
}
return res.json(person);
})
}
};
3 changes: 3 additions & 0 deletions api/src/routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const exportData = require('./controllers/export-data');
const records = require('./controllers/records');
const forms = require('./controllers/forms');
const users = require('./controllers/users');
const person = require('./controllers/person');
const { people, places } = require('@medic/contacts')(config, db);
const upgrade = require('./controllers/upgrade');
const settings = require('./controllers/settings');
Expand Down Expand Up @@ -475,6 +476,8 @@ app.postJson('/api/v1/people', function(req, res) {
.catch(err => serverUtils.error(err, req, res));
});

app.get('/api/v1/person/:uuid', person.v1.get);

app.postJson('/api/v1/bulk-delete', bulkDocs.bulkDelete);

// offline users are not allowed to hydrate documents via the hydrate API
Expand Down
8 changes: 8 additions & 0 deletions api/src/server-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,12 @@ module.exports = {
},

wantsJSON,

doOrError: (fn) => async (req, res) => {
try {
return await fn(req, res);
} catch (err) {
module.exports.error(err, req, res);
}
}
};
5 changes: 5 additions & 0 deletions api/src/services/data-context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const { getLocalDataContext } = require('@medic/cht-datasource');
const db = require('../db');
const config = require('../config');

module.exports = getLocalDataContext(config, db);
jkuester marked this conversation as resolved.
Show resolved Hide resolved
90 changes: 90 additions & 0 deletions api/tests/mocha/controllers/person.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
const sinon = require('sinon');
const { expect } = require('chai');
const { Person, Qualifier } = require('@medic/cht-datasource');
const auth = require('../../../src/auth');
const controller = require('../../../src/controllers/person');
const dataContext = require('../../../src/services/data-context');
const serverUtils = require('../../../src/server-utils');

describe('Person Controller', () => {
let authCheck;
let dataContextBind;
let serverUtilsError;
let req;
let res;

beforeEach(() => {
authCheck = sinon.stub(auth, 'check');
dataContextBind = sinon.stub(dataContext, 'bind');
serverUtilsError = sinon.stub(serverUtils, 'error');
res = {
json: sinon.stub(),
};
});

afterEach(() => sinon.restore());

describe('v1', () => {
describe('get', () => {
const qualifier = Object.freeze({ uuid: 'uuid' });
let byUuid;
let personGet;

beforeEach(() => {
req = { params: { uuid: 'uuid' } };
byUuid = sinon
.stub(Qualifier, 'byUuid')
.returns(qualifier);
personGet = sinon.stub();
dataContextBind
.withArgs(Person.v1.get)
.returns(personGet);
});

it('returns a person', async () => {
const person = { name: 'John Doe' };
personGet.resolves(person);

await controller.v1.get(req, res);

expect(authCheck.calledOnceWithExactly(req, 'can_view_contacts')).to.be.true;
expect(dataContextBind.calledOnceWithExactly(Person.v1.get)).to.be.true;
expect(byUuid.calledOnceWithExactly(req.params.uuid)).to.be.true;
expect(personGet.calledOnceWithExactly(qualifier)).to.be.true;
expect(res.json.calledOnceWithExactly(person)).to.be.true;
expect(serverUtilsError.notCalled).to.be.true;
});

it('returns a 404 error if person is not found', async () => {
personGet.resolves(null);

await controller.v1.get(req, res);

expect(authCheck.calledOnceWithExactly(req, 'can_view_contacts')).to.be.true;
expect(dataContextBind.calledOnceWithExactly(Person.v1.get)).to.be.true;
expect(byUuid.calledOnceWithExactly(req.params.uuid)).to.be.true;
expect(personGet.calledOnceWithExactly(qualifier)).to.be.true;
expect(res.json.notCalled).to.be.true;
expect(serverUtilsError.calledOnceWithExactly(
{ status: 404, message: 'Person not found' },
req,
res
)).to.be.true;
});

it('returns error if user unauthorized', async () => {
const error = new Error('Unauthorized');
authCheck.rejects(error);

await controller.v1.get(req, res);

expect(authCheck.calledOnceWithExactly(req, 'can_view_contacts')).to.be.true;
expect(dataContextBind.notCalled).to.be.true;
expect(byUuid.notCalled).to.be.true;
expect(personGet.notCalled).to.be.true;
expect(res.json.notCalled).to.be.true;
expect(serverUtilsError.calledOnceWithExactly(error, req, res)).to.be.true;
});
});
});
});
27 changes: 27 additions & 0 deletions api/tests/mocha/server-utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,31 @@ describe('Server utils', () => {
});
});

describe('doOrError', () => {
let serverUtilsError;

beforeEach(() => {
serverUtilsError = sinon.stub(serverUtils, 'error');
});

it('returns the function output when no error is thrown', async () => {
const fn = sinon.stub().resolves('result');

const result = await serverUtils.doOrError(fn)(req, res);

chai.expect(result).to.equal('result');
chai.expect(fn.calledOnceWithExactly(req, res)).to.be.true;
chai.expect(serverUtilsError.notCalled).to.be.true;
});

it('calls error when an error is thrown', async () => {
const error = new Error('error');
const fn = sinon.stub().rejects(error);

await serverUtils.doOrError(fn)(req, res);

chai.expect(fn.calledOnceWithExactly(req, res)).to.be.true;
chai.expect(serverUtilsError.calledOnceWithExactly(error, req, res)).to.be.true;
});
});
});
14 changes: 14 additions & 0 deletions api/tests/mocha/services/data-context.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const dataSource = require('@medic/cht-datasource');
const { expect } = require('chai');
const db = require('../../../src/db');
const config = require('../../../src/config');
const dataContext = require('../../../src/services/data-context');

describe('Data context service', () => {
it('is initialized with the methods from the data context', () => {
const expectedDataContext = dataSource.getLocalDataContext(config, db);

expect(dataContext.bind).is.a('function');
expect(dataContext).excluding('bind').to.deep.equal(expectedDataContext);
});
});