Skip to content

Commit 8736d05

Browse files
m5rjkuestersugat009
authored
feat(#9857): implement CouchDB Nouveau (#9541)
Co-authored-by: Joshua Kuestersteffen <[email protected]> Co-authored-by: Sugat Bajracharya <[email protected]>
1 parent 53d1aab commit 8736d05

40 files changed

+1422
-847
lines changed

admin/src/js/controllers/edit-user.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
const moment = require('moment');
22
const passwordTester = require('simple-password-tester');
33
const phoneNumber = require('@medic/phone-number');
4-
const cht = require('@medic/cht-datasource');
5-
const chtDatasource = cht.getDatasource(cht.getRemoteDataContext());
64
const PASSWORD_MINIMUM_LENGTH = 8;
75
const PASSWORD_MINIMUM_SCORE = 50;
86
const SHOW_PASSWORD_ICON = '/login/images/show-password.svg';
@@ -27,6 +25,7 @@ angular
2725
$scope,
2826
$translate,
2927
$uibModalInstance,
28+
CHTDatasource,
3029
ContactTypes,
3130
CreateUser,
3231
DB,
@@ -38,6 +37,7 @@ angular
3837
'use strict';
3938
'ngInject';
4039

40+
const datasource = CHTDatasource.datasource;
4141
$scope.cancel = () => $uibModalInstance.dismiss();
4242

4343
const getRoles = roles => {
@@ -58,7 +58,7 @@ angular
5858
};
5959

6060
const validateSkipPasswordPermission = () => {
61-
$scope.skipPasswordChange = chtDatasource.v1.hasPermissions(
61+
$scope.skipPasswordChange = datasource.v1.hasPermissions(
6262
['can_skip_password_change'], $scope.editUserModel.roles, $scope.permissions
6363
);
6464
};
@@ -293,7 +293,7 @@ angular
293293
return true;
294294
}
295295

296-
const userHasPermission = chtDatasource.v1.hasPermissions(
296+
const userHasPermission = datasource.v1.hasPermissions(
297297
['can_have_multiple_places'], $scope.editUserModel.roles, $scope.permissions
298298
);
299299

admin/src/js/main.js

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ require('./services/add-attachment');
100100
require('./services/auth');
101101
require('./services/cache');
102102
require('./services/changes');
103+
require('./services/cht-datasource');
103104
require('./services/contact-muted');
104105
require('./services/contact-types');
105106
require('./services/db');

admin/src/js/services/auth.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
const cht = require('@medic/cht-datasource');
2-
const chtDatasource = cht.getDatasource(cht.getRemoteDataContext());
3-
41
angular.module('inboxServices').factory('Auth',
52
function(
63
$log,
4+
CHTDatasource,
75
Session,
86
Settings
97
) {
108

119
'use strict';
1210
'ngInject';
1311

12+
const datasource = CHTDatasource.datasource;
13+
1414
/**
1515
* Receives a list of groups of permissions and returns a promise that will be resolved if the
1616
* current user's role has all the permissions of any of the provided groups.
@@ -37,7 +37,7 @@ angular.module('inboxServices').factory('Auth',
3737
return false;
3838
}
3939

40-
return chtDatasource.v1.hasAnyPermission(permissionsGroupList, userCtx.roles, settings.permissions);
40+
return datasource.v1.hasAnyPermission(permissionsGroupList, userCtx.roles, settings.permissions);
4141
})
4242
.catch(() => false);
4343
};
@@ -63,7 +63,7 @@ angular.module('inboxServices').factory('Auth',
6363
return false;
6464
}
6565

66-
return chtDatasource.v1.hasPermissions(permissions, userCtx.roles, settings.permissions);
66+
return datasource.v1.hasPermissions(permissions, userCtx.roles, settings.permissions);
6767
})
6868
.catch(() => false);
6969
};
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const cht = require('@medic/cht-datasource');
2+
3+
angular.module('inboxServices').factory('CHTDatasource',
4+
function(
5+
Location
6+
) {
7+
'use strict';
8+
'ngInject';
9+
10+
const dataContext = cht.getRemoteDataContext(Location.rootUrl);
11+
const datasource = cht.getDatasource(dataContext);
12+
return {
13+
dataContext,
14+
datasource,
15+
};
16+
});

admin/src/js/services/location.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ angular.module('inboxServices').factory('Location',
1010
const path = '/';
1111
const adminPath = '/admin/';
1212
const port = location.port ? ':' + location.port : '';
13-
const url = location.protocol + '//' + location.hostname + port + '/' + dbName;
13+
const rootUrl = location.protocol + '//' + location.hostname + port;
14+
const url = rootUrl + '/' + dbName;
1415

1516
return {
1617
path: path,
1718
adminPath: adminPath,
1819
dbName: dbName,
19-
url: url
20+
rootUrl: rootUrl,
21+
url: url,
2022
};
2123
});

admin/src/js/services/search.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ const Search = require('@medic/search');
88
// To make it easier to mock out
99
angular.module('inboxServices').factory('SearchFactory',
1010
function(
11-
$q,
11+
CHTDatasource,
1212
DB
1313
) {
1414

1515
'ngInject';
1616

1717
return function() {
18-
return Search($q, DB());
18+
return Search(DB(), CHTDatasource.dataContext);
1919
};
2020
});
2121

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
describe('CHTDatasource service', () => {
2+
'use strict';
3+
4+
let service;
5+
let Location;
6+
7+
beforeEach(() => {
8+
Location = { rootUrl: 'ftp//myhost:21' };
9+
module('adminApp');
10+
module($provide => {
11+
$provide.value('Location', Location);
12+
});
13+
inject(($injector) => {
14+
service = $injector.get('CHTDatasource');
15+
});
16+
});
17+
18+
it('initializes a datasource and data context', () => {
19+
chai.expect(service.dataContext.url).to.equal(Location.rootUrl);
20+
chai.expect(service.datasource).to.haveOwnProperty('v1');
21+
});
22+
});

api/src/services/export/contact-mapper.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const db = require('../../db');
2-
const search = require('@medic/search')(Promise, db.medic);
2+
const dataContext = require('../data-context');
3+
const search = require('@medic/search')(db.medic, dataContext);
34
const lineage = require('@medic/lineage')(Promise, db.medic);
45

56
module.exports = {

api/src/services/export/report-mapper.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ const _ = require('lodash');
22
const objectPath = require('object-path');
33
const db = require('../../db');
44
const dateFormat = require('./date-format');
5-
const search = require('@medic/search')(Promise, db.medic);
5+
const dataContext = require('../data-context');
6+
const search = require('@medic/search')(db.medic, dataContext);
67
const lineage = require('@medic/lineage')(Promise, db.medic);
78

89
// Flattens a given object into an object where the keys are dot-notation

api/src/services/monitoring.js

+49-5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ const VIEW_INDEXES_TO_MONITOR = {
2727
users: ['users'],
2828
};
2929

30+
const NOUVEAU_INDEXES_TO_MONITOR = {
31+
medic: {
32+
'medic': [
33+
'contacts_by_freetext',
34+
'reports_by_freetext',
35+
],
36+
},
37+
sentinel: {},
38+
usersmeta: {},
39+
users: {},
40+
};
41+
3042
const MESSAGE_QUEUE_STATUS_KEYS = ['due', 'scheduled', 'muted', 'failed', 'delivered'];
3143
const fromEntries = (keys, value) => {
3244
// "shim" of Object.fromEntries
@@ -115,7 +127,7 @@ const getFragmentation = ({ sizes }, viewIndexInfos) => {
115127
return totalFile / totalActive;
116128
};
117129

118-
const mapDbInfo = (dbInfo, viewIndexInfos) => {
130+
const mapDbInfo = (dbInfo, viewIndexInfos, nouveauIndexInfos) => {
119131
return {
120132
name: dbInfo.db_name || '',
121133
update_sequence: getSequenceNumber(dbInfo.update_seq),
@@ -131,8 +143,13 @@ const mapDbInfo = (dbInfo, viewIndexInfos) => {
131143
sizes: {
132144
active: defaultNumber(viewIndexInfo.view_index?.sizes?.active),
133145
file: defaultNumber(viewIndexInfo.view_index?.sizes?.file),
134-
}
135-
}))
146+
},
147+
})),
148+
nouveau_indexes: nouveauIndexInfos?.map(nouveauIndexInfo => ({
149+
name: nouveauIndexInfo.name,
150+
doc_count: defaultNumber(nouveauIndexInfo.search_index.num_docs),
151+
file_size: defaultNumber(nouveauIndexInfo.search_index.disk_size),
152+
})),
136153
};
137154
};
138155

@@ -167,11 +184,38 @@ const fetchViewIndexInfosForDb = (db) => Promise.all(
167184

168185
const fetchAllViewIndexInfos = () => Promise.all(Object.keys(VIEW_INDEXES_TO_MONITOR).map(fetchViewIndexInfosForDb));
169186

187+
const fetchNouveauIndexInfo = (db, designDoc, indexName) => request
188+
.get({
189+
url: `${environment.serverUrl}/${db}/_design/${designDoc}/_nouveau_info/${indexName}`,
190+
json: true
191+
})
192+
.then(data => ({ ...data, name: `${designDoc}/${indexName}` }))
193+
.catch(err => {
194+
logger.error('Error fetching nouveau index info: %o', err);
195+
return null;
196+
});
197+
198+
const fetchNouveauIndexInfosForDdoc = (db, ddoc) => NOUVEAU_INDEXES_TO_MONITOR[db][ddoc].map(
199+
indexName => fetchNouveauIndexInfo(DBS_TO_MONITOR[db], ddoc, indexName),
200+
);
201+
202+
const fetchNouveauIndexInfosForDb = (db) => Promise.all(Object.keys(NOUVEAU_INDEXES_TO_MONITOR[db]).flatMap(
203+
ddoc => fetchNouveauIndexInfosForDdoc(db, ddoc),
204+
)).then((nouveauIndexInfos) => nouveauIndexInfos.filter(info => info));
205+
206+
const fetchAllNouveauIndexInfos = () => Promise.all(
207+
Object.keys(NOUVEAU_INDEXES_TO_MONITOR).map(fetchNouveauIndexInfosForDb),
208+
);
209+
170210
const getDbInfos = async () => {
171-
const [dbInfos, viewIndexInfos] = await Promise.all([fetchDbsInfo(), fetchAllViewIndexInfos()]);
211+
const [dbInfos, viewIndexInfos, nouveauIndexInfos] = await Promise.all([
212+
fetchDbsInfo(),
213+
fetchAllViewIndexInfos(),
214+
fetchAllNouveauIndexInfos(),
215+
]);
172216
const result = {};
173217
Object.keys(DBS_TO_MONITOR).forEach((dbKey, i) => {
174-
result[dbKey] = mapDbInfo(dbInfos[i], viewIndexInfos[i]);
218+
result[dbKey] = mapDbInfo(dbInfos[i], viewIndexInfos[i], nouveauIndexInfos[i]);
175219
});
176220
return result;
177221
};

0 commit comments

Comments
 (0)