Skip to content

Commit

Permalink
feat: CE-976 Admin screen for CEEB (#605)
Browse files Browse the repository at this point in the history
Co-authored-by: Barrett Falk <[email protected]>
Co-authored-by: gregorylavery <[email protected]>
Co-authored-by: Mike <[email protected]>
Co-authored-by: dmitri-korin-bcps <[email protected]>
Co-authored-by: Mike Sears <[email protected]>
  • Loading branch information
6 people authored Aug 30, 2024
1 parent 1cac931 commit b072e01
Show file tree
Hide file tree
Showing 57 changed files with 1,907 additions and 62 deletions.
6 changes: 6 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ import { GirTypeCodeModule } from "./v1/gir_type_code/gir_type_code.module";
import { GeneralIncidentComplaintModule } from "./v1/gir_complaint/gir_complaint.module";
import { FeatureFlagModule } from "./v1/feature_flag/feature_flag.module";
import { FeatureCodeModule } from "./v1/feature_code/feature_code.module";
import { TeamModule } from "./v1/team/team.module";
import { TeamCodeModule } from "./v1/team_code/team_code.module";
import { OfficerTeamXrefModule } from "./v1/officer_team_xref/officer_team_xref.module";

console.log("Var check - POSTGRESQL_HOST", process.env.POSTGRESQL_HOST);
console.log("Var check - POSTGRESQL_DATABASE", process.env.POSTGRESQL_DATABASE);
Expand Down Expand Up @@ -118,6 +121,9 @@ if (process.env.POSTGRESQL_PASSWORD != null) {
GeneralIncidentComplaintModule,
FeatureFlagModule,
FeatureCodeModule,
TeamModule,
TeamCodeModule,
OfficerTeamXrefModule,
],
controllers: [AppController],
providers: [AppService, ComplaintSequenceResetScheduler],
Expand Down
5 changes: 5 additions & 0 deletions backend/src/common/has-role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const hasRole = (req, role: string) => {
const userroles = req.user.client_roles;
const hasRole = userroles?.includes(role);
return hasRole;
};
4 changes: 3 additions & 1 deletion backend/src/enum/role.enum.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export enum Role {
COS_OFFICER = "COS Officer",
COS_ADMIN = "COS Admin",
COS_ADMINISTRATOR = "COS Administrator",
CEEB = "CEEB",
CEEB_COMPLIANCE_COORDINATOR = "CEEB Compliance Coordinator",
CEEB_SECTION_HEAD = "CEEB Section Head",
TEMPORARY_TEST_ADMIN = "TEMPORARY_TEST_ADMIN",
}
10 changes: 10 additions & 0 deletions backend/src/external_api/css/css.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from "@nestjs/common";
import { CssService } from "./css.service";
import { ConfigurationModule } from "../../v1/configuration/configuration.module";

@Module({
imports: [ConfigurationModule],
providers: [CssService],
exports: [CssService],
})
export class CssModule {}
28 changes: 28 additions & 0 deletions backend/src/external_api/css/css.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Test, TestingModule } from "@nestjs/testing";
import { getRepositoryToken } from "@nestjs/typeorm";
import { ConfigurationService } from "../../v1/configuration/configuration.service";
import { Configuration } from "../../v1/configuration/entities/configuration.entity";
import { CssService } from "./css.service";

describe("CssService", () => {
let service: CssService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
CssService,
ConfigurationService,
{
provide: getRepositoryToken(Configuration),
useValue: {},
},
],
}).compile();

service = module.get<CssService>(CssService);
});

it("should be defined", () => {
expect(service).toBeDefined();
});
});
118 changes: 118 additions & 0 deletions backend/src/external_api/css/css.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { Inject, Injectable, Logger } from "@nestjs/common";
import { ExternalApiService } from "../external-api-service";
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import { get } from "../../helpers/axios-api";
import { ConfigurationService } from "../../v1/configuration/configuration.service";

@Injectable()
export class CssService implements ExternalApiService {
private readonly logger = new Logger(CssService.name);

readonly authApi: string;
readonly baseUri: string;
readonly clientId: string;
readonly clientSecret: string;
readonly grantType: string;
readonly env: string;

@Inject(ConfigurationService)
readonly configService: ConfigurationService;

constructor() {
this.authApi = process.env.CSS_TOKEN_URL;
this.baseUri = process.env.CSS_URL;
this.clientId = process.env.CSS_CLIENT_ID;
this.clientSecret = process.env.CSS_CLIENT_SECRET;
this.grantType = "client_credentials";
this.env = process.env.ENVIRONMENT;
}

authenticate = async (): Promise<string> => {
const response: AxiosResponse = await axios.post(
this.authApi,
{
client_id: this.clientId,
client_secret: this.clientSecret,
grant_type: this.grantType,
},
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
},
);
return response?.data?.access_token;
};

