Skip to content

Commit

Permalink
#1851 - Enable File Upload for Public Institution User (#1893)
Browse files Browse the repository at this point in the history
* #1851 - Enabled File Upload for Public Institution User

- Remove upload file button.
- Disable links to download/view the file.
- Wrote E2E tests.

* Updated code with review comments

* #1851 - Enabled File Upload for Public Institution User

- Remove upload file button.
- Disable links to download/view the file.
- Wrote E2E tests.

* Updated code with review comments

* Updated code with review comments

- Added additional e2e test

* Updated code with review comments

* Updated code with review comments

- Removed the AEST template code from the common file uploads component and moved it to the AEST specific component

* Code update with review comments

* Updated code to revert the template code for Upload Button and the Modal to the common component

* Updated code with review comments

* Updated code with review comments

* Updated code with review comments
  • Loading branch information
sh16011993 authored Apr 25, 2023
1 parent db0abba commit d5734da
Show file tree
Hide file tree
Showing 14 changed files with 475 additions and 188 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { HttpStatus, INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { DataSource, Repository } from "typeorm";
import {
authorizeUserTokenForLocation,
BEARER_AUTH_TYPE,
createTestingAppModule,
getAuthRelatedEntities,
getInstitutionToken,
InstitutionTokenTypes,
} from "../../../../testHelpers";
import {
createFakeInstitutionLocation,
createFakeApplication,
saveFakeStudent,
saveFakeStudentFileUpload,
} from "@sims/test-utils";
import {
Application,
FileOriginType,
Institution,
InstitutionLocation,
} from "@sims/sims-db";

describe("StudentInstitutionsController(e2e)-getStudentFileUploads", () => {
let app: INestApplication;
let appDataSource: DataSource;
let collegeF: Institution;
let collegeFLocation: InstitutionLocation;
let applicationRepo: Repository<Application>;

beforeAll(async () => {
const { nestApplication, dataSource } = await createTestingAppModule();
app = nestApplication;
appDataSource = dataSource;

const { institution } = await getAuthRelatedEntities(
appDataSource,
InstitutionTokenTypes.CollegeFUser,
);
collegeF = institution;
collegeFLocation = createFakeInstitutionLocation(collegeF);
await authorizeUserTokenForLocation(
appDataSource,
InstitutionTokenTypes.CollegeFUser,
collegeFLocation,
);
applicationRepo = appDataSource.getRepository(Application);
});

it("Should get the student file uploads when student has at least one application submitted for the institution.", async () => {
// Arrange.
// Student who has application submitted to institution.
const student = await saveFakeStudent(appDataSource);
const application = createFakeApplication({
location: collegeFLocation,
student,
});
await applicationRepo.save(application);

const institutionUserToken = await getInstitutionToken(
InstitutionTokenTypes.CollegeFUser,
);

// Save fake file upload for the student.
const studentUploadedFile = await saveFakeStudentFileUpload(appDataSource, {
student,
creator: student.user,
});

// Endpoint to test.
const endpoint = `/institutions/student/${student.id}/documents`;

// Act/Assert.
await request(app.getHttpServer())
.get(endpoint)
.auth(institutionUserToken, BEARER_AUTH_TYPE)
.expect(HttpStatus.OK)
.expect([
{
fileName: studentUploadedFile.fileName,
uniqueFileName: studentUploadedFile.uniqueFileName,
metadata: studentUploadedFile.metadata,
groupName: "Ministry communications",
updatedAt: studentUploadedFile.updatedAt.toISOString(),
fileOrigin: studentUploadedFile.fileOrigin,
},
]);
});

it("Should not get the student file uploads when student has at least one application submitted for the institution but the fileOrigin is set to Temporary", async () => {
// Arrange.
// Student who has application submitted to institution.
const student = await saveFakeStudent(appDataSource);
const application = createFakeApplication({
location: collegeFLocation,
student,
});
await applicationRepo.save(application);

const institutionUserToken = await getInstitutionToken(
InstitutionTokenTypes.CollegeFUser,
);

// Save fake file upload for the student.
await saveFakeStudentFileUpload(
appDataSource,
{
student,
creator: student.user,
},
{ fileOrigin: FileOriginType.Temporary },
);

// Endpoint to test.
const endpoint = `/institutions/student/${student.id}/documents`;

// Act/Assert.
await request(app.getHttpServer())
.get(endpoint)
.auth(institutionUserToken, BEARER_AUTH_TYPE)
.expect(HttpStatus.OK)
.expect([]);
});

afterAll(async () => {
await app?.close();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ export class StudentUploadFileAPIOutDTO {
}

/**
* AEST user to view student uploaded documents.
* AEST / Institution user to view student uploaded documents.
*/
export class AESTStudentFileAPIOutDTO extends StudentUploadFileAPIOutDTO {
export class StudentFileDetailsAPIOutDTO extends StudentUploadFileAPIOutDTO {
metadata: StudentFileMetadataAPIOutDTO;
groupName: string;
updatedAt: Date;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { UserGroups } from "../../auth/user-groups.enum";
import BaseController from "../BaseController";
import {
AESTFileUploadToStudentAPIInDTO,
AESTStudentFileAPIOutDTO,
StudentFileDetailsAPIOutDTO,
AESTStudentProfileAPIOutDTO,
StudentSearchAPIInDTO,
ApplicationSummaryAPIOutDTO,
Expand Down Expand Up @@ -103,18 +103,10 @@ export class StudentAESTController extends BaseController {
@Get(":studentId/documents")
async getAESTStudentFiles(
@Param("studentId", ParseIntPipe) studentId: number,
): Promise<AESTStudentFileAPIOutDTO[]> {
const studentDocuments = await this.fileService.getStudentUploadedFiles(
studentId,
);
return studentDocuments.map((studentDocument) => ({
fileName: studentDocument.fileName,
uniqueFileName: studentDocument.uniqueFileName,
metadata: studentDocument.metadata,
groupName: studentDocument.groupName,
updatedAt: studentDocument.updatedAt,
fileOrigin: studentDocument.fileOrigin,
}));
): Promise<StudentFileDetailsAPIOutDTO[]> {
return this.studentControllerService.getStudentUploadedFiles(studentId, {
extendedDetails: true,
}) as Promise<StudentFileDetailsAPIOutDTO[]>;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
ApplicationSummaryAPIOutDTO,
SearchStudentAPIOutDTO,
StudentProfileAPIOutDTO,
StudentFileDetailsAPIOutDTO,
StudentUploadFileAPIOutDTO,
} from "./models/student.dto";
import { transformAddressDetailsForAddressBlockForm } from "../utils/address-utils";

Expand Down Expand Up @@ -135,6 +137,34 @@ export class StudentControllerService {
};
}

/**
* Get the student uploaded files.
* @param studentId student id to retrieve the data.
* @param options related to student file uploads
* - `extendedDetails` option to specify the additional properties to be returned (metadata, groupName, updatedAt) as a part of the studentDocuments.
* @returns student file details.
*/
async getStudentUploadedFiles(
studentId: number,
options?: { extendedDetails: boolean },
): Promise<StudentFileDetailsAPIOutDTO[] | StudentUploadFileAPIOutDTO[]> {
const studentDocuments = await this.fileService.getStudentUploadedFiles(
studentId,
);
return studentDocuments.map((studentDocument) => ({
fileName: studentDocument.fileName,
uniqueFileName: studentDocument.uniqueFileName,
fileOrigin: studentDocument.fileOrigin,
metadata: options?.extendedDetails ? studentDocument.metadata : undefined,
groupName: options?.extendedDetails
? studentDocument.groupName
: undefined,
updatedAt: options?.extendedDetails
? studentDocument.updatedAt
: undefined,
}));
}

/**
* Get all the applications that belong to student.
* This API will be used by students.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
Post,
} from "@nestjs/common";
import { ApiNotFoundResponse, ApiTags } from "@nestjs/swagger";
import { StudentService } from "../../services";
import { StudentService, StudentFileService } from "../../services";
import { ClientTypeBaseRoute } from "../../types";
import { AuthorizedParties } from "../../auth/authorized-parties.enum";
import { AllowAuthorizedParty, UserToken } from "../../auth/decorators";
Expand All @@ -18,6 +18,7 @@ import {
StudentSearchAPIInDTO,
SearchStudentAPIOutDTO,
StudentProfileAPIOutDTO,
StudentFileDetailsAPIOutDTO,
} from "./models/student.dto";
import { IInstitutionUserToken } from "../../auth";
import { StudentControllerService } from "./student.controller.service";
Expand All @@ -31,6 +32,7 @@ import { StudentControllerService } from "./student.controller.service";
export class StudentInstitutionsController extends BaseController {
constructor(
private readonly studentService: StudentService,
private readonly fileService: StudentFileService,
private readonly studentControllerService: StudentControllerService,
) {
super();
Expand Down Expand Up @@ -73,4 +75,18 @@ export class StudentInstitutionsController extends BaseController {
): Promise<StudentProfileAPIOutDTO> {
return this.studentControllerService.getStudentProfile(studentId);
}

/**
* Get all the documents uploaded by student.
* @param studentId student id.
* @returns list of student documents.
*/
@Get(":studentId/documents")
async getInstitutionStudentFiles(
@Param("studentId", ParseIntPipe) studentId: number,
): Promise<StudentFileDetailsAPIOutDTO[]> {
return this.studentControllerService.getStudentUploadedFiles(studentId, {
extendedDetails: true,
}) as Promise<StudentFileDetailsAPIOutDTO[]>;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,10 @@ export class StudentStudentsController extends BaseController {
async getStudentFiles(
@UserToken() studentUserToken: StudentUserToken,
): Promise<StudentUploadFileAPIOutDTO[]> {
const studentDocuments = await this.fileService.getStudentUploadedFiles(
return this.studentControllerService.getStudentUploadedFiles(
studentUserToken.studentId,
);
return studentDocuments.map((studentDocument) => ({
fileName: studentDocument.fileName,
uniqueFileName: studentDocument.uniqueFileName,
fileOrigin: studentDocument.fileOrigin,
}));
{ extendedDetails: false },
) as Promise<StudentUploadFileAPIOutDTO[]>;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as faker from "faker";
import { FileOriginType, Student, StudentFile, User } from "@sims/sims-db";
import { DataSource } from "typeorm";
import { createFakeStudent } from "./student";

/**
* Create fake student file upload object.
* @params relations entity relations
* - `student` related student relation.
* - `creator` related user relation.
* @param options related to StudentFile
* - `fileOrigin` option for specifying the fileOrigin
* @returns created studentFile object.
*/
export function createFakeStudentFileUpload(
relations?: {
student?: Student;
creator?: User;
},
options?: {
fileOrigin?: FileOriginType;
},
): StudentFile {
const studentFile = new StudentFile();
studentFile.fileName = faker.system.fileName();
studentFile.uniqueFileName =
studentFile.fileName + faker.random.uuid() + "." + faker.system.fileType();
studentFile.groupName = "Ministry communications";
studentFile.mimeType = faker.system.mimeType();
studentFile.fileContent = Buffer.from(faker.random.words(50), "utf-8");
studentFile.student = relations?.student ?? createFakeStudent();
studentFile.creator = relations?.creator;
studentFile.fileOrigin = options?.fileOrigin ?? FileOriginType.Ministry;
return studentFile;
}

/**
* Save fake student file upload.
* @param dataSource data source to persist studentFileUpload.
* @param relations entity relations.
* - `student` related student relation.
* - `creator` related user relation.
* @param options related to StudentFile
* - `fileOrigin` option for specifying the fileOrigin
* @returns persisted studentFile.
*/
export async function saveFakeStudentFileUpload(
dataSource: DataSource,
relations?: { student?: Student; creator?: User },
options?: { fileOrigin: FileOriginType },
): Promise<StudentFile> {
const studentFile = createFakeStudentFileUpload(relations, options);
const studentFileRepo = dataSource.getRepository(StudentFile);
return studentFileRepo.save(studentFile);
}
1 change: 1 addition & 0 deletions sources/packages/backend/libs/test-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export * from "./factories/application-exception-request";
export * from "./factories/student-scholastic-standing";
export * from "./factories/student-appeal";
export * from "./factories/student-appeal-request";
export * from "./factories/student-file-uploads";
export * from "./models/common.model";
export * from "./mocks/zeebe-client-mock";
export * from "./mocks/ssh-service-mock";
Expand Down
Loading

0 comments on commit d5734da

Please sign in to comment.