Skip to content

Commit

Permalink
PER-9607 [back-end] Allow clearing a location
Browse files Browse the repository at this point in the history
  • Loading branch information
iuliandanea committed Nov 26, 2024
1 parent f0cc885 commit bd7fc1e
Show file tree
Hide file tree
Showing 11 changed files with 489 additions and 6 deletions.
2 changes: 2 additions & 0 deletions packages/api/docs/present/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ paths:
$ref: "./paths/feature_flags.yaml#/feature-flags"
/feature-flags/{id}:
$ref: "./paths/feature_flags.yaml#/feature-flags~1{id}"
/record/{id}:
$ref: "./paths/records.yaml#/record~1{id}"
17 changes: 17 additions & 0 deletions packages/api/docs/present/models/archive_file.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
archiveFile:
description: >
Archive File Object
type: object
properties:
fileId:
type: string
size:
type: number
format:
type: string
type:
type: string
fileUrl:
type: string
downloadUrl:
type: string
125 changes: 125 additions & 0 deletions packages/api/docs/present/models/record.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
tag:
description: >
Record Tag Object
type: object
properties:
tagId:
type: string
name:
type: string
type:
type: string
share:
description: >
Record Tag Object
type: object
properties:
shareId:
type: string
archiveId:
type: string
accessRole:
type: string
status:
type: string
archive:
$ref: "../models/record.yaml#/shareArchive"
shareArchive:
description: >
Record Share Archive Object
type: object
properties:
archiveId:
type: string
thumbUrl200:
type: string
name:
type: string
record:
description: >
Generic Record object
type: object
required:
- recordId
- displayName
- archiveId
- status
- uploadFileName
- uploadAccountId
- createdAt
- updatedAt
properties:
recordId:
type: string
displayName:
type: string
archiveId:
type: string
archiveNumber:
type: string
description:
type: string
publicAt:
type: string
downloadName:
type: string
uploadFileName:
type: string
uploadAccountId:
type: string
uploadPayerAccountId:
type: string
size:
type: number
displayDate:
type: string
format: date-time
fileCreatedAt:
type: string
format: date-time
imageRatio:
type: number
thumbUrl200:
type: string
thumbUrl500:
type: string
thumbUrl1000:
type: string
thumbUrl2000:
type: string
status:
type: string
type:
type: string
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
altText:
type: string
files:
type: array
items:
$ref: "../models/archive_file.yaml#/archiveFile"
folderLinkId:
type: string
folderLinkType:
type: string
parentFolderId:
type: string
parentFolderLinkId:
type: string
parentFolderArchiveNumber:
type: string
tags:
type: array
items:
$ref: "../models/record.yaml#/tag"
archiveArchiveNumber:
type: string
shares:
type: array
items:
$ref: "../models/record.yaml#/share"
36 changes: 36 additions & 0 deletions packages/api/docs/present/paths/records.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
record/{id}:
patch:
parameters:
- name: id
in: path
required: true
schema:
type: integer
summary: Update a record
security:
- bearerHttpAuthentication: []
operationId: patch-record
requestBody:
content:
application/json:
schema:
type: object
properties:
locationId:
type: integer
description:
type: string
responses:
"200":
description: The updated record
content:
application/json:
schema:
type: object
properties:
data:
$ref: "../models/record.yaml#/record"
"400":
$ref: "../../shared/errors.yaml#/400"
"500":
$ref: "../../shared/errors.yaml#/500"
116 changes: 114 additions & 2 deletions packages/api/src/record/controller.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import type { Request, Response, NextFunction } from "express";
import type { Request, NextFunction } from "express";
import { logger } from "@stela/logger";
import request from "supertest";
import { app } from "../app";
import { db } from "../database";
import { extractUserEmailFromAuthToken } from "../middleware";
import {
extractUserEmailFromAuthToken,
verifyUserAuthentication,
} from "../middleware";
import type { ArchiveFile, ArchiveRecord, Share, Tag } from "./models";

jest.mock("../database");
Expand Down Expand Up @@ -61,6 +64,8 @@ describe("record", () => {

afterEach(async () => {
await clearDatabase();
jest.restoreAllMocks();
jest.clearAllMocks();
});

const agent = request(app);
Expand Down Expand Up @@ -319,3 +324,110 @@ describe("record", () => {
expect(logger.error).toHaveBeenCalledWith(testError);
});
});