getUserIdirByName = async (firstName, lastName): Promise<AxiosResponse> => {
try {
const apiToken = await this.authenticate();
const url = `${this.baseUri}/api/v1/${this.env}/idir/users?firstName=${firstName}&lastName=${lastName}`;
const config: AxiosRequestConfig = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiToken}`,
},
};
const response = await get(apiToken, url, config);
return response?.data.data;
} catch (error) {
this.logger.error(`exception: unable to get user: ${firstName} ${lastName} - error: ${error}`);
throw new Error(`exception: unable to get user: ${firstName} ${lastName} - error: ${error}`);
}
};

getUserRoles = async (userIdir): Promise<{ name: string; composite: string }[]> => {
try {
const apiToken = await this.authenticate();
const url = `${this.baseUri}/api/v1/integrations/4794/${this.env}/users/${userIdir}/roles`;
const config: AxiosRequestConfig = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiToken}`,
},
};
const response = await get(apiToken, url, config);
return response?.data.data;
} catch (error) {
this.logger.error(`exception: unable to get user's roles ${userIdir} - error: ${error}`);
throw new Error(`exception: unable to get user's roles ${userIdir} - error: ${error}`);
}
};

updateUserRole = async (userIdir, userRoles): Promise<{ name: string; composite: string }[]> => {
try {
const apiToken = await this.authenticate();
const url = `${this.baseUri}/api/v1/integrations/4794/${this.env}/users/${userIdir}/roles`;
const config: AxiosRequestConfig = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiToken}`,
},
};
const response = await axios.post(url, userRoles, config);
return response?.data.data;
} catch (error) {
this.logger.error(`exception: unable to update user's roles ${userIdir} - error: ${error}`);
throw new Error(`exception: unable to update user's roles ${userIdir} - error: ${error}`);
}
};

deleteUserRole = async (userIdir, roleName): Promise<AxiosResponse> => {
try {
const apiToken = await this.authenticate();
const url = `${this.baseUri}/api/v1/integrations/4794/${this.env}/users/${userIdir}/roles/${roleName}`;
const config: AxiosRequestConfig = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiToken}`,
},
};
const response = await axios.delete(url, config);
return response?.data.data;
} catch (error) {
this.logger.error(`exception: unable to delete user's role ${userIdir} - error: ${error}`);
throw new Error(`exception: unable to delete user's role ${userIdir} - error: ${error}`);
}
};
}
3 changes: 3 additions & 0 deletions backend/src/types/models/code-tables/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { AssessmentType } from "./assessment-type";
import { PreventionType } from "./prevention-type";
import { Equipment } from "./equipment";
import { GirType } from "./gir-type";
import { TeamType } from "./team-type";

export const AvailableCodeTables = [
"agency",
Expand Down Expand Up @@ -48,6 +49,7 @@ export const AvailableCodeTables = [
"wildlife-outcomes",
"equipment",
"gir-type",
"team",
];

export const AvailableAgencies = ["cos"];
Expand All @@ -74,4 +76,5 @@ export {
PreventionType,
Equipment,
GirType,
TeamType,
};
5 changes: 5 additions & 0 deletions backend/src/types/models/code-tables/team-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { BaseCodeTable } from "./code-table";

export interface TeamType extends BaseCodeTable {
team: string;
}
6 changes: 6 additions & 0 deletions backend/src/v1/case_file/case_file.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
MockSpeciesCodeTableRepository,
MockViolationsCodeTableRepository,
MockGirTypeCodeRepository,
MockTeamCodeRepository,
} from "../../../test/mocks/mock-code-table-repositories";
import { ComplaintStatusCode } from "../complaint_status_code/entities/complaint_status_code.entity";
import { HwcrComplaintNatureCode } from "../hwcr_complaint_nature_code/entities/hwcr_complaint_nature_code.entity";
Expand All @@ -58,6 +59,7 @@ import { GirTypeCode } from "../gir_type_code/entities/gir_type_code.entity";
import { ComplaintUpdatesService } from "../complaint_updates/complaint_updates.service";
import { ActionTaken } from "../complaint/entities/action_taken.entity";
import { StagingComplaint } from "../staging_complaint/entities/staging_complaint.entity";
import { TeamCode } from "../team_code/entities/team_code.entity";

