From 7c69e908125e6841d880cca1a5afd779d29f86ef Mon Sep 17 00:00:00 2001 From: Liam Lloyd-Tucker Date: Fri, 18 Sep 2026 12:09:50 -0700 Subject: [PATCH] Add thumbnail and access copy status fields to records We want to be able to show users when thumbnail and access copy generation are in progress, or when they have failed. To that end this commit adds thumbnail and access copy status fields to records to surface this state. --- packages/api/docs/src/models/record.yaml | 14 ++ .../controller/get_records_page.test.ts | 117 +++++++++++- ...ate_test_status_field_account_archives.sql | 24 +++ .../create_test_status_field_archives.sql | 11 ++ .../create_test_status_field_files.sql | 85 +++++++++ .../create_test_status_field_folder_links.sql | 85 +++++++++ .../create_test_status_field_folders.sql | 22 +++ .../create_test_status_field_record_files.sql | 64 +++++++ .../create_test_status_field_records.sql | 135 ++++++++++++++ packages/api/src/record/models.ts | 26 +++ .../api/src/record/queries/get_records.sql | 170 ++++++++++++++++++ 11 files changed, 752 insertions(+), 1 deletion(-) create mode 100644 packages/api/src/record/fixtures/create_test_status_field_account_archives.sql create mode 100644 packages/api/src/record/fixtures/create_test_status_field_archives.sql create mode 100644 packages/api/src/record/fixtures/create_test_status_field_files.sql create mode 100644 packages/api/src/record/fixtures/create_test_status_field_folder_links.sql create mode 100644 packages/api/src/record/fixtures/create_test_status_field_folders.sql create mode 100644 packages/api/src/record/fixtures/create_test_status_field_record_files.sql create mode 100644 packages/api/src/record/fixtures/create_test_status_field_records.sql diff --git a/packages/api/docs/src/models/record.yaml b/packages/api/docs/src/models/record.yaml index bbc48c75..d486ff66 100644 --- a/packages/api/docs/src/models/record.yaml +++ b/packages/api/docs/src/models/record.yaml @@ -65,6 +65,13 @@ record: thumbnailUrls: type: object properties: + width256Status: + description: This field will be null for record types where we never generate thumbnails + type: string + enum: + - ok + - processing + - failed 200: type: string 256: @@ -93,6 +100,13 @@ record: type: array items: $ref: "./archive_file.yaml#/archiveFile" + accessCopyStatus: + description: This field will be null for record types where we never generate access copies + type: string + enum: + - ok + - processing + - failed folderLinkId: type: string folderLinkType: diff --git a/packages/api/src/record/controller/get_records_page.test.ts b/packages/api/src/record/controller/get_records_page.test.ts index 24761c73..ae22be15 100644 --- a/packages/api/src/record/controller/get_records_page.test.ts +++ b/packages/api/src/record/controller/get_records_page.test.ts @@ -11,7 +11,12 @@ import { import request from "supertest"; import { app } from "../../app.js"; import { db } from "../../database.js"; -import type { ArchiveRecord, GetRecordsResponse } from "../models.js"; +import { + AccessCopyStatus, + Thumbnail256Status, + type ArchiveRecord, + type GetRecordsResponse, +} from "../models.js"; import { runFixtures } from "../../../test/run_fixtures.js"; import { mockExtractShareTokenFromHeaders, @@ -48,6 +53,21 @@ const setupDatabase = async (): Promise => { ]); }; +const setupStatusFieldDatabase = async (): Promise => { + await runFixtures(db, [ + "record.fixtures.create_test_accounts", + "record.fixtures.create_test_archives", + "record.fixtures.create_test_account_archives", + "record.fixtures.create_test_status_field_archives", + "record.fixtures.create_test_status_field_account_archives", + "record.fixtures.create_test_status_field_folders", + "record.fixtures.create_test_status_field_records", + "record.fixtures.create_test_status_field_files", + "record.fixtures.create_test_status_field_record_files", + "record.fixtures.create_test_status_field_folder_links", + ]); +}; + const clearDatabase = async (): Promise => { await db.query( `TRUNCATE @@ -380,3 +400,98 @@ describe("GET /records", () => { expect(logger.error).toHaveBeenCalledWith(testError); }); }); + +describe("GET /records accessCopyStatus and thumbnailUrls.width256Status", () => { + beforeEach(async () => { + mockExtractUserEmailFromAuthToken("test@permanent.org"); + mockExtractShareTokenFromHeaders(); + await clearDatabase(); + await setupStatusFieldDatabase(); + }); + + afterEach(async () => { + vi.restoreAllMocks(); + vi.clearAllMocks(); + }); + + afterAll(async () => { + await clearDatabase(); + }); + + const agent = request(app); + + test("expect accessCopyStatus to be ok when an archivematica access copy file exists", async () => { + const response = await agent + .get("/api/v2/records?recordIds[]=20001&pageSize=100") + .expect(200); + const { body } = response as { body: GetRecordsResponse }; + expect(body.items).toHaveLength(1); + expect(body.items[0]?.accessCopyStatus).toEqual(AccessCopyStatus.Ok); + expect(body.items[0]?.thumbnailUrls.width256Status).toBeNull(); + }); + + test("expect accessCopyStatus and thumbnailUrls.width256Status to be processing for a recent record with only an original image file", async () => { + const response = await agent + .get("/api/v2/records?recordIds[]=20002&pageSize=100") + .expect(200); + const { body } = response as { body: GetRecordsResponse }; + expect(body.items).toHaveLength(1); + expect(body.items[0]?.accessCopyStatus).toEqual( + AccessCopyStatus.Processing, + ); + expect(body.items[0]?.thumbnailUrls.width256Status).toEqual( + Thumbnail256Status.Processing, + ); + }); + + test("expect thumbnailUrls.width256Status and accessCopyStatus to be failed for an old record with only an original image file and no 256px thumbnail", async () => { + const response = await agent + .get("/api/v2/records?recordIds[]=20003&pageSize=100") + .expect(200); + const { body } = response as { body: GetRecordsResponse }; + expect(body.items).toHaveLength(1); + expect(body.items[0]?.thumbnailUrls.width256Status).toEqual( + Thumbnail256Status.Failed, + ); + expect(body.items[0]?.accessCopyStatus).toBe(AccessCopyStatus.Failed); + }); + + test("expect thumbnailUrls.width256Status to be ok and the URL fields to pass through record thumbnail columns when a 256px thumbnail already exists", async () => { + const response = await agent + .get("/api/v2/records?recordIds[]=20004&pageSize=100") + .expect(200); + const { body } = response as { body: GetRecordsResponse }; + expect(body.items).toHaveLength(1); + expect(body.items[0]?.accessCopyStatus).toBeNull(); + expect(body.items[0]?.thumbnailUrls).toEqual({ + width256Status: Thumbnail256Status.Ok, + "200": "https://localcdn.permanent.org/20004/thumb200.jpg", + "500": "https://localcdn.permanent.org/20004/thumb500.jpg", + "1000": "https://localcdn.permanent.org/20004/thumb1000.jpg", + "2000": "https://localcdn.permanent.org/20004/thumb2000.jpg", + "256": "https://localcdn.permanent.org/20004/thumb256.jpg", + }); + }); + + test("expect accessCopyStatus and thumbnailUrls.width256Status to both be null for a record whose only file is an unsupported type", async () => { + const response = await agent + .get("/api/v2/records?recordIds[]=20005&pageSize=100") + .expect(200); + const { body } = response as { body: GetRecordsResponse }; + expect(body.items).toHaveLength(1); + expect(body.items[0]?.accessCopyStatus).toBeNull(); + expect(body.items[0]?.thumbnailUrls.width256Status).toBeNull(); + }); + + test("expect accessCopyStatus to be processing for video files while thumbnailUrls.width256Status stays null, since 256px thumbnails aren't generated for video", async () => { + const response = await agent + .get("/api/v2/records?recordIds[]=20006&pageSize=100") + .expect(200); + const { body } = response as { body: GetRecordsResponse }; + expect(body.items).toHaveLength(1); + expect(body.items[0]?.accessCopyStatus).toEqual( + AccessCopyStatus.Processing, + ); + expect(body.items[0]?.thumbnailUrls.width256Status).toBeNull(); + }); +}); diff --git a/packages/api/src/record/fixtures/create_test_status_field_account_archives.sql b/packages/api/src/record/fixtures/create_test_status_field_account_archives.sql new file mode 100644 index 00000000..5fc73ecd --- /dev/null +++ b/packages/api/src/record/fixtures/create_test_status_field_account_archives.sql @@ -0,0 +1,24 @@ +INSERT INTO +account_archive ( + account_archiveid, + accountid, + archiveid, + accessrole, + position, + type, + status, + createddt, + updateddt +) +VALUES +( + 100, + 2, + 100, + 'access.role.owner', + 0, + 'type.account.standard', + 'status.generic.ok', + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP +); diff --git a/packages/api/src/record/fixtures/create_test_status_field_archives.sql b/packages/api/src/record/fixtures/create_test_status_field_archives.sql new file mode 100644 index 00000000..1e020cdf --- /dev/null +++ b/packages/api/src/record/fixtures/create_test_status_field_archives.sql @@ -0,0 +1,11 @@ +INSERT INTO +archive (archiveid, archivenbr, payeraccountid, public, type, status) +VALUES +( + 100, + '0100-0100', + 2, + false, + 'type.archive.person', + 'status.generic.ok' +); diff --git a/packages/api/src/record/fixtures/create_test_status_field_files.sql b/packages/api/src/record/fixtures/create_test_status_field_files.sql new file mode 100644 index 00000000..d806377d --- /dev/null +++ b/packages/api/src/record/fixtures/create_test_status_field_files.sql @@ -0,0 +1,85 @@ +INSERT INTO file ( + fileid, + archiveid, + size, + format, + type, + status, + fileurl, + downloadurl, + createddt, + updateddt +) +VALUES +( + 20001, + 100, + 1024, + 'file.format.archivematica.access', + 'type.file.image.jpeg', + 'status.generic.ok', + 'https://localcdn.permanent.org/20001/file', + 'https://localcdn.permanent.org/20001/download', + '2023-06-21T00:00:00.000Z', + '2023-06-21T00:00:00.000Z' +), +( + 20002, + 100, + 1024, + 'file.format.original', + 'type.file.image.jpeg', + 'status.generic.ok', + 'https://localcdn.permanent.org/20002/file', + 'https://localcdn.permanent.org/20002/download', + '2023-06-21T00:00:00.000Z', + '2023-06-21T00:00:00.000Z' +), +( + 20003, + 100, + 1024, + 'file.format.original', + 'type.file.image.jpeg', + 'status.generic.ok', + 'https://localcdn.permanent.org/20003/file', + 'https://localcdn.permanent.org/20003/download', + '2023-06-21T00:00:00.000Z', + '2023-06-21T00:00:00.000Z' +), +( + 20004, + 100, + 1024, + 'file.format.converted', + 'type.file.image.jpg', + 'status.generic.ok', + 'https://localcdn.permanent.org/20004/file', + 'https://localcdn.permanent.org/20004/download', + '2023-06-21T00:00:00.000Z', + '2023-06-21T00:00:00.000Z' +), +( + 20005, + 100, + 1024, + 'file.format.original', + 'type.file.archive.zip', + 'status.generic.ok', + 'https://localcdn.permanent.org/20005/file', + 'https://localcdn.permanent.org/20005/download', + '2023-06-21T00:00:00.000Z', + '2023-06-21T00:00:00.000Z' +), +( + 20006, + 100, + 1024, + 'file.format.original', + 'type.file.video.mp4', + 'status.generic.ok', + 'https://localcdn.permanent.org/20006/file', + 'https://localcdn.permanent.org/20006/download', + '2023-06-21T00:00:00.000Z', + '2023-06-21T00:00:00.000Z' +); diff --git a/packages/api/src/record/fixtures/create_test_status_field_folder_links.sql b/packages/api/src/record/fixtures/create_test_status_field_folder_links.sql new file mode 100644 index 00000000..733fd38f --- /dev/null +++ b/packages/api/src/record/fixtures/create_test_status_field_folder_links.sql @@ -0,0 +1,85 @@ +INSERT INTO folder_link ( + folder_linkid, + recordid, + folderid, + parentfolderid, + parentfolder_linkid, + archiveid, + position, + accessrole, + status, + type +) +VALUES +( + 20001, + 20001, + NULL, + 100, + NULL, + 100, + 1, + 'access.role.owner', + 'status.generic.ok', + 'type.folder_link.private' +), +( + 20002, + 20002, + NULL, + 100, + NULL, + 100, + 1, + 'access.role.owner', + 'status.generic.ok', + 'type.folder_link.private' +), +( + 20003, + 20003, + NULL, + 100, + NULL, + 100, + 1, + 'access.role.owner', + 'status.generic.ok', + 'type.folder_link.private' +), +( + 20004, + 20004, + NULL, + 100, + NULL, + 100, + 1, + 'access.role.owner', + 'status.generic.ok', + 'type.folder_link.private' +), +( + 20005, + 20005, + NULL, + 100, + NULL, + 100, + 1, + 'access.role.owner', + 'status.generic.ok', + 'type.folder_link.private' +), +( + 20006, + 20006, + NULL, + 100, + NULL, + 100, + 1, + 'access.role.owner', + 'status.generic.ok', + 'type.folder_link.private' +); diff --git a/packages/api/src/record/fixtures/create_test_status_field_folders.sql b/packages/api/src/record/fixtures/create_test_status_field_folders.sql new file mode 100644 index 00000000..ec42c474 --- /dev/null +++ b/packages/api/src/record/fixtures/create_test_status_field_folders.sql @@ -0,0 +1,22 @@ +INSERT INTO +folder ( + folderid, + archiveid, + publicdt, + displayname, + downloadname, + status, + createddt, + type +) +VALUES +( + 100, + 100, + NULL, + 'Status Field Test Root', + 'Status Field Test Root', + 'status.generic.ok', + CURRENT_TIMESTAMP, + 'type.folder.root.private' +); diff --git a/packages/api/src/record/fixtures/create_test_status_field_record_files.sql b/packages/api/src/record/fixtures/create_test_status_field_record_files.sql new file mode 100644 index 00000000..6a178cf5 --- /dev/null +++ b/packages/api/src/record/fixtures/create_test_status_field_record_files.sql @@ -0,0 +1,64 @@ +INSERT INTO record_file ( + record_fileid, + recordid, + fileid, + status, + type, + createddt, + updateddt +) +VALUES +( + 20001, + 20001, + 20001, + 'status.generic.ok', + 'type.generic.placeholder', + '2023-06-21T00:00:00.000Z', + '2023-06-21T00:00:00.000Z' +), +( + 20002, + 20002, + 20002, + 'status.generic.ok', + 'type.generic.placeholder', + '2023-06-21T00:00:00.000Z', + '2023-06-21T00:00:00.000Z' +), +( + 20003, + 20003, + 20003, + 'status.generic.ok', + 'type.generic.placeholder', + '2023-06-21T00:00:00.000Z', + '2023-06-21T00:00:00.000Z' +), +( + 20004, + 20004, + 20004, + 'status.generic.ok', + 'type.generic.placeholder', + '2023-06-21T00:00:00.000Z', + '2023-06-21T00:00:00.000Z' +), +( + 20005, + 20005, + 20005, + 'status.generic.ok', + 'type.generic.placeholder', + '2023-06-21T00:00:00.000Z', + '2023-06-21T00:00:00.000Z' +), +( + 20006, + 20006, + 20006, + 'status.generic.ok', + 'type.generic.placeholder', + '2023-06-21T00:00:00.000Z', + '2023-06-21T00:00:00.000Z' +); diff --git a/packages/api/src/record/fixtures/create_test_status_field_records.sql b/packages/api/src/record/fixtures/create_test_status_field_records.sql new file mode 100644 index 00000000..e118ec98 --- /dev/null +++ b/packages/api/src/record/fixtures/create_test_status_field_records.sql @@ -0,0 +1,135 @@ +INSERT INTO +record ( + recordid, + archiveid, + archivenbr, + publicdt, + displayname, + uploadaccountid, + uploadpayeraccountid, + uploadfilename, + downloadname, + status, + type, + createddt, + thumbnail256, + thumburl200, + thumburl500, + thumburl1000, + thumburl2000 +) +VALUES +( + 20001, + 100, + NULL, + NULL, + 'Access Copy Ready', + 2, + 2, + 'access_copy_ready.jpg', + 'access_copy_ready.jpg', + 'status.generic.ok', + 'type.record.image', + '2023-06-21T00:00:00.000Z', + NULL, + NULL, + NULL, + NULL, + NULL +), +( + 20002, + 100, + NULL, + NULL, + 'Access Copy And Thumbnail Processing', + 2, + 2, + 'processing.jpg', + 'processing.jpg', + 'status.generic.ok', + 'type.record.image', + CURRENT_TIMESTAMP, + NULL, + NULL, + NULL, + NULL, + NULL +), +( + 20003, + 100, + NULL, + NULL, + 'Thumbnail Failed', + 2, + 2, + 'failed.jpg', + 'failed.jpg', + 'status.generic.ok', + 'type.record.image', + '2023-01-01T00:00:00.000Z', + NULL, + NULL, + NULL, + NULL, + NULL +), +( + 20004, + 100, + NULL, + NULL, + 'Thumbnail Already Generated', + 2, + 2, + 'already_generated.jpg', + 'already_generated.jpg', + 'status.generic.ok', + 'type.record.image', + '2023-06-21T00:00:00.000Z', + 'https://localcdn.permanent.org/20004/thumb256.jpg', + 'https://localcdn.permanent.org/20004/thumb200.jpg', + 'https://localcdn.permanent.org/20004/thumb500.jpg', + 'https://localcdn.permanent.org/20004/thumb1000.jpg', + 'https://localcdn.permanent.org/20004/thumb2000.jpg' +), +( + 20005, + 100, + NULL, + NULL, + 'Unsupported File Type', + 2, + 2, + 'archive.zip', + 'archive.zip', + 'status.generic.ok', + 'type.record.archive', + CURRENT_TIMESTAMP, + NULL, + NULL, + NULL, + NULL, + NULL +), +( + 20006, + 100, + NULL, + NULL, + 'Video Access Copy Processing', + 2, + 2, + 'video.mp4', + 'video.mp4', + 'status.generic.ok', + 'type.record.video', + CURRENT_TIMESTAMP, + NULL, + NULL, + NULL, + NULL, + NULL +); diff --git a/packages/api/src/record/models.ts b/packages/api/src/record/models.ts index 42576fdc..a3bbe2c6 100644 --- a/packages/api/src/record/models.ts +++ b/packages/api/src/record/models.ts @@ -54,6 +54,8 @@ export interface ArchiveRecord { }; }; accessRole: ArchiveMembershipRole; + accessCopyStatus: AccessCopyStatus | null; + thumbnailUrls: RecordThumbnailUrls; } export interface ArchiveRecordRow { @@ -107,6 +109,29 @@ export interface ArchiveRecordRow { archiveAccessRole: AccessRole | null; shareAccessRoles: ShareAccessRolePair[] | null; shareTokenGrantsAccess: boolean; + accessCopyStatus: AccessCopyStatus | null; + thumbnailUrls: RecordThumbnailUrls; +} + +export enum AccessCopyStatus { + Ok = "ok", + Processing = "processing", + Failed = "failed", +} + +export enum Thumbnail256Status { + Ok = "ok", + Processing = "processing", + Failed = "failed", +} + +export interface RecordThumbnailUrls { + width256Status: Thumbnail256Status | null; + "200": string | null; + "500": string | null; + "1000": string | null; + "2000": string | null; + "256": string | null; } export interface ArchiveFile { @@ -177,6 +202,7 @@ export enum RecordType { export enum FileFormat { Original = "file.format.original", Converted = "file.format.converted", + ArchivematicaAccess = "file.format.archivematica.access", } enum FolderLinkType { diff --git a/packages/api/src/record/queries/get_records.sql b/packages/api/src/record/queries/get_records.sql index 155307a5..b72a425f 100644 --- a/packages/api/src/record/queries/get_records.sql +++ b/packages/api/src/record/queries/get_records.sql @@ -215,6 +215,108 @@ all_records AS ( archive.archivenbr AS "archiveArchiveNumber", aggregated_shares.shares_as_json AS shares, COALESCE(record.publicdt <= NOW(), FALSE) AS "isPublic", + CASE + WHEN + EXISTS ( + SELECT 1 FROM UNNEST(aggregated_files.files) AS files + WHERE + files ->> 'format' = 'file.format.archivematica.access' + AND files ->> 'type' != 'type.file.unknown.null' + ) + THEN 'ok' + WHEN + EXISTS ( + SELECT 1 FROM UNNEST(aggregated_files.files) AS files + WHERE + files ->> 'format' = 'file.format.original' + AND files ->> 'type' IN ( + 'type.file.image.bmp', + 'type.file.image.gif', + 'type.file.image.jpeg', + 'type.file.image.png', + 'type.file.image.tiff', + 'type.file.image.tif', + 'type.file.image.jpg', + 'type.file.image.heic', + 'type.file.video.avi', + 'type.file.video.mov', + 'type.file.video.ogv', + 'type.file.video.webm', + 'type.file.video.mp4', + 'type.file.document.doc', + 'type.file.document.docx', + 'type.file.document.rtf', + 'type.file.document.eml', + 'type.file.document.odt', + 'type.file.document.txt', + 'type.file.pdf.pdf', + 'type.file.pdf.pdfa', + 'type.file.presentation.ppt', + 'type.file.presentation.pptx', + 'type.file.presentation.key', + 'type.file.presentation.odp', + 'type.file.spreadsheet.xls', + 'type.file.spreadsheet.xlsx', + 'type.file.spreadsheet.ods', + 'type.file.audio.aac', + 'type.file.audio.aiff', + 'type.file.audio.flac', + 'type.file.audio.m4a', + 'type.file.audio.ogg', + 'type.file.audio.wav', + 'type.file.audio.wma', + 'type.file.audio.mp3' + ) + ) + AND record.createddt > CURRENT_TIMESTAMP - '1 day'::INTERVAL + THEN 'processing' + WHEN + EXISTS ( + SELECT 1 FROM UNNEST(aggregated_files.files) AS files + WHERE + files ->> 'format' = 'file.format.original' + AND files ->> 'type' IN ( + 'type.file.image.bmp', + 'type.file.image.gif', + 'type.file.image.jpeg', + 'type.file.image.png', + 'type.file.image.tiff', + 'type.file.image.tif', + 'type.file.image.jpg', + 'type.file.image.heic', + 'type.file.video.avi', + 'type.file.video.mov', + 'type.file.video.ogv', + 'type.file.video.webm', + 'type.file.video.mp4', + 'type.file.document.doc', + 'type.file.document.docx', + 'type.file.document.rtf', + 'type.file.document.eml', + 'type.file.document.odt', + 'type.file.document.txt', + 'type.file.pdf.pdf', + 'type.file.pdf.pdfa', + 'type.file.presentation.ppt', + 'type.file.presentation.pptx', + 'type.file.presentation.key', + 'type.file.presentation.odp', + 'type.file.spreadsheet.xls', + 'type.file.spreadsheet.xlsx', + 'type.file.spreadsheet.ods', + 'type.file.audio.aac', + 'type.file.audio.aiff', + 'type.file.audio.flac', + 'type.file.audio.m4a', + 'type.file.audio.ogg', + 'type.file.audio.wav', + 'type.file.audio.wma', + 'type.file.audio.mp3' + ) + ) + AND record.createddt <= CURRENT_TIMESTAMP - '1 day'::INTERVAL + THEN 'failed' + END AS "accessCopyStatus", CASE WHEN EXISTS ( @@ -234,6 +336,74 @@ all_records AS ( THEN aggregated_pending_shares.pending_shares_as_json END AS "pendingShares", JSON_BUILD_OBJECT( + 'width256Status', + CASE + WHEN record.thumbnail256 IS NOT NULL THEN 'ok' + WHEN + EXISTS ( + SELECT 1 FROM UNNEST(aggregated_files.files) AS files + WHERE + files ->> 'format' = 'file.format.original' + AND files ->> 'type' IN ( + 'type.file.image.bmp', + 'type.file.image.gif', + 'type.file.image.jpeg', + 'type.file.image.png', + 'type.file.image.tiff', + 'type.file.image.tif', + 'type.file.image.jpg', + 'type.file.image.heic', + 'type.file.document.doc', + 'type.file.document.docx', + 'type.file.document.rtf', + 'type.file.document.eml', + 'type.file.document.odt', + 'type.file.pdf.pdf', + 'type.file.pdf.pdfa', + 'type.file.presentation.ppt', + 'type.file.presentation.pptx', + 'type.file.presentation.key', + 'type.file.presentation.odp', + 'type.file.spreadsheet.xls', + 'type.file.spreadsheet.xlsx', + 'type.file.spreadsheet.ods' + ) + ) + AND record.createddt > CURRENT_TIMESTAMP - '1 day'::INTERVAL + THEN 'processing' + WHEN + EXISTS ( + SELECT 1 FROM UNNEST(aggregated_files.files) AS files + WHERE + files ->> 'format' = 'file.format.original' + AND files ->> 'type' IN ( + 'type.file.image.bmp', + 'type.file.image.gif', + 'type.file.image.jpeg', + 'type.file.image.png', + 'type.file.image.tiff', + 'type.file.image.tif', + 'type.file.image.jpg', + 'type.file.image.heic', + 'type.file.document.doc', + 'type.file.document.docx', + 'type.file.document.rtf', + 'type.file.document.eml', + 'type.file.document.odt', + 'type.file.pdf.pdf', + 'type.file.pdf.pdfa', + 'type.file.presentation.ppt', + 'type.file.presentation.pptx', + 'type.file.presentation.key', + 'type.file.presentation.odp', + 'type.file.spreadsheet.xls', + 'type.file.spreadsheet.xlsx', + 'type.file.spreadsheet.ods' + ) + ) + AND record.createddt <= CURRENT_TIMESTAMP - '1 day'::INTERVAL + THEN 'failed' + END, '200', record.thumburl200, '500',