Skip to content

Commit

Permalink
Merge pull request #4487 from beyondessential/release-2023-17
Browse files Browse the repository at this point in the history
Release 2023 17
  • Loading branch information
chris-pollard authored Apr 27, 2023
2 parents ef5011b + f524151 commit f235188
Show file tree
Hide file tree
Showing 37 changed files with 926 additions and 321 deletions.
7 changes: 5 additions & 2 deletions packages/admin-panel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@
"lint:fix": "yarn lint --fix",
"start": "yarn package:start:react-scripts",
"start-dev": "yarn start",
"start-fullstack": "npm-run-all -c -l -p start-central-server start-entity-server start-report-server start-admin-panel-server start-dev",
"start-fullstack": "npm-run-all -c -l -p start-servers start-frontend",
"start-central-server": "yarn workspace @tupaia/central-server start-dev",
"start-entity-server": "yarn workspace @tupaia/entity-server start-dev",
"start-report-server": "yarn workspace @tupaia/report-server start-dev",
"start-admin-panel-server": "yarn workspace @tupaia/admin-panel-server start-dev",
"test": "echo No tests"
"test": "echo No tests",
"start-ui-components": "yarn workspace @tupaia/ui-components build-dev:watch",
"start-frontend": "npm-run-all -c -l -p start-ui-components start-dev",
"start-servers": "npm-run-all -c -l -p start-central-server start-entity-server start-report-server start-admin-panel-server"
},
"browserslist": [
"defaults"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export * from './useUser';
export * from './useDashboardVisualisation';
export * from './useMapOverlays';
export * from './useCountries';
export * from './useEntities';
export * from './useSearchPermissionGroups';
export * from './useSearchTransformSchemas';
25 changes: 25 additions & 0 deletions packages/admin-panel/src/VizBuilderApp/api/queries/useEntities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/
import { useQuery } from 'react-query';
import { stringifyQuery } from '@tupaia/utils';
import { get } from '../api';
import { DEFAULT_REACT_QUERY_OPTIONS } from '../constants';

export const useEntities = search =>
useQuery(
['entities', search],
async () => {
const endpoint = stringifyQuery(undefined, `entities`, {
columns: JSON.stringify(['name', 'code']),
filter: JSON.stringify({
name: { comparator: 'ilike', comparisonValue: `%${search}%`, castAs: 'text' },
}),
});
return get(endpoint);
},
{
...DEFAULT_REACT_QUERY_OPTIONS,
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export const PreviewFilters = ({ params, onChange, runtimeParams }) => {
<Grid key={p.id}>
<FilterComponent
{...p}
runtimeParams={runtimeParams} // organisationUnitCodes parameter needs hierarachy as input
value={runtimeParams[p.name]}
onChange={newValue => {
onChange(p.name, newValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,22 @@
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/

import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';

import PropTypes from 'prop-types';

import { ParameterType } from '../../editing';
import { useLocations } from '../../../../VizBuilderApp/api';
import { useEntities } from '../../../../VizBuilderApp/api';
import { Autocomplete } from '../../../../autocomplete';
import { useDebounce } from '../../../../utilities';
import { getArrayFieldValue } from './utils';

export const OrganisationUnitCodesField = ({ name, onChange, runtimeParams }) => {
const { hierarchy = 'explore' } = runtimeParams;
export const OrganisationUnitCodesField = ({ name, onChange }) => {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 100);
const [selectedOptions, setSelectedOptions] = useState([]); // [{code:"DL", name:"Demo Land"}]
const { data: locations = [], isLoading } = useLocations(hierarchy, searchTerm);
const limitedLocations = locations.slice(0, 20); // limit the options to 20 to stop the ui jamming

useEffect(() => {
setSelectedOptions([]);
onChange([]);
}, [hierarchy]); // hierarchy determines entity relation visibility
const { data: entities = [], isLoading } = useEntities(debouncedSearchTerm);
const limitedLocations = entities.slice(0, 20); // limit the options to 20 to stop the ui jamming

return (
<Autocomplete
Expand Down Expand Up @@ -50,9 +46,4 @@ export const OrganisationUnitCodesField = ({ name, onChange, runtimeParams }) =>
OrganisationUnitCodesField.propTypes = {
...ParameterType,
onChange: PropTypes.func.isRequired,
runtimeParams: PropTypes.object,
};

OrganisationUnitCodesField.defaultProps = {
runtimeParams: {},
};
6 changes: 6 additions & 0 deletions packages/admin-panel/src/editor/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ export const openBulkEditModal = (
type: EDITOR_DATA_FETCH_SUCCESS,
recordData: response.body,
});
dispatch({
type: EDITOR_OPEN,
fields,
recordData: response.body,
endpoint: bulkUpdateEndpoint,
});
} catch (error) {
dispatch({
type: EDITOR_ERROR,
Expand Down
8 changes: 8 additions & 0 deletions packages/admin-panel/src/pages/resources/DataTablesPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ const COLUMNS = [
},
},
},
{
Header: 'Delete',
source: 'id',
type: 'delete',
actionConfig: {
endpoint: DATA_TABLES_ENDPOINT,
},
},
];

const CREATE_CONFIG = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { hasBESAdminAccess, BES_ADMIN_PERMISSION_GROUP } from '../../permissions';
import {
getAdminPanelAllowedEntityIds,
getAdminPanelAllowedCountryIds,
getAdminPanelAllowedCountryCodes,
mergeFilter,
} from '../utilities';
Expand Down Expand Up @@ -76,7 +76,7 @@ export const createAccessRequestDBFilter = async (accessPolicy, models, criteria
// If we don't have BES Admin access, add a filter to the SQL query
const dbConditions = { ...criteria };
dbConditions['access_request.entity_id'] = mergeFilter(
await getAdminPanelAllowedEntityIds(accessPolicy, models),
await getAdminPanelAllowedCountryIds(accessPolicy, models),
dbConditions['access_request.entity_id'],
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@

import { QUERY_CONJUNCTIONS } from '@tupaia/database';
import { hasBESAdminAccess } from '../../permissions';
import { getPermissionListWithWildcard } from '../utilities';

const { RAW } = QUERY_CONJUNCTIONS;

const getPermissionListWithWildcard = async accessPolicy => {
const userPermissionGroups = accessPolicy.getPermissionGroups();
return ['*', ...userPermissionGroups];
};

export const assertDataGroupGETPermissions = async (accessPolicy, models, dataGroupId) => {
if (await assertDataGroupPermissions(accessPolicy, models, dataGroupId, 'some')) {
return true;
Expand Down
26 changes: 0 additions & 26 deletions packages/central-server/src/apiV2/dataTables/CreateDataTables.js

This file was deleted.

4 changes: 2 additions & 2 deletions packages/central-server/src/apiV2/dataTables/GETDataTables.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ export class GETDataTables extends GETHandler {
permissionsFilteredInternally = true;

async findSingleRecord(dataTableId, options) {
const dataGroupPermissionChecker = accessPolicy =>
const dataTablePermissionChecker = accessPolicy =>
assertDataTableGETPermissions(accessPolicy, this.models, dataTableId);
await this.assertPermissions(
assertAnyPermissions([assertBESAdminAccess, dataGroupPermissionChecker]),
assertAnyPermissions([assertBESAdminAccess, dataTablePermissionChecker]),
);

return super.findSingleRecord(dataTableId, options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
*/

import { hasBESAdminAccess } from '../../permissions';

const getPermissionListWithWildcard = async accessPolicy => {
const userPermissionGroups = accessPolicy.getPermissionGroups();
return ['*', ...userPermissionGroups];
};
import { getPermissionListWithWildcard } from '../utilities';

export const assertDataTableGETPermissions = async (accessPolicy, models, dataTableId) => {
// User requires access to any permission group
Expand All @@ -18,16 +14,6 @@ export const assertDataTableGETPermissions = async (accessPolicy, models, dataTa
throw new Error('You do not have permission to view this data-table');
};

export const assertDataTableEditPermissions = async (accessPolicy, models, dataTableId) => {
// User requires access to all permission groups
if (await assertDataTablePermissions(accessPolicy, models, dataTableId, 'every')) {
return true;
}
throw new Error(
'You require access to all of a data-tables permission groups to perform this action',
);
};

const assertDataTablePermissions = async (accessPolicy, models, dataTableId, test) => {
const dataTable = await models.dataTable.findById(dataTableId);
if (!dataTable) {
Expand Down
1 change: 0 additions & 1 deletion packages/central-server/src/apiV2/dataTables/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@
*/

export { GETDataTables } from './GETDataTables';
export { CreateDataTables } from './CreateDataTables';
5 changes: 4 additions & 1 deletion packages/central-server/src/apiV2/import/importUsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import {
ObjectValidator,
hasContent,
constructIsOneOf,
constructIsEmptyOr,
} from '@tupaia/utils';
import { hashAndSaltPassword } from '@tupaia/auth';
import { VerifiedEmail } from '@tupaia/types';
import { assertBESAdminAccess } from '../../permissions';

export async function importUsers(req, res) {
Expand Down Expand Up @@ -90,10 +92,11 @@ const FIELD_VALIDATORS = {
first_name: [hasContent],
last_name: [],
email: [hasContent],
gender: [hasContent, constructIsOneOf(['f', 'm'])],
gender: [constructIsEmptyOr(constructIsOneOf(['f', 'm', 'unknown']))],
employer: [hasContent],
position: [hasContent],
mobile_number: [],
password: [hasContent],
permission_group: [hasContent],
verified_email: [constructIsEmptyOr(constructIsOneOf(Object.values(VerifiedEmail)))],
};
5 changes: 3 additions & 2 deletions packages/central-server/src/apiV2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { GETClinics } from './GETClinics';
import { GETDisasters } from './GETDisasters';
import { GETDataElements, EditDataElements, DeleteDataElements } from './dataElements';
import { GETDataGroups, EditDataGroups, DeleteDataGroups } from './dataGroups';
import { CreateDataTables, GETDataTables } from './dataTables';
import { GETDataTables } from './dataTables';
import { GETFeedItems } from './GETFeedItems';
import { GETGeographicalAreas } from './GETGeographicalAreas';
import { GETSurveyGroups } from './GETSurveyGroups';
Expand Down Expand Up @@ -264,7 +264,7 @@ apiV2.post('/surveyResponses', catchAsyncErrors(surveyResponse));
apiV2.post('/countries', useRouteHandler(BESAdminCreateHandler));
apiV2.post('/dataElements', useRouteHandler(BESAdminCreateHandler));
apiV2.post('/dataGroups', useRouteHandler(BESAdminCreateHandler));
apiV2.post('/dataTables', useRouteHandler(CreateDataTables));
apiV2.post('/dataTables', useRouteHandler(BESAdminCreateHandler));
apiV2.post('/dashboards', useRouteHandler(CreateDashboard));
apiV2.post('/mapOverlayGroups', useRouteHandler(CreateMapOverlayGroups));
apiV2.post('/disasters', useRouteHandler(BESAdminCreateHandler));
Expand Down Expand Up @@ -330,6 +330,7 @@ apiV2.delete('/answers/:recordId', useRouteHandler(DeleteAnswers));
apiV2.delete('/surveyResponses/:parentRecordId/answers/:recordId', useRouteHandler(DeleteAnswers));
apiV2.delete('/dataElements/:recordId', useRouteHandler(DeleteDataElements));
apiV2.delete('/dataGroups/:recordId', useRouteHandler(DeleteDataGroups));
apiV2.delete('/dataTables/:recordId', useRouteHandler(BESAdminDeleteHandler));
apiV2.delete('/disasters/:recordId', useRouteHandler(BESAdminDeleteHandler));
apiV2.delete('/entities/:recordId', useRouteHandler(DeleteEntity));
apiV2.delete('/feedItems/:recordId', useRouteHandler(BESAdminDeleteHandler));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,11 @@ export class DeleteMapOverlays extends DeleteHandler {
async assertUserHasAccess() {
await this.assertPermissions(assertBESAdminAccess);
}

async deleteRecord() {
const mapOverlay = await this.resourceModel.findById(this.recordId);
const reportModel = mapOverlay.legacy ? this.models.legacyReport : this.models.report;
await reportModel.delete({ code: mapOverlay.report_code });
return super.deleteRecord();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
BES_ADMIN_PERMISSION_GROUP,
TUPAIA_ADMIN_PANEL_PERMISSION_GROUP,
} from '../../permissions';
import { getAdminPanelAllowedEntityIds, getAdminPanelAllowedCountryCodes } from '../utilities';
import { getAdminPanelAllowedCountryIds, getAdminPanelAllowedCountryCodes } from '../utilities';

export const assertUserEntityPermissionPermissions = async (
accessPolicy,
Expand Down Expand Up @@ -78,7 +78,7 @@ export const createUserEntityPermissionDBFilter = async (accessPolicy, models, c
}
// If we don't have BES Admin access, add a filter to the SQL query
const dbConditions = {
'user_entity_permission.entity_id': await getAdminPanelAllowedEntityIds(accessPolicy, models),
'user_entity_permission.entity_id': await getAdminPanelAllowedCountryIds(accessPolicy, models),
...criteria,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,18 @@ export const getAdminPanelAllowedCountryCodes = accessPolicy => {
};

/*
* Get a list of entity ids this user has tupaia admin panel access to, or throw an error if they have none
* Get a list of country ids this user has tupaia admin panel access to, or throw an error if they have none
*
* @param {AccessPolicy} accessPolicy
* @param {ModelRegistry} models
*
* @returns string[] The entity ids
*/

export const getAdminPanelAllowedEntityIds = async (accessPolicy, models) => {
export const getAdminPanelAllowedCountryIds = async (accessPolicy, models) => {
const accessibleCountryCodes = getAdminPanelAllowedCountryCodes(accessPolicy);
const entities = await models.entity.find({
country_code: accessibleCountryCodes,
code: accessibleCountryCodes,
});

return entities.map(e => e.id);
Expand Down
4 changes: 2 additions & 2 deletions packages/central-server/src/apiV2/utilities/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ export {
} from './fetchCountriesByPermissionGroup';
export { fetchRequestingMeditrakDevice } from './fetchRequestingMeditrakDevice';
export {
getAdminPanelAllowedEntityIds,
getAdminPanelAllowedCountryIds,
getAdminPanelAllowedCountryCodes,
} from './getAdminPanelAllowedEntityIds';
} from './getAdminPanelAllowedCountries';
export { getArrayQueryParameter } from './getArrayQueryParameter';
export { getColumnsForMeditrakApp } from './getColumnsForMeditrakApp';
export { hasAccessToEntityForVisualisation } from './hasAccessToEntityForVisualisation';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ const DASHBOARD_ITEMS = [
config: { name: 'Modern Dashboard Item', type: 'view', viewType: 'singleValue' },
report_code: 'Modern_Report',
legacy: false,
permissionGroupIds: [],
},
{
id: generateTestId(),
code: 'Legacy_Dashboard_Item',
config: { name: 'Legacy Dashboard Item', type: 'chart', chartType: 'bar' },
report_code: 'Legacy_Report',
legacy: true,
permissionGroupIds: [],
},
{
id: generateTestId(),
Expand Down
2 changes: 1 addition & 1 deletion packages/database/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"refresh-database": "node ./scripts/refreshDatabase.js",
"test": "yarn package:test:withdb --runInBand",
"test:coverage": "yarn test --coverage",
"update-test-data": "bash -c 'source .env && pg_dump -s -U $DB_USER -O $DB_NAME > src/__tests__/testData/testDataDump.sql && pg_dump -t migrations -c -U $DB_USER -O $DB_NAME >> src/__tests__/testData/testDataDump.sql'",
"update-test-data": "bash -c 'source .env && pg_dump -s -h $DB_URL -U $DB_USER -O $DB_NAME > src/__tests__/testData/testDataDump.sql && pg_dump -t migrations -c -h $DB_URL -U $DB_USER -O $DB_NAME >> src/__tests__/testData/testDataDump.sql'",
"setup-test-database": "DB_NAME=tupaia_test scripts/setupTestDatabase.sh",
"check-test-database-exists": "DB_NAME=tupaia_test scripts/checkTestDatabaseExists.sh"
},
Expand Down
Loading

0 comments on commit f235188

Please sign in to comment.