describe("Testing: Case File Service", () => {
let service: CaseFileService;
Expand Down Expand Up @@ -146,6 +148,10 @@ describe("Testing: Case File Service", () => {
provide: getRepositoryToken(GirTypeCode),
useFactory: MockGirTypeCodeRepository,
},
{
provide: getRepositoryToken(TeamCode),
useFactory: MockTeamCodeRepository,
},
{
provide: getRepositoryToken(PersonComplaintXref),
useValue: {},
Expand Down
6 changes: 6 additions & 0 deletions backend/src/v1/code-table/code-table.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
MockSpeciesCodeTableRepository,
MockViolationsCodeTableRepository,
MockGirTypeCodeRepository,
MockTeamCodeRepository,
} from "../../../test/mocks/mock-code-table-repositories";
import { AgencyCode } from "../agency_code/entities/agency_code.entity";
import { AttractantCode } from "../attractant_code/entities/attractant_code.entity";
Expand All @@ -38,6 +39,7 @@ import { CosGeoOrgUnit } from "../cos_geo_org_unit/entities/cos_geo_org_unit.ent
import { ComplaintTypeCode } from "../complaint_type_code/entities/complaint_type_code.entity";
import { ReportedByCode } from "../reported_by_code/entities/reported_by_code.entity";
import { GirTypeCode } from "../gir_type_code/entities/gir_type_code.entity";
import { TeamCode } from "../team_code/entities/team_code.entity";

describe("Testing: CodeTable Controller", () => {
let app: INestApplication;
Expand Down Expand Up @@ -100,6 +102,10 @@ describe("Testing: CodeTable Controller", () => {
provide: getRepositoryToken(GirTypeCode),
useFactory: MockGirTypeCodeRepository,
},
{
provide: getRepositoryToken(TeamCode),
useFactory: MockTeamCodeRepository,
},
],
})
.overrideGuard(JwtAuthGuard)
Expand Down
2 changes: 1 addition & 1 deletion backend/src/v1/code-table/code-table.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class CodeTableController {
constructor(private readonly service: CodeTableService) {}

@Get(":table")
@Roles(Role.COS_OFFICER, Role.CEEB)
@Roles(Role.COS_OFFICER, Role.CEEB, Role.TEMPORARY_TEST_ADMIN)
async getCodeTableByName(@Param("table") table: string, @Token() token): Promise<BaseCodeTable[]> {
if (!AvailableCodeTables.includes(table)) {
throw new NotFoundException();
Expand Down
2 changes: 2 additions & 0 deletions backend/src/v1/code-table/code-table.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { CosGeoOrgUnit } from "../cos_geo_org_unit/entities/cos_geo_org_unit.ent
import { ComplaintTypeCode } from "../complaint_type_code/entities/complaint_type_code.entity";
import { ReportedByCode } from "../reported_by_code/entities/reported_by_code.entity";
import { GirTypeCode } from "../gir_type_code/entities/gir_type_code.entity";
import { TeamCode } from "../team_code/entities/team_code.entity";

@Module({
imports: [
Expand All @@ -31,6 +32,7 @@ import { GirTypeCode } from "../gir_type_code/entities/gir_type_code.entity";
TypeOrmModule.forFeature([ComplaintTypeCode]),
TypeOrmModule.forFeature([ReportedByCode]),
TypeOrmModule.forFeature([GirTypeCode]),
TypeOrmModule.forFeature([TeamCode]),
],
controllers: [CodeTableController, CaseManagementCodeTableController],
providers: [CodeTableService],
Expand Down
18 changes: 18 additions & 0 deletions backend/src/v1/code-table/code-table.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
MockRegionCodeTableServiceRepository,
MockReportedByCodeTableRepository,
MockGirTypeCodeRepository,
MockTeamCodeRepository,
} from "../../../test/mocks/mock-code-table-repositories";
import { HwcrComplaintNatureCode } from "../hwcr_complaint_nature_code/entities/hwcr_complaint_nature_code.entity";
import { GeoOrgUnitTypeCode } from "../geo_org_unit_type_code/entities/geo_org_unit_type_code.entity";
Expand All @@ -33,6 +34,7 @@ import { CosGeoOrgUnit } from "../cos_geo_org_unit/entities/cos_geo_org_unit.ent
import { ComplaintTypeCode } from "../complaint_type_code/entities/complaint_type_code.entity";
import { ReportedByCode } from "../reported_by_code/entities/reported_by_code.entity";
import { GirTypeCode } from "../gir_type_code/entities/gir_type_code.entity";
import { TeamCode } from "../team_code/entities/team_code.entity";

describe("Testing: CodeTable Service", () => {
let service: CodeTableService;
Expand Down Expand Up @@ -93,6 +95,10 @@ describe("Testing: CodeTable Service", () => {
provide: getRepositoryToken(GirTypeCode),
useFactory: MockGirTypeCodeRepository,
},
{
provide: getRepositoryToken(TeamCode),
useFactory: MockTeamCodeRepository,
},
],
}).compile();

Expand Down Expand Up @@ -319,6 +325,10 @@ describe("Testing: CodeTable service", () => {
provide: getRepositoryToken(GirTypeCode),
useFactory: MockGirTypeCodeRepository,
},
{
provide: getRepositoryToken(TeamCode),
useFactory: MockTeamCodeRepository,
},
],
}).compile();

Expand Down Expand Up @@ -402,6 +412,10 @@ describe("Testing: CodeTable service", () => {
provide: getRepositoryToken(GirTypeCode),
useFactory: MockGirTypeCodeRepository,
},
{
provide: getRepositoryToken(TeamCode),
useFactory: MockTeamCodeRepository,
},
],
}).compile();

Expand Down Expand Up @@ -485,6 +499,10 @@ describe("Testing: CodeTable service", () => {
provide: getRepositoryToken(GirTypeCode),
useFactory: MockGirTypeCodeRepository,
},
{
provide: getRepositoryToken(TeamCode),
useFactory: MockTeamCodeRepository,
},
],
}).compile();

Expand Down
Loading

0 comments on commit b072e01

Please sign in to comment.