describe("patch record", () => {
const agent = request(app);

beforeEach(async () => {
(verifyUserAuthentication as jest.Mock).mockImplementation(
async (
req: Request<
unknown,
unknown,
{ userSubjectFromAuthToken?: string; emailFromAuthToken?: string }
>,
__,
next: NextFunction
) => {
req.body.emailFromAuthToken = "test+1@permanent.org";
req.body.userSubjectFromAuthToken =
"b5461dc2-1eb0-450e-b710-fef7b2cafe1e";

next();
}
);
await clearDatabase();
await setupDatabase();
});

afterEach(async () => {
await clearDatabase();
jest.restoreAllMocks();
jest.clearAllMocks();
});

test("expect an empty query to cause a 400 error", async () => {
await agent.patch("/api/v2/record/1").send({}).expect(400);
});

test("expect non existent record to cause a 404 error", async () => {
await agent
.patch("/api/v2/record/111111111")
.send({ description: "aa" })
.expect(404);
});

test("expect request to have an email from auth token if an auth token exists", async () => {
(extractUserEmailFromAuthToken as jest.Mock).mockImplementation(
(req: Request, __, next: NextFunction) => {
(req.body as { emailFromAuthToken: string }).emailFromAuthToken =
"not an email";
next();
}
);

await agent.patch("/api/v2/record/1").expect(400);
});

test("expect location id is updated", async () => {
await agent.patch("/api/v2/record/1").send({ locationId: 123 }).expect(200);

const result = await db.query(
"SELECT locnid FROM record WHERE recordId = :recordId",
{
recordId: 1,
}
);

expect(result.rows[0]).toEqual({ locnid: "123" });
});

test("expect location id is updated when set to null", async () => {
await agent
.patch("/api/v2/record/1")
.send({ locationId: null })
.expect(200);

const result = await db.query(
"SELECT locnid FROM record WHERE recordId = :recordId",
{
recordId: 1,
}
);

expect(result.rows[0]).toStrictEqual({ locnid: null });
});

test("expect 400 error if location id is wrong type", async () => {
await agent
.patch("/api/v2/record/1")
.send({
locationId: false,
})
.expect(400);
});

test("expect to log error and return 500 if database update fails", async () => {
const testError = new Error("test error");
jest.spyOn(db, "sql").mockImplementation(async () => {
throw testError;
});

await agent
.patch("/api/v2/record/1")
.send({ locationId: 123 })
.expect(500);

expect(logger.error).toHaveBeenCalledWith(testError);
});
});
37 changes: 34 additions & 3 deletions packages/api/src/record/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@ import {
type Response,
type NextFunction,
} from "express";
import { extractUserEmailFromAuthToken } from "../middleware";
import { getRecordById } from "./service";
import { validateGetRecordQuery } from "./validators";
import {
extractUserEmailFromAuthToken,
verifyUserAuthentication,
} from "../middleware";
import { getRecordById, patchRecord } from "./service";
import {
validateGetRecordQuery,
validatePatchRecordRequest,
validateRecordRequest,
} from "./validators";
import { validateOptionalEmailFromAuthentication } from "../validators/shared";
import { isValidationError } from "../validators/validator_util";

Expand All @@ -33,3 +40,27 @@ recordController.get(
}
}
);

recordController.patch(
"/:recordId",
verifyUserAuthentication,
async (req: Request, res: Response, next: NextFunction) => {
try {
validateRecordRequest(req.params);
validatePatchRecordRequest(req.body);
const recordId = await patchRecord(req.params.recordId, req.body);
const record = await getRecordById({
recordIds: [recordId],
accountEmail: req.body.emailFromAuthToken,
});

res.status(200).send({ data: record });
} catch (err) {
if (isValidationError(err)) {
res.status(400).json({ error: err.message });
return;
}
next(err);
}
}
);
12 changes: 12 additions & 0 deletions packages/api/src/record/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { PatchRecordRequest, RecordColumnsForUpdate } from "./models";

export function requestFieldsToDatabaseFields(
request: PatchRecordRequest,
recordId: string
): RecordColumnsForUpdate {
return {
locnid: request.locationId,
description: request.description,
recordId,
};
}
Loading

0 comments on commit bd7fc1e

Please sign in to comment.