Skip to content

Commit

Permalink
ATS client push API: New POST API, New backend type for ATSClient, at…
Browse files Browse the repository at this point in the history
…sController, route, services, validator updated
  • Loading branch information
sanjaytkbabu committed Oct 22, 2024
1 parent b16cd66 commit 5d51b35
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 15 deletions.
16 changes: 15 additions & 1 deletion app/src/controllers/ats.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { atsService } from '../services';

import { IdentityProvider } from '../utils/enums/application';
import { getCurrentUsername } from '../utils/utils';

import type { NextFunction, Request, Response } from 'express';
import type { ATSUserSearchParameters } from '../types';
import type { ATSClientResource, ATSUserSearchParameters } from '../types';

const controller = {
searchATSUsers: async (
Expand All @@ -12,6 +15,17 @@ const controller = {
try {
const response = await atsService.searchATSUsers(req.query);

res.status(response.status).json(response.data);
} catch (e: unknown) {
next(e);
}
},

createATSClient: async (req: Request<never, never, ATSClientResource, never>, res: Response, next: NextFunction) => {
try {
const atsClient = req.body;
atsClient.createdBy = `${IdentityProvider.IDIR.toUpperCase()}\\${getCurrentUsername(req.currentContext)}`;
const response = await atsService.createATSClient(atsClient);
res.status(response.status).json(response.data);
} catch (e: unknown) {
next(e);
Expand Down
13 changes: 12 additions & 1 deletion app/src/routes/v1/ats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { atsController } from '../../controllers';
import { hasAuthorization } from '../../middleware/authorization';
import { requireSomeAuth } from '../../middleware/requireSomeAuth';
import { requireSomeGroup } from '../../middleware/requireSomeGroup';
import { atsValidator } from '../../validators';
import { Action, Resource } from '../../utils/enums/application';

import type { NextFunction, Request, Response } from 'express';
import type { ATSUserSearchParameters } from '../../types';
import type { ATSClientResource, ATSUserSearchParameters } from '../../types';

const router = express.Router();
router.use(requireSomeAuth);
Expand All @@ -21,4 +22,14 @@ router.get(
}
);

/** Creates a client in ATS */
router.post(
'/client',
hasAuthorization(Resource.ATS, Action.CREATE),
atsValidator.createATSClient,
(req: Request<never, never, ATSClientResource, never>, res: Response, next: NextFunction): void => {
atsController.createATSClient(req, res, next);
}
);

export default router;
28 changes: 27 additions & 1 deletion app/src/services/ats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios from 'axios';
import config from 'config';

import type { AxiosInstance } from 'axios';
import type { ATSUserSearchParameters } from '../types';
import type { ATSClientResource, ATSUserSearchParameters } from '../types';

/**
* @function getToken
Expand Down Expand Up @@ -75,6 +75,32 @@ const service = {
};
}
}
},

/**
* @function createATSClient
* Creates a client in ATS
* @param {ATSClientResource} data The client data
* @returns {Promise<data | null>} The result of calling the post api
*/

createATSClient: async (atsClient: ATSClientResource) => {
try {
const { data, status } = await atsAxios().post('/clients', atsClient);
return { data: data, status };
} catch (e: unknown) {
if (axios.isAxiosError(e)) {
return {
data: e.response?.data.message,
status: e.response ? e.response.status : 500
};
} else {
return {
data: 'Error',
status: 500
};
}
}
}
};

Expand Down
23 changes: 23 additions & 0 deletions app/src/types/ATSClientResource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { BasicResponse } from '../utils/enums/application';

type AddressResource = {
'@type': 'AddressResource';
addressLine1: string;
addressLine2: string | null;
city: string;
provinceCode: string;
countryCode: string;
postalCode: string | null;
primaryPhone: string;
email: string;
};

export type ATSClientResource = {
'@type': 'ClientResource';
address: AddressResource;
firstName: string;
surName: string;
regionName: string;
optOutOfBCStatSurveyInd: BasicResponse.YES | BasicResponse.NO;
createdBy: string;
};
1 change: 1 addition & 0 deletions app/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export type { Activity } from './Activity';
export type { AccessRequest } from './AccessRequest';
export type { ATSClientResource } from './ATSClientResource';
export type { ATSUserSearchParameters } from './ATSUserSearchParameters';
export type { BceidSearchParameters } from './BceidSearchParameters';
export type { BringForward } from './BringForward';
Expand Down
33 changes: 33 additions & 0 deletions app/src/validators/ats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Joi from 'joi';

import { validate } from '../middleware/validation';

import { BasicResponse } from '../utils/enums/application';

const addressBody = {
'@type': 'AddressResource',
addressLine1: Joi.string().max(255).allow(null),
city: Joi.string().max(255).allow(null),
provinceCode: Joi.string().max(255).allow(null),
primaryPhone: Joi.string().max(255).allow(null),
email: Joi.string().max(255).allow(null)
};

const clientBody = {
'@type': 'ClientResource',
firstName: Joi.string().max(255).required(),
surName: Joi.string().max(255).required(),
regionName: Joi.string().max(255).required(),
optOutOfBCStatSurveyInd: BasicResponse.NO.toUpperCase(),
address: Joi.object(addressBody).allow(null)
};

const schema = {
createATSClient: {
body: Joi.object(clientBody)
}
};

export default {
createATSClient: validate(schema.createATSClient)
};
1 change: 1 addition & 0 deletions app/src/validators/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { default as accessRequestValidator } from './accessRequest';
export { default as atsValidator } from './ats';
export { default as documentValidator } from './document';
export { default as enquiryValidator } from './enquiry';
export { default as noteValidator } from './note';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ onMounted(async () => {
<Button
v-if="!atsClientNumber"
aria-label="New ATS client"
class="h-2rem ml-4"
class="h-2rem ml-3"
@click="atsUserCreateModalVisible = true"
>
New ATS Client
Expand Down
25 changes: 16 additions & 9 deletions frontend/src/components/user/ATSUserCreateModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Button, Column, DataTable, Dialog, useToast } from '@/lib/primevue';
import { Spinner } from '@/components/layout';
import { atsService } from '@/services';
import { BasicResponse } from '@/utils/enums/application';
import { BasicResponse, Initiative } from '@/utils/enums/application';
import type { Ref } from 'vue';
import type { ATSUser, Submission } from '@/types';
Expand All @@ -18,9 +18,6 @@ const { submission } = defineProps<{
// Emits
const emit = defineEmits(['atsUserLink:link']);
// Constants
const REGION_BC = 'BC';
// State
const atsClientNumber: Ref<string> = ref('');
const loading: Ref<boolean> = ref(false);
Expand Down Expand Up @@ -49,14 +46,15 @@ async function createATSClient() {
},
firstName: submission.contactFirstName,
surName: submission.contactLastName,
regionName: REGION_BC,
regionName: Initiative.HOUSING,
optOutOfBCStatSurveyInd: BasicResponse.NO.toUpperCase()
});
const response = await atsService.createATSClient(data);
if (response.status === 201) {
atsClientNumber.value = response.data.clientId;
emit('atsUserLink:link', atsClientNumber.value);
visible.value = false;
toast.success('New client pushed to ATS');
} else {
toast.error('Error pushing client to ATS');
Expand Down Expand Up @@ -140,7 +138,7 @@ onMounted(async () => {
header="Last Name"
/>
<Column
field="lastName"
field="phone"
header="Phone"
/>
<Column
Expand All @@ -152,15 +150,24 @@ onMounted(async () => {
header="Location address"
/>
</DataTable>
<div class="flex justify-content-end">
<div
v-if="!loading"
class="flex justify-content-start"
>
<Button
class="p-button-solid"
class="p-button-solid mr-3"
label="Push to ATS"
icon="pi pi-upload"
visible="false"
@click="createATSClient()"
/>
<Button
class="mr-0"
outlined
label="Cancel"
@click="visible = false"
/>
</div>
<Spinner v-if="loading" />
</div>
</Dialog>
</template>
Expand Down
10 changes: 8 additions & 2 deletions frontend/src/components/user/ATSUserLinkModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,19 @@ onMounted(async () => {
sortable
/>
</DataTable>
<div class="flex justify-content-end">
<div class="flex justify-content-start">
<Button
class="p-button-solid mr-0"
class="p-button-solid mr-3"
label="Link to PCNS"
:disabled="!selectedUser"
@click="emit('atsUserLink:link', selectedUser)"
/>
<Button
class="mr-0"
outlined
label="Cancel"
@click="visible = false"
/>
</div>
</Dialog>
</template>
Expand Down

0 comments on commit 5d51b35

Please sign in to comment.