-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
79b0776
commit cb7327a
Showing
36 changed files
with
714 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { atsService } from '../services'; | ||
|
||
import type { NextFunction, Request, Response } from 'express'; | ||
import type { ATSUserSearchParameters } from '../types'; | ||
|
||
const controller = { | ||
searchATSUsers: async ( | ||
req: Request<never, never, never, ATSUserSearchParameters>, | ||
res: Response, | ||
next: NextFunction | ||
) => { | ||
try { | ||
const response = await atsService.searchATSUsers(req.query); | ||
|
||
res.status(response.status).json(response.data); | ||
} catch (e: unknown) { | ||
next(e); | ||
} | ||
} | ||
}; | ||
|
||
export default controller; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
249 changes: 249 additions & 0 deletions
249
app/src/db/migrations/20241002000000_011-ats-integration.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
/* eslint-disable max-len */ | ||
import type { Knex } from 'knex'; | ||
|
||
import { Action, GroupName, Initiative, Resource } from '../../utils/enums/application'; | ||
|
||
const resources = [ | ||
{ | ||
name: Resource.ATS | ||
} | ||
]; | ||
|
||
const actions = [ | ||
{ | ||
name: Action.CREATE | ||
}, | ||
{ | ||
name: Action.READ | ||
}, | ||
{ | ||
name: Action.UPDATE | ||
}, | ||
{ | ||
name: Action.DELETE | ||
} | ||
]; | ||
|
||
export async function up(knex: Knex): Promise<void> { | ||
return ( | ||
Promise.resolve() | ||
// Add ats ats_client_number to enquiry table | ||
.then(() => | ||
knex.schema.alterTable('enquiry', (table) => { | ||
table.text('ats_client_number'); | ||
}) | ||
) | ||
// Drop added_to_ats from submission table as it is not needed anymore with the new link ats ui | ||
.then(() => | ||
knex.schema.alterTable('submission', (table) => { | ||
table.dropColumn('added_to_ats'); | ||
}) | ||
) | ||
|
||
.then(() => { | ||
return knex('yars.resource').insert(resources); | ||
}) | ||
|
||
.then(() => { | ||
/* | ||
* Add policies | ||
*/ | ||
|
||
const items = []; | ||
for (const resource of resources) { | ||
for (const action of actions) { | ||
items.push({ | ||
resource_id: knex('yars.resource').where({ name: resource.name }).select('resource_id'), | ||
action_id: knex('yars.action').where({ name: action.name }).select('action_id') | ||
}); | ||
} | ||
} | ||
|
||
return knex('yars.policy').insert(items); | ||
}) | ||
|
||
.then(async () => { | ||
/* | ||
* Add roles | ||
*/ | ||
|
||
const items: Array<{ name: string; description: string }> = []; | ||
|
||
const addRolesForResource = (resourceName: string) => { | ||
items.push( | ||
{ | ||
name: `${resourceName.toUpperCase()}_CREATOR`, | ||
description: `Can create ${resourceName.toLowerCase()}s` | ||
}, | ||
{ | ||
name: `${resourceName.toUpperCase()}_VIEWER`, | ||
description: `Can view ${resourceName.toLowerCase()}s` | ||
}, | ||
{ | ||
name: `${resourceName.toUpperCase()}_EDITOR`, | ||
description: `Can edit ${resourceName.toLowerCase()}s` | ||
} | ||
); | ||
}; | ||
|
||
for (const resource of resources) { | ||
addRolesForResource(resource.name); | ||
} | ||
|
||
return knex('yars.role').insert(items); | ||
}) | ||
|
||
.then(async () => { | ||
/* | ||
* Add role to policy mappings | ||
*/ | ||
|
||
const policies = await knex | ||
.select('p.policy_id', 'r.name as resource_name', 'a.name as action_name') | ||
.from({ p: 'yars.policy' }) | ||
.innerJoin({ r: 'yars.resource' }, 'p.resource_id', '=', 'r.resource_id') | ||
.innerJoin({ a: 'yars.action' }, 'p.action_id', '=', 'a.action_id'); | ||
|
||
const items: Array<{ role_id: number; policy_id: number }> = []; | ||
|
||
const addRolePolicies = async (resourceName: string) => { | ||
const creatorId = await knex('yars.role') | ||
.where({ name: `${resourceName.toUpperCase()}_CREATOR` }) | ||
.select('role_id'); | ||
const viewerId = await knex('yars.role') | ||
.where({ name: `${resourceName.toUpperCase()}_VIEWER` }) | ||
.select('role_id'); | ||
const editorId = await knex('yars.role') | ||
.where({ name: `${resourceName.toUpperCase()}_EDITOR` }) | ||
.select('role_id'); | ||
|
||
const resourcePolicies = policies.filter((x) => x.resource_name === resourceName); | ||
items.push( | ||
{ | ||
role_id: creatorId[0].role_id, | ||
policy_id: resourcePolicies.find((x) => x.action_name == Action.CREATE).policy_id | ||
}, | ||
{ | ||
role_id: viewerId[0].role_id, | ||
policy_id: resourcePolicies.find((x) => x.action_name == Action.READ).policy_id | ||
}, | ||
{ | ||
role_id: editorId[0].role_id, | ||
policy_id: resourcePolicies.find((x) => x.action_name == Action.UPDATE).policy_id | ||
}, | ||
|
||
{ | ||
role_id: editorId[0].role_id, | ||
policy_id: resourcePolicies.find((x) => x.action_name == Action.DELETE).policy_id | ||
} | ||
); | ||
}; | ||
|
||
await addRolePolicies(Resource.ATS); | ||
|
||
return knex('yars.role_policy').insert(items); | ||
}) | ||
|
||
.then(async () => { | ||
/* | ||
* Add group to role mappings | ||
*/ | ||
|
||
const housing_id = knex('initiative') | ||
.where({ | ||
code: Initiative.HOUSING | ||
}) | ||
.select('initiative_id'); | ||
|
||
const navigator_group_id = await knex('yars.group') | ||
.where({ initiative_id: housing_id, name: GroupName.NAVIGATOR }) | ||
.select('group_id'); | ||
|
||
const navigator_read_group_id = await knex('yars.group') | ||
.where({ initiative_id: housing_id, name: GroupName.NAVIGATOR_READ_ONLY }) | ||
.select('group_id'); | ||
|
||
const superviser_group_id = await knex('yars.group') | ||
.where({ initiative_id: housing_id, name: GroupName.SUPERVISOR }) | ||
.select('group_id'); | ||
|
||
const admin_group_id = await knex('yars.group') | ||
.where({ initiative_id: housing_id, name: GroupName.ADMIN }) | ||
.select('group_id'); | ||
|
||
const items: Array<{ group_id: number; role_id: number }> = []; | ||
|
||
const addResourceRoles = async (group_id: number, resourceName: Resource, actionNames: Array<Action>) => { | ||
if (actionNames.includes(Action.CREATE)) { | ||
items.push({ | ||
group_id: group_id, | ||
role_id: ( | ||
await knex('yars.role') | ||
.where({ name: `${resourceName}_CREATOR` }) | ||
.select('role_id') | ||
)[0].role_id | ||
}); | ||
} | ||
|
||
if (actionNames.includes(Action.READ)) { | ||
items.push({ | ||
group_id: group_id, | ||
role_id: ( | ||
await knex('yars.role') | ||
.where({ name: `${resourceName}_VIEWER` }) | ||
.select('role_id') | ||
)[0].role_id | ||
}); | ||
} | ||
|
||
if (actionNames.includes(Action.UPDATE) || actionNames.includes(Action.DELETE)) { | ||
items.push({ | ||
group_id: group_id, | ||
role_id: ( | ||
await knex('yars.role') | ||
.where({ name: `${resourceName}_EDITOR` }) | ||
.select('role_id') | ||
)[0].role_id | ||
}); | ||
} | ||
}; | ||
|
||
// Note: Only UPDATE or DELETE is required to be given EDITOR role, don't include both | ||
// prettier-ignore | ||
{ | ||
// Add all navigator role mappings | ||
await addResourceRoles(navigator_group_id[0].group_id, Resource.ATS, [Action.CREATE, Action.READ]); | ||
|
||
// Add all navigator read only role mappings | ||
await addResourceRoles(navigator_read_group_id[0].group_id, Resource.ATS, [Action.READ]); | ||
|
||
|
||
// Add all supervisor role mappings | ||
await addResourceRoles(superviser_group_id[0].group_id, Resource.ATS, [Action.CREATE, Action.READ]); | ||
|
||
// Add all admin role mappings | ||
await addResourceRoles(admin_group_id[0].group_id, Resource.ATS, [Action.READ]); | ||
|
||
} | ||
return knex('yars.group_role').insert(items); | ||
}) | ||
); | ||
} | ||
|
||
export async function down(knex: Knex): Promise<void> { | ||
return ( | ||
Promise.resolve() | ||
// Drop client_id from enquiry table | ||
.then(() => | ||
knex.schema.alterTable('enquiry', (table) => { | ||
table.dropColumn('ats_client_number'); | ||
}) | ||
) | ||
// Add added_to_ats to submission table | ||
.then(() => | ||
knex.schema.alterTable('submission', (table) => { | ||
table.boolean('added_to_ats').notNullable().defaultTo(false); | ||
}) | ||
) | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import express from 'express'; | ||
|
||
import { atsController } from '../../controllers'; | ||
import { hasAuthorization } from '../../middleware/authorization'; | ||
import { requireSomeAuth } from '../../middleware/requireSomeAuth'; | ||
import { requireSomeGroup } from '../../middleware/requireSomeGroup'; | ||
import { Action, Resource } from '../../utils/enums/application'; | ||
|
||
import type { NextFunction, Request, Response } from 'express'; | ||
import type { ATSUserSearchParameters } from '../../types'; | ||
|
||
const router = express.Router(); | ||
router.use(requireSomeAuth); | ||
router.use(requireSomeGroup); | ||
|
||
router.get( | ||
'/clients', | ||
hasAuthorization(Resource.ATS, Action.READ), | ||
(req: Request<never, never, never, ATSUserSearchParameters>, res: Response, next: NextFunction): void => { | ||
atsController.searchATSUsers(req, res, next); | ||
} | ||
); | ||
|
||
export default router; |
Oops, something went wrong.