diff --git a/.gitignore b/.gitignore index e6268ba..d63da0c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ pids node_modules # compiled app -dist +#dist # prisma src/generated/prisma/ diff --git a/README.md b/README.md index 329d71c..3bf5ba3 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,30 @@ The arocapi provides the following endpoints: - `GET /file/:id` - Download or access file content - `POST /search` - Search entities using OpenSearch +## Customising OpenSearch query + +The OpenSearch query builder used in the `/search` endpoint can be customised to match any +structure of the actual OpenSearch data. The default OpensearchQueryBuilder class is used to +build all the queries for the search and an optional config can be passed on +to customise the class behaviour. + +### Query Builder (Optional) +Use the `queryBuilderClass` optional parameter to specify a custom builder class. +If not apecified, the default `OpensearchQueryBuilder` is used. +The custom class should extend the `OpensearchQueryBuilder` class and +may override any or all of the methods as required: + +- `buildQuery`: For generating the base search query. +- `buildAggregations`: For generating the facets or aggregations related query. +- `buildSort`: For generating the ordering/sorting part of the query. + +### Query Builder Options (Optional) +The `queryBuilderOptions` optional parameter is passed as a constructor argument +when instantiating the queryBuilderClass. The default `OpensearchQueryBuilder` can be +configured with: + +- `aggregations`: This field controls the aggregation and will be passed verbatim as the `aggs` field in the generated search query. + ## Customising Entity Responses The API provides a flexible transformer system for customising entity responses diff --git a/dist/app.d.ts b/dist/app.d.ts new file mode 100644 index 0000000..6f65aa4 --- /dev/null +++ b/dist/app.d.ts @@ -0,0 +1,33 @@ +import type { Client } from '@opensearch-project/opensearch'; +import type { FastifyPluginAsync } from 'fastify'; +import type { PrismaClient } from './generated/prisma/client.js'; +import type { FileHandler, RoCrateHandler } from './types/fileHandlers.js'; +import type { AccessTransformer, EntityTransformer, FileAccessTransformer, FileTransformer } from './types/transformers.js'; +import { OpensearchQueryBuilder, QueryBuilderOptions } from './utils/queryBuilder.js'; +export type { AuthorisedEntity, AuthorisedFile, StandardEntity, StandardFile } from './transformers/default.js'; +export { AllPublicAccessTransformer, AllPublicFileAccessTransformer } from './transformers/default.js'; +export type { FileHandler, FileHandlerContext, FileMetadata, FilePathResult, FileRedirectResult, FileResult, FileStreamResult, GetFileHandler, GetRoCrateHandler, HeadFileHandler, HeadRoCrateHandler, RoCrateHandler, } from './types/fileHandlers.js'; +export type { AccessTransformer, EntityTransformer, FileAccessTransformer, FileTransformer, TransformerContext, } from './types/transformers.js'; +export { OpensearchQueryBuilder }; +export type { QueryBuilderOptions }; +declare module 'fastify' { + interface FastifyInstance { + prisma: PrismaClient; + opensearch: Client; + } +} +export type Options = { + prisma: PrismaClient; + opensearch: Client; + queryBuilderClass?: typeof OpensearchQueryBuilder; + queryBuilderOptions?: QueryBuilderOptions; + disableCors?: boolean; + accessTransformer: AccessTransformer; + entityTransformers?: EntityTransformer[]; + fileAccessTransformer: FileAccessTransformer; + fileTransformers?: FileTransformer[]; + fileHandler: FileHandler; + roCrateHandler: RoCrateHandler; +}; +declare const _default: FastifyPluginAsync; +export default _default; diff --git a/dist/app.js b/dist/app.js new file mode 100644 index 0000000..de1cee2 --- /dev/null +++ b/dist/app.js @@ -0,0 +1,83 @@ +import cors from '@fastify/cors'; +import sensible from '@fastify/sensible'; +import fp from 'fastify-plugin'; +import { hasZodFastifySchemaValidationErrors, serializerCompiler, validatorCompiler } from 'fastify-type-provider-zod'; +import crate from './routes/crate.js'; +import entities from './routes/entities.js'; +import entity from './routes/entity.js'; +import file from './routes/file.js'; +import files from './routes/files.js'; +import search from './routes/search.js'; +import { createValidationError } from './utils/errors.js'; +import { OpensearchQueryBuilder } from './utils/queryBuilder.js'; +export { AllPublicAccessTransformer, AllPublicFileAccessTransformer } from './transformers/default.js'; +export { OpensearchQueryBuilder }; +const setupValidation = (fastify) => { + fastify.setValidatorCompiler(validatorCompiler); + fastify.setSerializerCompiler(serializerCompiler); + fastify.setErrorHandler((error, _req, reply) => { + if (hasZodFastifySchemaValidationErrors(error)) { + return reply.code(400).send(createValidationError('The request parameters are invalid', error.validation)); + } + const err = error; + return reply.code(500).send({ + error: 'Internal Server Error', + message: err.message, + }); + }); +}; +const setupDatabase = async (fastify, prisma) => { + await prisma.$connect(); + fastify.decorate('prisma', prisma); + fastify.addHook('onClose', async () => { + await prisma.$disconnect(); + }); +}; +const setupSearch = async (fastify, opensearch) => { + try { + await opensearch.ping(); + fastify.log.info(`Connected to OpenSearch`); + } + catch (error) { + fastify.log.error(`Failed to connect to OpenSearch: ${error}`); + throw error; + } + fastify.decorate('opensearch', opensearch); + fastify.addHook('onClose', () => opensearch.close()); +}; +const app = async (fastify, options) => { + const { prisma, opensearch, queryBuilderClass, queryBuilderOptions, disableCors = false, accessTransformer, entityTransformers, fileAccessTransformer, fileTransformers, fileHandler, roCrateHandler, } = options; + if (!prisma) { + throw new Error('Prisma client is required'); + } + if (!opensearch) { + throw new Error('OpenSearch client is required'); + } + if (!accessTransformer) { + throw new Error('accessTransformer is required'); + } + if (!fileAccessTransformer) { + throw new Error('fileAccessTransformer is required'); + } + if (!fileHandler) { + throw new Error('fileHandler is required'); + } + if (!roCrateHandler) { + throw new Error('roCrateHandler is required'); + } + fastify.register(sensible); + if (!disableCors) { + fastify.register(cors); + } + setupValidation(fastify); + await setupDatabase(fastify, prisma); + await setupSearch(fastify, opensearch); + fastify.register(entities, { accessTransformer, entityTransformers }); + fastify.register(entity, { accessTransformer, entityTransformers }); + fastify.register(files, { fileAccessTransformer, fileTransformers }); + fastify.register(file, { fileHandler }); + fastify.register(crate, { roCrateHandler }); + fastify.register(search, { accessTransformer, entityTransformers, queryBuilderClass, queryBuilderOptions }); +}; +export default fp(app); +//# sourceMappingURL=app.js.map \ No newline at end of file diff --git a/dist/app.js.map b/dist/app.js.map new file mode 100644 index 0000000..5b083e6 --- /dev/null +++ b/dist/app.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/app.test.d.ts b/dist/app.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/app.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/app.test.js b/dist/app.test.js new file mode 100644 index 0000000..0d1d26b --- /dev/null +++ b/dist/app.test.js @@ -0,0 +1,124 @@ +import Fastify from 'fastify'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { mockReset } from 'vitest-mock-extended'; +import app, { AllPublicAccessTransformer, AllPublicFileAccessTransformer } from './app.js'; +import { opensearch, prisma } from './test/helpers/fastify.js'; +describe('Entity Route', () => { + beforeEach(() => { + mockReset(prisma); + mockReset(opensearch); + }); + describe('App Registration', () => { + it('should handle missing prisma', async () => { + const fastify = Fastify({ logger: false }); + fastify.register(app, { + opensearch, + disableCors: false, + }); + await expect(() => fastify.ready()).rejects.toThrowError('Prisma client is required'); + }); + it('should handle missing opensearch', async () => { + const fastify = Fastify({ logger: false }); + fastify.register(app, { + prisma, + disableCors: false, + }); + await expect(() => fastify.ready()).rejects.toThrowError('OpenSearch client is required'); + }); + it('should handle missing accessTransformer', async () => { + const fastify = Fastify({ logger: false }); + fastify.register(app, { + prisma, + opensearch, + disableCors: false, + }); + await expect(() => fastify.ready()).rejects.toThrowError('accessTransformer is required'); + }); + it('should handle missing fileAccessTransformer', async () => { + const fastify = Fastify({ logger: false }); + fastify.register(app, { + prisma, + opensearch, + disableCors: false, + accessTransformer: AllPublicAccessTransformer, + }); + await expect(() => fastify.ready()).rejects.toThrowError('fileAccessTransformer is required'); + }); + it('should handle missing fileHandler', async () => { + const fastify = Fastify({ logger: false }); + fastify.register(app, { + prisma, + opensearch, + disableCors: false, + accessTransformer: AllPublicAccessTransformer, + fileAccessTransformer: AllPublicFileAccessTransformer, + }); + await expect(() => fastify.ready()).rejects.toThrowError('fileHandler is required'); + }); + it('should handle missing roCrateHandler', async () => { + const fastify = Fastify({ logger: false }); + fastify.register(app, { + prisma, + opensearch, + disableCors: false, + accessTransformer: AllPublicAccessTransformer, + fileAccessTransformer: AllPublicFileAccessTransformer, + fileHandler: { + get: vi.fn(), + head: vi.fn(), + }, + }); + await expect(() => fastify.ready()).rejects.toThrowError('roCrateHandler is required'); + }); + it('should handle broken opensearch', async () => { + opensearch.ping.mockRejectedValue(new Error('Connection failed')); + const fastify = Fastify({ logger: false }); + fastify.register(app, { + prisma, + opensearch, + disableCors: false, + accessTransformer: AllPublicAccessTransformer, + fileAccessTransformer: AllPublicFileAccessTransformer, + fileHandler: { + get: vi.fn(), + head: vi.fn(), + }, + roCrateHandler: { + get: vi.fn(), + head: vi.fn(), + }, + }); + await expect(() => fastify.ready()).rejects.toThrowError('Connection failed'); + }); + it('should handle random errors', async () => { + const fastify = Fastify({ logger: false }); + await fastify.register(app, { + prisma, + opensearch, + disableCors: false, + accessTransformer: AllPublicAccessTransformer, + fileAccessTransformer: AllPublicFileAccessTransformer, + fileHandler: { + get: vi.fn(), + head: vi.fn(), + }, + roCrateHandler: { + get: vi.fn(), + head: vi.fn(), + }, + }); + fastify.get('/error', async () => { + throw new Error('Random'); + }); + await fastify.ready(); + const response = await fastify.inject({ + method: 'GET', + url: '/error', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(500); + expect(body).toMatchSnapshot(); + }); + }); +}); +//# sourceMappingURL=app.test.js.map \ No newline at end of file diff --git a/dist/app.test.js.map b/dist/app.test.js.map new file mode 100644 index 0000000..70d41bb --- /dev/null +++ b/dist/app.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.test.js","sourceRoot":"","sources":["../src/app.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/express.d.ts b/dist/express.d.ts new file mode 100644 index 0000000..daa834b --- /dev/null +++ b/dist/express.d.ts @@ -0,0 +1,4 @@ +import type { Express } from 'express'; +import { type Options } from './app.js'; +declare const _default: (options: Options) => Promise; +export default _default; diff --git a/dist/express.js b/dist/express.js new file mode 100644 index 0000000..72e1d0a --- /dev/null +++ b/dist/express.js @@ -0,0 +1,14 @@ +import express from 'express'; +import Fastify from 'fastify'; +import arocapi from './app.js'; +const fastify = Fastify(); +export default async (options) => { + fastify.register(arocapi, options); + await fastify.ready(); + const app = express(); + app.use(async (req, res) => { + fastify.routing(req, res); + }); + return app; +}; +//# sourceMappingURL=express.js.map \ No newline at end of file diff --git a/dist/express.js.map b/dist/express.js.map new file mode 100644 index 0000000..27eff06 --- /dev/null +++ b/dist/express.js.map @@ -0,0 +1 @@ +{"version":3,"file":"express.js","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,OAAyB,MAAM,UAAU,CAAC;AAEjD,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC;AAE1B,eAAe,KAAK,EAAE,OAAgB,EAAE,EAAE;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAEnC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IAEtB,MAAM,GAAG,GAAY,OAAO,EAAE,CAAC;IAE/B,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACzB,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/generated/prisma/browser.d.ts b/dist/generated/prisma/browser.d.ts new file mode 100644 index 0000000..e04261c --- /dev/null +++ b/dist/generated/prisma/browser.d.ts @@ -0,0 +1,6 @@ +import * as Prisma from './internal/prismaNamespaceBrowser.js'; +export { Prisma }; +export * as $Enums from './enums.js'; +export * from './enums.js'; +export type Entity = Prisma.EntityModel; +export type File = Prisma.FileModel; diff --git a/dist/generated/prisma/browser.js b/dist/generated/prisma/browser.js new file mode 100644 index 0000000..790d274 --- /dev/null +++ b/dist/generated/prisma/browser.js @@ -0,0 +1,5 @@ +import * as Prisma from './internal/prismaNamespaceBrowser.js'; +export { Prisma }; +export * as $Enums from './enums.js'; +export * from './enums.js'; +//# sourceMappingURL=browser.js.map \ No newline at end of file diff --git a/dist/generated/prisma/browser.js.map b/dist/generated/prisma/browser.js.map new file mode 100644 index 0000000..d9f72f6 --- /dev/null +++ b/dist/generated/prisma/browser.js.map @@ -0,0 +1 @@ +{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../../src/generated/prisma/browser.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,MAAM,MAAM,sCAAsC,CAAA;AAC9D,OAAO,EAAE,MAAM,EAAE,CAAA;AACjB,OAAO,KAAK,MAAM,MAAM,YAAY,CAAA;AACpC,cAAc,YAAY,CAAC"} \ No newline at end of file diff --git a/dist/generated/prisma/client.d.ts b/dist/generated/prisma/client.d.ts new file mode 100644 index 0000000..3ce2a38 --- /dev/null +++ b/dist/generated/prisma/client.d.ts @@ -0,0 +1,10 @@ +import * as runtime from "@prisma/client/runtime/library"; +import * as $Class from "./internal/class.js"; +import * as Prisma from "./internal/prismaNamespace.js"; +export * as $Enums from './enums.js'; +export * from "./enums.js"; +export declare const PrismaClient: $Class.PrismaClientConstructor; +export type PrismaClient = $Class.PrismaClient; +export { Prisma }; +export type Entity = Prisma.EntityModel; +export type File = Prisma.FileModel; diff --git a/dist/generated/prisma/client.js b/dist/generated/prisma/client.js new file mode 100644 index 0000000..93a7bca --- /dev/null +++ b/dist/generated/prisma/client.js @@ -0,0 +1,13 @@ +import * as process from 'node:process'; +import * as path from 'node:path'; +import { fileURLToPath } from 'node:url'; +globalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url)); +import * as $Class from "./internal/class.js"; +import * as Prisma from "./internal/prismaNamespace.js"; +export * as $Enums from './enums.js'; +export * from "./enums.js"; +export const PrismaClient = $Class.getPrismaClientClass(__dirname); +export { Prisma }; +path.join(__dirname, "libquery_engine-linux-musl-arm64-openssl-3.0.x.so.node"); +path.join(process.cwd(), "src/generated/prisma/libquery_engine-linux-musl-arm64-openssl-3.0.x.so.node"); +//# sourceMappingURL=client.js.map \ No newline at end of file diff --git a/dist/generated/prisma/client.js.map b/dist/generated/prisma/client.js.map new file mode 100644 index 0000000..c018b35 --- /dev/null +++ b/dist/generated/prisma/client.js.map @@ -0,0 +1 @@ +{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/generated/prisma/client.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,UAAU,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAItE,OAAO,KAAK,MAAM,MAAM,qBAAqB,CAAA;AAC7C,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAA;AAEvD,OAAO,KAAK,MAAM,MAAM,YAAY,CAAA;AACpC,cAAc,YAAY,CAAA;AAc1B,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;AAElE,OAAO,EAAE,MAAM,EAAE,CAAA;AAIjB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,wDAAwD,CAAC,CAAA;AAC9E,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,6EAA6E,CAAC,CAAA"} \ No newline at end of file diff --git a/dist/generated/prisma/commonInputTypes.d.ts b/dist/generated/prisma/commonInputTypes.d.ts new file mode 100644 index 0000000..6943e66 --- /dev/null +++ b/dist/generated/prisma/commonInputTypes.d.ts @@ -0,0 +1,350 @@ +import type * as runtime from "@prisma/client/runtime/library"; +import type * as Prisma from "./internal/prismaNamespace.js"; +export type IntFilter<$PrismaModel = never> = { + equals?: number | Prisma.IntFieldRefInput<$PrismaModel>; + in?: number[]; + notIn?: number[]; + lt?: number | Prisma.IntFieldRefInput<$PrismaModel>; + lte?: number | Prisma.IntFieldRefInput<$PrismaModel>; + gt?: number | Prisma.IntFieldRefInput<$PrismaModel>; + gte?: number | Prisma.IntFieldRefInput<$PrismaModel>; + not?: Prisma.NestedIntFilter<$PrismaModel> | number; +}; +export type StringFilter<$PrismaModel = never> = { + equals?: string | Prisma.StringFieldRefInput<$PrismaModel>; + in?: string[]; + notIn?: string[]; + lt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + lte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + contains?: string | Prisma.StringFieldRefInput<$PrismaModel>; + startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + search?: string; + not?: Prisma.NestedStringFilter<$PrismaModel> | string; +}; +export type StringNullableFilter<$PrismaModel = never> = { + equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null; + in?: string[] | null; + notIn?: string[] | null; + lt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + lte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + contains?: string | Prisma.StringFieldRefInput<$PrismaModel>; + startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + search?: string; + not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null; +}; +export type JsonNullableFilter<$PrismaModel = never> = Prisma.PatchUndefined>, Exclude>, 'path'>>, Required>> | Prisma.OptionalFlat>, 'path'>>; +export type JsonNullableFilterBase<$PrismaModel = never> = { + equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter; + path?: string; + mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>; + string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>; + string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>; + string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>; + array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null; + array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null; + array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null; + lt?: runtime.InputJsonValue; + lte?: runtime.InputJsonValue; + gt?: runtime.InputJsonValue; + gte?: runtime.InputJsonValue; + not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter; +}; +export type DateTimeFilter<$PrismaModel = never> = { + equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + in?: Date[] | string[]; + notIn?: Date[] | string[]; + lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string; +}; +export type SortOrderInput = { + sort: Prisma.SortOrder; + nulls?: Prisma.NullsOrder; +}; +export type IntWithAggregatesFilter<$PrismaModel = never> = { + equals?: number | Prisma.IntFieldRefInput<$PrismaModel>; + in?: number[]; + notIn?: number[]; + lt?: number | Prisma.IntFieldRefInput<$PrismaModel>; + lte?: number | Prisma.IntFieldRefInput<$PrismaModel>; + gt?: number | Prisma.IntFieldRefInput<$PrismaModel>; + gte?: number | Prisma.IntFieldRefInput<$PrismaModel>; + not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number; + _count?: Prisma.NestedIntFilter<$PrismaModel>; + _avg?: Prisma.NestedFloatFilter<$PrismaModel>; + _sum?: Prisma.NestedIntFilter<$PrismaModel>; + _min?: Prisma.NestedIntFilter<$PrismaModel>; + _max?: Prisma.NestedIntFilter<$PrismaModel>; +}; +export type StringWithAggregatesFilter<$PrismaModel = never> = { + equals?: string | Prisma.StringFieldRefInput<$PrismaModel>; + in?: string[]; + notIn?: string[]; + lt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + lte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + contains?: string | Prisma.StringFieldRefInput<$PrismaModel>; + startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + search?: string; + not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string; + _count?: Prisma.NestedIntFilter<$PrismaModel>; + _min?: Prisma.NestedStringFilter<$PrismaModel>; + _max?: Prisma.NestedStringFilter<$PrismaModel>; +}; +export type StringNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null; + in?: string[] | null; + notIn?: string[] | null; + lt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + lte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + contains?: string | Prisma.StringFieldRefInput<$PrismaModel>; + startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + search?: string; + not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null; + _count?: Prisma.NestedIntNullableFilter<$PrismaModel>; + _min?: Prisma.NestedStringNullableFilter<$PrismaModel>; + _max?: Prisma.NestedStringNullableFilter<$PrismaModel>; +}; +export type JsonNullableWithAggregatesFilter<$PrismaModel = never> = Prisma.PatchUndefined>, Exclude>, 'path'>>, Required>> | Prisma.OptionalFlat>, 'path'>>; +export type JsonNullableWithAggregatesFilterBase<$PrismaModel = never> = { + equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter; + path?: string; + mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>; + string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>; + string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>; + string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>; + array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null; + array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null; + array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null; + lt?: runtime.InputJsonValue; + lte?: runtime.InputJsonValue; + gt?: runtime.InputJsonValue; + gte?: runtime.InputJsonValue; + not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter; + _count?: Prisma.NestedIntNullableFilter<$PrismaModel>; + _min?: Prisma.NestedJsonNullableFilter<$PrismaModel>; + _max?: Prisma.NestedJsonNullableFilter<$PrismaModel>; +}; +export type DateTimeWithAggregatesFilter<$PrismaModel = never> = { + equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + in?: Date[] | string[]; + notIn?: Date[] | string[]; + lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string; + _count?: Prisma.NestedIntFilter<$PrismaModel>; + _min?: Prisma.NestedDateTimeFilter<$PrismaModel>; + _max?: Prisma.NestedDateTimeFilter<$PrismaModel>; +}; +export type BigIntFilter<$PrismaModel = never> = { + equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + in?: bigint[] | number[]; + notIn?: bigint[] | number[]; + lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + not?: Prisma.NestedBigIntFilter<$PrismaModel> | bigint | number; +}; +export type BigIntWithAggregatesFilter<$PrismaModel = never> = { + equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + in?: bigint[] | number[]; + notIn?: bigint[] | number[]; + lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + not?: Prisma.NestedBigIntWithAggregatesFilter<$PrismaModel> | bigint | number; + _count?: Prisma.NestedIntFilter<$PrismaModel>; + _avg?: Prisma.NestedFloatFilter<$PrismaModel>; + _sum?: Prisma.NestedBigIntFilter<$PrismaModel>; + _min?: Prisma.NestedBigIntFilter<$PrismaModel>; + _max?: Prisma.NestedBigIntFilter<$PrismaModel>; +}; +export type NestedIntFilter<$PrismaModel = never> = { + equals?: number | Prisma.IntFieldRefInput<$PrismaModel>; + in?: number[]; + notIn?: number[]; + lt?: number | Prisma.IntFieldRefInput<$PrismaModel>; + lte?: number | Prisma.IntFieldRefInput<$PrismaModel>; + gt?: number | Prisma.IntFieldRefInput<$PrismaModel>; + gte?: number | Prisma.IntFieldRefInput<$PrismaModel>; + not?: Prisma.NestedIntFilter<$PrismaModel> | number; +}; +export type NestedStringFilter<$PrismaModel = never> = { + equals?: string | Prisma.StringFieldRefInput<$PrismaModel>; + in?: string[]; + notIn?: string[]; + lt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + lte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + contains?: string | Prisma.StringFieldRefInput<$PrismaModel>; + startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + search?: string; + not?: Prisma.NestedStringFilter<$PrismaModel> | string; +}; +export type NestedStringNullableFilter<$PrismaModel = never> = { + equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null; + in?: string[] | null; + notIn?: string[] | null; + lt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + lte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + contains?: string | Prisma.StringFieldRefInput<$PrismaModel>; + startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + search?: string; + not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null; +}; +export type NestedDateTimeFilter<$PrismaModel = never> = { + equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + in?: Date[] | string[]; + notIn?: Date[] | string[]; + lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string; +}; +export type NestedIntWithAggregatesFilter<$PrismaModel = never> = { + equals?: number | Prisma.IntFieldRefInput<$PrismaModel>; + in?: number[]; + notIn?: number[]; + lt?: number | Prisma.IntFieldRefInput<$PrismaModel>; + lte?: number | Prisma.IntFieldRefInput<$PrismaModel>; + gt?: number | Prisma.IntFieldRefInput<$PrismaModel>; + gte?: number | Prisma.IntFieldRefInput<$PrismaModel>; + not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number; + _count?: Prisma.NestedIntFilter<$PrismaModel>; + _avg?: Prisma.NestedFloatFilter<$PrismaModel>; + _sum?: Prisma.NestedIntFilter<$PrismaModel>; + _min?: Prisma.NestedIntFilter<$PrismaModel>; + _max?: Prisma.NestedIntFilter<$PrismaModel>; +}; +export type NestedFloatFilter<$PrismaModel = never> = { + equals?: number | Prisma.FloatFieldRefInput<$PrismaModel>; + in?: number[]; + notIn?: number[]; + lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>; + lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>; + gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>; + gte?: number | Prisma.FloatFieldRefInput<$PrismaModel>; + not?: Prisma.NestedFloatFilter<$PrismaModel> | number; +}; +export type NestedStringWithAggregatesFilter<$PrismaModel = never> = { + equals?: string | Prisma.StringFieldRefInput<$PrismaModel>; + in?: string[]; + notIn?: string[]; + lt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + lte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + contains?: string | Prisma.StringFieldRefInput<$PrismaModel>; + startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + search?: string; + not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string; + _count?: Prisma.NestedIntFilter<$PrismaModel>; + _min?: Prisma.NestedStringFilter<$PrismaModel>; + _max?: Prisma.NestedStringFilter<$PrismaModel>; +}; +export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = { + equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null; + in?: string[] | null; + notIn?: string[] | null; + lt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + lte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gt?: string | Prisma.StringFieldRefInput<$PrismaModel>; + gte?: string | Prisma.StringFieldRefInput<$PrismaModel>; + contains?: string | Prisma.StringFieldRefInput<$PrismaModel>; + startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>; + search?: string; + not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null; + _count?: Prisma.NestedIntNullableFilter<$PrismaModel>; + _min?: Prisma.NestedStringNullableFilter<$PrismaModel>; + _max?: Prisma.NestedStringNullableFilter<$PrismaModel>; +}; +export type NestedIntNullableFilter<$PrismaModel = never> = { + equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null; + in?: number[] | null; + notIn?: number[] | null; + lt?: number | Prisma.IntFieldRefInput<$PrismaModel>; + lte?: number | Prisma.IntFieldRefInput<$PrismaModel>; + gt?: number | Prisma.IntFieldRefInput<$PrismaModel>; + gte?: number | Prisma.IntFieldRefInput<$PrismaModel>; + not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null; +}; +export type NestedJsonNullableFilter<$PrismaModel = never> = Prisma.PatchUndefined>, Exclude>, 'path'>>, Required>> | Prisma.OptionalFlat>, 'path'>>; +export type NestedJsonNullableFilterBase<$PrismaModel = never> = { + equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter; + path?: string; + mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>; + string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>; + string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>; + string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>; + array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null; + array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null; + array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null; + lt?: runtime.InputJsonValue; + lte?: runtime.InputJsonValue; + gt?: runtime.InputJsonValue; + gte?: runtime.InputJsonValue; + not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter; +}; +export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = { + equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + in?: Date[] | string[]; + notIn?: Date[] | string[]; + lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>; + not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string; + _count?: Prisma.NestedIntFilter<$PrismaModel>; + _min?: Prisma.NestedDateTimeFilter<$PrismaModel>; + _max?: Prisma.NestedDateTimeFilter<$PrismaModel>; +}; +export type NestedBigIntFilter<$PrismaModel = never> = { + equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + in?: bigint[] | number[]; + notIn?: bigint[] | number[]; + lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + not?: Prisma.NestedBigIntFilter<$PrismaModel> | bigint | number; +}; +export type NestedBigIntWithAggregatesFilter<$PrismaModel = never> = { + equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + in?: bigint[] | number[]; + notIn?: bigint[] | number[]; + lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>; + not?: Prisma.NestedBigIntWithAggregatesFilter<$PrismaModel> | bigint | number; + _count?: Prisma.NestedIntFilter<$PrismaModel>; + _avg?: Prisma.NestedFloatFilter<$PrismaModel>; + _sum?: Prisma.NestedBigIntFilter<$PrismaModel>; + _min?: Prisma.NestedBigIntFilter<$PrismaModel>; + _max?: Prisma.NestedBigIntFilter<$PrismaModel>; +}; diff --git a/dist/generated/prisma/commonInputTypes.js b/dist/generated/prisma/commonInputTypes.js new file mode 100644 index 0000000..8c50359 --- /dev/null +++ b/dist/generated/prisma/commonInputTypes.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=commonInputTypes.js.map \ No newline at end of file diff --git a/dist/generated/prisma/commonInputTypes.js.map b/dist/generated/prisma/commonInputTypes.js.map new file mode 100644 index 0000000..6116df8 --- /dev/null +++ b/dist/generated/prisma/commonInputTypes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"commonInputTypes.js","sourceRoot":"","sources":["../../../src/generated/prisma/commonInputTypes.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/generated/prisma/enums.d.ts b/dist/generated/prisma/enums.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/generated/prisma/enums.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/generated/prisma/enums.js b/dist/generated/prisma/enums.js new file mode 100644 index 0000000..2c3a48f --- /dev/null +++ b/dist/generated/prisma/enums.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=enums.js.map \ No newline at end of file diff --git a/dist/generated/prisma/enums.js.map b/dist/generated/prisma/enums.js.map new file mode 100644 index 0000000..b1f3c41 --- /dev/null +++ b/dist/generated/prisma/enums.js.map @@ -0,0 +1 @@ +{"version":3,"file":"enums.js","sourceRoot":"","sources":["../../../src/generated/prisma/enums.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/generated/prisma/internal/class.d.ts b/dist/generated/prisma/internal/class.d.ts new file mode 100644 index 0000000..db967c8 --- /dev/null +++ b/dist/generated/prisma/internal/class.d.ts @@ -0,0 +1,38 @@ +import * as runtime from "@prisma/client/runtime/library"; +import type * as Prisma from "./prismaNamespace.js"; +export type LogOptions = 'log' extends keyof ClientOptions ? ClientOptions['log'] extends Array ? Prisma.GetEvents : never : never; +export interface PrismaClientConstructor { + new = LogOptions, OmitOpts extends Prisma.PrismaClientOptions['omit'] = Options extends { + omit: infer U; + } ? U : Prisma.PrismaClientOptions['omit'], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs>(options?: Prisma.Subset): PrismaClient; +} +export interface PrismaClient { + [K: symbol]: { + types: Prisma.TypeMap['other']; + }; + $on(eventType: V, callback: (event: V extends 'query' ? Prisma.QueryEvent : Prisma.LogEvent) => void): PrismaClient; + $connect(): runtime.Types.Utils.JsPromise; + $disconnect(): runtime.Types.Utils.JsPromise; + $executeRaw(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise; + $executeRawUnsafe(query: string, ...values: any[]): Prisma.PrismaPromise; + $queryRaw(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise; + $queryRawUnsafe(query: string, ...values: any[]): Prisma.PrismaPromise; + $transaction

[]>(arg: [...P], options?: { + isolationLevel?: Prisma.TransactionIsolationLevel; + }): runtime.Types.Utils.JsPromise>; + $transaction(fn: (prisma: Omit) => runtime.Types.Utils.JsPromise, options?: { + maxWait?: number; + timeout?: number; + isolationLevel?: Prisma.TransactionIsolationLevel; + }): runtime.Types.Utils.JsPromise; + $extends: runtime.Types.Extensions.ExtendsHook<"extends", Prisma.TypeMapCb, ExtArgs, runtime.Types.Utils.Call, { + extArgs: ExtArgs; + }>>; + get entity(): Prisma.EntityDelegate; + get file(): Prisma.FileDelegate; +} +export declare function getPrismaClientClass(dirname: string): PrismaClientConstructor; diff --git a/dist/generated/prisma/internal/class.js b/dist/generated/prisma/internal/class.js new file mode 100644 index 0000000..f553c3a --- /dev/null +++ b/dist/generated/prisma/internal/class.js @@ -0,0 +1,61 @@ +import * as runtime from "@prisma/client/runtime/library"; +const config = { + "generator": { + "name": "client", + "provider": { + "fromEnvVar": null, + "value": "prisma-client" + }, + "output": { + "value": "/workspaces/codes/arocapi/src/generated/prisma", + "fromEnvVar": null + }, + "config": { + "importFileExtension": "js", + "engineType": "library" + }, + "binaryTargets": [ + { + "fromEnvVar": null, + "value": "linux-musl-arm64-openssl-3.0.x", + "native": true + } + ], + "previewFeatures": [], + "sourceFilePath": "/workspaces/codes/arocapi/prisma/schema.prisma", + "isCustomOutput": true + }, + "relativePath": "../../../prisma", + "clientVersion": "6.19.0", + "engineVersion": "2ba551f319ab1df4bc874a89965d8b3641056773", + "datasourceNames": [ + "db" + ], + "activeProvider": "mysql", + "postinstall": false, + "inlineDatasources": { + "db": { + "url": { + "fromEnvVar": "DATABASE_URL", + "value": null + } + } + }, + "inlineSchema": "model Entity {\n id Int @id @default(autoincrement())\n\n rocrateId String @db.VarChar(2048)\n\n name String @db.VarChar(1024)\n description String @db.Text\n\n entityType String @db.VarChar(1024)\n memberOf String? @db.VarChar(2048)\n rootCollection String? @db.VarChar(2048)\n metadataLicenseId String @db.VarChar(2048)\n contentLicenseId String @db.VarChar(2048)\n fileId String? @db.VarChar(2048)\n\n meta Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel File {\n id Int @id @default(autoincrement())\n\n fileId String @db.VarChar(2048)\n\n filename String @db.VarChar(255)\n mediaType String @db.VarChar(127)\n size BigInt\n\n memberOf String @db.VarChar(2048)\n rootCollection String @db.VarChar(2048)\n contentLicenseId String @db.VarChar(2048)\n\n meta Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([fileId(length: 768)])\n}\n\n// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../src/generated/prisma\"\n importFileExtension = \"js\"\n}\n\ngenerator json {\n provider = \"prisma-json-types-generator\"\n}\n\ndatasource db {\n provider = \"mysql\"\n url = env(\"DATABASE_URL\")\n}\n", + "inlineSchemaHash": "4b16b94a03778418cb57df4ad0fc12812f1352090f7f1f819a32ffaae09c56b8", + "copyEngine": true, + "runtimeDataModel": { + "models": {}, + "enums": {}, + "types": {} + }, + "dirname": "" +}; +config.runtimeDataModel = JSON.parse("{\"models\":{\"Entity\":{\"dbName\":null,\"schema\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"nativeType\":null,\"default\":{\"name\":\"autoincrement\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rocrateId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"2048\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"name\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"1024\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"description\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"Text\",[]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"entityType\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"1024\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"memberOf\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"2048\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rootCollection\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"2048\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"metadataLicenseId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"2048\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"contentLicenseId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"2048\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fileId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"2048\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"meta\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Json\",\"nativeType\":null,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"nativeType\":null,\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"nativeType\":null,\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false},\"File\":{\"dbName\":null,\"schema\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"nativeType\":null,\"default\":{\"name\":\"autoincrement\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"fileId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"2048\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"filename\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"255\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"mediaType\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"127\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"size\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BigInt\",\"nativeType\":null,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"memberOf\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"2048\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"rootCollection\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"2048\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"contentLicenseId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":[\"VarChar\",[\"2048\"]],\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"meta\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"Json\",\"nativeType\":null,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"nativeType\":null,\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"DateTime\",\"nativeType\":null,\"isGenerated\":false,\"isUpdatedAt\":true}],\"primaryKey\":null,\"uniqueFields\":[[\"fileId\"]],\"uniqueIndexes\":[{\"name\":null,\"fields\":[\"fileId\"]}],\"isGenerated\":false}},\"enums\":{},\"types\":{}}"); +config.engineWasm = undefined; +config.compilerWasm = undefined; +export function getPrismaClientClass(dirname) { + config.dirname = dirname; + return runtime.getPrismaClient(config); +} +//# sourceMappingURL=class.js.map \ No newline at end of file diff --git a/dist/generated/prisma/internal/class.js.map b/dist/generated/prisma/internal/class.js.map new file mode 100644 index 0000000..63e518b --- /dev/null +++ b/dist/generated/prisma/internal/class.js.map @@ -0,0 +1 @@ +{"version":3,"file":"class.js","sourceRoot":"","sources":["../../../../src/generated/prisma/internal/class.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,OAAO,MAAM,gCAAgC,CAAA;AAIzD,MAAM,MAAM,GAAkC;IAC5C,WAAW,EAAE;QACX,MAAM,EAAE,QAAQ;QAChB,UAAU,EAAE;YACV,YAAY,EAAE,IAAI;YAClB,OAAO,EAAE,eAAe;SACzB;QACD,QAAQ,EAAE;YACR,OAAO,EAAE,gDAAgD;YACzD,YAAY,EAAE,IAAI;SACnB;QACD,QAAQ,EAAE;YACR,qBAAqB,EAAE,IAAI;YAC3B,YAAY,EAAE,SAAS;SACxB;QACD,eAAe,EAAE;YACf;gBACE,YAAY,EAAE,IAAI;gBAClB,OAAO,EAAE,gCAAgC;gBACzC,QAAQ,EAAE,IAAI;aACf;SACF;QACD,iBAAiB,EAAE,EAAE;QACrB,gBAAgB,EAAE,gDAAgD;QAClE,gBAAgB,EAAE,IAAI;KACvB;IACD,cAAc,EAAE,iBAAiB;IACjC,eAAe,EAAE,QAAQ;IACzB,eAAe,EAAE,0CAA0C;IAC3D,iBAAiB,EAAE;QACjB,IAAI;KACL;IACD,gBAAgB,EAAE,OAAO;IACzB,aAAa,EAAE,KAAK;IACpB,mBAAmB,EAAE;QACnB,IAAI,EAAE;YACJ,KAAK,EAAE;gBACL,YAAY,EAAE,cAAc;gBAC5B,OAAO,EAAE,IAAI;aACd;SACF;KACF;IACD,cAAc,EAAE,s4CAAs4C;IACt5C,kBAAkB,EAAE,kEAAkE;IACtF,YAAY,EAAE,IAAI;IAClB,kBAAkB,EAAE;QAClB,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,EAAE;QACX,OAAO,EAAE,EAAE;KACZ;IACD,SAAS,EAAE,EAAE;CACd,CAAA;AAED,MAAM,CAAC,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,8mNAA8mN,CAAC,CAAA;AACppN,MAAM,CAAC,UAAU,GAAG,SAAS,CAAA;AAC7B,MAAM,CAAC,YAAY,GAAG,SAAS,CAAA;AA0J/B,MAAM,UAAU,oBAAoB,CAAC,OAAe;IAClD,MAAM,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,OAAO,OAAO,CAAC,eAAe,CAAC,MAAM,CAAuC,CAAA;AAC9E,CAAC"} \ No newline at end of file diff --git a/dist/generated/prisma/internal/prismaNamespace.d.ts b/dist/generated/prisma/internal/prismaNamespace.d.ts new file mode 100644 index 0000000..d4e3a53 --- /dev/null +++ b/dist/generated/prisma/internal/prismaNamespace.d.ts @@ -0,0 +1,519 @@ +import * as runtime from "@prisma/client/runtime/library"; +import type * as Prisma from "../models.js"; +import { type PrismaClient } from "./class.js"; +export type * from '../models.js'; +export type DMMF = typeof runtime.DMMF; +export type PrismaPromise = runtime.Types.Public.PrismaPromise; +export declare const PrismaClientKnownRequestError: typeof runtime.PrismaClientKnownRequestError; +export type PrismaClientKnownRequestError = runtime.PrismaClientKnownRequestError; +export declare const PrismaClientUnknownRequestError: typeof runtime.PrismaClientUnknownRequestError; +export type PrismaClientUnknownRequestError = runtime.PrismaClientUnknownRequestError; +export declare const PrismaClientRustPanicError: typeof runtime.PrismaClientRustPanicError; +export type PrismaClientRustPanicError = runtime.PrismaClientRustPanicError; +export declare const PrismaClientInitializationError: typeof runtime.PrismaClientInitializationError; +export type PrismaClientInitializationError = runtime.PrismaClientInitializationError; +export declare const PrismaClientValidationError: typeof runtime.PrismaClientValidationError; +export type PrismaClientValidationError = runtime.PrismaClientValidationError; +export declare const sql: typeof runtime.sqltag; +export declare const empty: runtime.Sql; +export declare const join: typeof runtime.join; +export declare const raw: typeof runtime.raw; +export declare const Sql: typeof runtime.Sql; +export type Sql = runtime.Sql; +export declare const Decimal: typeof runtime.Decimal; +export type Decimal = runtime.Decimal; +export type DecimalJsLike = runtime.DecimalJsLike; +export type Metrics = runtime.Metrics; +export type Metric = runtime.Metric; +export type MetricHistogram = runtime.MetricHistogram; +export type MetricHistogramBucket = runtime.MetricHistogramBucket; +export type Extension = runtime.Types.Extensions.UserArgs; +export declare const getExtensionContext: typeof runtime.Extensions.getExtensionContext; +export type Args = runtime.Types.Public.Args; +export type Payload = runtime.Types.Public.Payload; +export type Result = runtime.Types.Public.Result; +export type Exact = runtime.Types.Public.Exact; +export type PrismaVersion = { + client: string; + engine: string; +}; +export declare const prismaVersion: PrismaVersion; +export type Bytes = runtime.Bytes; +export type JsonObject = runtime.JsonObject; +export type JsonArray = runtime.JsonArray; +export type JsonValue = runtime.JsonValue; +export type InputJsonObject = runtime.InputJsonObject; +export type InputJsonArray = runtime.InputJsonArray; +export type InputJsonValue = runtime.InputJsonValue; +export declare const NullTypes: { + DbNull: (new (secret: never) => typeof runtime.objectEnumValues.instances.DbNull); + JsonNull: (new (secret: never) => typeof runtime.objectEnumValues.instances.JsonNull); + AnyNull: (new (secret: never) => typeof runtime.objectEnumValues.instances.AnyNull); +}; +export declare const DbNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; +}; +export declare const JsonNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; +}; +export declare const AnyNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; +}; +type SelectAndInclude = { + select: any; + include: any; +}; +type SelectAndOmit = { + select: any; + omit: any; +}; +type Prisma__Pick = { + [P in K]: T[P]; +}; +export type Enumerable = T | Array; +export type Subset = { + [key in keyof T]: key extends keyof U ? T[key] : never; +}; +export type SelectSubset = { + [key in keyof T]: key extends keyof U ? T[key] : never; +} & (T extends SelectAndInclude ? 'Please either choose `select` or `include`.' : T extends SelectAndOmit ? 'Please either choose `select` or `omit`.' : {}); +export type SubsetIntersection = { + [key in keyof T]: key extends keyof U ? T[key] : never; +} & K; +type Without = { + [P in Exclude]?: never; +}; +export type XOR = T extends object ? U extends object ? (Without & U) | (Without & T) : U : T; +type IsObject = T extends Array ? False : T extends Date ? False : T extends Uint8Array ? False : T extends BigInt ? False : T extends object ? True : False; +export type UnEnumerate = T extends Array ? U : T; +type __Either = Omit & { + [P in K]: Prisma__Pick; +}[K]; +type EitherStrict = Strict<__Either>; +type EitherLoose = ComputeRaw<__Either>; +type _Either = { + 1: EitherStrict; + 0: EitherLoose; +}[strict]; +export type Either = O extends unknown ? _Either : never; +export type Union = any; +export type PatchUndefined = { + [K in keyof O]: O[K] extends undefined ? At : O[K]; +} & {}; +export type IntersectOf = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never; +export type Overwrite = { + [K in keyof O]: K extends keyof O1 ? O1[K] : O[K]; +} & {}; +type _Merge = IntersectOf; +}>>; +type Key = string | number | symbol; +type AtStrict = O[K & keyof O]; +type AtLoose = O extends unknown ? AtStrict : never; +export type At = { + 1: AtStrict; + 0: AtLoose; +}[strict]; +export type ComputeRaw = A extends Function ? A : { + [K in keyof A]: A[K]; +} & {}; +export type OptionalFlat = { + [K in keyof O]?: O[K]; +} & {}; +type _Record = { + [P in K]: T; +}; +type NoExpand = T extends unknown ? T : never; +export type AtLeast = NoExpand; +type _Strict = U extends unknown ? U & OptionalFlat<_Record, keyof U>, never>> : never; +export type Strict = ComputeRaw<_Strict>; +export type Merge = ComputeRaw<_Merge>>; +export type Boolean = True | False; +export type True = 1; +export type False = 0; +export type Not = { + 0: 1; + 1: 0; +}[B]; +export type Extends = [A1] extends [never] ? 0 : A1 extends A2 ? 1 : 0; +export type Has = Not, U1>>; +export type Or = { + 0: { + 0: 0; + 1: 1; + }; + 1: { + 0: 1; + 1: 1; + }; +}[B1][B2]; +export type Keys = U extends unknown ? keyof U : never; +export type GetScalarType = O extends object ? { + [P in keyof T]: P extends keyof O ? O[P] : never; +} : never; +type FieldPaths> = IsObject extends True ? U : T; +export type GetHavingFields = { + [K in keyof T]: Or, Extends<'AND', K>>, Extends<'NOT', K>> extends True ? T[K] extends infer TK ? GetHavingFields extends object ? Merge> : never> : never : {} extends FieldPaths ? never : K; +}[keyof T]; +type _TupleToUnion = T extends (infer E)[] ? E : never; +type TupleToUnion = _TupleToUnion; +export type MaybeTupleToUnion = T extends any[] ? TupleToUnion : T; +export type PickEnumerable | keyof T> = Prisma__Pick>; +export type ExcludeUnderscoreKeys = T extends `_${string}` ? never : T; +export type FieldRef = runtime.FieldRef; +type FieldRefInputType = Model extends never ? never : FieldRef; +export declare const ModelName: { + readonly Entity: "Entity"; + readonly File: "File"; +}; +export type ModelName = (typeof ModelName)[keyof typeof ModelName]; +export interface TypeMapCb extends runtime.Types.Utils.Fn<{ + extArgs: runtime.Types.Extensions.InternalArgs; +}, runtime.Types.Utils.Record> { + returns: TypeMap; +} +export type TypeMap = { + globalOmitOptions: { + omit: GlobalOmitOptions; + }; + meta: { + modelProps: "entity" | "file"; + txIsolationLevel: TransactionIsolationLevel; + }; + model: { + Entity: { + payload: Prisma.$EntityPayload; + fields: Prisma.EntityFieldRefs; + operations: { + findUnique: { + args: Prisma.EntityFindUniqueArgs; + result: runtime.Types.Utils.PayloadToResult | null; + }; + findUniqueOrThrow: { + args: Prisma.EntityFindUniqueOrThrowArgs; + result: runtime.Types.Utils.PayloadToResult; + }; + findFirst: { + args: Prisma.EntityFindFirstArgs; + result: runtime.Types.Utils.PayloadToResult | null; + }; + findFirstOrThrow: { + args: Prisma.EntityFindFirstOrThrowArgs; + result: runtime.Types.Utils.PayloadToResult; + }; + findMany: { + args: Prisma.EntityFindManyArgs; + result: runtime.Types.Utils.PayloadToResult[]; + }; + create: { + args: Prisma.EntityCreateArgs; + result: runtime.Types.Utils.PayloadToResult; + }; + createMany: { + args: Prisma.EntityCreateManyArgs; + result: BatchPayload; + }; + delete: { + args: Prisma.EntityDeleteArgs; + result: runtime.Types.Utils.PayloadToResult; + }; + update: { + args: Prisma.EntityUpdateArgs; + result: runtime.Types.Utils.PayloadToResult; + }; + deleteMany: { + args: Prisma.EntityDeleteManyArgs; + result: BatchPayload; + }; + updateMany: { + args: Prisma.EntityUpdateManyArgs; + result: BatchPayload; + }; + upsert: { + args: Prisma.EntityUpsertArgs; + result: runtime.Types.Utils.PayloadToResult; + }; + aggregate: { + args: Prisma.EntityAggregateArgs; + result: runtime.Types.Utils.Optional; + }; + groupBy: { + args: Prisma.EntityGroupByArgs; + result: runtime.Types.Utils.Optional[]; + }; + count: { + args: Prisma.EntityCountArgs; + result: runtime.Types.Utils.Optional | number; + }; + }; + }; + File: { + payload: Prisma.$FilePayload; + fields: Prisma.FileFieldRefs; + operations: { + findUnique: { + args: Prisma.FileFindUniqueArgs; + result: runtime.Types.Utils.PayloadToResult | null; + }; + findUniqueOrThrow: { + args: Prisma.FileFindUniqueOrThrowArgs; + result: runtime.Types.Utils.PayloadToResult; + }; + findFirst: { + args: Prisma.FileFindFirstArgs; + result: runtime.Types.Utils.PayloadToResult | null; + }; + findFirstOrThrow: { + args: Prisma.FileFindFirstOrThrowArgs; + result: runtime.Types.Utils.PayloadToResult; + }; + findMany: { + args: Prisma.FileFindManyArgs; + result: runtime.Types.Utils.PayloadToResult[]; + }; + create: { + args: Prisma.FileCreateArgs; + result: runtime.Types.Utils.PayloadToResult; + }; + createMany: { + args: Prisma.FileCreateManyArgs; + result: BatchPayload; + }; + delete: { + args: Prisma.FileDeleteArgs; + result: runtime.Types.Utils.PayloadToResult; + }; + update: { + args: Prisma.FileUpdateArgs; + result: runtime.Types.Utils.PayloadToResult; + }; + deleteMany: { + args: Prisma.FileDeleteManyArgs; + result: BatchPayload; + }; + updateMany: { + args: Prisma.FileUpdateManyArgs; + result: BatchPayload; + }; + upsert: { + args: Prisma.FileUpsertArgs; + result: runtime.Types.Utils.PayloadToResult; + }; + aggregate: { + args: Prisma.FileAggregateArgs; + result: runtime.Types.Utils.Optional; + }; + groupBy: { + args: Prisma.FileGroupByArgs; + result: runtime.Types.Utils.Optional[]; + }; + count: { + args: Prisma.FileCountArgs; + result: runtime.Types.Utils.Optional | number; + }; + }; + }; + }; +} & { + other: { + payload: any; + operations: { + $executeRaw: { + args: [query: TemplateStringsArray | Sql, ...values: any[]]; + result: any; + }; + $executeRawUnsafe: { + args: [query: string, ...values: any[]]; + result: any; + }; + $queryRaw: { + args: [query: TemplateStringsArray | Sql, ...values: any[]]; + result: any; + }; + $queryRawUnsafe: { + args: [query: string, ...values: any[]]; + result: any; + }; + }; + }; +}; +export declare const TransactionIsolationLevel: { + readonly ReadUncommitted: "ReadUncommitted"; + readonly ReadCommitted: "ReadCommitted"; + readonly RepeatableRead: "RepeatableRead"; + readonly Serializable: "Serializable"; +}; +export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]; +export declare const EntityScalarFieldEnum: { + readonly id: "id"; + readonly rocrateId: "rocrateId"; + readonly name: "name"; + readonly description: "description"; + readonly entityType: "entityType"; + readonly memberOf: "memberOf"; + readonly rootCollection: "rootCollection"; + readonly metadataLicenseId: "metadataLicenseId"; + readonly contentLicenseId: "contentLicenseId"; + readonly fileId: "fileId"; + readonly meta: "meta"; + readonly createdAt: "createdAt"; + readonly updatedAt: "updatedAt"; +}; +export type EntityScalarFieldEnum = (typeof EntityScalarFieldEnum)[keyof typeof EntityScalarFieldEnum]; +export declare const FileScalarFieldEnum: { + readonly id: "id"; + readonly fileId: "fileId"; + readonly filename: "filename"; + readonly mediaType: "mediaType"; + readonly size: "size"; + readonly memberOf: "memberOf"; + readonly rootCollection: "rootCollection"; + readonly contentLicenseId: "contentLicenseId"; + readonly meta: "meta"; + readonly createdAt: "createdAt"; + readonly updatedAt: "updatedAt"; +}; +export type FileScalarFieldEnum = (typeof FileScalarFieldEnum)[keyof typeof FileScalarFieldEnum]; +export declare const SortOrder: { + readonly asc: "asc"; + readonly desc: "desc"; +}; +export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]; +export declare const NullableJsonNullValueInput: { + readonly DbNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; + }; + readonly JsonNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; + }; +}; +export type NullableJsonNullValueInput = (typeof NullableJsonNullValueInput)[keyof typeof NullableJsonNullValueInput]; +export declare const JsonNullValueFilter: { + readonly DbNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; + }; + readonly JsonNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; + }; + readonly AnyNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; + }; +}; +export type JsonNullValueFilter = (typeof JsonNullValueFilter)[keyof typeof JsonNullValueFilter]; +export declare const QueryMode: { + readonly default: "default"; + readonly insensitive: "insensitive"; +}; +export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]; +export declare const NullsOrder: { + readonly first: "first"; + readonly last: "last"; +}; +export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]; +export declare const EntityOrderByRelevanceFieldEnum: { + readonly rocrateId: "rocrateId"; + readonly name: "name"; + readonly description: "description"; + readonly entityType: "entityType"; + readonly memberOf: "memberOf"; + readonly rootCollection: "rootCollection"; + readonly metadataLicenseId: "metadataLicenseId"; + readonly contentLicenseId: "contentLicenseId"; + readonly fileId: "fileId"; +}; +export type EntityOrderByRelevanceFieldEnum = (typeof EntityOrderByRelevanceFieldEnum)[keyof typeof EntityOrderByRelevanceFieldEnum]; +export declare const FileOrderByRelevanceFieldEnum: { + readonly fileId: "fileId"; + readonly filename: "filename"; + readonly mediaType: "mediaType"; + readonly memberOf: "memberOf"; + readonly rootCollection: "rootCollection"; + readonly contentLicenseId: "contentLicenseId"; +}; +export type FileOrderByRelevanceFieldEnum = (typeof FileOrderByRelevanceFieldEnum)[keyof typeof FileOrderByRelevanceFieldEnum]; +export type IntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Int'>; +export type StringFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'String'>; +export type JsonFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Json'>; +export type EnumQueryModeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'QueryMode'>; +export type DateTimeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'DateTime'>; +export type BigIntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'BigInt'>; +export type FloatFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Float'>; +export type BatchPayload = { + count: number; +}; +export type Datasource = { + url?: string; +}; +export type Datasources = { + db?: Datasource; +}; +export declare const defineExtension: runtime.Types.Extensions.ExtendsHook<"define", TypeMapCb, runtime.Types.Extensions.DefaultArgs>; +export type DefaultPrismaClient = PrismaClient; +export type ErrorFormat = 'pretty' | 'colorless' | 'minimal'; +export interface PrismaClientOptions { + datasources?: Datasources; + datasourceUrl?: string; + errorFormat?: ErrorFormat; + log?: (LogLevel | LogDefinition)[]; + transactionOptions?: { + maxWait?: number; + timeout?: number; + isolationLevel?: TransactionIsolationLevel; + }; + adapter?: runtime.SqlDriverAdapterFactory | null; + omit?: GlobalOmitConfig; +} +export type GlobalOmitConfig = { + entity?: Prisma.EntityOmit; + file?: Prisma.FileOmit; +}; +export type LogLevel = 'info' | 'query' | 'warn' | 'error'; +export type LogDefinition = { + level: LogLevel; + emit: 'stdout' | 'event'; +}; +export type CheckIsLogLevel = T extends LogLevel ? T : never; +export type GetLogType = CheckIsLogLevel; +export type GetEvents = T extends Array ? GetLogType : never; +export type QueryEvent = { + timestamp: Date; + query: string; + params: string; + duration: number; + target: string; +}; +export type LogEvent = { + timestamp: Date; + message: string; + target: string; +}; +export type PrismaAction = 'findUnique' | 'findUniqueOrThrow' | 'findMany' | 'findFirst' | 'findFirstOrThrow' | 'create' | 'createMany' | 'createManyAndReturn' | 'update' | 'updateMany' | 'updateManyAndReturn' | 'upsert' | 'delete' | 'deleteMany' | 'executeRaw' | 'queryRaw' | 'aggregate' | 'count' | 'runCommandRaw' | 'findRaw' | 'groupBy'; +export type TransactionClient = Omit; diff --git a/dist/generated/prisma/internal/prismaNamespace.js b/dist/generated/prisma/internal/prismaNamespace.js new file mode 100644 index 0000000..e815ad5 --- /dev/null +++ b/dist/generated/prisma/internal/prismaNamespace.js @@ -0,0 +1,105 @@ +import * as runtime from "@prisma/client/runtime/library"; +export const PrismaClientKnownRequestError = runtime.PrismaClientKnownRequestError; +export const PrismaClientUnknownRequestError = runtime.PrismaClientUnknownRequestError; +export const PrismaClientRustPanicError = runtime.PrismaClientRustPanicError; +export const PrismaClientInitializationError = runtime.PrismaClientInitializationError; +export const PrismaClientValidationError = runtime.PrismaClientValidationError; +export const sql = runtime.sqltag; +export const empty = runtime.empty; +export const join = runtime.join; +export const raw = runtime.raw; +export const Sql = runtime.Sql; +export const Decimal = runtime.Decimal; +export const getExtensionContext = runtime.Extensions.getExtensionContext; +export const prismaVersion = { + client: "6.19.0", + engine: "2ba551f319ab1df4bc874a89965d8b3641056773" +}; +export const NullTypes = { + DbNull: runtime.objectEnumValues.classes.DbNull, + JsonNull: runtime.objectEnumValues.classes.JsonNull, + AnyNull: runtime.objectEnumValues.classes.AnyNull, +}; +export const DbNull = runtime.objectEnumValues.instances.DbNull; +export const JsonNull = runtime.objectEnumValues.instances.JsonNull; +export const AnyNull = runtime.objectEnumValues.instances.AnyNull; +export const ModelName = { + Entity: 'Entity', + File: 'File' +}; +export const TransactionIsolationLevel = runtime.makeStrictEnum({ + ReadUncommitted: 'ReadUncommitted', + ReadCommitted: 'ReadCommitted', + RepeatableRead: 'RepeatableRead', + Serializable: 'Serializable' +}); +export const EntityScalarFieldEnum = { + id: 'id', + rocrateId: 'rocrateId', + name: 'name', + description: 'description', + entityType: 'entityType', + memberOf: 'memberOf', + rootCollection: 'rootCollection', + metadataLicenseId: 'metadataLicenseId', + contentLicenseId: 'contentLicenseId', + fileId: 'fileId', + meta: 'meta', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +}; +export const FileScalarFieldEnum = { + id: 'id', + fileId: 'fileId', + filename: 'filename', + mediaType: 'mediaType', + size: 'size', + memberOf: 'memberOf', + rootCollection: 'rootCollection', + contentLicenseId: 'contentLicenseId', + meta: 'meta', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +}; +export const SortOrder = { + asc: 'asc', + desc: 'desc' +}; +export const NullableJsonNullValueInput = { + DbNull: DbNull, + JsonNull: JsonNull +}; +export const JsonNullValueFilter = { + DbNull: DbNull, + JsonNull: JsonNull, + AnyNull: AnyNull +}; +export const QueryMode = { + default: 'default', + insensitive: 'insensitive' +}; +export const NullsOrder = { + first: 'first', + last: 'last' +}; +export const EntityOrderByRelevanceFieldEnum = { + rocrateId: 'rocrateId', + name: 'name', + description: 'description', + entityType: 'entityType', + memberOf: 'memberOf', + rootCollection: 'rootCollection', + metadataLicenseId: 'metadataLicenseId', + contentLicenseId: 'contentLicenseId', + fileId: 'fileId' +}; +export const FileOrderByRelevanceFieldEnum = { + fileId: 'fileId', + filename: 'filename', + mediaType: 'mediaType', + memberOf: 'memberOf', + rootCollection: 'rootCollection', + contentLicenseId: 'contentLicenseId' +}; +export const defineExtension = runtime.Extensions.defineExtension; +//# sourceMappingURL=prismaNamespace.js.map \ No newline at end of file diff --git a/dist/generated/prisma/internal/prismaNamespace.js.map b/dist/generated/prisma/internal/prismaNamespace.js.map new file mode 100644 index 0000000..64e43d3 --- /dev/null +++ b/dist/generated/prisma/internal/prismaNamespace.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prismaNamespace.js","sourceRoot":"","sources":["../../../../src/generated/prisma/internal/prismaNamespace.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts b/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts new file mode 100644 index 0000000..83fa906 --- /dev/null +++ b/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts @@ -0,0 +1,141 @@ +import * as runtime from "@prisma/client/runtime/index-browser"; +export type * from '../models.js'; +export type * from './prismaNamespace.js'; +export declare const Decimal: typeof runtime.Decimal; +export declare const NullTypes: { + DbNull: (new (secret: never) => typeof runtime.objectEnumValues.instances.DbNull); + JsonNull: (new (secret: never) => typeof runtime.objectEnumValues.instances.JsonNull); + AnyNull: (new (secret: never) => typeof runtime.objectEnumValues.instances.AnyNull); +}; +export declare const DbNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; +}; +export declare const JsonNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; +}; +export declare const AnyNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; +}; +export declare const ModelName: { + readonly Entity: "Entity"; + readonly File: "File"; +}; +export type ModelName = (typeof ModelName)[keyof typeof ModelName]; +export declare const TransactionIsolationLevel: { + readonly ReadUncommitted: "ReadUncommitted"; + readonly ReadCommitted: "ReadCommitted"; + readonly RepeatableRead: "RepeatableRead"; + readonly Serializable: "Serializable"; +}; +export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]; +export declare const EntityScalarFieldEnum: { + readonly id: "id"; + readonly rocrateId: "rocrateId"; + readonly name: "name"; + readonly description: "description"; + readonly entityType: "entityType"; + readonly memberOf: "memberOf"; + readonly rootCollection: "rootCollection"; + readonly metadataLicenseId: "metadataLicenseId"; + readonly contentLicenseId: "contentLicenseId"; + readonly fileId: "fileId"; + readonly meta: "meta"; + readonly createdAt: "createdAt"; + readonly updatedAt: "updatedAt"; +}; +export type EntityScalarFieldEnum = (typeof EntityScalarFieldEnum)[keyof typeof EntityScalarFieldEnum]; +export declare const FileScalarFieldEnum: { + readonly id: "id"; + readonly fileId: "fileId"; + readonly filename: "filename"; + readonly mediaType: "mediaType"; + readonly size: "size"; + readonly memberOf: "memberOf"; + readonly rootCollection: "rootCollection"; + readonly contentLicenseId: "contentLicenseId"; + readonly meta: "meta"; + readonly createdAt: "createdAt"; + readonly updatedAt: "updatedAt"; +}; +export type FileScalarFieldEnum = (typeof FileScalarFieldEnum)[keyof typeof FileScalarFieldEnum]; +export declare const SortOrder: { + readonly asc: "asc"; + readonly desc: "desc"; +}; +export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]; +export declare const NullableJsonNullValueInput: { + readonly DbNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; + }; + readonly JsonNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; + }; +}; +export type NullableJsonNullValueInput = (typeof NullableJsonNullValueInput)[keyof typeof NullableJsonNullValueInput]; +export declare const JsonNullValueFilter: { + readonly DbNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; + }; + readonly JsonNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; + }; + readonly AnyNull: { + "__#private@#private": any; + _getNamespace(): string; + _getName(): string; + toString(): string; + }; +}; +export type JsonNullValueFilter = (typeof JsonNullValueFilter)[keyof typeof JsonNullValueFilter]; +export declare const QueryMode: { + readonly default: "default"; + readonly insensitive: "insensitive"; +}; +export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]; +export declare const NullsOrder: { + readonly first: "first"; + readonly last: "last"; +}; +export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]; +export declare const EntityOrderByRelevanceFieldEnum: { + readonly rocrateId: "rocrateId"; + readonly name: "name"; + readonly description: "description"; + readonly entityType: "entityType"; + readonly memberOf: "memberOf"; + readonly rootCollection: "rootCollection"; + readonly metadataLicenseId: "metadataLicenseId"; + readonly contentLicenseId: "contentLicenseId"; + readonly fileId: "fileId"; +}; +export type EntityOrderByRelevanceFieldEnum = (typeof EntityOrderByRelevanceFieldEnum)[keyof typeof EntityOrderByRelevanceFieldEnum]; +export declare const FileOrderByRelevanceFieldEnum: { + readonly fileId: "fileId"; + readonly filename: "filename"; + readonly mediaType: "mediaType"; + readonly memberOf: "memberOf"; + readonly rootCollection: "rootCollection"; + readonly contentLicenseId: "contentLicenseId"; +}; +export type FileOrderByRelevanceFieldEnum = (typeof FileOrderByRelevanceFieldEnum)[keyof typeof FileOrderByRelevanceFieldEnum]; diff --git a/dist/generated/prisma/internal/prismaNamespaceBrowser.js b/dist/generated/prisma/internal/prismaNamespaceBrowser.js new file mode 100644 index 0000000..2b24ddd --- /dev/null +++ b/dist/generated/prisma/internal/prismaNamespaceBrowser.js @@ -0,0 +1,89 @@ +import * as runtime from "@prisma/client/runtime/index-browser"; +export const Decimal = runtime.Decimal; +export const NullTypes = { + DbNull: runtime.objectEnumValues.classes.DbNull, + JsonNull: runtime.objectEnumValues.classes.JsonNull, + AnyNull: runtime.objectEnumValues.classes.AnyNull, +}; +export const DbNull = runtime.objectEnumValues.instances.DbNull; +export const JsonNull = runtime.objectEnumValues.instances.JsonNull; +export const AnyNull = runtime.objectEnumValues.instances.AnyNull; +export const ModelName = { + Entity: 'Entity', + File: 'File' +}; +export const TransactionIsolationLevel = runtime.makeStrictEnum({ + ReadUncommitted: 'ReadUncommitted', + ReadCommitted: 'ReadCommitted', + RepeatableRead: 'RepeatableRead', + Serializable: 'Serializable' +}); +export const EntityScalarFieldEnum = { + id: 'id', + rocrateId: 'rocrateId', + name: 'name', + description: 'description', + entityType: 'entityType', + memberOf: 'memberOf', + rootCollection: 'rootCollection', + metadataLicenseId: 'metadataLicenseId', + contentLicenseId: 'contentLicenseId', + fileId: 'fileId', + meta: 'meta', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +}; +export const FileScalarFieldEnum = { + id: 'id', + fileId: 'fileId', + filename: 'filename', + mediaType: 'mediaType', + size: 'size', + memberOf: 'memberOf', + rootCollection: 'rootCollection', + contentLicenseId: 'contentLicenseId', + meta: 'meta', + createdAt: 'createdAt', + updatedAt: 'updatedAt' +}; +export const SortOrder = { + asc: 'asc', + desc: 'desc' +}; +export const NullableJsonNullValueInput = { + DbNull: DbNull, + JsonNull: JsonNull +}; +export const JsonNullValueFilter = { + DbNull: DbNull, + JsonNull: JsonNull, + AnyNull: AnyNull +}; +export const QueryMode = { + default: 'default', + insensitive: 'insensitive' +}; +export const NullsOrder = { + first: 'first', + last: 'last' +}; +export const EntityOrderByRelevanceFieldEnum = { + rocrateId: 'rocrateId', + name: 'name', + description: 'description', + entityType: 'entityType', + memberOf: 'memberOf', + rootCollection: 'rootCollection', + metadataLicenseId: 'metadataLicenseId', + contentLicenseId: 'contentLicenseId', + fileId: 'fileId' +}; +export const FileOrderByRelevanceFieldEnum = { + fileId: 'fileId', + filename: 'filename', + mediaType: 'mediaType', + memberOf: 'memberOf', + rootCollection: 'rootCollection', + contentLicenseId: 'contentLicenseId' +}; +//# sourceMappingURL=prismaNamespaceBrowser.js.map \ No newline at end of file diff --git a/dist/generated/prisma/internal/prismaNamespaceBrowser.js.map b/dist/generated/prisma/internal/prismaNamespaceBrowser.js.map new file mode 100644 index 0000000..6a3f4fd --- /dev/null +++ b/dist/generated/prisma/internal/prismaNamespaceBrowser.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prismaNamespaceBrowser.js","sourceRoot":"","sources":["../../../../src/generated/prisma/internal/prismaNamespaceBrowser.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,OAAO,MAAM,sCAAsC,CAAA;AAK/D,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAA;AAGtC,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC,OAAO,CAAC,MAAmF;IAC5H,QAAQ,EAAE,OAAO,CAAC,gBAAgB,CAAC,OAAO,CAAC,QAAuF;IAClI,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAqF;CAChI,CAAA;AAMD,MAAM,CAAC,MAAM,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,MAAM,CAAA;AAM/D,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,QAAQ,CAAA;AAMnE,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,OAAO,CAAA;AAGjE,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,MAAM;CACJ,CAAA;AAQV,MAAM,CAAC,MAAM,yBAAyB,GAAG,OAAO,CAAC,cAAc,CAAC;IAC9D,eAAe,EAAE,iBAAiB;IAClC,aAAa,EAAE,eAAe;IAC9B,cAAc,EAAE,gBAAgB;IAChC,YAAY,EAAE,cAAc;CACpB,CAAC,CAAA;AAKX,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,EAAE,EAAE,IAAI;IACR,SAAS,EAAE,WAAW;IACtB,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,aAAa;IAC1B,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;IACpB,cAAc,EAAE,gBAAgB;IAChC,iBAAiB,EAAE,mBAAmB;IACtC,gBAAgB,EAAE,kBAAkB;IACpC,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,MAAM;IACZ,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;CACd,CAAA;AAKV,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,EAAE,EAAE,IAAI;IACR,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,WAAW;IACtB,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,UAAU;IACpB,cAAc,EAAE,gBAAgB;IAChC,gBAAgB,EAAE,kBAAkB;IACpC,IAAI,EAAE,MAAM;IACZ,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;CACd,CAAA;AAKV,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,MAAM;CACJ,CAAA;AAKV,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,QAAQ;CACV,CAAA;AAKV,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,OAAO;CACR,CAAA;AAKV,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,OAAO,EAAE,SAAS;IAClB,WAAW,EAAE,aAAa;CAClB,CAAA;AAKV,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;CACJ,CAAA;AAKV,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C,SAAS,EAAE,WAAW;IACtB,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,aAAa;IAC1B,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;IACpB,cAAc,EAAE,gBAAgB;IAChC,iBAAiB,EAAE,mBAAmB;IACtC,gBAAgB,EAAE,kBAAkB;IACpC,MAAM,EAAE,QAAQ;CACR,CAAA;AAKV,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;IACpB,cAAc,EAAE,gBAAgB;IAChC,gBAAgB,EAAE,kBAAkB;CAC5B,CAAA"} \ No newline at end of file diff --git a/dist/generated/prisma/models.d.ts b/dist/generated/prisma/models.d.ts new file mode 100644 index 0000000..5591e03 --- /dev/null +++ b/dist/generated/prisma/models.d.ts @@ -0,0 +1,3 @@ +export type * from './models/Entity.js'; +export type * from './models/File.js'; +export type * from './commonInputTypes.js'; diff --git a/dist/generated/prisma/models.js b/dist/generated/prisma/models.js new file mode 100644 index 0000000..5b8a5ab --- /dev/null +++ b/dist/generated/prisma/models.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=models.js.map \ No newline at end of file diff --git a/dist/generated/prisma/models.js.map b/dist/generated/prisma/models.js.map new file mode 100644 index 0000000..a9e2382 --- /dev/null +++ b/dist/generated/prisma/models.js.map @@ -0,0 +1 @@ +{"version":3,"file":"models.js","sourceRoot":"","sources":["../../../src/generated/prisma/models.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/generated/prisma/models/Entity.d.ts b/dist/generated/prisma/models/Entity.d.ts new file mode 100644 index 0000000..7326d40 --- /dev/null +++ b/dist/generated/prisma/models/Entity.d.ts @@ -0,0 +1,621 @@ +import type * as runtime from "@prisma/client/runtime/library"; +import type * as Prisma from "../internal/prismaNamespace.js"; +export type EntityModel = runtime.Types.Result.DefaultSelection; +export type AggregateEntity = { + _count: EntityCountAggregateOutputType | null; + _avg: EntityAvgAggregateOutputType | null; + _sum: EntitySumAggregateOutputType | null; + _min: EntityMinAggregateOutputType | null; + _max: EntityMaxAggregateOutputType | null; +}; +export type EntityAvgAggregateOutputType = { + id: number | null; +}; +export type EntitySumAggregateOutputType = { + id: number | null; +}; +export type EntityMinAggregateOutputType = { + id: number | null; + rocrateId: string | null; + name: string | null; + description: string | null; + entityType: string | null; + memberOf: string | null; + rootCollection: string | null; + metadataLicenseId: string | null; + contentLicenseId: string | null; + fileId: string | null; + createdAt: Date | null; + updatedAt: Date | null; +}; +export type EntityMaxAggregateOutputType = { + id: number | null; + rocrateId: string | null; + name: string | null; + description: string | null; + entityType: string | null; + memberOf: string | null; + rootCollection: string | null; + metadataLicenseId: string | null; + contentLicenseId: string | null; + fileId: string | null; + createdAt: Date | null; + updatedAt: Date | null; +}; +export type EntityCountAggregateOutputType = { + id: number; + rocrateId: number; + name: number; + description: number; + entityType: number; + memberOf: number; + rootCollection: number; + metadataLicenseId: number; + contentLicenseId: number; + fileId: number; + meta: number; + createdAt: number; + updatedAt: number; + _all: number; +}; +export type EntityAvgAggregateInputType = { + id?: true; +}; +export type EntitySumAggregateInputType = { + id?: true; +}; +export type EntityMinAggregateInputType = { + id?: true; + rocrateId?: true; + name?: true; + description?: true; + entityType?: true; + memberOf?: true; + rootCollection?: true; + metadataLicenseId?: true; + contentLicenseId?: true; + fileId?: true; + createdAt?: true; + updatedAt?: true; +}; +export type EntityMaxAggregateInputType = { + id?: true; + rocrateId?: true; + name?: true; + description?: true; + entityType?: true; + memberOf?: true; + rootCollection?: true; + metadataLicenseId?: true; + contentLicenseId?: true; + fileId?: true; + createdAt?: true; + updatedAt?: true; +}; +export type EntityCountAggregateInputType = { + id?: true; + rocrateId?: true; + name?: true; + description?: true; + entityType?: true; + memberOf?: true; + rootCollection?: true; + metadataLicenseId?: true; + contentLicenseId?: true; + fileId?: true; + meta?: true; + createdAt?: true; + updatedAt?: true; + _all?: true; +}; +export type EntityAggregateArgs = { + where?: Prisma.EntityWhereInput; + orderBy?: Prisma.EntityOrderByWithRelationInput | Prisma.EntityOrderByWithRelationInput[]; + cursor?: Prisma.EntityWhereUniqueInput; + take?: number; + skip?: number; + _count?: true | EntityCountAggregateInputType; + _avg?: EntityAvgAggregateInputType; + _sum?: EntitySumAggregateInputType; + _min?: EntityMinAggregateInputType; + _max?: EntityMaxAggregateInputType; +}; +export type GetEntityAggregateType = { + [P in keyof T & keyof AggregateEntity]: P extends '_count' | 'count' ? T[P] extends true ? number : Prisma.GetScalarType : Prisma.GetScalarType; +}; +export type EntityGroupByArgs = { + where?: Prisma.EntityWhereInput; + orderBy?: Prisma.EntityOrderByWithAggregationInput | Prisma.EntityOrderByWithAggregationInput[]; + by: Prisma.EntityScalarFieldEnum[] | Prisma.EntityScalarFieldEnum; + having?: Prisma.EntityScalarWhereWithAggregatesInput; + take?: number; + skip?: number; + _count?: EntityCountAggregateInputType | true; + _avg?: EntityAvgAggregateInputType; + _sum?: EntitySumAggregateInputType; + _min?: EntityMinAggregateInputType; + _max?: EntityMaxAggregateInputType; +}; +export type EntityGroupByOutputType = { + id: number; + rocrateId: string; + name: string; + description: string; + entityType: string; + memberOf: string | null; + rootCollection: string | null; + metadataLicenseId: string; + contentLicenseId: string; + fileId: string | null; + meta: unknown | null; + createdAt: Date; + updatedAt: Date; + _count: EntityCountAggregateOutputType | null; + _avg: EntityAvgAggregateOutputType | null; + _sum: EntitySumAggregateOutputType | null; + _min: EntityMinAggregateOutputType | null; + _max: EntityMaxAggregateOutputType | null; +}; +type GetEntityGroupByPayload = Prisma.PrismaPromise & { + [P in ((keyof T) & (keyof EntityGroupByOutputType))]: P extends '_count' ? T[P] extends boolean ? number : Prisma.GetScalarType : Prisma.GetScalarType; +}>>; +export type EntityWhereInput = { + AND?: Prisma.EntityWhereInput | Prisma.EntityWhereInput[]; + OR?: Prisma.EntityWhereInput[]; + NOT?: Prisma.EntityWhereInput | Prisma.EntityWhereInput[]; + id?: Prisma.IntFilter<"Entity"> | number; + rocrateId?: Prisma.StringFilter<"Entity"> | string; + name?: Prisma.StringFilter<"Entity"> | string; + description?: Prisma.StringFilter<"Entity"> | string; + entityType?: Prisma.StringFilter<"Entity"> | string; + memberOf?: Prisma.StringNullableFilter<"Entity"> | string | null; + rootCollection?: Prisma.StringNullableFilter<"Entity"> | string | null; + metadataLicenseId?: Prisma.StringFilter<"Entity"> | string; + contentLicenseId?: Prisma.StringFilter<"Entity"> | string; + fileId?: Prisma.StringNullableFilter<"Entity"> | string | null; + meta?: Prisma.JsonNullableFilter<"Entity">; + createdAt?: Prisma.DateTimeFilter<"Entity"> | Date | string; + updatedAt?: Prisma.DateTimeFilter<"Entity"> | Date | string; +}; +export type EntityOrderByWithRelationInput = { + id?: Prisma.SortOrder; + rocrateId?: Prisma.SortOrder; + name?: Prisma.SortOrder; + description?: Prisma.SortOrder; + entityType?: Prisma.SortOrder; + memberOf?: Prisma.SortOrderInput | Prisma.SortOrder; + rootCollection?: Prisma.SortOrderInput | Prisma.SortOrder; + metadataLicenseId?: Prisma.SortOrder; + contentLicenseId?: Prisma.SortOrder; + fileId?: Prisma.SortOrderInput | Prisma.SortOrder; + meta?: Prisma.SortOrderInput | Prisma.SortOrder; + createdAt?: Prisma.SortOrder; + updatedAt?: Prisma.SortOrder; + _relevance?: Prisma.EntityOrderByRelevanceInput; +}; +export type EntityWhereUniqueInput = Prisma.AtLeast<{ + id?: number; + AND?: Prisma.EntityWhereInput | Prisma.EntityWhereInput[]; + OR?: Prisma.EntityWhereInput[]; + NOT?: Prisma.EntityWhereInput | Prisma.EntityWhereInput[]; + rocrateId?: Prisma.StringFilter<"Entity"> | string; + name?: Prisma.StringFilter<"Entity"> | string; + description?: Prisma.StringFilter<"Entity"> | string; + entityType?: Prisma.StringFilter<"Entity"> | string; + memberOf?: Prisma.StringNullableFilter<"Entity"> | string | null; + rootCollection?: Prisma.StringNullableFilter<"Entity"> | string | null; + metadataLicenseId?: Prisma.StringFilter<"Entity"> | string; + contentLicenseId?: Prisma.StringFilter<"Entity"> | string; + fileId?: Prisma.StringNullableFilter<"Entity"> | string | null; + meta?: Prisma.JsonNullableFilter<"Entity">; + createdAt?: Prisma.DateTimeFilter<"Entity"> | Date | string; + updatedAt?: Prisma.DateTimeFilter<"Entity"> | Date | string; +}, "id">; +export type EntityOrderByWithAggregationInput = { + id?: Prisma.SortOrder; + rocrateId?: Prisma.SortOrder; + name?: Prisma.SortOrder; + description?: Prisma.SortOrder; + entityType?: Prisma.SortOrder; + memberOf?: Prisma.SortOrderInput | Prisma.SortOrder; + rootCollection?: Prisma.SortOrderInput | Prisma.SortOrder; + metadataLicenseId?: Prisma.SortOrder; + contentLicenseId?: Prisma.SortOrder; + fileId?: Prisma.SortOrderInput | Prisma.SortOrder; + meta?: Prisma.SortOrderInput | Prisma.SortOrder; + createdAt?: Prisma.SortOrder; + updatedAt?: Prisma.SortOrder; + _count?: Prisma.EntityCountOrderByAggregateInput; + _avg?: Prisma.EntityAvgOrderByAggregateInput; + _max?: Prisma.EntityMaxOrderByAggregateInput; + _min?: Prisma.EntityMinOrderByAggregateInput; + _sum?: Prisma.EntitySumOrderByAggregateInput; +}; +export type EntityScalarWhereWithAggregatesInput = { + AND?: Prisma.EntityScalarWhereWithAggregatesInput | Prisma.EntityScalarWhereWithAggregatesInput[]; + OR?: Prisma.EntityScalarWhereWithAggregatesInput[]; + NOT?: Prisma.EntityScalarWhereWithAggregatesInput | Prisma.EntityScalarWhereWithAggregatesInput[]; + id?: Prisma.IntWithAggregatesFilter<"Entity"> | number; + rocrateId?: Prisma.StringWithAggregatesFilter<"Entity"> | string; + name?: Prisma.StringWithAggregatesFilter<"Entity"> | string; + description?: Prisma.StringWithAggregatesFilter<"Entity"> | string; + entityType?: Prisma.StringWithAggregatesFilter<"Entity"> | string; + memberOf?: Prisma.StringNullableWithAggregatesFilter<"Entity"> | string | null; + rootCollection?: Prisma.StringNullableWithAggregatesFilter<"Entity"> | string | null; + metadataLicenseId?: Prisma.StringWithAggregatesFilter<"Entity"> | string; + contentLicenseId?: Prisma.StringWithAggregatesFilter<"Entity"> | string; + fileId?: Prisma.StringNullableWithAggregatesFilter<"Entity"> | string | null; + meta?: Prisma.JsonNullableWithAggregatesFilter<"Entity">; + createdAt?: Prisma.DateTimeWithAggregatesFilter<"Entity"> | Date | string; + updatedAt?: Prisma.DateTimeWithAggregatesFilter<"Entity"> | Date | string; +}; +export type EntityCreateInput = { + rocrateId: string; + name: string; + description: string; + entityType: string; + memberOf?: string | null; + rootCollection?: string | null; + metadataLicenseId: string; + contentLicenseId: string; + fileId?: string | null; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Date | string; + updatedAt?: Date | string; +}; +export type EntityUncheckedCreateInput = { + id?: number; + rocrateId: string; + name: string; + description: string; + entityType: string; + memberOf?: string | null; + rootCollection?: string | null; + metadataLicenseId: string; + contentLicenseId: string; + fileId?: string | null; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Date | string; + updatedAt?: Date | string; +}; +export type EntityUpdateInput = { + rocrateId?: Prisma.StringFieldUpdateOperationsInput | string; + name?: Prisma.StringFieldUpdateOperationsInput | string; + description?: Prisma.StringFieldUpdateOperationsInput | string; + entityType?: Prisma.StringFieldUpdateOperationsInput | string; + memberOf?: Prisma.NullableStringFieldUpdateOperationsInput | string | null; + rootCollection?: Prisma.NullableStringFieldUpdateOperationsInput | string | null; + metadataLicenseId?: Prisma.StringFieldUpdateOperationsInput | string; + contentLicenseId?: Prisma.StringFieldUpdateOperationsInput | string; + fileId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; +}; +export type EntityUncheckedUpdateInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number; + rocrateId?: Prisma.StringFieldUpdateOperationsInput | string; + name?: Prisma.StringFieldUpdateOperationsInput | string; + description?: Prisma.StringFieldUpdateOperationsInput | string; + entityType?: Prisma.StringFieldUpdateOperationsInput | string; + memberOf?: Prisma.NullableStringFieldUpdateOperationsInput | string | null; + rootCollection?: Prisma.NullableStringFieldUpdateOperationsInput | string | null; + metadataLicenseId?: Prisma.StringFieldUpdateOperationsInput | string; + contentLicenseId?: Prisma.StringFieldUpdateOperationsInput | string; + fileId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; +}; +export type EntityCreateManyInput = { + id?: number; + rocrateId: string; + name: string; + description: string; + entityType: string; + memberOf?: string | null; + rootCollection?: string | null; + metadataLicenseId: string; + contentLicenseId: string; + fileId?: string | null; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Date | string; + updatedAt?: Date | string; +}; +export type EntityUpdateManyMutationInput = { + rocrateId?: Prisma.StringFieldUpdateOperationsInput | string; + name?: Prisma.StringFieldUpdateOperationsInput | string; + description?: Prisma.StringFieldUpdateOperationsInput | string; + entityType?: Prisma.StringFieldUpdateOperationsInput | string; + memberOf?: Prisma.NullableStringFieldUpdateOperationsInput | string | null; + rootCollection?: Prisma.NullableStringFieldUpdateOperationsInput | string | null; + metadataLicenseId?: Prisma.StringFieldUpdateOperationsInput | string; + contentLicenseId?: Prisma.StringFieldUpdateOperationsInput | string; + fileId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; +}; +export type EntityUncheckedUpdateManyInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number; + rocrateId?: Prisma.StringFieldUpdateOperationsInput | string; + name?: Prisma.StringFieldUpdateOperationsInput | string; + description?: Prisma.StringFieldUpdateOperationsInput | string; + entityType?: Prisma.StringFieldUpdateOperationsInput | string; + memberOf?: Prisma.NullableStringFieldUpdateOperationsInput | string | null; + rootCollection?: Prisma.NullableStringFieldUpdateOperationsInput | string | null; + metadataLicenseId?: Prisma.StringFieldUpdateOperationsInput | string; + contentLicenseId?: Prisma.StringFieldUpdateOperationsInput | string; + fileId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; +}; +export type EntityOrderByRelevanceInput = { + fields: Prisma.EntityOrderByRelevanceFieldEnum | Prisma.EntityOrderByRelevanceFieldEnum[]; + sort: Prisma.SortOrder; + search: string; +}; +export type EntityCountOrderByAggregateInput = { + id?: Prisma.SortOrder; + rocrateId?: Prisma.SortOrder; + name?: Prisma.SortOrder; + description?: Prisma.SortOrder; + entityType?: Prisma.SortOrder; + memberOf?: Prisma.SortOrder; + rootCollection?: Prisma.SortOrder; + metadataLicenseId?: Prisma.SortOrder; + contentLicenseId?: Prisma.SortOrder; + fileId?: Prisma.SortOrder; + meta?: Prisma.SortOrder; + createdAt?: Prisma.SortOrder; + updatedAt?: Prisma.SortOrder; +}; +export type EntityAvgOrderByAggregateInput = { + id?: Prisma.SortOrder; +}; +export type EntityMaxOrderByAggregateInput = { + id?: Prisma.SortOrder; + rocrateId?: Prisma.SortOrder; + name?: Prisma.SortOrder; + description?: Prisma.SortOrder; + entityType?: Prisma.SortOrder; + memberOf?: Prisma.SortOrder; + rootCollection?: Prisma.SortOrder; + metadataLicenseId?: Prisma.SortOrder; + contentLicenseId?: Prisma.SortOrder; + fileId?: Prisma.SortOrder; + createdAt?: Prisma.SortOrder; + updatedAt?: Prisma.SortOrder; +}; +export type EntityMinOrderByAggregateInput = { + id?: Prisma.SortOrder; + rocrateId?: Prisma.SortOrder; + name?: Prisma.SortOrder; + description?: Prisma.SortOrder; + entityType?: Prisma.SortOrder; + memberOf?: Prisma.SortOrder; + rootCollection?: Prisma.SortOrder; + metadataLicenseId?: Prisma.SortOrder; + contentLicenseId?: Prisma.SortOrder; + fileId?: Prisma.SortOrder; + createdAt?: Prisma.SortOrder; + updatedAt?: Prisma.SortOrder; +}; +export type EntitySumOrderByAggregateInput = { + id?: Prisma.SortOrder; +}; +export type StringFieldUpdateOperationsInput = { + set?: string; +}; +export type NullableStringFieldUpdateOperationsInput = { + set?: string | null; +}; +export type DateTimeFieldUpdateOperationsInput = { + set?: Date | string; +}; +export type IntFieldUpdateOperationsInput = { + set?: number; + increment?: number; + decrement?: number; + multiply?: number; + divide?: number; +}; +export type EntitySelect = runtime.Types.Extensions.GetSelect<{ + id?: boolean; + rocrateId?: boolean; + name?: boolean; + description?: boolean; + entityType?: boolean; + memberOf?: boolean; + rootCollection?: boolean; + metadataLicenseId?: boolean; + contentLicenseId?: boolean; + fileId?: boolean; + meta?: boolean; + createdAt?: boolean; + updatedAt?: boolean; +}, ExtArgs["result"]["entity"]>; +export type EntitySelectScalar = { + id?: boolean; + rocrateId?: boolean; + name?: boolean; + description?: boolean; + entityType?: boolean; + memberOf?: boolean; + rootCollection?: boolean; + metadataLicenseId?: boolean; + contentLicenseId?: boolean; + fileId?: boolean; + meta?: boolean; + createdAt?: boolean; + updatedAt?: boolean; +}; +export type EntityOmit = runtime.Types.Extensions.GetOmit<"id" | "rocrateId" | "name" | "description" | "entityType" | "memberOf" | "rootCollection" | "metadataLicenseId" | "contentLicenseId" | "fileId" | "meta" | "createdAt" | "updatedAt", ExtArgs["result"]["entity"]>; +export type $EntityPayload = { + name: "Entity"; + objects: {}; + scalars: runtime.Types.Extensions.GetPayloadResult<{ + id: number; + rocrateId: string; + name: string; + description: string; + entityType: string; + memberOf: string | null; + rootCollection: string | null; + metadataLicenseId: string; + contentLicenseId: string; + fileId: string | null; + meta: unknown | null; + createdAt: Date; + updatedAt: Date; + }, ExtArgs["result"]["entity"]>; + composites: {}; +}; +export type EntityGetPayload = runtime.Types.Result.GetResult; +export type EntityCountArgs = Omit & { + select?: EntityCountAggregateInputType | true; +}; +export interface EntityDelegate { + [K: symbol]: { + types: Prisma.TypeMap['model']['Entity']; + meta: { + name: 'Entity'; + }; + }; + findUnique(args: Prisma.SelectSubset>): Prisma.Prisma__EntityClient, T, "findUnique", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>; + findUniqueOrThrow(args: Prisma.SelectSubset>): Prisma.Prisma__EntityClient, T, "findUniqueOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions>; + findFirst(args?: Prisma.SelectSubset>): Prisma.Prisma__EntityClient, T, "findFirst", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>; + findFirstOrThrow(args?: Prisma.SelectSubset>): Prisma.Prisma__EntityClient, T, "findFirstOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions>; + findMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions>>; + create(args: Prisma.SelectSubset>): Prisma.Prisma__EntityClient, T, "create", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions>; + createMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise; + delete(args: Prisma.SelectSubset>): Prisma.Prisma__EntityClient, T, "delete", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions>; + update(args: Prisma.SelectSubset>): Prisma.Prisma__EntityClient, T, "update", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions>; + deleteMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise; + updateMany(args: Prisma.SelectSubset>): Prisma.PrismaPromise; + upsert(args: Prisma.SelectSubset>): Prisma.Prisma__EntityClient, T, "upsert", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions>; + count(args?: Prisma.Subset): Prisma.PrismaPromise ? T['select'] extends true ? number : Prisma.GetScalarType : number>; + aggregate(args: Prisma.Subset): Prisma.PrismaPromise>; + groupBy>, Prisma.Extends<'take', Prisma.Keys>>, OrderByArg extends Prisma.True extends HasSelectOrTake ? { + orderBy: EntityGroupByArgs['orderBy']; + } : { + orderBy?: EntityGroupByArgs['orderBy']; + }, OrderFields extends Prisma.ExcludeUnderscoreKeys>>, ByFields extends Prisma.MaybeTupleToUnion, ByValid extends Prisma.Has, HavingFields extends Prisma.GetHavingFields, HavingValid extends Prisma.Has, ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, InputErrors extends ByEmpty extends Prisma.True ? `Error: "by" must not be empty.` : HavingValid extends Prisma.False ? { + [P in HavingFields]: P extends ByFields ? never : P extends string ? `Error: Field "${P}" used in "having" needs to be provided in "by".` : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"` + ]; + }[HavingFields] : 'take' extends Prisma.Keys ? 'orderBy' extends Prisma.Keys ? ByValid extends Prisma.True ? {} : { + [P in OrderFields]: P extends ByFields ? never : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`; + }[OrderFields] : 'Error: If you provide "take", you also need to provide "orderBy"' : 'skip' extends Prisma.Keys ? 'orderBy' extends Prisma.Keys ? ByValid extends Prisma.True ? {} : { + [P in OrderFields]: P extends ByFields ? never : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`; + }[OrderFields] : 'Error: If you provide "skip", you also need to provide "orderBy"' : ByValid extends Prisma.True ? {} : { + [P in OrderFields]: P extends ByFields ? never : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`; + }[OrderFields]>(args: Prisma.SubsetIntersection & InputErrors): {} extends InputErrors ? GetEntityGroupByPayload : Prisma.PrismaPromise; + readonly fields: EntityFieldRefs; +} +export interface Prisma__EntityClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise"; + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise; + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise; + finally(onfinally?: (() => void) | undefined | null): runtime.Types.Utils.JsPromise; +} +export interface EntityFieldRefs { + readonly id: Prisma.FieldRef<"Entity", 'Int'>; + readonly rocrateId: Prisma.FieldRef<"Entity", 'String'>; + readonly name: Prisma.FieldRef<"Entity", 'String'>; + readonly description: Prisma.FieldRef<"Entity", 'String'>; + readonly entityType: Prisma.FieldRef<"Entity", 'String'>; + readonly memberOf: Prisma.FieldRef<"Entity", 'String'>; + readonly rootCollection: Prisma.FieldRef<"Entity", 'String'>; + readonly metadataLicenseId: Prisma.FieldRef<"Entity", 'String'>; + readonly contentLicenseId: Prisma.FieldRef<"Entity", 'String'>; + readonly fileId: Prisma.FieldRef<"Entity", 'String'>; + readonly meta: Prisma.FieldRef<"Entity", 'Json'>; + readonly createdAt: Prisma.FieldRef<"Entity", 'DateTime'>; + readonly updatedAt: Prisma.FieldRef<"Entity", 'DateTime'>; +} +export type EntityFindUniqueArgs = { + select?: Prisma.EntitySelect | null; + omit?: Prisma.EntityOmit | null; + where: Prisma.EntityWhereUniqueInput; +}; +export type EntityFindUniqueOrThrowArgs = { + select?: Prisma.EntitySelect | null; + omit?: Prisma.EntityOmit | null; + where: Prisma.EntityWhereUniqueInput; +}; +export type EntityFindFirstArgs = { + select?: Prisma.EntitySelect | null; + omit?: Prisma.EntityOmit | null; + where?: Prisma.EntityWhereInput; + orderBy?: Prisma.EntityOrderByWithRelationInput | Prisma.EntityOrderByWithRelationInput[]; + cursor?: Prisma.EntityWhereUniqueInput; + take?: number; + skip?: number; + distinct?: Prisma.EntityScalarFieldEnum | Prisma.EntityScalarFieldEnum[]; +}; +export type EntityFindFirstOrThrowArgs = { + select?: Prisma.EntitySelect | null; + omit?: Prisma.EntityOmit | null; + where?: Prisma.EntityWhereInput; + orderBy?: Prisma.EntityOrderByWithRelationInput | Prisma.EntityOrderByWithRelationInput[]; + cursor?: Prisma.EntityWhereUniqueInput; + take?: number; + skip?: number; + distinct?: Prisma.EntityScalarFieldEnum | Prisma.EntityScalarFieldEnum[]; +}; +export type EntityFindManyArgs = { + select?: Prisma.EntitySelect | null; + omit?: Prisma.EntityOmit | null; + where?: Prisma.EntityWhereInput; + orderBy?: Prisma.EntityOrderByWithRelationInput | Prisma.EntityOrderByWithRelationInput[]; + cursor?: Prisma.EntityWhereUniqueInput; + take?: number; + skip?: number; + distinct?: Prisma.EntityScalarFieldEnum | Prisma.EntityScalarFieldEnum[]; +}; +export type EntityCreateArgs = { + select?: Prisma.EntitySelect | null; + omit?: Prisma.EntityOmit | null; + data: Prisma.XOR; +}; +export type EntityCreateManyArgs = { + data: Prisma.EntityCreateManyInput | Prisma.EntityCreateManyInput[]; + skipDuplicates?: boolean; +}; +export type EntityUpdateArgs = { + select?: Prisma.EntitySelect | null; + omit?: Prisma.EntityOmit | null; + data: Prisma.XOR; + where: Prisma.EntityWhereUniqueInput; +}; +export type EntityUpdateManyArgs = { + data: Prisma.XOR; + where?: Prisma.EntityWhereInput; + limit?: number; +}; +export type EntityUpsertArgs = { + select?: Prisma.EntitySelect | null; + omit?: Prisma.EntityOmit | null; + where: Prisma.EntityWhereUniqueInput; + create: Prisma.XOR; + update: Prisma.XOR; +}; +export type EntityDeleteArgs = { + select?: Prisma.EntitySelect | null; + omit?: Prisma.EntityOmit | null; + where: Prisma.EntityWhereUniqueInput; +}; +export type EntityDeleteManyArgs = { + where?: Prisma.EntityWhereInput; + limit?: number; +}; +export type EntityDefaultArgs = { + select?: Prisma.EntitySelect | null; + omit?: Prisma.EntityOmit | null; +}; +export {}; diff --git a/dist/generated/prisma/models/Entity.js b/dist/generated/prisma/models/Entity.js new file mode 100644 index 0000000..9c9ecc3 --- /dev/null +++ b/dist/generated/prisma/models/Entity.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=Entity.js.map \ No newline at end of file diff --git a/dist/generated/prisma/models/Entity.js.map b/dist/generated/prisma/models/Entity.js.map new file mode 100644 index 0000000..7f974d7 --- /dev/null +++ b/dist/generated/prisma/models/Entity.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Entity.js","sourceRoot":"","sources":["../../../../src/generated/prisma/models/Entity.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/generated/prisma/models/File.d.ts b/dist/generated/prisma/models/File.d.ts new file mode 100644 index 0000000..f5db41f --- /dev/null +++ b/dist/generated/prisma/models/File.d.ts @@ -0,0 +1,566 @@ +import type * as runtime from "@prisma/client/runtime/library"; +import type * as Prisma from "../internal/prismaNamespace.js"; +export type FileModel = runtime.Types.Result.DefaultSelection; +export type AggregateFile = { + _count: FileCountAggregateOutputType | null; + _avg: FileAvgAggregateOutputType | null; + _sum: FileSumAggregateOutputType | null; + _min: FileMinAggregateOutputType | null; + _max: FileMaxAggregateOutputType | null; +}; +export type FileAvgAggregateOutputType = { + id: number | null; + size: number | null; +}; +export type FileSumAggregateOutputType = { + id: number | null; + size: bigint | null; +}; +export type FileMinAggregateOutputType = { + id: number | null; + fileId: string | null; + filename: string | null; + mediaType: string | null; + size: bigint | null; + memberOf: string | null; + rootCollection: string | null; + contentLicenseId: string | null; + createdAt: Date | null; + updatedAt: Date | null; +}; +export type FileMaxAggregateOutputType = { + id: number | null; + fileId: string | null; + filename: string | null; + mediaType: string | null; + size: bigint | null; + memberOf: string | null; + rootCollection: string | null; + contentLicenseId: string | null; + createdAt: Date | null; + updatedAt: Date | null; +}; +export type FileCountAggregateOutputType = { + id: number; + fileId: number; + filename: number; + mediaType: number; + size: number; + memberOf: number; + rootCollection: number; + contentLicenseId: number; + meta: number; + createdAt: number; + updatedAt: number; + _all: number; +}; +export type FileAvgAggregateInputType = { + id?: true; + size?: true; +}; +export type FileSumAggregateInputType = { + id?: true; + size?: true; +}; +export type FileMinAggregateInputType = { + id?: true; + fileId?: true; + filename?: true; + mediaType?: true; + size?: true; + memberOf?: true; + rootCollection?: true; + contentLicenseId?: true; + createdAt?: true; + updatedAt?: true; +}; +export type FileMaxAggregateInputType = { + id?: true; + fileId?: true; + filename?: true; + mediaType?: true; + size?: true; + memberOf?: true; + rootCollection?: true; + contentLicenseId?: true; + createdAt?: true; + updatedAt?: true; +}; +export type FileCountAggregateInputType = { + id?: true; + fileId?: true; + filename?: true; + mediaType?: true; + size?: true; + memberOf?: true; + rootCollection?: true; + contentLicenseId?: true; + meta?: true; + createdAt?: true; + updatedAt?: true; + _all?: true; +}; +export type FileAggregateArgs = { + where?: Prisma.FileWhereInput; + orderBy?: Prisma.FileOrderByWithRelationInput | Prisma.FileOrderByWithRelationInput[]; + cursor?: Prisma.FileWhereUniqueInput; + take?: number; + skip?: number; + _count?: true | FileCountAggregateInputType; + _avg?: FileAvgAggregateInputType; + _sum?: FileSumAggregateInputType; + _min?: FileMinAggregateInputType; + _max?: FileMaxAggregateInputType; +}; +export type GetFileAggregateType = { + [P in keyof T & keyof AggregateFile]: P extends '_count' | 'count' ? T[P] extends true ? number : Prisma.GetScalarType : Prisma.GetScalarType; +}; +export type FileGroupByArgs = { + where?: Prisma.FileWhereInput; + orderBy?: Prisma.FileOrderByWithAggregationInput | Prisma.FileOrderByWithAggregationInput[]; + by: Prisma.FileScalarFieldEnum[] | Prisma.FileScalarFieldEnum; + having?: Prisma.FileScalarWhereWithAggregatesInput; + take?: number; + skip?: number; + _count?: FileCountAggregateInputType | true; + _avg?: FileAvgAggregateInputType; + _sum?: FileSumAggregateInputType; + _min?: FileMinAggregateInputType; + _max?: FileMaxAggregateInputType; +}; +export type FileGroupByOutputType = { + id: number; + fileId: string; + filename: string; + mediaType: string; + size: bigint; + memberOf: string; + rootCollection: string; + contentLicenseId: string; + meta: unknown | null; + createdAt: Date; + updatedAt: Date; + _count: FileCountAggregateOutputType | null; + _avg: FileAvgAggregateOutputType | null; + _sum: FileSumAggregateOutputType | null; + _min: FileMinAggregateOutputType | null; + _max: FileMaxAggregateOutputType | null; +}; +type GetFileGroupByPayload = Prisma.PrismaPromise & { + [P in ((keyof T) & (keyof FileGroupByOutputType))]: P extends '_count' ? T[P] extends boolean ? number : Prisma.GetScalarType : Prisma.GetScalarType; +}>>; +export type FileWhereInput = { + AND?: Prisma.FileWhereInput | Prisma.FileWhereInput[]; + OR?: Prisma.FileWhereInput[]; + NOT?: Prisma.FileWhereInput | Prisma.FileWhereInput[]; + id?: Prisma.IntFilter<"File"> | number; + fileId?: Prisma.StringFilter<"File"> | string; + filename?: Prisma.StringFilter<"File"> | string; + mediaType?: Prisma.StringFilter<"File"> | string; + size?: Prisma.BigIntFilter<"File"> | bigint | number; + memberOf?: Prisma.StringFilter<"File"> | string; + rootCollection?: Prisma.StringFilter<"File"> | string; + contentLicenseId?: Prisma.StringFilter<"File"> | string; + meta?: Prisma.JsonNullableFilter<"File">; + createdAt?: Prisma.DateTimeFilter<"File"> | Date | string; + updatedAt?: Prisma.DateTimeFilter<"File"> | Date | string; +}; +export type FileOrderByWithRelationInput = { + id?: Prisma.SortOrder; + fileId?: Prisma.SortOrder; + filename?: Prisma.SortOrder; + mediaType?: Prisma.SortOrder; + size?: Prisma.SortOrder; + memberOf?: Prisma.SortOrder; + rootCollection?: Prisma.SortOrder; + contentLicenseId?: Prisma.SortOrder; + meta?: Prisma.SortOrderInput | Prisma.SortOrder; + createdAt?: Prisma.SortOrder; + updatedAt?: Prisma.SortOrder; + _relevance?: Prisma.FileOrderByRelevanceInput; +}; +export type FileWhereUniqueInput = Prisma.AtLeast<{ + id?: number; + fileId?: string; + AND?: Prisma.FileWhereInput | Prisma.FileWhereInput[]; + OR?: Prisma.FileWhereInput[]; + NOT?: Prisma.FileWhereInput | Prisma.FileWhereInput[]; + filename?: Prisma.StringFilter<"File"> | string; + mediaType?: Prisma.StringFilter<"File"> | string; + size?: Prisma.BigIntFilter<"File"> | bigint | number; + memberOf?: Prisma.StringFilter<"File"> | string; + rootCollection?: Prisma.StringFilter<"File"> | string; + contentLicenseId?: Prisma.StringFilter<"File"> | string; + meta?: Prisma.JsonNullableFilter<"File">; + createdAt?: Prisma.DateTimeFilter<"File"> | Date | string; + updatedAt?: Prisma.DateTimeFilter<"File"> | Date | string; +}, "id" | "fileId">; +export type FileOrderByWithAggregationInput = { + id?: Prisma.SortOrder; + fileId?: Prisma.SortOrder; + filename?: Prisma.SortOrder; + mediaType?: Prisma.SortOrder; + size?: Prisma.SortOrder; + memberOf?: Prisma.SortOrder; + rootCollection?: Prisma.SortOrder; + contentLicenseId?: Prisma.SortOrder; + meta?: Prisma.SortOrderInput | Prisma.SortOrder; + createdAt?: Prisma.SortOrder; + updatedAt?: Prisma.SortOrder; + _count?: Prisma.FileCountOrderByAggregateInput; + _avg?: Prisma.FileAvgOrderByAggregateInput; + _max?: Prisma.FileMaxOrderByAggregateInput; + _min?: Prisma.FileMinOrderByAggregateInput; + _sum?: Prisma.FileSumOrderByAggregateInput; +}; +export type FileScalarWhereWithAggregatesInput = { + AND?: Prisma.FileScalarWhereWithAggregatesInput | Prisma.FileScalarWhereWithAggregatesInput[]; + OR?: Prisma.FileScalarWhereWithAggregatesInput[]; + NOT?: Prisma.FileScalarWhereWithAggregatesInput | Prisma.FileScalarWhereWithAggregatesInput[]; + id?: Prisma.IntWithAggregatesFilter<"File"> | number; + fileId?: Prisma.StringWithAggregatesFilter<"File"> | string; + filename?: Prisma.StringWithAggregatesFilter<"File"> | string; + mediaType?: Prisma.StringWithAggregatesFilter<"File"> | string; + size?: Prisma.BigIntWithAggregatesFilter<"File"> | bigint | number; + memberOf?: Prisma.StringWithAggregatesFilter<"File"> | string; + rootCollection?: Prisma.StringWithAggregatesFilter<"File"> | string; + contentLicenseId?: Prisma.StringWithAggregatesFilter<"File"> | string; + meta?: Prisma.JsonNullableWithAggregatesFilter<"File">; + createdAt?: Prisma.DateTimeWithAggregatesFilter<"File"> | Date | string; + updatedAt?: Prisma.DateTimeWithAggregatesFilter<"File"> | Date | string; +}; +export type FileCreateInput = { + fileId: string; + filename: string; + mediaType: string; + size: bigint | number; + memberOf: string; + rootCollection: string; + contentLicenseId: string; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Date | string; + updatedAt?: Date | string; +}; +export type FileUncheckedCreateInput = { + id?: number; + fileId: string; + filename: string; + mediaType: string; + size: bigint | number; + memberOf: string; + rootCollection: string; + contentLicenseId: string; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Date | string; + updatedAt?: Date | string; +}; +export type FileUpdateInput = { + fileId?: Prisma.StringFieldUpdateOperationsInput | string; + filename?: Prisma.StringFieldUpdateOperationsInput | string; + mediaType?: Prisma.StringFieldUpdateOperationsInput | string; + size?: Prisma.BigIntFieldUpdateOperationsInput | bigint | number; + memberOf?: Prisma.StringFieldUpdateOperationsInput | string; + rootCollection?: Prisma.StringFieldUpdateOperationsInput | string; + contentLicenseId?: Prisma.StringFieldUpdateOperationsInput | string; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; +}; +export type FileUncheckedUpdateInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number; + fileId?: Prisma.StringFieldUpdateOperationsInput | string; + filename?: Prisma.StringFieldUpdateOperationsInput | string; + mediaType?: Prisma.StringFieldUpdateOperationsInput | string; + size?: Prisma.BigIntFieldUpdateOperationsInput | bigint | number; + memberOf?: Prisma.StringFieldUpdateOperationsInput | string; + rootCollection?: Prisma.StringFieldUpdateOperationsInput | string; + contentLicenseId?: Prisma.StringFieldUpdateOperationsInput | string; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; +}; +export type FileCreateManyInput = { + id?: number; + fileId: string; + filename: string; + mediaType: string; + size: bigint | number; + memberOf: string; + rootCollection: string; + contentLicenseId: string; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Date | string; + updatedAt?: Date | string; +}; +export type FileUpdateManyMutationInput = { + fileId?: Prisma.StringFieldUpdateOperationsInput | string; + filename?: Prisma.StringFieldUpdateOperationsInput | string; + mediaType?: Prisma.StringFieldUpdateOperationsInput | string; + size?: Prisma.BigIntFieldUpdateOperationsInput | bigint | number; + memberOf?: Prisma.StringFieldUpdateOperationsInput | string; + rootCollection?: Prisma.StringFieldUpdateOperationsInput | string; + contentLicenseId?: Prisma.StringFieldUpdateOperationsInput | string; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; +}; +export type FileUncheckedUpdateManyInput = { + id?: Prisma.IntFieldUpdateOperationsInput | number; + fileId?: Prisma.StringFieldUpdateOperationsInput | string; + filename?: Prisma.StringFieldUpdateOperationsInput | string; + mediaType?: Prisma.StringFieldUpdateOperationsInput | string; + size?: Prisma.BigIntFieldUpdateOperationsInput | bigint | number; + memberOf?: Prisma.StringFieldUpdateOperationsInput | string; + rootCollection?: Prisma.StringFieldUpdateOperationsInput | string; + contentLicenseId?: Prisma.StringFieldUpdateOperationsInput | string; + meta?: unknown | Prisma.NullableJsonNullValueInput; + createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; + updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string; +}; +export type FileOrderByRelevanceInput = { + fields: Prisma.FileOrderByRelevanceFieldEnum | Prisma.FileOrderByRelevanceFieldEnum[]; + sort: Prisma.SortOrder; + search: string; +}; +export type FileCountOrderByAggregateInput = { + id?: Prisma.SortOrder; + fileId?: Prisma.SortOrder; + filename?: Prisma.SortOrder; + mediaType?: Prisma.SortOrder; + size?: Prisma.SortOrder; + memberOf?: Prisma.SortOrder; + rootCollection?: Prisma.SortOrder; + contentLicenseId?: Prisma.SortOrder; + meta?: Prisma.SortOrder; + createdAt?: Prisma.SortOrder; + updatedAt?: Prisma.SortOrder; +}; +export type FileAvgOrderByAggregateInput = { + id?: Prisma.SortOrder; + size?: Prisma.SortOrder; +}; +export type FileMaxOrderByAggregateInput = { + id?: Prisma.SortOrder; + fileId?: Prisma.SortOrder; + filename?: Prisma.SortOrder; + mediaType?: Prisma.SortOrder; + size?: Prisma.SortOrder; + memberOf?: Prisma.SortOrder; + rootCollection?: Prisma.SortOrder; + contentLicenseId?: Prisma.SortOrder; + createdAt?: Prisma.SortOrder; + updatedAt?: Prisma.SortOrder; +}; +export type FileMinOrderByAggregateInput = { + id?: Prisma.SortOrder; + fileId?: Prisma.SortOrder; + filename?: Prisma.SortOrder; + mediaType?: Prisma.SortOrder; + size?: Prisma.SortOrder; + memberOf?: Prisma.SortOrder; + rootCollection?: Prisma.SortOrder; + contentLicenseId?: Prisma.SortOrder; + createdAt?: Prisma.SortOrder; + updatedAt?: Prisma.SortOrder; +}; +export type FileSumOrderByAggregateInput = { + id?: Prisma.SortOrder; + size?: Prisma.SortOrder; +}; +export type BigIntFieldUpdateOperationsInput = { + set?: bigint | number; + increment?: bigint | number; + decrement?: bigint | number; + multiply?: bigint | number; + divide?: bigint | number; +}; +export type FileSelect = runtime.Types.Extensions.GetSelect<{ + id?: boolean; + fileId?: boolean; + filename?: boolean; + mediaType?: boolean; + size?: boolean; + memberOf?: boolean; + rootCollection?: boolean; + contentLicenseId?: boolean; + meta?: boolean; + createdAt?: boolean; + updatedAt?: boolean; +}, ExtArgs["result"]["file"]>; +export type FileSelectScalar = { + id?: boolean; + fileId?: boolean; + filename?: boolean; + mediaType?: boolean; + size?: boolean; + memberOf?: boolean; + rootCollection?: boolean; + contentLicenseId?: boolean; + meta?: boolean; + createdAt?: boolean; + updatedAt?: boolean; +}; +export type FileOmit = runtime.Types.Extensions.GetOmit<"id" | "fileId" | "filename" | "mediaType" | "size" | "memberOf" | "rootCollection" | "contentLicenseId" | "meta" | "createdAt" | "updatedAt", ExtArgs["result"]["file"]>; +export type $FilePayload = { + name: "File"; + objects: {}; + scalars: runtime.Types.Extensions.GetPayloadResult<{ + id: number; + fileId: string; + filename: string; + mediaType: string; + size: bigint; + memberOf: string; + rootCollection: string; + contentLicenseId: string; + meta: unknown | null; + createdAt: Date; + updatedAt: Date; + }, ExtArgs["result"]["file"]>; + composites: {}; +}; +export type FileGetPayload = runtime.Types.Result.GetResult; +export type FileCountArgs = Omit & { + select?: FileCountAggregateInputType | true; +}; +export interface FileDelegate { + [K: symbol]: { + types: Prisma.TypeMap['model']['File']; + meta: { + name: 'File'; + }; + }; + findUnique(args: Prisma.SelectSubset>): Prisma.Prisma__FileClient, T, "findUnique", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>; + findUniqueOrThrow(args: Prisma.SelectSubset>): Prisma.Prisma__FileClient, T, "findUniqueOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions>; + findFirst(args?: Prisma.SelectSubset>): Prisma.Prisma__FileClient, T, "findFirst", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>; + findFirstOrThrow(args?: Prisma.SelectSubset>): Prisma.Prisma__FileClient, T, "findFirstOrThrow", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions>; + findMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise, T, "findMany", GlobalOmitOptions>>; + create(args: Prisma.SelectSubset>): Prisma.Prisma__FileClient, T, "create", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions>; + createMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise; + delete(args: Prisma.SelectSubset>): Prisma.Prisma__FileClient, T, "delete", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions>; + update(args: Prisma.SelectSubset>): Prisma.Prisma__FileClient, T, "update", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions>; + deleteMany(args?: Prisma.SelectSubset>): Prisma.PrismaPromise; + updateMany(args: Prisma.SelectSubset>): Prisma.PrismaPromise; + upsert(args: Prisma.SelectSubset>): Prisma.Prisma__FileClient, T, "upsert", GlobalOmitOptions>, never, ExtArgs, GlobalOmitOptions>; + count(args?: Prisma.Subset): Prisma.PrismaPromise ? T['select'] extends true ? number : Prisma.GetScalarType : number>; + aggregate(args: Prisma.Subset): Prisma.PrismaPromise>; + groupBy>, Prisma.Extends<'take', Prisma.Keys>>, OrderByArg extends Prisma.True extends HasSelectOrTake ? { + orderBy: FileGroupByArgs['orderBy']; + } : { + orderBy?: FileGroupByArgs['orderBy']; + }, OrderFields extends Prisma.ExcludeUnderscoreKeys>>, ByFields extends Prisma.MaybeTupleToUnion, ByValid extends Prisma.Has, HavingFields extends Prisma.GetHavingFields, HavingValid extends Prisma.Has, ByEmpty extends T['by'] extends never[] ? Prisma.True : Prisma.False, InputErrors extends ByEmpty extends Prisma.True ? `Error: "by" must not be empty.` : HavingValid extends Prisma.False ? { + [P in HavingFields]: P extends ByFields ? never : P extends string ? `Error: Field "${P}" used in "having" needs to be provided in "by".` : [ + Error, + 'Field ', + P, + ` in "having" needs to be provided in "by"` + ]; + }[HavingFields] : 'take' extends Prisma.Keys ? 'orderBy' extends Prisma.Keys ? ByValid extends Prisma.True ? {} : { + [P in OrderFields]: P extends ByFields ? never : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`; + }[OrderFields] : 'Error: If you provide "take", you also need to provide "orderBy"' : 'skip' extends Prisma.Keys ? 'orderBy' extends Prisma.Keys ? ByValid extends Prisma.True ? {} : { + [P in OrderFields]: P extends ByFields ? never : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`; + }[OrderFields] : 'Error: If you provide "skip", you also need to provide "orderBy"' : ByValid extends Prisma.True ? {} : { + [P in OrderFields]: P extends ByFields ? never : `Error: Field "${P}" in "orderBy" needs to be provided in "by"`; + }[OrderFields]>(args: Prisma.SubsetIntersection & InputErrors): {} extends InputErrors ? GetFileGroupByPayload : Prisma.PrismaPromise; + readonly fields: FileFieldRefs; +} +export interface Prisma__FileClient extends Prisma.PrismaPromise { + readonly [Symbol.toStringTag]: "PrismaPromise"; + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise; + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): runtime.Types.Utils.JsPromise; + finally(onfinally?: (() => void) | undefined | null): runtime.Types.Utils.JsPromise; +} +export interface FileFieldRefs { + readonly id: Prisma.FieldRef<"File", 'Int'>; + readonly fileId: Prisma.FieldRef<"File", 'String'>; + readonly filename: Prisma.FieldRef<"File", 'String'>; + readonly mediaType: Prisma.FieldRef<"File", 'String'>; + readonly size: Prisma.FieldRef<"File", 'BigInt'>; + readonly memberOf: Prisma.FieldRef<"File", 'String'>; + readonly rootCollection: Prisma.FieldRef<"File", 'String'>; + readonly contentLicenseId: Prisma.FieldRef<"File", 'String'>; + readonly meta: Prisma.FieldRef<"File", 'Json'>; + readonly createdAt: Prisma.FieldRef<"File", 'DateTime'>; + readonly updatedAt: Prisma.FieldRef<"File", 'DateTime'>; +} +export type FileFindUniqueArgs = { + select?: Prisma.FileSelect | null; + omit?: Prisma.FileOmit | null; + where: Prisma.FileWhereUniqueInput; +}; +export type FileFindUniqueOrThrowArgs = { + select?: Prisma.FileSelect | null; + omit?: Prisma.FileOmit | null; + where: Prisma.FileWhereUniqueInput; +}; +export type FileFindFirstArgs = { + select?: Prisma.FileSelect | null; + omit?: Prisma.FileOmit | null; + where?: Prisma.FileWhereInput; + orderBy?: Prisma.FileOrderByWithRelationInput | Prisma.FileOrderByWithRelationInput[]; + cursor?: Prisma.FileWhereUniqueInput; + take?: number; + skip?: number; + distinct?: Prisma.FileScalarFieldEnum | Prisma.FileScalarFieldEnum[]; +}; +export type FileFindFirstOrThrowArgs = { + select?: Prisma.FileSelect | null; + omit?: Prisma.FileOmit | null; + where?: Prisma.FileWhereInput; + orderBy?: Prisma.FileOrderByWithRelationInput | Prisma.FileOrderByWithRelationInput[]; + cursor?: Prisma.FileWhereUniqueInput; + take?: number; + skip?: number; + distinct?: Prisma.FileScalarFieldEnum | Prisma.FileScalarFieldEnum[]; +}; +export type FileFindManyArgs = { + select?: Prisma.FileSelect | null; + omit?: Prisma.FileOmit | null; + where?: Prisma.FileWhereInput; + orderBy?: Prisma.FileOrderByWithRelationInput | Prisma.FileOrderByWithRelationInput[]; + cursor?: Prisma.FileWhereUniqueInput; + take?: number; + skip?: number; + distinct?: Prisma.FileScalarFieldEnum | Prisma.FileScalarFieldEnum[]; +}; +export type FileCreateArgs = { + select?: Prisma.FileSelect | null; + omit?: Prisma.FileOmit | null; + data: Prisma.XOR; +}; +export type FileCreateManyArgs = { + data: Prisma.FileCreateManyInput | Prisma.FileCreateManyInput[]; + skipDuplicates?: boolean; +}; +export type FileUpdateArgs = { + select?: Prisma.FileSelect | null; + omit?: Prisma.FileOmit | null; + data: Prisma.XOR; + where: Prisma.FileWhereUniqueInput; +}; +export type FileUpdateManyArgs = { + data: Prisma.XOR; + where?: Prisma.FileWhereInput; + limit?: number; +}; +export type FileUpsertArgs = { + select?: Prisma.FileSelect | null; + omit?: Prisma.FileOmit | null; + where: Prisma.FileWhereUniqueInput; + create: Prisma.XOR; + update: Prisma.XOR; +}; +export type FileDeleteArgs = { + select?: Prisma.FileSelect | null; + omit?: Prisma.FileOmit | null; + where: Prisma.FileWhereUniqueInput; +}; +export type FileDeleteManyArgs = { + where?: Prisma.FileWhereInput; + limit?: number; +}; +export type FileDefaultArgs = { + select?: Prisma.FileSelect | null; + omit?: Prisma.FileOmit | null; +}; +export {}; diff --git a/dist/generated/prisma/models/File.js b/dist/generated/prisma/models/File.js new file mode 100644 index 0000000..0cfa977 --- /dev/null +++ b/dist/generated/prisma/models/File.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=File.js.map \ No newline at end of file diff --git a/dist/generated/prisma/models/File.js.map b/dist/generated/prisma/models/File.js.map new file mode 100644 index 0000000..640e73d --- /dev/null +++ b/dist/generated/prisma/models/File.js.map @@ -0,0 +1 @@ +{"version":3,"file":"File.js","sourceRoot":"","sources":["../../../../src/generated/prisma/models/File.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/generated/prisma/pjtg.d.ts b/dist/generated/prisma/pjtg.d.ts new file mode 100644 index 0000000..715162a --- /dev/null +++ b/dist/generated/prisma/pjtg.d.ts @@ -0,0 +1,89 @@ +import * as Prisma from './internal/prismaNamespace.js'; +declare global { + namespace PrismaJson { + } +} +export type NullableListFilter = { + equals?: T | T[] | null; + has?: T | null; + hasEvery?: T[]; + hasSome?: T[]; + isEmpty?: boolean; +}; +export type UpdateInput = T extends object ? { + [P in keyof T]?: UpdateInput; +} : T; +export type UpdateManyInput = T | T[] | { + set?: T[]; + push?: T | T[]; +}; +export type CreateManyInput = T | T[] | { + set?: T[]; +}; +export type TypedNestedStringFilter = Prisma.StringFilter & { + equals?: S; + in?: S[]; + notIn?: S[]; + not?: TypedNestedStringFilter | S; +}; +export type TypedStringFilter = Prisma.StringFilter & { + equals?: S; + in?: S[]; + notIn?: S[]; + not?: TypedNestedStringFilter | S; +}; +export type TypedNestedStringNullableFilter = Prisma.StringNullableFilter & { + equals?: S | null; + in?: S[] | null; + notIn?: S[] | null; + not?: TypedNestedStringNullableFilter | S | null; +}; +export type TypedStringNullableFilter = Prisma.StringNullableFilter & { + equals?: S | null; + in?: S[] | null; + notIn?: S[] | null; + not?: TypedNestedStringNullableFilter | S | null; +}; +export type TypedNestedStringWithAggregatesFilter = Prisma.NestedStringWithAggregatesFilter & { + equals?: S; + in?: S[]; + notIn?: S[]; + not?: TypedNestedStringWithAggregatesFilter | S; +}; +export type TypedStringWithAggregatesFilter = Prisma.StringWithAggregatesFilter & { + equals?: S; + in?: S[]; + notIn?: S[]; + not?: TypedNestedStringWithAggregatesFilter | S; +}; +export type TypedNestedStringNullableWithAggregatesFilter = Prisma.NestedStringNullableWithAggregatesFilter & { + equals?: S | null; + in?: S[] | null; + notIn?: S[] | null; + not?: TypedNestedStringNullableWithAggregatesFilter | S | null; +}; +export type TypedStringNullableWithAggregatesFilter = Prisma.StringNullableWithAggregatesFilter & { + equals?: S | null; + in?: S[] | null; + notIn?: S[] | null; + not?: TypedNestedStringNullableWithAggregatesFilter | S | null; +}; +export type TypedStringFieldUpdateOperationsInput = Prisma.StringFieldUpdateOperationsInput & { + set?: S; +}; +export type TypedNullableStringFieldUpdateOperationsInput = Prisma.NullableStringFieldUpdateOperationsInput & { + set?: S | null; +}; +export type TypedStringNullableListFilter = Prisma.StringNullableListFilter & { + equals?: S[] | null; + has?: S | null; + hasEvery?: S[]; + hasSome?: S[]; +}; +export type UpdateStringArrayInput = { + set?: S[]; + push?: S | S[]; +}; +export type CreateStringArrayInput = { + set?: S[]; +}; diff --git a/dist/generated/prisma/pjtg.js b/dist/generated/prisma/pjtg.js new file mode 100644 index 0000000..927ed8e --- /dev/null +++ b/dist/generated/prisma/pjtg.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=pjtg.js.map \ No newline at end of file diff --git a/dist/generated/prisma/pjtg.js.map b/dist/generated/prisma/pjtg.js.map new file mode 100644 index 0000000..e0e6630 --- /dev/null +++ b/dist/generated/prisma/pjtg.js.map @@ -0,0 +1 @@ +{"version":3,"file":"pjtg.js","sourceRoot":"","sources":["../../../src/generated/prisma/pjtg.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..983ab69 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,8 @@ +import { Client } from '@opensearch-project/opensearch'; +import { PrismaClient } from './generated/prisma/client.js'; +declare module 'fastify' { + interface FastifyInstance { + prisma: PrismaClient; + opensearch: Client; + } +} diff --git a/dist/index.dev.d.ts b/dist/index.dev.d.ts new file mode 100644 index 0000000..983ab69 --- /dev/null +++ b/dist/index.dev.d.ts @@ -0,0 +1,8 @@ +import { Client } from '@opensearch-project/opensearch'; +import { PrismaClient } from './generated/prisma/client.js'; +declare module 'fastify' { + interface FastifyInstance { + prisma: PrismaClient; + opensearch: Client; + } +} diff --git a/dist/index.dev.js b/dist/index.dev.js new file mode 100644 index 0000000..d67e55e --- /dev/null +++ b/dist/index.dev.js @@ -0,0 +1,146 @@ +import { createReadStream, existsSync, readFileSync, statSync } from 'node:fs'; +import { Readable } from 'node:stream'; +import { Client } from '@opensearch-project/opensearch'; +import Fastify from 'fastify'; +import arocapi, { AllPublicAccessTransformer, AllPublicFileAccessTransformer, } from './app.js'; +import { PrismaClient } from './generated/prisma/client.js'; +const prisma = new PrismaClient(); +if (!process.env.OPENSEARCH_URL) { + throw new Error('OPENSEARCH_URL environment variable is not set'); +} +const opensearchUrl = process.env.OPENSEARCH_URL; +const opensearch = new Client({ node: opensearchUrl }); +const fileHandler = { + head: async (file) => { + const filePath = file.meta.storagePath; + if (!existsSync(filePath)) { + return false; + } + const stats = statSync(filePath); + return { + contentType: file.mediaType, + contentLength: Number(file.size), + lastModified: stats.mtime, + }; + }, + get: async (file) => { + const filePath = file.meta.storagePath; + if (!existsSync(filePath)) { + return false; + } + const stats = statSync(filePath); + return { + type: 'stream', + stream: createReadStream(filePath), + metadata: { + contentType: file.mediaType, + contentLength: Number(file.size), + lastModified: stats.mtime, + }, + }; + }, +}; +const transformRoCrate = (filePath, remapRootTo) => { + const fileContents = readFileSync(filePath, 'utf-8'); + const rocrate = JSON.parse(fileContents); + const graph = rocrate['@graph'] || []; + const rootNode = graph.find((node) => node['@id'] === 'ro-crate-metadata.json'); + if (!rootNode) { + return false; + } + rootNode.about['@id'] = remapRootTo; + const jsonString = JSON.stringify(rocrate); + return { rocrate, jsonString }; +}; +const roCrateHandler = { + head: async (entity) => { + const meta = entity.meta; + const filePath = meta.storagePath; + if (!existsSync(filePath)) { + return false; + } + if (meta.remapRootTo) { + const result = transformRoCrate(filePath, meta.remapRootTo); + if (!result) { + return false; + } + return { + contentType: 'application/ld+json', + contentLength: Buffer.byteLength(result.jsonString), + lastModified: statSync(filePath).mtime, + }; + } + const stats = statSync(filePath); + return { + contentType: 'application/ld+json', + contentLength: stats.size, + lastModified: stats.mtime, + }; + }, + get: async (entity) => { + const meta = entity.meta; + const filePath = meta.storagePath; + if (!existsSync(filePath)) { + return false; + } + if (meta.remapRootTo) { + const result = transformRoCrate(filePath, meta.remapRootTo); + if (!result) { + return false; + } + return { + type: 'stream', + stream: Readable.from(result.jsonString), + metadata: { + contentType: 'application/ld+json', + contentLength: Buffer.byteLength(result.jsonString), + lastModified: statSync(filePath).mtime, + }, + }; + } + const stats = statSync(filePath); + return { + type: 'stream', + stream: createReadStream(filePath), + metadata: { + contentType: 'application/ld+json', + contentLength: stats.size, + lastModified: stats.mtime, + }, + }; + }, +}; +const oniTransformer = (entity, { request: _r, fastify: _f }) => { + return { + ...entity, + accessControl: 'Public', + counts: { + collections: 0, + objects: 0, + files: 0, + }, + }; +}; +const fastify = Fastify({ + logger: true, +}); +fastify.register(arocapi, { + prisma, + opensearch, + fileHandler, + roCrateHandler, + accessTransformer: AllPublicAccessTransformer, + entityTransformers: [oniTransformer], + fileAccessTransformer: AllPublicFileAccessTransformer, +}); +const start = async () => { + try { + await fastify.listen({ port: 9000 }); + } + catch (err) { + fastify.log.error(err); + process.exit(1); + } +}; +start(); +//# sourceMappingURL=index.dev.js.map \ No newline at end of file diff --git a/dist/index.dev.js.map b/dist/index.dev.js.map new file mode 100644 index 0000000..575d6c2 --- /dev/null +++ b/dist/index.dev.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.dev.js","sourceRoot":"","sources":["../src/index.dev.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..eda1697 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,29 @@ +import { Client } from '@opensearch-project/opensearch'; +import Fastify from 'fastify'; +import arocapi, { AllPublicAccessTransformer } from './app.js'; +import { PrismaClient } from './generated/prisma/client.js'; +const prisma = new PrismaClient(); +if (!process.env.OPENSEARCH_URL) { + throw new Error('OPENSEARCH_URL environment variable is not set'); +} +const opensearchUrl = process.env.OPENSEARCH_URL; +const opensearch = new Client({ node: opensearchUrl }); +const fastify = Fastify({ + logger: true, +}); +fastify.register(arocapi, { + prisma, + opensearch, + accessTransformer: AllPublicAccessTransformer, +}); +const start = async () => { + try { + await fastify.listen({ port: 3000 }); + } + catch (err) { + fastify.log.error(err); + process.exit(1); + } +}; +start(); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map new file mode 100644 index 0000000..463d786 --- /dev/null +++ b/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AACxD,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,OAAO,EAAE,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAS5D,MAAM,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;AAElC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC;IAChC,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;AACpE,CAAC;AACD,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;AACjD,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;AAEvD,MAAM,OAAO,GAAG,OAAO,CAAC;IACtB,MAAM,EAAE,IAAI;CACb,CAAC,CAAC;AACH,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE;IACxB,MAAM;IACN,UAAU;IACV,iBAAiB,EAAE,0BAA0B;CAC9C,CAAC,CAAC;AAEH,MAAM,KAAK,GAAG,KAAK,IAAI,EAAE;IACvB,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC;AACF,KAAK,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/routes/crate.d.ts b/dist/routes/crate.d.ts new file mode 100644 index 0000000..21ae78e --- /dev/null +++ b/dist/routes/crate.d.ts @@ -0,0 +1,7 @@ +import type { FastifyPluginAsync } from 'fastify'; +import type { RoCrateHandler } from '../types/fileHandlers.js'; +type CrateRouteOptions = { + roCrateHandler: RoCrateHandler; +}; +declare const crate: FastifyPluginAsync; +export default crate; diff --git a/dist/routes/crate.js b/dist/routes/crate.js new file mode 100644 index 0000000..f911c79 --- /dev/null +++ b/dist/routes/crate.js @@ -0,0 +1,101 @@ +import { createReadStream } from 'node:fs'; +import { z } from 'zod/v4'; +import { createInternalError, createNotFoundError } from '../utils/errors.js'; +const paramsSchema = z.object({ + id: z.url(), +}); +const setFileHeaders = (reply, metadata) => { + reply.header('Content-Type', metadata.contentType); + reply.header('Content-Length', metadata.contentLength.toString()); + if (metadata.etag) { + reply.header('ETag', metadata.etag); + } + if (metadata.lastModified) { + reply.header('Last-Modified', metadata.lastModified.toUTCString()); + } +}; +const crate = async (fastify, opts) => { + const { roCrateHandler } = opts; + fastify.withTypeProvider().head('/entity/:id/rocrate', { + schema: { + params: paramsSchema, + }, + }, async (request, reply) => { + const { id } = request.params; + try { + const entity = await fastify.prisma.entity.findFirst({ + where: { + rocrateId: id, + }, + }); + if (!entity) { + return reply.code(404).send(createNotFoundError('The requested entity was not found', id)); + } + const metadata = await roCrateHandler.head(entity, { + request, + fastify, + }); + if (!metadata) { + return reply.code(404).send(createNotFoundError('The requested RO-Crate metadata was not found', id)); + } + setFileHeaders(reply, metadata); + return reply.code(200).send(); + } + catch (error) { + const err = error; + fastify.log.error(`RO-Crate metadata retrieval error: ${err}`); + return reply.code(500).send(createInternalError()); + } + }); + fastify.withTypeProvider().get('/entity/:id/rocrate', { + schema: { + params: paramsSchema, + }, + }, async (request, reply) => { + const { id } = request.params; + try { + const entity = await fastify.prisma.entity.findFirst({ + where: { + rocrateId: id, + }, + }); + if (!entity) { + return reply.code(404).send(createNotFoundError('The requested entity was not found', id)); + } + const result = await roCrateHandler.get(entity, { + request, + fastify, + }); + if (!result) { + return reply.code(404).send(createNotFoundError('The requested RO-Crate could not be retrieved', id)); + } + if (result.type === 'redirect') { + return reply.code(302).redirect(result.url); + } + const metadata = { + ...result.metadata, + contentType: 'application/ld+json', + }; + setFileHeaders(reply, metadata); + if (result.type === 'stream') { + return reply.code(200).send(result.stream); + } + if (result.type === 'file') { + if (result.accelPath) { + reply.header('X-Accel-Redirect', result.accelPath); + return reply.code(200).send(); + } + const stream = createReadStream(result.path); + return reply.code(200).send(stream); + } + throw new Error(`Unexpected RO-Crate result type: ${result.type}`); + } + catch (error) { + const err = error; + fastify.log.error(`RO-Crate retrieval error: ${err.message}`); + return reply.code(500).send(createInternalError()); + } + }); +}; +export default crate; +//# sourceMappingURL=crate.js.map \ No newline at end of file diff --git a/dist/routes/crate.js.map b/dist/routes/crate.js.map new file mode 100644 index 0000000..c3b85f7 --- /dev/null +++ b/dist/routes/crate.js.map @@ -0,0 +1 @@ +{"version":3,"file":"crate.js","sourceRoot":"","sources":["../../src/routes/crate.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/routes/crate.test.d.ts b/dist/routes/crate.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/routes/crate.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/routes/crate.test.js b/dist/routes/crate.test.js new file mode 100644 index 0000000..e54d607 --- /dev/null +++ b/dist/routes/crate.test.js @@ -0,0 +1,344 @@ +import { createReadStream } from 'node:fs'; +import { Readable } from 'node:stream'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { fastify, fastifyAfter, fastifyBefore, prisma } from '../test/helpers/fastify.js'; +import crateRoute from './crate.js'; +vi.mock('node:fs', () => ({ + createReadStream: vi.fn(), +})); +describe('Crate Route', () => { + const mockRoCrateHandler = { + get: vi.fn(), + head: vi.fn(), + }; + beforeEach(async () => { + await fastifyBefore(); + await fastify.register(crateRoute, { roCrateHandler: mockRoCrateHandler }); + vi.clearAllMocks(); + }); + afterEach(async () => { + await fastifyAfter(); + }); + const mockFileEntity = { + id: 1, + rocrateId: 'http://example.com/entity/file.wav', + name: 'test.wav', + description: 'A test file', + entityType: 'http://schema.org/MediaObject', + fileId: null, + memberOf: 'http://example.com/collection', + rootCollection: 'http://example.com/collection', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + meta: {}, + }; + const mockCollectionEntity = { + ...mockFileEntity, + rocrateId: 'http://example.com/collection', + name: 'Test Collection', + entityType: 'http://pcdm.org/models#Collection', + memberOf: null, + rootCollection: null, + }; + const mockObjectEntity = { + ...mockFileEntity, + rocrateId: 'http://example.com/object', + name: 'Test Object', + entityType: 'http://pcdm.org/models#Object', + memberOf: 'http://example.com/collection', + rootCollection: 'http://example.com/collection', + }; + describe('GET /entity/:id/rocrate', () => { + it('should stream RO-Crate metadata for File entity', async () => { + prisma.entity.findFirst.mockResolvedValue(mockFileEntity); + const mockStream = Readable.from(['{"@context": "https://w3id.org/ro/crate/1.1/context"}']); + const mockResult = { + type: 'stream', + stream: mockStream, + metadata: { + contentType: 'text/plain', + contentLength: 52, + etag: '"rocrate123"', + lastModified: new Date('2025-01-01'), + }, + }; + vi.mocked(mockRoCrateHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/file.wav')}/rocrate`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-type']).toBe('application/ld+json'); + expect(response.headers['content-length']).toBe('52'); + expect(response.headers.etag).toBe('"rocrate123"'); + expect(response.body).toBe('{"@context": "https://w3id.org/ro/crate/1.1/context"}'); + expect(mockRoCrateHandler.get).toHaveBeenCalledWith(mockFileEntity, expect.objectContaining({ + request: expect.any(Object), + fastify: expect.any(Object), + })); + }); + it('should stream RO-Crate metadata for Collection entity', async () => { + prisma.entity.findFirst.mockResolvedValue(mockCollectionEntity); + const mockStream = Readable.from(['{"@context": "https://w3id.org/ro/crate/1.1/context"}']); + const mockResult = { + type: 'stream', + stream: mockStream, + metadata: { + contentType: 'application/json', + contentLength: 52, + }, + }; + vi.mocked(mockRoCrateHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/collection')}/rocrate`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-type']).toBe('application/ld+json'); + expect(mockRoCrateHandler.get).toHaveBeenCalledWith(mockCollectionEntity, expect.any(Object)); + }); + it('should stream RO-Crate metadata for Object entity', async () => { + prisma.entity.findFirst.mockResolvedValue(mockObjectEntity); + const mockStream = Readable.from(['{"@context": "https://w3id.org/ro/crate/1.1/context"}']); + const mockResult = { + type: 'stream', + stream: mockStream, + metadata: { + contentType: 'application/ld+json', + contentLength: 52, + }, + }; + vi.mocked(mockRoCrateHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/object')}/rocrate`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-type']).toBe('application/ld+json'); + expect(mockRoCrateHandler.get).toHaveBeenCalledWith(mockObjectEntity, expect.any(Object)); + }); + it('should handle redirect response', async () => { + prisma.entity.findFirst.mockResolvedValue(mockFileEntity); + const mockResult = { + type: 'redirect', + url: 'https://storage.example.com/ro-crate-metadata.json', + }; + vi.mocked(mockRoCrateHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/file.wav')}/rocrate`, + }); + expect(response.statusCode).toBe(302); + expect(response.headers.location).toBe('https://storage.example.com/ro-crate-metadata.json'); + }); + it('should handle file path response without nginx X-Accel-Redirect', async () => { + prisma.entity.findFirst.mockResolvedValue(mockCollectionEntity); + const mockStream = Readable.from(['rocrate content']); + vi.mocked(createReadStream).mockReturnValue(mockStream); + const mockResult = { + type: 'file', + path: '/data/rocrate/ro-crate-metadata.json', + metadata: { + contentType: 'application/ld+json', + contentLength: 1024, + }, + }; + vi.mocked(mockRoCrateHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/collection')}/rocrate`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-type']).toBe('application/ld+json'); + expect(response.headers['content-length']).toBe('1024'); + expect(response.headers['x-accel-redirect']).toBeUndefined(); + expect(createReadStream).toHaveBeenCalledWith('/data/rocrate/ro-crate-metadata.json'); + }); + it('should handle file path response with nginx X-Accel-Redirect', async () => { + prisma.entity.findFirst.mockResolvedValue(mockFileEntity); + const mockResult = { + type: 'file', + path: '/data/rocrate/ro-crate-metadata.json', + accelPath: '/internal/rocrate/ro-crate-metadata.json', + metadata: { + contentType: 'application/ld+json', + contentLength: 2048, + }, + }; + vi.mocked(mockRoCrateHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/file.wav')}/rocrate`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-type']).toBe('application/ld+json'); + expect(response.headers['x-accel-redirect']).toBe('/internal/rocrate/ro-crate-metadata.json'); + expect(response.body).toBe(''); + }); + it('should return 404 when entity not found', async () => { + prisma.entity.findFirst.mockResolvedValue(null); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/nonexistent')}/rocrate`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(404); + expect(body.error.code).toBe('NOT_FOUND'); + expect(body.error.message).toBe('The requested entity was not found'); + expect(mockRoCrateHandler.get).not.toHaveBeenCalled(); + }); + it('should return 404 when handler returns false', async () => { + prisma.entity.findFirst.mockResolvedValue(mockFileEntity); + vi.mocked(mockRoCrateHandler.get).mockResolvedValue(false); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/file.wav')}/rocrate`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(404); + expect(body.error.code).toBe('NOT_FOUND'); + expect(body.error.message).toBe('The requested RO-Crate could not be retrieved'); + }); + it('should return 500 when database error occurs', async () => { + prisma.entity.findFirst.mockRejectedValue(new Error('Database connection failed')); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/file.wav')}/rocrate`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(500); + expect(body.error.code).toBe('INTERNAL_ERROR'); + }); + it('should return 500 when roCrateHandler throws error', async () => { + prisma.entity.findFirst.mockResolvedValue(mockFileEntity); + vi.mocked(mockRoCrateHandler.get).mockRejectedValue(new Error('RO-Crate error')); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/file.wav')}/rocrate`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(500); + expect(body.error.code).toBe('INTERNAL_ERROR'); + }); + it('should validate ID parameter format', async () => { + const response = await fastify.inject({ + method: 'GET', + url: '/entity/invalid-id/rocrate', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(400); + expect(body.error).toBe('Bad Request'); + }); + }); + describe('HEAD /entity/:id/rocrate', () => { + it('should return RO-Crate metadata headers for File entity', async () => { + prisma.entity.findFirst.mockResolvedValue(mockFileEntity); + const mockMetadata = { + contentType: 'application/ld+json', + contentLength: 1024, + etag: '"rocrate-etag"', + lastModified: new Date('2025-01-15'), + }; + vi.mocked(mockRoCrateHandler.head).mockResolvedValue(mockMetadata); + const response = await fastify.inject({ + method: 'HEAD', + url: `/entity/${encodeURIComponent('http://example.com/entity/file.wav')}/rocrate`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-type']).toBe('application/ld+json'); + expect(response.headers['content-length']).toBe('1024'); + expect(response.headers.etag).toBe('"rocrate-etag"'); + expect(response.body).toBe(''); + expect(mockRoCrateHandler.head).toHaveBeenCalledWith(mockFileEntity, expect.objectContaining({ + request: expect.any(Object), + fastify: expect.any(Object), + })); + }); + it('should return RO-Crate metadata headers for Collection entity', async () => { + prisma.entity.findFirst.mockResolvedValue(mockCollectionEntity); + const mockMetadata = { + contentType: 'text/plain', + contentLength: 512, + }; + vi.mocked(mockRoCrateHandler.head).mockResolvedValue(mockMetadata); + const response = await fastify.inject({ + method: 'HEAD', + url: `/entity/${encodeURIComponent('http://example.com/collection')}/rocrate`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-type']).toBe('text/plain'); + expect(response.headers['content-length']).toBe('512'); + expect(response.body).toBe(''); + }); + it('should return RO-Crate metadata headers for Object entity', async () => { + prisma.entity.findFirst.mockResolvedValue(mockObjectEntity); + const mockMetadata = { + contentType: 'application/json', + contentLength: 2048, + }; + vi.mocked(mockRoCrateHandler.head).mockResolvedValue(mockMetadata); + const response = await fastify.inject({ + method: 'HEAD', + url: `/entity/${encodeURIComponent('http://example.com/object')}/rocrate`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-type']).toBe('application/json'); + expect(response.headers['content-length']).toBe('2048'); + expect(response.body).toBe(''); + }); + it('should return 404 when entity not found', async () => { + prisma.entity.findFirst.mockResolvedValue(null); + const response = await fastify.inject({ + method: 'HEAD', + url: `/entity/${encodeURIComponent('http://example.com/nonexistent')}/rocrate`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(404); + expect(body.error.code).toBe('NOT_FOUND'); + expect(mockRoCrateHandler.head).not.toHaveBeenCalled(); + }); + it('should return 404 when head handler returns false', async () => { + prisma.entity.findFirst.mockResolvedValue(mockFileEntity); + vi.mocked(mockRoCrateHandler.head).mockResolvedValue(false); + const response = await fastify.inject({ + method: 'HEAD', + url: `/entity/${encodeURIComponent('http://example.com/entity/file.wav')}/rocrate`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(404); + expect(body.error.code).toBe('NOT_FOUND'); + expect(body.error.message).toBe('The requested RO-Crate metadata was not found'); + }); + it('should return 500 when head handler throws error', async () => { + prisma.entity.findFirst.mockResolvedValue(mockFileEntity); + vi.mocked(mockRoCrateHandler.head).mockRejectedValue(new Error('Metadata error')); + const response = await fastify.inject({ + method: 'HEAD', + url: `/entity/${encodeURIComponent('http://example.com/entity/file.wav')}/rocrate`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(500); + expect(body.error.code).toBe('INTERNAL_ERROR'); + }); + }); + describe('GET /entity/:id/rocrate - exhaustiveness check', () => { + it('should handle unexpected RO-Crate result type', async () => { + prisma.entity.findFirst.mockResolvedValue(mockFileEntity); + const invalidResult = { + type: 'invalid', + metadata: { contentType: 'application/ld+json', contentLength: 1024 }, + }; + vi.mocked(mockRoCrateHandler.get).mockResolvedValue(invalidResult); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/file.wav')}/rocrate`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(500); + expect(body.error.code).toBe('INTERNAL_ERROR'); + }); + }); +}); +//# sourceMappingURL=crate.test.js.map \ No newline at end of file diff --git a/dist/routes/crate.test.js.map b/dist/routes/crate.test.js.map new file mode 100644 index 0000000..3de0c5f --- /dev/null +++ b/dist/routes/crate.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"crate.test.js","sourceRoot":"","sources":["../../src/routes/crate.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/routes/entities.d.ts b/dist/routes/entities.d.ts new file mode 100644 index 0000000..adec388 --- /dev/null +++ b/dist/routes/entities.d.ts @@ -0,0 +1,8 @@ +import type { FastifyPluginAsync } from 'fastify'; +import type { AccessTransformer, EntityTransformer } from '../types/transformers.js'; +type EntitiesRouteOptions = { + accessTransformer: AccessTransformer; + entityTransformers?: EntityTransformer[]; +}; +declare const entities: FastifyPluginAsync; +export default entities; diff --git a/dist/routes/entities.js b/dist/routes/entities.js new file mode 100644 index 0000000..719063b --- /dev/null +++ b/dist/routes/entities.js @@ -0,0 +1,82 @@ +import { z } from 'zod/v4'; +import { baseEntityTransformer, resolveEntityReferences } from '../transformers/default.js'; +import { createInternalError } from '../utils/errors.js'; +const querySchema = z.object({ + memberOf: z.url().optional(), + entityType: z + .string() + .optional() + .transform((val) => { + if (!val) + return undefined; + return val.split(',').map((item) => item.trim()); + }) + .pipe(z.array(z.string()).optional()), + limit: z.coerce.number().int().min(1).max(1000).default(100), + offset: z.coerce.number().int().min(0).default(0), + sort: z.enum(['id', 'name', 'createdAt', 'updatedAt']).default('id'), + order: z.enum(['asc', 'desc']).default('asc'), +}); +const entities = async (fastify, opts) => { + const { accessTransformer, entityTransformers = [] } = opts; + fastify.withTypeProvider().get('/entities', { + schema: { + querystring: querySchema, + }, + }, async (request, reply) => { + const { memberOf, entityType, limit, offset, sort, order } = request.query; + try { + const where = {}; + if (memberOf) { + where.memberOf = memberOf; + } + if (entityType && entityType.length > 0) { + where.entityType = { + in: entityType, + }; + } + const sortField = sort === 'id' ? 'rocrateId' : sort; + const dbEntities = await fastify.prisma.entity.findMany({ + where, + orderBy: { + [sortField]: order, + }, + skip: offset, + take: limit, + }); + const total = await fastify.prisma.entity.count({ where }); + const refMap = await resolveEntityReferences(dbEntities, fastify.prisma); + const entities = await Promise.all(dbEntities.map(async (dbEntity) => { + const base = baseEntityTransformer(dbEntity); + const standardEntity = { + ...base, + memberOf: base.memberOf ? (refMap.get(base.memberOf) ?? null) : null, + rootCollection: base.rootCollection ? (refMap.get(base.rootCollection) ?? null) : null, + }; + const authorisedEntity = await accessTransformer(standardEntity, { + request, + fastify, + }); + let result = authorisedEntity; + for (const transformer of entityTransformers) { + result = await transformer(result, { + request, + fastify, + }); + } + return result; + })); + return { + total, + entities, + }; + } + catch (error) { + const err = error; + fastify.log.error(`Database error: ${err.message}`); + return reply.code(500).send(createInternalError()); + } + }); +}; +export default entities; +//# sourceMappingURL=entities.js.map \ No newline at end of file diff --git a/dist/routes/entities.js.map b/dist/routes/entities.js.map new file mode 100644 index 0000000..d5e4668 --- /dev/null +++ b/dist/routes/entities.js.map @@ -0,0 +1 @@ +{"version":3,"file":"entities.js","sourceRoot":"","sources":["../../src/routes/entities.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/routes/entities.test.d.ts b/dist/routes/entities.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/routes/entities.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/routes/entities.test.js b/dist/routes/entities.test.js new file mode 100644 index 0000000..ff2e948 --- /dev/null +++ b/dist/routes/entities.test.js @@ -0,0 +1,272 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { fastify, fastifyAfter, fastifyBefore, prisma } from '../test/helpers/fastify.js'; +import { AllPublicAccessTransformer } from '../transformers/default.js'; +import entitiesRoute from './entities.js'; +describe('Entities Route', () => { + beforeEach(async () => { + await fastifyBefore(); + await fastify.register(entitiesRoute, { accessTransformer: AllPublicAccessTransformer }); + }); + afterEach(async () => { + await fastifyAfter(); + }); + describe('GET /entities', () => { + it('should return entities with default pagination', async () => { + const mockEntities = [ + { + id: 1, + rocrateId: 'http://example.com/entity/1', + name: 'Test Entity 1', + description: 'First test entity', + entityType: 'http://pcdm.org/models#Collection', + fileId: null, + memberOf: null, + rootCollection: null, + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + meta: {}, + }, + { + id: 2, + rocrateId: 'http://example.com/entity/2', + name: 'Test Entity 2', + description: 'Second test entity', + entityType: 'http://pcdm.org/models#Object', + fileId: null, + memberOf: 'http://example.com/entity/1', + rootCollection: 'http://example.com/entity/1', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + meta: {}, + }, + ]; + prisma.entity.findMany.mockResolvedValue(mockEntities); + prisma.entity.count.mockResolvedValue(2); + const response = await fastify.inject({ + method: 'GET', + url: '/entities', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body).toMatchSnapshot(); + expect(prisma.entity.findMany).toHaveBeenCalledWith({ + where: {}, + orderBy: { rocrateId: 'asc' }, + skip: 0, + take: 100, + }); + }); + it('should filter by memberOf', async () => { + prisma.entity.findMany.mockResolvedValue([]); + prisma.entity.count.mockResolvedValue(0); + const response = await fastify.inject({ + method: 'GET', + url: '/entities', + query: { + memberOf: 'http://example.com/collection/1', + }, + }); + expect(response.statusCode).toBe(200); + expect(prisma.entity.findMany).toHaveBeenCalledWith({ + where: { memberOf: 'http://example.com/collection/1' }, + orderBy: { rocrateId: 'asc' }, + skip: 0, + take: 100, + }); + }); + it('should filter by single entity type', async () => { + prisma.entity.findMany.mockResolvedValue([]); + prisma.entity.count.mockResolvedValue(0); + const response = await fastify.inject({ + method: 'GET', + url: '/entities', + query: { + entityType: 'http://pcdm.org/models#Collection', + }, + }); + expect(response.statusCode).toBe(200); + expect(prisma.entity.findMany).toHaveBeenCalledWith({ + where: { entityType: { in: ['http://pcdm.org/models#Collection'] } }, + orderBy: { rocrateId: 'asc' }, + skip: 0, + take: 100, + }); + }); + it('should filter by multiple entity types', async () => { + prisma.entity.findMany.mockResolvedValue([]); + prisma.entity.count.mockResolvedValue(0); + const response = await fastify.inject({ + method: 'GET', + url: '/entities', + query: { + entityType: 'http://pcdm.org/models#Collection,http://pcdm.org/models#Object', + }, + }); + expect(response.statusCode).toBe(200); + expect(prisma.entity.findMany).toHaveBeenCalledWith({ + where: { + entityType: { + in: ['http://pcdm.org/models#Collection', 'http://pcdm.org/models#Object'], + }, + }, + orderBy: { rocrateId: 'asc' }, + skip: 0, + take: 100, + }); + }); + it('should handle pagination parameters', async () => { + prisma.entity.findMany.mockResolvedValue([]); + prisma.entity.count.mockResolvedValue(0); + const response = await fastify.inject({ + method: 'GET', + url: '/entities', + query: { + limit: '50', + offset: '10', + }, + }); + expect(response.statusCode).toBe(200); + expect(prisma.entity.findMany).toHaveBeenCalledWith({ + where: {}, + orderBy: { rocrateId: 'asc' }, + skip: 10, + take: 50, + }); + }); + it('should handle sorting by name descending', async () => { + prisma.entity.findMany.mockResolvedValue([]); + prisma.entity.count.mockResolvedValue(0); + const response = await fastify.inject({ + method: 'GET', + url: '/entities', + query: { + sort: 'name', + order: 'desc', + }, + }); + expect(response.statusCode).toBe(200); + expect(prisma.entity.findMany).toHaveBeenCalledWith({ + where: {}, + orderBy: { name: 'desc' }, + skip: 0, + take: 100, + }); + }); + it('should map id sort to rocrateId field', async () => { + prisma.entity.findMany.mockResolvedValue([]); + prisma.entity.count.mockResolvedValue(0); + const response = await fastify.inject({ + method: 'GET', + url: '/entities', + query: { + sort: 'id', + }, + }); + expect(response.statusCode).toBe(200); + expect(prisma.entity.findMany).toHaveBeenCalledWith({ + where: {}, + orderBy: { rocrateId: 'asc' }, + skip: 0, + take: 100, + }); + }); + it('should return 500 when database error occurs', async () => { + prisma.entity.findMany.mockRejectedValue(new Error('Database connection failed')); + const response = await fastify.inject({ + method: 'GET', + url: '/entities', + }); + expect(response.statusCode).toBe(500); + const body = JSON.parse(response.body); + expect(body).toMatchSnapshot(); + }); + it('should validate limit parameter bounds', async () => { + const response = await fastify.inject({ + method: 'GET', + url: '/entities', + query: { + limit: '0', + }, + }); + expect(response.statusCode).toBe(400); + }); + it('should validate negative offset', async () => { + const response = await fastify.inject({ + method: 'GET', + url: '/entities', + query: { + offset: '-1', + }, + }); + expect(response.statusCode).toBe(400); + }); + it('should apply custom entity transformers', async () => { + const customTransformer = async (entity) => ({ + ...entity, + tested: true, + }); + await fastifyBefore(); + await fastify.register(entitiesRoute, { + accessTransformer: AllPublicAccessTransformer, + entityTransformers: [customTransformer], + }); + const mockEntities = [ + { + id: 1, + rocrateId: 'http://example.com/entity/1', + name: 'Test Entity 1', + description: 'First test entity', + entityType: 'http://pcdm.org/models#Collection', + memberOf: null, + rootCollection: null, + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + }, + ]; + prisma.entity.findMany.mockResolvedValue(mockEntities); + const response = await fastify.inject({ + method: 'GET', + url: '/entities', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body).toMatchSnapshot(); + }); + it('should return null for memberOf/rootCollection when parent entity not found', async () => { + const mockEntities = [ + { + id: 1, + rocrateId: 'http://example.com/entity/1', + name: 'Test Entity 1', + description: 'Entity with missing parent', + entityType: 'http://pcdm.org/models#Object', + fileId: null, + memberOf: 'http://example.com/entity/deleted', + rootCollection: 'http://example.com/entity/deleted', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + meta: {}, + }, + ]; + prisma.entity.findMany.mockResolvedValueOnce(mockEntities); + prisma.entity.findMany.mockResolvedValueOnce([]); + prisma.entity.count.mockResolvedValue(1); + const response = await fastify.inject({ + method: 'GET', + url: '/entities', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body).toMatchSnapshot(); + }); + }); +}); +//# sourceMappingURL=entities.test.js.map \ No newline at end of file diff --git a/dist/routes/entities.test.js.map b/dist/routes/entities.test.js.map new file mode 100644 index 0000000..a645a1c --- /dev/null +++ b/dist/routes/entities.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"entities.test.js","sourceRoot":"","sources":["../../src/routes/entities.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/routes/entity.d.ts b/dist/routes/entity.d.ts new file mode 100644 index 0000000..68b2075 --- /dev/null +++ b/dist/routes/entity.d.ts @@ -0,0 +1,8 @@ +import type { FastifyPluginAsync } from 'fastify'; +import type { AccessTransformer, EntityTransformer } from '../types/transformers.js'; +type EntityRouteOptions = { + accessTransformer: AccessTransformer; + entityTransformers?: EntityTransformer[]; +}; +declare const entity: FastifyPluginAsync; +export default entity; diff --git a/dist/routes/entity.js b/dist/routes/entity.js new file mode 100644 index 0000000..5e3fcb0 --- /dev/null +++ b/dist/routes/entity.js @@ -0,0 +1,52 @@ +import { z } from 'zod/v4'; +import { baseEntityTransformer, resolveEntityReferences } from '../transformers/default.js'; +import { createInternalError, createNotFoundError } from '../utils/errors.js'; +const paramsSchema = z.object({ + id: z.url(), +}); +const entity = async (fastify, opts) => { + const { accessTransformer, entityTransformers = [] } = opts; + fastify.withTypeProvider().get('/entity/:id', { + schema: { + params: paramsSchema, + }, + }, async (request, reply) => { + const { id } = request.params; + try { + const entity = await fastify.prisma.entity.findFirst({ + where: { + rocrateId: id, + }, + }); + if (!entity) { + return reply.code(404).send(createNotFoundError('The requested entity was not found', id)); + } + const refMap = await resolveEntityReferences([entity], fastify.prisma); + const base = baseEntityTransformer(entity); + const standardEntity = { + ...base, + memberOf: base.memberOf ? (refMap.get(base.memberOf) ?? null) : null, + rootCollection: base.rootCollection ? (refMap.get(base.rootCollection) ?? null) : null, + }; + const authorisedEntity = await accessTransformer(standardEntity, { + request, + fastify, + }); + let result = authorisedEntity; + for (const transformer of entityTransformers) { + result = await transformer(result, { + request, + fastify, + }); + } + return result; + } + catch (error) { + const err = error; + fastify.log.error(`Database error: ${err.message}`); + return reply.code(500).send(createInternalError()); + } + }); +}; +export default entity; +//# sourceMappingURL=entity.js.map \ No newline at end of file diff --git a/dist/routes/entity.js.map b/dist/routes/entity.js.map new file mode 100644 index 0000000..9f8cbf7 --- /dev/null +++ b/dist/routes/entity.js.map @@ -0,0 +1 @@ +{"version":3,"file":"entity.js","sourceRoot":"","sources":["../../src/routes/entity.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAE5F,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9E,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE;CACZ,CAAC,CAAC;AAOH,MAAM,MAAM,GAA2C,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAC7E,MAAM,EAAE,iBAAiB,EAAE,kBAAkB,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC;IAC5D,OAAO,CAAC,gBAAgB,EAAmB,CAAC,GAAG,CAC7C,aAAa,EACb;QACE,MAAM,EAAE;YACN,MAAM,EAAE,YAAY;SACrB;KACF,EACD,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QAE9B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;gBACnD,KAAK,EAAE;oBACL,SAAS,EAAE,EAAE;iBACd;aACF,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,oCAAoC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC7F,CAAC;YAGD,MAAM,MAAM,GAAG,MAAM,uBAAuB,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;YAEvE,MAAM,IAAI,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;YAC3C,MAAM,cAAc,GAAG;gBACrB,GAAG,IAAI;gBACP,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;gBACpE,cAAc,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;aACvF,CAAC;YACF,MAAM,gBAAgB,GAAG,MAAM,iBAAiB,CAAC,cAAc,EAAE;gBAC/D,OAAO;gBACP,OAAO;aACR,CAAC,CAAC;YAEH,IAAI,MAAM,GAAG,gBAAgB,CAAC;YAC9B,KAAK,MAAM,WAAW,IAAI,kBAAkB,EAAE,CAAC;gBAC7C,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE;oBACjC,OAAO;oBACP,OAAO;iBACR,CAAC,CAAC;YACL,CAAC;YAED,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,GAAG,GAAG,KAAc,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,mBAAmB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAEpD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC;QACrD,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,MAAM,CAAC"} \ No newline at end of file diff --git a/dist/routes/entity.test.d.ts b/dist/routes/entity.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/routes/entity.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/routes/entity.test.js b/dist/routes/entity.test.js new file mode 100644 index 0000000..d88e2eb --- /dev/null +++ b/dist/routes/entity.test.js @@ -0,0 +1,134 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { fastify, fastifyAfter, fastifyBefore, prisma } from '../test/helpers/fastify.js'; +import { AllPublicAccessTransformer } from '../transformers/default.js'; +import entityRoute from './entity.js'; +describe('Entity Route', () => { + beforeEach(async () => { + await fastifyBefore(); + await fastify.register(entityRoute, { accessTransformer: AllPublicAccessTransformer }); + }); + afterEach(async () => { + await fastifyAfter(); + }); + describe('GET /entity/:id', () => { + it('should return entity when found', async () => { + const mockEntity = { + id: 1, + rocrateId: 'http://example.com/entity/123', + name: 'Test Entity', + description: 'A test entity', + entityType: 'http://schema.org/Person', + fileId: null, + memberOf: null, + rootCollection: null, + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + meta: {}, + }; + prisma.entity.findFirst.mockResolvedValue(mockEntity); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/123')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body).toMatchSnapshot(); + expect(prisma.entity.findFirst).toHaveBeenCalledWith({ + where: { + rocrateId: 'http://example.com/entity/123', + }, + }); + }); + it('should return 404 when entity not found', async () => { + console.log('🪚 ♊'); + prisma.entity.findFirst.mockResolvedValue(null); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/nonexistent')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(404); + expect(body).toMatchSnapshot(); + }); + it('should return 500 when database error occurs', async () => { + prisma.entity.findFirst.mockRejectedValue(new Error('Database connection failed')); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/123')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(500); + expect(body).toMatchSnapshot(); + }); + it('should validate ID parameter format', async () => { + const response = await fastify.inject({ + method: 'GET', + url: '/entity/invalid-id', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(400); + expect(body.error).toBe('Bad Request'); + }); + it('should apply custom entity transformers', async () => { + const customTransformer = async (entity) => ({ + ...entity, + tested: true, + }); + await fastifyBefore(); + await fastify.register(entityRoute, { + accessTransformer: AllPublicAccessTransformer, + entityTransformers: [customTransformer], + }); + const mockEntity = { + id: 1, + rocrateId: 'http://example.com/entity/123', + name: 'Test Entity', + description: 'A test entity', + entityType: 'http://pcdm.org/models#Collection', + memberOf: null, + rootCollection: null, + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + }; + prisma.entity.findFirst.mockResolvedValue(mockEntity); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/123')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body).toMatchSnapshot(); + }); + it('should return null for memberOf/rootCollection when parent entity not found', async () => { + const mockEntity = { + id: 1, + rocrateId: 'http://example.com/entity/123', + name: 'Test Entity', + description: 'A test entity', + entityType: 'http://pcdm.org/models#Object', + fileId: null, + memberOf: 'http://example.com/entity/deleted', + rootCollection: 'http://example.com/entity/deleted', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + meta: {}, + }; + prisma.entity.findFirst.mockResolvedValue(mockEntity); + prisma.entity.findMany.mockResolvedValue([]); + const response = await fastify.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/123')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body).toMatchSnapshot(); + }); + }); +}); +//# sourceMappingURL=entity.test.js.map \ No newline at end of file diff --git a/dist/routes/entity.test.js.map b/dist/routes/entity.test.js.map new file mode 100644 index 0000000..c1ed35b --- /dev/null +++ b/dist/routes/entity.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"entity.test.js","sourceRoot":"","sources":["../../src/routes/entity.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/routes/file.d.ts b/dist/routes/file.d.ts new file mode 100644 index 0000000..0f33a5e --- /dev/null +++ b/dist/routes/file.d.ts @@ -0,0 +1,7 @@ +import type { FastifyPluginAsync } from 'fastify'; +import type { FileHandler } from '../types/fileHandlers.js'; +type FileRouteOptions = { + fileHandler: FileHandler; +}; +declare const file: FastifyPluginAsync; +export default file; diff --git a/dist/routes/file.js b/dist/routes/file.js new file mode 100644 index 0000000..9032428 --- /dev/null +++ b/dist/routes/file.js @@ -0,0 +1,113 @@ +import { createReadStream } from 'node:fs'; +import { z } from 'zod/v4'; +import { createInternalError, createNotFoundError } from '../utils/errors.js'; +const paramsSchema = z.object({ + id: z.url(), +}); +const querySchema = z.object({ + disposition: z.enum(['inline', 'attachment']).optional().default('inline'), + filename: z.string().optional(), + noRedirect: z.coerce.boolean().optional().default(false), +}); +const setFileHeaders = (reply, metadata) => { + reply.header('Content-Type', metadata.contentType); + reply.header('Content-Length', metadata.contentLength.toString()); + if (metadata.etag) { + reply.header('ETag', metadata.etag); + } + if (metadata.lastModified) { + reply.header('Last-Modified', metadata.lastModified.toUTCString()); + } +}; +const file = async (fastify, opts) => { + const { fileHandler } = opts; + fastify.withTypeProvider().head('/file/:id', { + schema: { + params: paramsSchema, + }, + }, async (request, reply) => { + const { id } = request.params; + try { + const file = await fastify.prisma.file.findFirst({ + where: { + fileId: id, + }, + }); + if (!file) { + return reply.code(404).send(createNotFoundError('The requested file was not found', id)); + } + const metadata = await fileHandler.head(file, { + request, + fastify, + }); + if (!metadata) { + return reply.code(404).send(createNotFoundError('The requested file metadata was not found', id)); + } + setFileHeaders(reply, metadata); + return reply.code(200).send(); + } + catch (error) { + const err = error; + fastify.log.error(`File metadata retrieval error: ${err.message}`); + return reply.code(500).send(createInternalError()); + } + }); + fastify.withTypeProvider().get('/file/:id', { + schema: { + params: paramsSchema, + querystring: querySchema, + }, + }, async (request, reply) => { + const { id } = request.params; + try { + const file = await fastify.prisma.file.findFirst({ + where: { + fileId: id, + }, + }); + if (!file) { + return reply.code(404).send(createNotFoundError('The requested file was not found', id)); + } + const result = await fileHandler.get(file, { + request, + fastify, + }); + if (!result) { + return reply.code(404).send(createNotFoundError('The requested file could not be retrieved', id)); + } + if (result.type === 'redirect') { + if (request.query.noRedirect) { + return reply.code(200).send({ location: result.url }); + } + return reply.code(302).redirect(result.url); + } + const { disposition, filename: customFilename } = request.query; + const filename = customFilename || file.filename; + setFileHeaders(reply, result.metadata); + reply.header('Content-Disposition', `${disposition}; filename="${filename}"`); + if (result.type === 'stream') { + return reply.code(200).send(result.stream); + } + if (result.type === 'file') { + if (result.accelPath) { + reply.header('X-Accel-Redirect', result.accelPath); + return reply.code(200).send(); + } + const stream = createReadStream(result.path); + return reply.code(200).send(stream); + } + console.error(`Unexpected file result type: ${result.type}`); + reply.removeHeader('Content-Disposition'); + reply.removeHeader('Content-Type'); + reply.removeHeader('Content-Length'); + return reply.code(500).send(createInternalError()); + } + catch (error) { + const err = error; + fastify.log.error(`File retrieval error: ${err}`); + return reply.code(500).send(createInternalError()); + } + }); +}; +export default file; +//# sourceMappingURL=file.js.map \ No newline at end of file diff --git a/dist/routes/file.js.map b/dist/routes/file.js.map new file mode 100644 index 0000000..88197cf --- /dev/null +++ b/dist/routes/file.js.map @@ -0,0 +1 @@ +{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/routes/file.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/routes/file.test.d.ts b/dist/routes/file.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/routes/file.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/routes/file.test.js b/dist/routes/file.test.js new file mode 100644 index 0000000..8d52bdb --- /dev/null +++ b/dist/routes/file.test.js @@ -0,0 +1,319 @@ +import { createReadStream } from 'node:fs'; +import { Readable } from 'node:stream'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { fastify, fastifyAfter, fastifyBefore, prisma } from '../test/helpers/fastify.js'; +import fileRoute from './file.js'; +vi.mock('node:fs', () => ({ + createReadStream: vi.fn(), +})); +describe('File Route', () => { + const mockFileHandler = { + get: vi.fn(), + head: vi.fn(), + }; + beforeEach(async () => { + await fastifyBefore(); + await fastify.register(fileRoute, { fileHandler: mockFileHandler }); + vi.clearAllMocks(); + }); + afterEach(async () => { + await fastifyAfter(); + }); + const mockFile = { + id: 1, + fileId: 'http://example.com/file/test.wav', + filename: 'test.wav', + mediaType: 'audio/wav', + size: BigInt(1024), + memberOf: 'http://example.com/collection', + rootCollection: 'http://example.com/collection', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + meta: { storagePath: '/data/files/test.wav' }, + createdAt: new Date(), + updatedAt: new Date(), + }; + describe('GET /file/:id', () => { + it('should stream file content successfully', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + const mockStream = Readable.from(['test file content']); + const mockResult = { + type: 'stream', + stream: mockStream, + metadata: { + contentType: 'audio/wav', + contentLength: 17, + etag: '"abc123"', + lastModified: new Date('2025-01-01'), + }, + }; + vi.mocked(mockFileHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-type']).toBe('audio/wav'); + expect(response.headers['content-length']).toBe('17'); + expect(response.headers.etag).toBe('"abc123"'); + expect(response.headers['content-disposition']).toBe('inline; filename="test.wav"'); + expect(response.body).toBe('test file content'); + expect(mockFileHandler.get).toHaveBeenCalledWith(mockFile, expect.objectContaining({ + request: expect.any(Object), + fastify: expect.any(Object), + })); + }); + it('should handle attachment disposition', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + const mockStream = Readable.from(['test']); + const mockResult = { + type: 'stream', + stream: mockStream, + metadata: { + contentType: 'audio/wav', + contentLength: 4, + }, + }; + vi.mocked(mockFileHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}?disposition=attachment`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-disposition']).toBe('attachment; filename="test.wav"'); + }); + it('should use custom filename when provided', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + const mockStream = Readable.from(['test']); + const mockResult = { + type: 'stream', + stream: mockStream, + metadata: { + contentType: 'audio/wav', + contentLength: 4, + }, + }; + vi.mocked(mockFileHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}?filename=custom-name.wav`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-disposition']).toBe('inline; filename="custom-name.wav"'); + }); + it('should handle redirect response with noRedirect=false (default)', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + const mockResult = { + type: 'redirect', + url: 'https://storage.example.com/files/test.wav', + }; + vi.mocked(mockFileHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}`, + }); + expect(response.statusCode).toBe(302); + expect(response.headers.location).toBe('https://storage.example.com/files/test.wav'); + }); + it('should handle redirect response with noRedirect=true', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + const mockResult = { + type: 'redirect', + url: 'https://storage.example.com/files/test.wav', + }; + vi.mocked(mockFileHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}?noRedirect=true`, + }); + expect(response.statusCode).toBe(200); + const body = JSON.parse(response.body); + expect(body).toEqual({ location: 'https://storage.example.com/files/test.wav' }); + }); + it('should handle file path response without nginx X-Accel-Redirect', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + const mockStream = Readable.from(['file content']); + vi.mocked(createReadStream).mockReturnValue(mockStream); + const mockResult = { + type: 'file', + path: '/data/files/test.wav', + metadata: { + contentType: 'audio/wav', + contentLength: 1024, + }, + }; + vi.mocked(mockFileHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-type']).toBe('audio/wav'); + expect(response.headers['content-length']).toBe('1024'); + expect(response.headers['x-accel-redirect']).toBeUndefined(); + expect(createReadStream).toHaveBeenCalledWith('/data/files/test.wav'); + }); + it('should handle file path response with nginx X-Accel-Redirect', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + const mockResult = { + type: 'file', + path: '/data/files/test.wav', + accelPath: '/internal/files/test.wav', + metadata: { + contentType: 'audio/wav', + contentLength: 1024, + }, + }; + vi.mocked(mockFileHandler.get).mockResolvedValue(mockResult); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-type']).toBe('audio/wav'); + expect(response.headers['content-length']).toBe('0'); + expect(response.headers['x-accel-redirect']).toBe('/internal/files/test.wav'); + expect(response.body).toBe(''); + }); + it('should return 404 when entity not found', async () => { + prisma.entity.findFirst.mockResolvedValue(null); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/nonexistent')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(404); + expect(body.error.code).toBe('NOT_FOUND'); + expect(body.error.message).toBe('The requested file was not found'); + expect(mockFileHandler.get).not.toHaveBeenCalled(); + }); + it('should return 500 when database error occurs', async () => { + prisma.file.findFirst.mockRejectedValue(new Error('Database connection failed')); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(500); + expect(body.error.code).toBe('INTERNAL_ERROR'); + }); + it('should return 500 when fileHandler throws error', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + vi.mocked(mockFileHandler.get).mockRejectedValue(new Error('File not found in storage')); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(500); + expect(body.error.code).toBe('INTERNAL_ERROR'); + }); + it('should validate ID parameter format', async () => { + const response = await fastify.inject({ + method: 'GET', + url: '/file/invalid-id', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(400); + expect(body.error).toBe('Bad Request'); + }); + it('should validate disposition parameter', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}?disposition=invalid`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(400); + expect(body.error).toBe('Bad Request'); + }); + it('should return 404 when get handler returns false', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + vi.mocked(mockFileHandler.get).mockResolvedValue(false); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(404); + expect(body.error.code).toBe('NOT_FOUND'); + expect(body.error.message).toBe('The requested file could not be retrieved'); + }); + }); + describe('HEAD /file/:id', () => { + it('should return headers without body using head handler', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + const mockMetadata = { + contentType: 'audio/wav', + contentLength: 17, + etag: '"abc123"', + lastModified: new Date('2025-01-01'), + }; + vi.mocked(mockFileHandler.head).mockResolvedValue(mockMetadata); + const response = await fastify.inject({ + method: 'HEAD', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}`, + }); + expect(response.statusCode).toBe(200); + expect(response.headers['content-type']).toBe('audio/wav'); + expect(response.headers['content-length']).toBe('17'); + expect(response.headers.etag).toBe('"abc123"'); + expect(response.body).toBe(''); + expect(mockFileHandler.head).toHaveBeenCalledWith(mockFile, expect.objectContaining({ + request: expect.any(Object), + fastify: expect.any(Object), + })); + }); + it('should return 404 when file not found', async () => { + prisma.file.findFirst.mockResolvedValue(null); + const response = await fastify.inject({ + method: 'HEAD', + url: `/file/${encodeURIComponent('http://example.com/file/nonexistent')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(404); + expect(body.error.code).toBe('NOT_FOUND'); + expect(mockFileHandler.get).not.toHaveBeenCalled(); + }); + it('should return 500 when head handler throws error', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + vi.mocked(mockFileHandler.head).mockRejectedValue(new Error('Failed to get metadata')); + const response = await fastify.inject({ + method: 'HEAD', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(500); + expect(body.error.code).toBe('INTERNAL_ERROR'); + }); + it('should return 404 when head handler returns false', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + vi.mocked(mockFileHandler.head).mockResolvedValue(false); + const response = await fastify.inject({ + method: 'HEAD', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(404); + expect(body.error.code).toBe('NOT_FOUND'); + expect(body.error.message).toBe('The requested file metadata was not found'); + }); + }); + describe('GET /file/:id - exhaustiveness check', () => { + it('should handle unexpected file result type', async () => { + prisma.file.findFirst.mockResolvedValue(mockFile); + const invalidResult = { + type: 'invalid', + metadata: { contentType: 'audio/wav', contentLength: 1024 }, + }; + vi.mocked(mockFileHandler.get).mockResolvedValue(invalidResult); + const response = await fastify.inject({ + method: 'GET', + url: `/file/${encodeURIComponent('http://example.com/file/test.wav')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(500); + expect(body.error.code).toBe('INTERNAL_ERROR'); + }); + }); +}); +//# sourceMappingURL=file.test.js.map \ No newline at end of file diff --git a/dist/routes/file.test.js.map b/dist/routes/file.test.js.map new file mode 100644 index 0000000..f9ed6c4 --- /dev/null +++ b/dist/routes/file.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"file.test.js","sourceRoot":"","sources":["../../src/routes/file.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/routes/files.d.ts b/dist/routes/files.d.ts new file mode 100644 index 0000000..7403258 --- /dev/null +++ b/dist/routes/files.d.ts @@ -0,0 +1,8 @@ +import type { FastifyPluginAsync } from 'fastify'; +import type { FileAccessTransformer, FileTransformer } from '../types/transformers.js'; +type FilesRouteOptions = { + fileAccessTransformer: FileAccessTransformer; + fileTransformers?: FileTransformer[]; +}; +declare const files: FastifyPluginAsync; +export default files; diff --git a/dist/routes/files.js b/dist/routes/files.js new file mode 100644 index 0000000..ec82da7 --- /dev/null +++ b/dist/routes/files.js @@ -0,0 +1,62 @@ +import { z } from 'zod/v4'; +import { baseFileTransformer } from '../transformers/default.js'; +import { createInternalError } from '../utils/errors.js'; +const querySchema = z.object({ + memberOf: z.url().optional(), + limit: z.coerce.number().int().min(1).max(1000).default(100), + offset: z.coerce.number().int().min(0).default(0), + sort: z.enum(['id', 'filename', 'createdAt', 'updatedAt']).default('id'), + order: z.enum(['asc', 'desc']).default('asc'), +}); +const files = async (fastify, opts) => { + const { fileAccessTransformer, fileTransformers = [] } = opts; + fastify.withTypeProvider().get('/files', { + schema: { + querystring: querySchema, + }, + }, async (request, reply) => { + const { memberOf, limit, offset, sort, order } = request.query; + try { + const where = {}; + if (memberOf) { + where.memberOf = memberOf; + } + const sortField = sort === 'id' ? 'fileId' : sort; + const dbFiles = await fastify.prisma.file.findMany({ + where, + orderBy: { + [sortField]: order, + }, + skip: offset, + take: limit, + }); + const total = await fastify.prisma.file.count({ where }); + const filesWithAccess = await Promise.all(dbFiles.map(async (dbFile) => { + const standardFile = baseFileTransformer(dbFile); + const authorisedFile = await fileAccessTransformer(standardFile, { + request, + fastify, + }); + let result = authorisedFile; + for (const transformer of fileTransformers) { + result = await transformer(result, { + request, + fastify, + }); + } + return result; + })); + return { + total, + files: filesWithAccess, + }; + } + catch (error) { + const err = error; + fastify.log.error(`Database error: ${err.message}`); + return reply.code(500).send(createInternalError()); + } + }); +}; +export default files; +//# sourceMappingURL=files.js.map \ No newline at end of file diff --git a/dist/routes/files.js.map b/dist/routes/files.js.map new file mode 100644 index 0000000..963f053 --- /dev/null +++ b/dist/routes/files.js.map @@ -0,0 +1 @@ +{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/routes/files.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/routes/files.test.d.ts b/dist/routes/files.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/routes/files.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/routes/files.test.js b/dist/routes/files.test.js new file mode 100644 index 0000000..c54c04a --- /dev/null +++ b/dist/routes/files.test.js @@ -0,0 +1,244 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { fastify, fastifyAfter, fastifyBefore, prisma } from '../test/helpers/fastify.js'; +import { AllPublicFileAccessTransformer } from '../transformers/default.js'; +import filesRoute from './files.js'; +describe('Files Route', () => { + beforeEach(async () => { + await fastifyBefore(); + await fastify.register(filesRoute, { fileAccessTransformer: AllPublicFileAccessTransformer }); + }); + afterEach(async () => { + await fastifyAfter(); + }); + const mockFile1 = { + id: 1, + fileId: 'http://example.com/file1.wav', + filename: 'file1.wav', + mediaType: 'audio/wav', + size: BigInt(1024), + memberOf: 'http://example.com/collection/1', + rootCollection: 'http://example.com/collection/1', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + meta: {}, + createdAt: new Date('2025-01-01'), + updatedAt: new Date('2025-01-01'), + }; + const mockFile2 = { + id: 2, + fileId: 'http://example.com/file2.txt', + filename: 'file2.txt', + mediaType: 'text/plain', + size: BigInt(512), + memberOf: 'http://example.com/collection/1', + rootCollection: 'http://example.com/collection/1', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + meta: {}, + createdAt: new Date('2025-01-02'), + updatedAt: new Date('2025-01-02'), + }; + describe('GET /files', () => { + it('should list all files successfully', async () => { + prisma.file.findMany.mockResolvedValue([mockFile1, mockFile2]); + prisma.file.count.mockResolvedValue(2); + const response = await fastify.inject({ + method: 'GET', + url: '/files', + }); + expect(response.statusCode).toBe(200); + const body = JSON.parse(response.body); + expect(body.total).toBe(2); + expect(body.files).toHaveLength(2); + expect(body.files[0]).toMatchObject({ + id: 'http://example.com/file1.wav', + filename: 'file1.wav', + mediaType: 'audio/wav', + size: 1024, + memberOf: 'http://example.com/collection/1', + rootCollection: 'http://example.com/collection/1', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + access: { + content: true, + }, + }); + expect(body.files[0]).not.toHaveProperty('metadataLicenseId'); + }); + it('should filter files by memberOf', async () => { + prisma.file.findMany.mockResolvedValue([mockFile1]); + prisma.file.count.mockResolvedValue(1); + const response = await fastify.inject({ + method: 'GET', + url: '/files?memberOf=http://example.com/collection/1', + }); + expect(response.statusCode).toBe(200); + const body = JSON.parse(response.body); + expect(body.total).toBe(1); + expect(prisma.file.findMany).toHaveBeenCalledWith(expect.objectContaining({ + where: { memberOf: 'http://example.com/collection/1' }, + })); + }); + it('should support pagination with limit and offset', async () => { + prisma.file.findMany.mockResolvedValue([mockFile2]); + prisma.file.count.mockResolvedValue(2); + const response = await fastify.inject({ + method: 'GET', + url: '/files?limit=1&offset=1', + }); + expect(response.statusCode).toBe(200); + const body = JSON.parse(response.body); + expect(body.total).toBe(2); + expect(body.files).toHaveLength(1); + expect(prisma.file.findMany).toHaveBeenCalledWith(expect.objectContaining({ + skip: 1, + take: 1, + })); + }); + it('should support sorting by filename ascending', async () => { + prisma.file.findMany.mockResolvedValue([mockFile1, mockFile2]); + prisma.file.count.mockResolvedValue(2); + const response = await fastify.inject({ + method: 'GET', + url: '/files?sort=filename&order=asc', + }); + expect(response.statusCode).toBe(200); + expect(prisma.file.findMany).toHaveBeenCalledWith(expect.objectContaining({ + orderBy: { filename: 'asc' }, + })); + }); + it('should support sorting by id (maps to fileId)', async () => { + prisma.file.findMany.mockResolvedValue([mockFile1, mockFile2]); + prisma.file.count.mockResolvedValue(2); + const response = await fastify.inject({ + method: 'GET', + url: '/files?sort=id&order=desc', + }); + expect(response.statusCode).toBe(200); + expect(prisma.file.findMany).toHaveBeenCalledWith(expect.objectContaining({ + orderBy: { fileId: 'desc' }, + })); + }); + it('should return empty list when no files found', async () => { + prisma.file.findMany.mockResolvedValue([]); + prisma.file.count.mockResolvedValue(0); + const response = await fastify.inject({ + method: 'GET', + url: '/files', + }); + expect(response.statusCode).toBe(200); + const body = JSON.parse(response.body); + expect(body.total).toBe(0); + expect(body.files).toHaveLength(0); + }); + it('should not include metadataLicenseId in response', async () => { + prisma.file.findMany.mockResolvedValue([mockFile1]); + prisma.file.count.mockResolvedValue(1); + const response = await fastify.inject({ + method: 'GET', + url: '/files', + }); + expect(response.statusCode).toBe(200); + const body = JSON.parse(response.body); + expect(body.files[0]).toMatchObject({ + id: 'http://example.com/file1.wav', + filename: 'file1.wav', + memberOf: 'http://example.com/collection/1', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + }); + expect(body.files[0]).not.toHaveProperty('metadataLicenseId'); + }); + it('should return 500 when database error occurs', async () => { + prisma.file.findMany.mockRejectedValue(new Error('Database connection failed')); + const response = await fastify.inject({ + method: 'GET', + url: '/files', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(500); + expect(body.error.code).toBe('INTERNAL_ERROR'); + }); + it('should validate limit parameter', async () => { + const response = await fastify.inject({ + method: 'GET', + url: '/files?limit=2000', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(400); + expect(body.error).toBe('Bad Request'); + }); + it('should validate memberOf parameter format', async () => { + const response = await fastify.inject({ + method: 'GET', + url: '/files?memberOf=invalid-url', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(400); + expect(body.error).toBe('Bad Request'); + }); + it('should validate sort parameter', async () => { + const response = await fastify.inject({ + method: 'GET', + url: '/files?sort=invalid', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(400); + expect(body.error).toBe('Bad Request'); + }); + }); + describe('Custom File Transformers', () => { + it('should apply custom file transformers', async () => { + await fastifyBefore(); + const customTransformer = (file) => ({ + ...file, + customField: 'test-value', + uppercaseFilename: file.filename.toUpperCase(), + }); + await fastify.register(filesRoute, { + fileAccessTransformer: AllPublicFileAccessTransformer, + fileTransformers: [customTransformer], + }); + prisma.file.findMany.mockResolvedValue([mockFile1]); + prisma.file.count.mockResolvedValue(1); + const response = await fastify.inject({ + method: 'GET', + url: '/files', + }); + expect(response.statusCode).toBe(200); + const body = JSON.parse(response.body); + expect(body.files[0]).toMatchObject({ + id: 'http://example.com/file1.wav', + filename: 'file1.wav', + customField: 'test-value', + uppercaseFilename: 'FILE1.WAV', + }); + await fastifyAfter(); + }); + it('should apply custom file access transformer', async () => { + await fastifyBefore(); + const customFileAccessTransformer = (file) => ({ + ...file, + access: { + content: false, + contentAuthorizationUrl: 'https://example.com/request-access', + }, + }); + await fastify.register(filesRoute, { + fileAccessTransformer: customFileAccessTransformer, + }); + prisma.file.findMany.mockResolvedValue([mockFile1]); + prisma.file.count.mockResolvedValue(1); + const response = await fastify.inject({ + method: 'GET', + url: '/files', + }); + expect(response.statusCode).toBe(200); + const body = JSON.parse(response.body); + expect(body.files[0]).toMatchObject({ + access: { + content: false, + contentAuthorizationUrl: 'https://example.com/request-access', + }, + }); + await fastifyAfter(); + }); + }); +}); +//# sourceMappingURL=files.test.js.map \ No newline at end of file diff --git a/dist/routes/files.test.js.map b/dist/routes/files.test.js.map new file mode 100644 index 0000000..7f29731 --- /dev/null +++ b/dist/routes/files.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"files.test.js","sourceRoot":"","sources":["../../src/routes/files.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/routes/search.d.ts b/dist/routes/search.d.ts new file mode 100644 index 0000000..8350f7c --- /dev/null +++ b/dist/routes/search.d.ts @@ -0,0 +1,11 @@ +import type { FastifyPluginAsync } from 'fastify'; +import type { AccessTransformer, EntityTransformer } from '../types/transformers.js'; +import { OpensearchQueryBuilder, QueryBuilderOptions } from '../utils/queryBuilder.js'; +type SearchRouteOptions = { + accessTransformer: AccessTransformer; + entityTransformers?: EntityTransformer[]; + queryBuilderClass?: typeof OpensearchQueryBuilder; + queryBuilderOptions?: QueryBuilderOptions; +}; +declare const search: FastifyPluginAsync; +export default search; diff --git a/dist/routes/search.js b/dist/routes/search.js new file mode 100644 index 0000000..7060cfa --- /dev/null +++ b/dist/routes/search.js @@ -0,0 +1,147 @@ +import { z } from 'zod/v4'; +import { baseEntityTransformer, resolveEntityReferences } from '../transformers/default.js'; +import { createInternalError } from '../utils/errors.js'; +import { OpensearchQueryBuilder } from '../utils/queryBuilder.js'; +const boundingBoxSchema = z.object({ + topRight: z.object({ + lat: z.number(), + lng: z.number(), + }), + bottomLeft: z.object({ + lat: z.number(), + lng: z.number(), + }), +}); +const searchParamsSchema = z.object({ + searchType: z.enum(['basic', 'advanced']).default('basic'), + query: z.string(), + filters: z.record(z.string(), z.array(z.string())).optional(), + boundingBox: boundingBoxSchema.optional(), + geohashPrecision: z.number().int().min(0).max(12).default(5), + limit: z.number().int().min(1).max(1000).default(100), + offset: z.number().int().min(0).default(0), + sort: z.enum(['id', 'name', 'createdAt', 'updatedAt', 'relevance']).default('relevance'), + order: z.enum(['asc', 'desc']).default('asc'), +}); +const search = async (fastify, opts) => { + const { accessTransformer, entityTransformers = [], queryBuilderClass = OpensearchQueryBuilder, queryBuilderOptions } = opts; + const queryBuilder = new queryBuilderClass(queryBuilderOptions); + fastify.withTypeProvider().post('/search', { + schema: { + body: searchParamsSchema, + }, + }, async (request, reply) => { + const { searchType, query, filters, boundingBox, geohashPrecision, limit, offset, sort, order } = request.body; + try { + const opensearchQuery = { + index: 'entities', + body: { + query: queryBuilder.buildQuery(searchType, query, filters, boundingBox), + aggs: queryBuilder.buildAggregations(geohashPrecision, boundingBox), + highlight: { + fields: { + name: {}, + description: {}, + }, + }, + sort: queryBuilder.buildSort(sort, order), + from: offset, + size: limit, + }, + }; + console.log(JSON.stringify(opensearchQuery, null, 2)); + const response = await fastify.opensearch.search(opensearchQuery); + if (!response.body?.hits?.hits) { + throw new Error('Invalid search response: missing hits data'); + } + const rocrateIds = response.body.hits.hits + .map((hit) => hit._source?.rocrateId) + .filter(Boolean); + const dbEntities = await fastify.prisma.entity.findMany({ + where: { + rocrateId: { + in: rocrateIds, + }, + }, + }); + const entityMap = new Map(dbEntities.map((entity) => [entity.rocrateId, entity])); + const refMap = await resolveEntityReferences(dbEntities, fastify.prisma); + const entities = await Promise.all(response.body.hits.hits.map(async (hit) => { + if (!hit._source?.rocrateId) { + throw new Error('Missing rocrateId in search hit'); + } + const dbEntity = entityMap.get(hit._source.rocrateId); + if (!dbEntity) { + fastify.log.warn(`Entity ${hit._source.rocrateId} found in OpenSearch but not in database`); + return null; + } + const base = baseEntityTransformer(dbEntity); + const standardEntity = { + ...base, + memberOf: base.memberOf ? (refMap.get(base.memberOf) ?? null) : null, + rootCollection: base.rootCollection ? (refMap.get(base.rootCollection) ?? null) : null, + }; + const authorisedEntity = await accessTransformer(standardEntity, { + request, + fastify, + }); + let result = authorisedEntity; + for (const transformer of entityTransformers) { + result = await transformer(result, { + request, + fastify, + }); + } + return { + ...result, + searchExtra: { + score: hit._score, + highlight: hit.highlight, + }, + }; + })).then((results) => results.filter(Boolean)); + const facets = {}; + if (response.body.aggregations) { + Object.keys(response.body.aggregations).forEach((key) => { + if (key !== 'geohash_grid') { + const agg = response.body.aggregations?.[key]; + if (agg?.buckets && Array.isArray(agg.buckets)) { + facets[key] = agg.buckets.map((bucket) => ({ + name: bucket.key, + count: bucket.doc_count, + })); + } + } + }); + } + let geohashGrid; + if (response.body.aggregations?.geohash_grid) { + const geohashAgg = response.body.aggregations.geohash_grid; + if (geohashAgg?.buckets && Array.isArray(geohashAgg.buckets)) { + geohashAgg.buckets.forEach((bucket) => { + geohashGrid ||= {}; + geohashGrid[bucket.key] = bucket.doc_count; + }); + } + } + const total = typeof response.body.hits.total === 'number' + ? response.body.hits.total + : response.body.hits.total?.value || 0; + const result = { + total, + searchTime: response.body.took, + entities, + facets: Object.keys(facets).length > 0 ? facets : undefined, + geohashGrid, + }; + return result; + } + catch (error) { + const err = error; + fastify.log.error(`Search error: ${err.message}`); + return reply.code(500).send(createInternalError('Search failed')); + } + }); +}; +export default search; +//# sourceMappingURL=search.js.map \ No newline at end of file diff --git a/dist/routes/search.js.map b/dist/routes/search.js.map new file mode 100644 index 0000000..ca0059b --- /dev/null +++ b/dist/routes/search.js.map @@ -0,0 +1 @@ +{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/routes/search.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/routes/search.test.d.ts b/dist/routes/search.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/routes/search.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/routes/search.test.js b/dist/routes/search.test.js new file mode 100644 index 0000000..0f2a6e3 --- /dev/null +++ b/dist/routes/search.test.js @@ -0,0 +1,748 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { fastify, fastifyAfter, fastifyBefore, opensearch, prisma } from '../test/helpers/fastify.js'; +import { AllPublicAccessTransformer } from '../transformers/default.js'; +import searchRoute from './search.js'; +describe('Search Route', () => { + beforeEach(async () => { + await fastifyBefore(); + await fastify.register(searchRoute, { accessTransformer: AllPublicAccessTransformer }); + }); + afterEach(async () => { + await fastifyAfter(); + }); + describe('POST /search', () => { + it('should perform basic search successfully', async () => { + const mockEntities = [ + { + rocrateId: 'http://example.com/entity/1', + name: 'Test Entity 1', + description: 'A test entity', + entityType: 'http://pcdm.org/models#Collection', + memberOf: null, + rootCollection: null, + metadataLicenseId: null, + contentLicenseId: null, + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-01'), + }, + { + rocrateId: 'http://example.com/entity/2', + name: 'Test Entity 2', + description: 'Another test entity', + entityType: 'http://pcdm.org/models#Object', + memberOf: null, + rootCollection: null, + metadataLicenseId: null, + contentLicenseId: null, + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-01'), + }, + ]; + const mockSearchResponse = { + body: { + took: 10, + hits: { + total: { value: 2 }, + hits: [ + { + _score: 1.5, + _source: { + rocrateId: 'http://example.com/entity/1', + }, + highlight: { + name: ['Test Entity 1'], + }, + }, + { + _score: 1.2, + _source: { + rocrateId: 'http://example.com/entity/2', + }, + highlight: { + description: ['Another test entity'], + }, + }, + ], + }, + aggregations: { + entityType: { + buckets: [ + { key: 'http://pcdm.org/models#Collection', doc_count: 1 }, + { key: 'http://pcdm.org/models#Object', doc_count: 1 }, + ], + }, + }, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + prisma.entity.findMany.mockResolvedValue(mockEntities); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + searchType: 'basic', + }, + }); + expect(response.statusCode).toBe(200); + const body = JSON.parse(response.body); + expect(body).toMatchSnapshot(); + expect(prisma.entity.findMany).toHaveBeenCalledWith({ + where: { + rocrateId: { + in: ['http://example.com/entity/1', 'http://example.com/entity/2'], + }, + }, + }); + expect(opensearch.search).toHaveBeenCalledWith({ + index: 'entities', + body: { + query: { + bool: { + must: [ + { + multi_match: { + query: 'test', + fields: ['name^2', 'description'], + type: 'best_fields', + fuzziness: 'AUTO', + zero_terms_query: 'all', + }, + }, + ], + filter: [], + }, + }, + aggs: { + inLanguage: { terms: { field: 'inLanguage.keyword', size: 20 } }, + mediaType: { terms: { field: 'mediaType.keyword', size: 20 } }, + communicationMode: { terms: { field: 'communicationMode.keyword', size: 20 } }, + entityType: { terms: { field: 'entityType.keyword', size: 20 } }, + }, + highlight: { + fields: { + name: {}, + description: {}, + }, + }, + sort: undefined, + from: 0, + size: 100, + }, + }); + }); + it('should perform advanced search with query string', async () => { + const mockSearchResponse = { + body: { + took: 5, + hits: { + total: { value: 0 }, + hits: [], + }, + aggregations: {}, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + prisma.entity.findMany.mockResolvedValue([]); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'name:test AND description:entity', + searchType: 'advanced', + }, + }); + expect(response.statusCode).toBe(200); + expect(opensearch.search).toHaveBeenCalledWith({ + index: 'entities', + body: { + query: { + bool: { + must: [ + { + query_string: { + query: 'name:test AND description:entity', + fields: ['name^2', 'description'], + default_operator: 'AND', + }, + }, + ], + filter: [], + }, + }, + aggs: { + inLanguage: { terms: { field: 'inLanguage.keyword', size: 20 } }, + mediaType: { terms: { field: 'mediaType.keyword', size: 20 } }, + communicationMode: { terms: { field: 'communicationMode.keyword', size: 20 } }, + entityType: { terms: { field: 'entityType.keyword', size: 20 } }, + }, + highlight: { + fields: { + name: {}, + description: {}, + }, + }, + sort: undefined, + from: 0, + size: 100, + }, + }); + }); + it('should apply filters correctly', async () => { + const mockSearchResponse = { + body: { + took: 8, + hits: { + total: { value: 0 }, + hits: [], + }, + aggregations: {}, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + prisma.entity.findMany.mockResolvedValue([]); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + filters: { + entityType: ['http://pcdm.org/models#Collection'], + inLanguage: ['en', 'fr'], + }, + }, + }); + expect(response.statusCode).toBe(200); + const expectedFilters = [ + { + terms: { + entityType: ['http://pcdm.org/models#Collection'], + }, + }, + { + terms: { + inLanguage: ['en', 'fr'], + }, + }, + ]; + expect(opensearch.search).toHaveBeenCalledWith(expect.objectContaining({ + body: expect.objectContaining({ + query: expect.objectContaining({ + bool: expect.objectContaining({ + filter: expectedFilters, + }), + }), + }), + })); + }); + it('should handle geospatial search with bounding box', async () => { + const mockSearchResponse = { + body: { + took: 12, + hits: { + total: { value: 0 }, + hits: [], + }, + aggregations: { + geohash_grid: { + buckets: [ + { key: 'gbsuv', doc_count: 3 }, + { key: 'gbsvb', doc_count: 1 }, + ], + }, + }, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + prisma.entity.findMany.mockResolvedValue([]); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + boundingBox: { + topRight: { lat: 51.5, lng: 0.1 }, + bottomLeft: { lat: 51.4, lng: 0.0 }, + }, + geohashPrecision: 5, + }, + }); + expect(response.statusCode).toBe(200); + const body = JSON.parse(response.body); + expect(body.geohashGrid).toEqual({ + gbsuv: 3, + gbsvb: 1, + }); + expect(opensearch.search).toHaveBeenCalledWith(expect.objectContaining({ + body: expect.objectContaining({ + query: expect.objectContaining({ + bool: expect.objectContaining({ + filter: [ + { + geo_bounding_box: { + location: { + top_left: { lat: 51.5, lon: 0.0 }, + bottom_right: { lat: 51.4, lon: 0.1 }, + }, + }, + }, + ], + }), + }), + aggs: expect.objectContaining({ + geohash_grid: { + geohash_grid: { + field: 'location', + precision: 5, + bounds: { + top_left: { lat: 51.5, lon: 0.0 }, + bottom_right: { lat: 51.4, lon: 0.1 }, + }, + }, + }, + }), + }), + })); + }); + it('should handle pagination and sorting', async () => { + const mockSearchResponse = { + body: { + took: 6, + hits: { + total: { value: 0 }, + hits: [], + }, + aggregations: {}, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + prisma.entity.findMany.mockResolvedValue([]); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + limit: 50, + offset: 20, + sort: 'name', + order: 'desc', + }, + }); + expect(response.statusCode).toBe(200); + expect(opensearch.search).toHaveBeenCalledWith(expect.objectContaining({ + body: expect.objectContaining({ + from: 20, + size: 50, + sort: [{ 'name.keyword': 'desc' }], + }), + })); + }); + it('should handle opensearch errors', async () => { + opensearch.search.mockRejectedValue(new Error('OpenSearch connection failed')); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + }, + }); + expect(response.statusCode).toBe(500); + const body = JSON.parse(response.body); + expect(body).toMatchSnapshot(); + }); + it('should validate required query parameter', async () => { + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: {}, + }); + expect(response.statusCode).toBe(400); + }); + it('should skip entities not found in database and log warning', async () => { + const mockEntities = [ + { + rocrateId: 'http://example.com/entity/1', + name: 'Test Entity 1', + description: 'A test entity', + entityType: 'http://pcdm.org/models#Collection', + memberOf: null, + rootCollection: null, + metadataLicenseId: null, + contentLicenseId: null, + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-01'), + }, + ]; + const mockSearchResponse = { + body: { + took: 10, + hits: { + total: { value: 2 }, + hits: [ + { + _score: 1.5, + _source: { + rocrateId: 'http://example.com/entity/1', + }, + highlight: {}, + }, + { + _score: 1.2, + _source: { + rocrateId: 'http://example.com/entity/2', + }, + highlight: {}, + }, + ], + }, + aggregations: {}, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + prisma.entity.findMany.mockResolvedValue(mockEntities); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + }, + }); + expect(response.statusCode).toBe(200); + const body = JSON.parse(response.body); + expect(body).toMatchSnapshot(); + }); + it('should handle missing rocrateId in search hit', async () => { + const mockSearchResponse = { + body: { + took: 5, + hits: { + total: { value: 1 }, + hits: [ + { + _score: 1.5, + _source: {}, + }, + ], + }, + aggregations: {}, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + prisma.entity.findMany.mockResolvedValue([]); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + }, + }); + expect(response.statusCode).toBe(500); + console.log(response.body); + }); + it('should handle rocrateId explicitly set to undefined', async () => { + const mockSearchResponse = { + body: { + took: 5, + hits: { + total: { value: 1 }, + hits: [ + { + _score: 1.5, + _source: { + rocrateId: undefined, + name: 'Test Entity', + }, + }, + ], + }, + aggregations: {}, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + }, + }); + expect(response.statusCode).toBe(500); + const body = JSON.parse(response.body); + expect(body).toMatchSnapshot(); + }); + it('should handle invalid search response with missing hits data', async () => { + const mockSearchResponse = { + body: { + took: 5, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + }, + }); + expect(response.statusCode).toBe(500); + const body = JSON.parse(response.body); + expect(body.error.code).toBe('INTERNAL_ERROR'); + expect(body.error.message).toBe('Search failed'); + }); + it('should apply custom entity transformers', async () => { + const customTransformer = async (entity) => ({ + ...entity, + tested: true, + }); + await fastifyBefore(); + await fastify.register(searchRoute, { + accessTransformer: AllPublicAccessTransformer, + entityTransformers: [customTransformer], + }); + const mockEntities = [ + { + rocrateId: 'http://example.com/entity/1', + name: 'Test Entity 1', + description: 'A test entity', + entityType: 'http://pcdm.org/models#Collection', + memberOf: null, + rootCollection: null, + metadataLicenseId: null, + contentLicenseId: null, + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-01'), + }, + ]; + const mockSearchResponse = { + body: { + took: 10, + hits: { + total: { value: 1 }, + hits: [ + { + _score: 1.5, + _source: { + rocrateId: 'http://example.com/entity/1', + }, + highlight: {}, + }, + ], + }, + aggregations: {}, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + prisma.entity.findMany.mockResolvedValue(mockEntities); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body).toMatchSnapshot(); + }); + it('should handle search response with no aggregations field', async () => { + const mockEntities = [ + { + rocrateId: 'http://example.com/entity/1', + name: 'Test Entity 1', + description: 'A test entity', + entityType: 'http://pcdm.org/models#Collection', + memberOf: null, + rootCollection: null, + metadataLicenseId: null, + contentLicenseId: null, + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-01'), + }, + ]; + const mockSearchResponse = { + body: { + took: 10, + hits: { + total: { value: 1 }, + hits: [ + { + _score: 1.5, + _source: { + rocrateId: 'http://example.com/entity/1', + }, + highlight: {}, + }, + ], + }, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + prisma.entity.findMany.mockResolvedValue(mockEntities); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.facets).toBeUndefined(); + expect(body.geohashGrid).toBeUndefined(); + expect(body.entities).toHaveLength(1); + }); + it('should handle search response with malformed aggregation buckets', async () => { + const mockEntities = [ + { + rocrateId: 'http://example.com/entity/1', + name: 'Test Entity 1', + description: 'A test entity', + entityType: 'http://pcdm.org/models#Collection', + memberOf: null, + rootCollection: null, + metadataLicenseId: null, + contentLicenseId: null, + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-01'), + }, + ]; + const mockSearchResponse = { + body: { + took: 10, + hits: { + total: { value: 1 }, + hits: [ + { + _score: 1.5, + _source: { + rocrateId: 'http://example.com/entity/1', + }, + highlight: {}, + }, + ], + }, + aggregations: { + entityType: { + buckets: null, + }, + inLanguage: {}, + }, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + prisma.entity.findMany.mockResolvedValue(mockEntities); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.facets).toBeUndefined(); + expect(body.entities).toHaveLength(1); + }); + it('should handle search response with malformed geohash aggregation buckets', async () => { + const mockEntities = [ + { + rocrateId: 'http://example.com/entity/1', + name: 'Test Entity 1', + description: 'A test entity', + entityType: 'http://pcdm.org/models#Collection', + memberOf: null, + rootCollection: null, + metadataLicenseId: null, + contentLicenseId: null, + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-01'), + }, + ]; + const mockSearchResponse = { + body: { + took: 10, + hits: { + total: { value: 1 }, + hits: [ + { + _score: 1.5, + _source: { + rocrateId: 'http://example.com/entity/1', + }, + highlight: {}, + }, + ], + }, + aggregations: { + geohash_grid: { + buckets: 'invalid', + }, + }, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + prisma.entity.findMany.mockResolvedValue(mockEntities); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.geohashGrid).toBeUndefined(); + expect(body.entities).toHaveLength(1); + }); + it('should return null for memberOf/rootCollection when parent entity not found', async () => { + const mockEntities = [ + { + id: 1, + fileId: null, + meta: {}, + rocrateId: 'http://example.com/entity/1', + name: 'Test Entity 1', + description: 'Entity with missing parent', + entityType: 'http://pcdm.org/models#Object', + memberOf: 'http://example.com/entity/deleted', + rootCollection: 'http://example.com/entity/deleted', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-01'), + }, + ]; + const mockSearchResponse = { + body: { + took: 10, + hits: { + total: { value: 1 }, + hits: [ + { + _score: 1.5, + _source: { + rocrateId: 'http://example.com/entity/1', + }, + }, + ], + }, + aggregations: {}, + }, + }; + opensearch.search.mockResolvedValue(mockSearchResponse); + prisma.entity.findMany.mockResolvedValueOnce(mockEntities); + prisma.entity.findMany.mockResolvedValueOnce([]); + const response = await fastify.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body).toMatchSnapshot(); + }); + }); +}); +//# sourceMappingURL=search.test.js.map \ No newline at end of file diff --git a/dist/routes/search.test.js.map b/dist/routes/search.test.js.map new file mode 100644 index 0000000..7835052 --- /dev/null +++ b/dist/routes/search.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"search.test.js","sourceRoot":"","sources":["../../src/routes/search.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/test/helpers/fastify.d.ts b/dist/test/helpers/fastify.d.ts new file mode 100644 index 0000000..b9cfbc0 --- /dev/null +++ b/dist/test/helpers/fastify.d.ts @@ -0,0 +1,9 @@ +import type { Client } from '@opensearch-project/opensearch'; +import type { FastifyInstance } from 'fastify'; +import Fastify from 'fastify'; +import type { PrismaClient } from '../../generated/prisma/client.js'; +export declare let fastify: FastifyInstance; +export declare const prisma: import("vitest-mock-extended").DeepMockProxy; +export declare const opensearch: import("vitest-mock-extended").DeepMockProxy; +export declare const fastifyBefore: () => Promise, Fastify.FastifyBaseLogger, Fastify.FastifyTypeProviderDefault>>; +export declare const fastifyAfter: () => Promise; diff --git a/dist/test/helpers/fastify.js b/dist/test/helpers/fastify.js new file mode 100644 index 0000000..ca6c451 --- /dev/null +++ b/dist/test/helpers/fastify.js @@ -0,0 +1,24 @@ +import Fastify from 'fastify'; +import { serializerCompiler, validatorCompiler } from 'fastify-type-provider-zod'; +import { mockDeep, mockReset } from 'vitest-mock-extended'; +export let fastify; +export const prisma = mockDeep(); +export const opensearch = mockDeep(); +prisma.getter = undefined; +prisma.setter = undefined; +opensearch.getter = undefined; +opensearch.setter = undefined; +export const fastifyBefore = async () => { + mockReset(prisma); + mockReset(opensearch); + fastify = Fastify({ logger: false }); + fastify.decorate('prisma', prisma); + fastify.decorate('opensearch', opensearch); + fastify.setValidatorCompiler(validatorCompiler); + fastify.setSerializerCompiler(serializerCompiler); + return fastify; +}; +export const fastifyAfter = async () => { + await fastify.close(); +}; +//# sourceMappingURL=fastify.js.map \ No newline at end of file diff --git a/dist/test/helpers/fastify.js.map b/dist/test/helpers/fastify.js.map new file mode 100644 index 0000000..3298147 --- /dev/null +++ b/dist/test/helpers/fastify.js.map @@ -0,0 +1 @@ +{"version":3,"file":"fastify.js","sourceRoot":"","sources":["../../../src/test/helpers/fastify.ts"],"names":[],"mappings":"AAEA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAI3D,MAAM,CAAC,IAAI,OAAwB,CAAC;AACpC,MAAM,CAAC,MAAM,MAAM,GAAG,QAAQ,EAAgB,CAAC;AAC/C,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,EAAU,CAAC;AAG7C,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC;AAE1B,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC;AAG1B,UAAU,CAAC,MAAM,GAAG,SAAS,CAAC;AAE9B,UAAU,CAAC,MAAM,GAAG,SAAS,CAAC;AAE9B,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE;IACtC,SAAS,CAAC,MAAM,CAAC,CAAC;IAClB,SAAS,CAAC,UAAU,CAAC,CAAC;IAEtB,OAAO,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAE3C,OAAO,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;IAChD,OAAO,CAAC,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;IAElD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,IAAI,EAAE;IACrC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;AACxB,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/test/integration.setup.d.ts b/dist/test/integration.setup.d.ts new file mode 100644 index 0000000..99f6bf2 --- /dev/null +++ b/dist/test/integration.setup.d.ts @@ -0,0 +1,31 @@ +import type { FastifyInstance } from 'fastify'; +import Fastify from 'fastify'; +export declare const getTestApp: () => FastifyInstance, Fastify.FastifyBaseLogger, Fastify.FastifyTypeProviderDefault>; +export declare function setupIntegrationTests(): Promise; +export declare function teardownIntegrationTests(): Promise; +export declare function cleanupTestData(): Promise; +export declare function seedTestData(): Promise<({ + id: number; + rocrateId: string; + name: string; + description: string; + entityType: string; + memberOf: null; + rootCollection: null; + metadataLicenseId: string; + contentLicenseId: string; + createdAt: Date; + updatedAt: Date; +} | { + id: number; + rocrateId: string; + name: string; + description: string; + entityType: string; + memberOf: string; + rootCollection: string; + metadataLicenseId: string; + contentLicenseId: string; + createdAt: Date; + updatedAt: Date; +})[]>; diff --git a/dist/test/integration.setup.js b/dist/test/integration.setup.js new file mode 100644 index 0000000..22879f0 --- /dev/null +++ b/dist/test/integration.setup.js @@ -0,0 +1,168 @@ +import { Client } from '@opensearch-project/opensearch'; +import Fastify from 'fastify'; +import app, { AllPublicAccessTransformer, AllPublicFileAccessTransformer } from '../app.js'; +import { PrismaClient } from '../generated/prisma/client.js'; +let fastify; +let prisma; +let opensearch; +export const getTestApp = () => fastify; +export async function setupIntegrationTests() { + process.env.DATABASE_URL = 'mysql://root:password@localhost:3307/catalog_test'; + process.env.OPENSEARCH_URL = 'http://localhost:9201'; + process.env.NODE_ENV = 'test'; + prisma = new PrismaClient({ + datasources: { + db: { + url: process.env.DATABASE_URL, + }, + }, + }); + opensearch = new Client({ + node: process.env.OPENSEARCH_URL, + }); + fastify = Fastify({ logger: false }); + await fastify.register(app, { + prisma, + opensearch, + disableCors: true, + accessTransformer: AllPublicAccessTransformer, + fileAccessTransformer: AllPublicFileAccessTransformer, + fileHandler: { + get: async () => { + throw new Error('File handler not implemented in integration tests'); + }, + head: async () => { + throw new Error('File handler not implemented in integration tests'); + }, + }, + roCrateHandler: { + get: async () => { + throw new Error('RO-Crate handler not implemented in integration tests'); + }, + head: async () => { + throw new Error('RO-Crate head handler not implemented in integration tests'); + }, + }, + }); + await fastify.ready(); +} +export async function teardownIntegrationTests() { + await cleanupTestData(); + await fastify.close(); + await prisma.$disconnect(); + opensearch.close(); +} +export async function cleanupTestData() { + await prisma.entity.deleteMany({}); + await opensearch.indices.delete({ + index: 'entities', + ignore_unavailable: true, + }); +} +export async function seedTestData() { + await cleanupTestData(); + const testEntities = [ + { + id: 1, + rocrateId: 'http://example.com/entity/1', + name: 'Test Collection', + description: 'First test entity', + entityType: 'http://pcdm.org/models#Collection', + memberOf: null, + rootCollection: null, + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + id: 2, + rocrateId: 'http://example.com/entity/2', + name: 'Test Object', + description: 'Second test entity', + entityType: 'http://pcdm.org/models#Object', + memberOf: 'http://example.com/entity/1', + rootCollection: 'http://example.com/entity/1', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + id: 3, + rocrateId: 'http://example.com/entity/3', + name: 'Test Person', + description: 'Third test entity', + entityType: 'http://schema.org/Person', + memberOf: 'http://example.com/entity/1', + rootCollection: 'http://example.com/entity/1', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + id: 4, + rocrateId: 'http://example.com/entity/4', + name: 'test-audio.wav', + description: 'Test audio file', + entityType: 'http://schema.org/MediaObject', + memberOf: 'http://example.com/entity/2', + rootCollection: 'http://example.com/entity/1', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + }, + { + id: 5, + rocrateId: 'http://example.com/entity/5', + name: 'collection-metadata.csv', + description: 'Collection metadata file', + entityType: 'http://schema.org/MediaObject', + memberOf: 'http://example.com/entity/1', + rootCollection: 'http://example.com/entity/1', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + }, + ]; + await prisma.entity.createMany({ + data: testEntities, + }); + await opensearch.indices.create({ + index: 'entities', + body: { + mappings: { + properties: { + rocrateId: { type: 'keyword' }, + name: { + type: 'text', + fields: { + keyword: { type: 'keyword' }, + }, + }, + description: { type: 'text' }, + entityType: { type: 'keyword' }, + memberOf: { type: 'keyword' }, + rootCollection: { type: 'keyword' }, + location: { type: 'geo_point' }, + inLanguage: { type: 'keyword' }, + mediaType: { type: 'keyword' }, + communicationMode: { type: 'keyword' }, + }, + }, + }, + }); + const testDocs = testEntities.flatMap((entity, index) => [ + { index: { _index: 'entities', _id: `${index + 1}` } }, + entity, + ]); + await opensearch.bulk({ + body: testDocs, + refresh: true, + }); + return testEntities; +} +//# sourceMappingURL=integration.setup.js.map \ No newline at end of file diff --git a/dist/test/integration.setup.js.map b/dist/test/integration.setup.js.map new file mode 100644 index 0000000..36083e2 --- /dev/null +++ b/dist/test/integration.setup.js.map @@ -0,0 +1 @@ +{"version":3,"file":"integration.setup.js","sourceRoot":"","sources":["../../src/test/integration.setup.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/test/integration.test.d.ts b/dist/test/integration.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/test/integration.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/test/integration.test.js b/dist/test/integration.test.js new file mode 100644 index 0000000..bdcbf5d --- /dev/null +++ b/dist/test/integration.test.js @@ -0,0 +1,337 @@ +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest'; +import { cleanupTestData, getTestApp, seedTestData, setupIntegrationTests, teardownIntegrationTests, } from './integration.setup.js'; +describe('Integration Tests', () => { + beforeAll(async () => { + await setupIntegrationTests(); + }); + afterAll(async () => { + await teardownIntegrationTests(); + }); + beforeEach(async () => { + await seedTestData(); + }); + afterEach(async () => { + await cleanupTestData(); + }); + describe('GET /entity/:id', () => { + it('should return entity from database', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/1')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body).toMatchSnapshot(); + }); + it('should return File entity from database', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: `/entity/${encodeURIComponent('http://example.com/entity/4')}`, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.id).toBe('http://example.com/entity/4'); + expect(body.name).toBe('test-audio.wav'); + expect(body.entityType).toBe('http://schema.org/MediaObject'); + expect(body.memberOf?.id).toBe('http://example.com/entity/2'); + expect(body.rootCollection?.id).toBe('http://example.com/entity/1'); + }); + it('should return 404 for non-existent entity', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: '/entity/http://example.com/entity/nonexistent', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(404); + expect(body.error).toBe('Not Found'); + }); + }); + describe('GET /entities', () => { + it('should return all entities with pagination', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: '/entities', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.total).toBe(5); + expect(body.entities).toHaveLength(5); + expect(body.entities[0]).toEqual({ + id: 'http://example.com/entity/1', + name: 'Test Collection', + description: 'First test entity', + entityType: 'http://pcdm.org/models#Collection', + memberOf: null, + rootCollection: null, + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + access: { + metadata: true, + content: true, + }, + }); + }); + it('should filter entities by memberOf', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: '/entities', + query: { + memberOf: 'http://example.com/entity/1', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.total).toBe(3); + expect(body.entities).toHaveLength(3); + expect(body.entities[0].id).toBe('http://example.com/entity/2'); + }); + it('should filter entities by entityType', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: '/entities', + query: { + entityType: 'http://schema.org/Person', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.total).toBe(1); + expect(body.entities).toHaveLength(1); + expect(body.entities[0].id).toBe('http://example.com/entity/3'); + }); + it('should filter entities by File entityType', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: '/entities', + query: { + entityType: 'http://schema.org/MediaObject', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.total).toBe(2); + expect(body.entities).toHaveLength(2); + expect(body.entities[0].entityType).toBe('http://schema.org/MediaObject'); + expect(body.entities[1].entityType).toBe('http://schema.org/MediaObject'); + }); + it('should filter File entities by memberOf (Object parent)', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: '/entities', + query: { + memberOf: 'http://example.com/entity/2', + entityType: 'http://schema.org/MediaObject', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.total).toBe(1); + expect(body.entities).toHaveLength(1); + expect(body.entities[0].id).toBe('http://example.com/entity/4'); + expect(body.entities[0].name).toBe('test-audio.wav'); + expect(body.entities[0].memberOf?.id).toBe('http://example.com/entity/2'); + }); + it('should filter File entities by memberOf (Collection parent)', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: '/entities', + query: { + memberOf: 'http://example.com/entity/1', + entityType: 'http://schema.org/MediaObject', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.total).toBe(1); + expect(body.entities).toHaveLength(1); + expect(body.entities[0].id).toBe('http://example.com/entity/5'); + expect(body.entities[0].name).toBe('collection-metadata.csv'); + expect(body.entities[0].memberOf?.id).toBe('http://example.com/entity/1'); + }); + it('should handle pagination', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: '/entities', + query: { + limit: '2', + offset: '1', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.total).toBe(5); + expect(body.entities).toHaveLength(2); + }); + it('should sort entities by name', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: '/entities', + query: { + sort: 'name', + order: 'desc', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.entities[0].name).toBe('test-audio.wav'); + expect(body.entities[1].name).toBe('Test Person'); + expect(body.entities[2].name).toBe('Test Object'); + }); + }); + describe('POST /search', () => { + it('should perform basic search', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + searchType: 'basic', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.total).toBeGreaterThan(0); + expect(body.entities).toBeDefined(); + expect(Array.isArray(body.entities)).toBe(true); + }); + it('should handle search with no results', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'nonexistentterm', + searchType: 'basic', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.total).toBe(0); + expect(body.entities).toHaveLength(0); + }); + it('should return aggregations', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + searchType: 'basic', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.facets).toBeDefined(); + }); + it('should handle search pagination', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + limit: 1, + offset: 0, + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.entities.length).toBeLessThanOrEqual(1); + }); + it('should sort entities by id', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + sort: 'id', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.entities[0].name).toBe('Test Collection'); + expect(body.entities[1].name).toBe('Test Object'); + expect(body.entities[2].name).toBe('Test Person'); + }); + it('should filter search results by File entityType', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'test', + searchType: 'basic', + filters: { + entityType: ['http://schema.org/MediaObject'], + }, + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.entities.every((e) => e.entityType === 'http://schema.org/MediaObject')).toBe(true); + }); + it('should search for File entities by name', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'POST', + url: '/search', + payload: { + query: 'audio', + searchType: 'basic', + }, + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(200); + expect(body.total).toBeGreaterThan(0); + const audioFile = body.entities.find((e) => e.name === 'test-audio.wav'); + expect(audioFile).toBeDefined(); + expect(audioFile?.entityType).toBe('http://schema.org/MediaObject'); + }); + }); + describe('Error Handling', () => { + it('should handle invalid entity ID format', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: '/entity/invalid-id', + }); + const body = JSON.parse(response.body); + expect(response.statusCode).toBe(400); + expect(body.error.code).toBe('VALIDATION_ERROR'); + }); + it('should handle invalid query parameters for entities', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'GET', + url: '/entities', + query: { + limit: '-1', + }, + }); + expect(response.statusCode).toBe(400); + }); + it('should handle missing search query', async () => { + const app = getTestApp(); + const response = await app.inject({ + method: 'POST', + url: '/search', + payload: {}, + }); + expect(response.statusCode).toBe(400); + }); + }); +}); +//# sourceMappingURL=integration.test.js.map \ No newline at end of file diff --git a/dist/test/integration.test.js.map b/dist/test/integration.test.js.map new file mode 100644 index 0000000..070f7cd --- /dev/null +++ b/dist/test/integration.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"integration.test.js","sourceRoot":"","sources":["../../src/test/integration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAG1F,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,EACZ,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAEhC,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,qBAAqB,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,MAAM,wBAAwB,EAAE,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,YAAY,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,eAAe,EAAE,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,WAAW,kBAAkB,CAAC,6BAA6B,CAAC,EAAE;aACpE,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAEvC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,WAAW,kBAAkB,CAAC,6BAA6B,CAAC,EAAE;aACpE,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAqB,CAAC;YAE3D,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;YACpD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;YAC9D,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;YAC9D,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;YACzD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,+CAA+C;aACrD,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAA0B,CAAC;YAEhE,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YAC1D,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,WAAW;aACjB,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;gBAC/B,EAAE,EAAE,6BAA6B;gBACjC,IAAI,EAAE,iBAAiB;gBACvB,WAAW,EAAE,mBAAmB;gBAChC,UAAU,EAAE,mCAAmC;gBAC/C,QAAQ,EAAE,IAAI;gBACd,cAAc,EAAE,IAAI;gBACpB,iBAAiB,EAAE,8CAA8C;gBACjE,gBAAgB,EAAE,8CAA8C;gBAChE,MAAM,EAAE;oBACN,QAAQ,EAAE,IAAI;oBACd,OAAO,EAAE,IAAI;iBACd;aACF,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,WAAW;gBAChB,KAAK,EAAE;oBACL,QAAQ,EAAE,6BAA6B;iBACxC;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,WAAW;gBAChB,KAAK,EAAE;oBACL,UAAU,EAAE,0BAA0B;iBACvC;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;YACzD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,WAAW;gBAChB,KAAK,EAAE;oBACL,UAAU,EAAE,+BAA+B;iBAC5C;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;YAC1E,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;YACvE,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,WAAW;gBAChB,KAAK,EAAE;oBACL,QAAQ,EAAE,6BAA6B;oBACvC,UAAU,EAAE,+BAA+B;iBAC5C;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;YAChE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACrD,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;YAC3E,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,WAAW;gBAChB,KAAK,EAAE;oBACL,QAAQ,EAAE,6BAA6B;oBACvC,UAAU,EAAE,+BAA+B;iBAC5C;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;YAChE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;YAC9D,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;YACxC,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,WAAW;gBAChB,KAAK,EAAE;oBACL,KAAK,EAAE,GAAG;oBACV,MAAM,EAAE,GAAG;iBACZ;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;YAC5C,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,WAAW;gBAChB,KAAK,EAAE;oBACL,IAAI,EAAE,MAAM;oBACZ,KAAK,EAAE,MAAM;iBACd;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACrD,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAClD,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,MAAM;gBACd,GAAG,EAAE,SAAS;gBACd,OAAO,EAAE;oBACP,KAAK,EAAE,MAAM;oBACb,UAAU,EAAE,OAAO;iBACpB;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,MAAM;gBACd,GAAG,EAAE,SAAS;gBACd,OAAO,EAAE;oBACP,KAAK,EAAE,iBAAiB;oBACxB,UAAU,EAAE,OAAO;iBACpB;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC1C,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,MAAM;gBACd,GAAG,EAAE,SAAS;gBACd,OAAO,EAAE;oBACP,KAAK,EAAE,MAAM;oBACb,UAAU,EAAE,OAAO;iBACpB;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAkE,CAAC;YAExG,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;YAC/C,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,MAAM;gBACd,GAAG,EAAE,SAAS;gBACd,OAAO,EAAE;oBACP,KAAK,EAAE,MAAM;oBACb,KAAK,EAAE,CAAC;oBACR,MAAM,EAAE,CAAC;iBACV;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC1C,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,MAAM;gBACd,GAAG,EAAE,SAAS;gBACd,OAAO,EAAE;oBACP,KAAK,EAAE,MAAM;oBACb,IAAI,EAAE,IAAI;iBACX;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YACtD,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAClD,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;YAC/D,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,MAAM;gBACd,GAAG,EAAE,SAAS;gBACd,OAAO,EAAE;oBACP,KAAK,EAAE,MAAM;oBACb,UAAU,EAAE,OAAO;oBACnB,OAAO,EAAE;wBACP,UAAU,EAAE,CAAC,+BAA+B,CAAC;qBAC9C;iBACF;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,+BAA+B,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClG,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,MAAM;gBACd,GAAG,EAAE,SAAS;gBACd,OAAO,EAAE;oBACP,KAAK,EAAE,OAAO;oBACd,UAAU,EAAE,OAAO;iBACpB;aACF,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAoD,CAAC;YAE1F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YACtC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC;YACzE,MAAM,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;YAChC,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,oBAAoB;aAC1B,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAA0B,CAAC;YAEhE,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YACnE,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,WAAW;gBAChB,KAAK,EAAE;oBACL,KAAK,EAAE,IAAI;iBACZ;aACF,CAAC,CAAC;YAEH,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;YAEzB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;gBAChC,MAAM,EAAE,MAAM;gBACd,GAAG,EAAE,SAAS;gBACd,OAAO,EAAE,EAAE;aACZ,CAAC,CAAC;YAEH,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/transformers/default.d.ts b/dist/transformers/default.d.ts new file mode 100644 index 0000000..22de631 --- /dev/null +++ b/dist/transformers/default.d.ts @@ -0,0 +1,68 @@ +import type { Entity, File, PrismaClient } from '../generated/prisma/client.js'; +export type EntityReference = { + id: string; + name: string; +}; +export type BaseEntity = { + id: string; + name: string; + description: string; + memberOf: string | null; + rootCollection: string | null; + metadataLicenseId: string; + contentLicenseId: string; +} & ({ + entityType: string; + fileId?: never; +} | { + entityType: 'http://schema.org/MediaObject'; + fileId: string; +}); +export type StandardEntity = { + id: string; + name: string; + description: string; + memberOf: EntityReference | null; + rootCollection: EntityReference | null; + metadataLicenseId: string; + contentLicenseId: string; +} & ({ + entityType: string; + fileId?: never; +} | { + entityType: 'http://schema.org/MediaObject'; + fileId: string; +}); +type AccessInfo = { + metadata: boolean; + content: boolean; + contentAuthorizationUrl?: string; +}; +export type AuthorisedEntity = StandardEntity & { + access: AccessInfo; +}; +type FileAccessInfo = { + content: boolean; + contentAuthorizationUrl?: string; +}; +export type StandardFile = { + id: string; + filename: string; + mediaType: string; + size: number; + memberOf: string; + rootCollection: string; + contentLicenseId: string; +}; +export type AuthorisedFile = StandardFile & { + access: FileAccessInfo; +}; +export declare const baseEntityTransformer: (entity: Entity) => BaseEntity; +export declare const AllPublicAccessTransformer: (entity: StandardEntity) => AuthorisedEntity; +export declare const baseFileTransformer: (file: File) => StandardFile; +export declare const AllPublicFileAccessTransformer: (file: StandardFile) => AuthorisedFile; +export declare const resolveEntityReferences: (entities: Array<{ + memberOf: string | null; + rootCollection: string | null; +}>, prisma: PrismaClient) => Promise>; +export {}; diff --git a/dist/transformers/default.js b/dist/transformers/default.js new file mode 100644 index 0000000..f306694 --- /dev/null +++ b/dist/transformers/default.js @@ -0,0 +1,65 @@ +export const baseEntityTransformer = (entity) => { + const base = { + id: entity.rocrateId, + name: entity.name, + description: entity.description, + entityType: entity.entityType, + memberOf: entity.memberOf, + rootCollection: entity.rootCollection, + metadataLicenseId: entity.metadataLicenseId, + contentLicenseId: entity.contentLicenseId, + }; + if (base.entityType === 'http://schema.org/MediaObject') { + if (!entity.fileId) { + return base; + } + return { + ...base, + entityType: base.entityType, + fileId: entity.fileId, + }; + } + return base; +}; +export const AllPublicAccessTransformer = (entity) => ({ + ...entity, + access: { + metadata: true, + content: true, + }, +}); +export const baseFileTransformer = (file) => ({ + id: file.fileId, + filename: file.filename, + mediaType: file.mediaType, + size: Number(file.size), + memberOf: file.memberOf, + rootCollection: file.rootCollection, + contentLicenseId: file.contentLicenseId, +}); +export const AllPublicFileAccessTransformer = (file) => ({ + ...file, + access: { + content: true, + }, +}); +export const resolveEntityReferences = async (entities, prisma) => { + const refIds = new Set(); + entities.forEach((e) => { + if (e.memberOf) { + refIds.add(e.memberOf); + } + if (e.rootCollection) { + refIds.add(e.rootCollection); + } + }); + if (refIds.size === 0) { + return new Map(); + } + const refs = await prisma.entity.findMany({ + where: { rocrateId: { in: [...refIds] } }, + select: { rocrateId: true, name: true }, + }); + return new Map(refs.map((r) => [r.rocrateId, { id: r.rocrateId, name: r.name }])); +}; +//# sourceMappingURL=default.js.map \ No newline at end of file diff --git a/dist/transformers/default.js.map b/dist/transformers/default.js.map new file mode 100644 index 0000000..be66861 --- /dev/null +++ b/dist/transformers/default.js.map @@ -0,0 +1 @@ +{"version":3,"file":"default.js","sourceRoot":"","sources":["../../src/transformers/default.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/transformers/default.test.d.ts b/dist/transformers/default.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/transformers/default.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/transformers/default.test.js b/dist/transformers/default.test.js new file mode 100644 index 0000000..08319c4 --- /dev/null +++ b/dist/transformers/default.test.js @@ -0,0 +1,172 @@ +import { describe, expect, it } from 'vitest'; +import { AllPublicAccessTransformer, baseEntityTransformer } from './default.js'; +describe('baseEntityTransformer', () => { + it('should transform entity to standard entity shape', () => { + const entity = { + id: 1, + rocrateId: 'http://example.com/entity/123', + name: 'Test Entity', + description: 'A test entity description', + entityType: 'http://pcdm.org/models#Collection', + fileId: null, + memberOf: 'http://example.com/parent', + rootCollection: 'http://example.com/root', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by-sa/4.0/', + createdAt: new Date('2025-01-01'), + updatedAt: new Date('2025-01-02'), + meta: { test: 'data' }, + }; + const result = baseEntityTransformer(entity); + expect(result).toEqual({ + id: 'http://example.com/entity/123', + name: 'Test Entity', + description: 'A test entity description', + entityType: 'http://pcdm.org/models#Collection', + memberOf: 'http://example.com/parent', + rootCollection: 'http://example.com/root', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by-sa/4.0/', + }); + }); + it('should handle null memberOf and rootCollection', () => { + const entity = { + id: 1, + rocrateId: 'http://example.com/collection', + name: 'Top Collection', + description: 'A top-level collection', + entityType: 'http://pcdm.org/models#Collection', + fileId: null, + memberOf: null, + rootCollection: null, + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + meta: null, + }; + const result = baseEntityTransformer(entity); + expect(result.memberOf).toBeNull(); + expect(result.rootCollection).toBeNull(); + }); + it('should exclude database-specific fields', () => { + const entity = { + id: 1, + rocrateId: 'http://example.com/entity/456', + name: 'Test', + description: 'Test', + entityType: 'http://pcdm.org/models#Object', + fileId: null, + memberOf: null, + rootCollection: null, + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + meta: { storage: 'path' }, + }; + const result = baseEntityTransformer(entity); + expect(result.id).toBe('http://example.com/entity/456'); + expect(result).not.toHaveProperty('createdAt'); + expect(result).not.toHaveProperty('updatedAt'); + expect(result).not.toHaveProperty('meta'); + expect(Object.keys(result)).toEqual([ + 'id', + 'name', + 'description', + 'entityType', + 'memberOf', + 'rootCollection', + 'metadataLicenseId', + 'contentLicenseId', + ]); + }); + it('should handle File entity (MediaObject) with fileId', () => { + const entity = { + id: 1, + rocrateId: 'http://example.com/file/audio.wav', + name: 'Audio File', + description: 'An audio recording', + entityType: 'http://schema.org/MediaObject', + fileId: 'http://example.com/files/audio.wav', + memberOf: 'http://example.com/collection', + rootCollection: 'http://example.com/collection', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + createdAt: new Date(), + updatedAt: new Date(), + meta: null, + }; + const result = baseEntityTransformer(entity); + expect(result).toEqual({ + id: 'http://example.com/file/audio.wav', + name: 'Audio File', + description: 'An audio recording', + entityType: 'http://schema.org/MediaObject', + fileId: 'http://example.com/files/audio.wav', + memberOf: 'http://example.com/collection', + rootCollection: 'http://example.com/collection', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + }); + expect(result.fileId).toBe('http://example.com/files/audio.wav'); + }); +}); +describe('AllPublicAccessTransformer', () => { + it('should grant full access to metadata and content', () => { + const standardEntity = { + id: 'http://example.com/entity/123', + name: 'Test Entity', + description: 'A test entity', + entityType: 'http://schema.org/MediaObject', + memberOf: { id: 'http://example.com/parent', name: 'Parent Entity' }, + rootCollection: { id: 'http://example.com/root', name: 'Root Collection' }, + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + }; + const result = AllPublicAccessTransformer(standardEntity); + expect(result).toEqual({ + ...standardEntity, + access: { + metadata: true, + content: true, + }, + }); + }); + it('should preserve all standard entity fields', () => { + const standardEntity = { + id: 'http://example.com/entity/789', + name: 'Another Entity', + description: 'Another description', + entityType: 'http://schema.org/Person', + memberOf: null, + rootCollection: null, + metadataLicenseId: 'https://creativecommons.org/publicdomain/zero/1.0/', + contentLicenseId: 'https://creativecommons.org/publicdomain/zero/1.0/', + }; + const result = AllPublicAccessTransformer(standardEntity); + expect(result.id).toBe(standardEntity.id); + expect(result.name).toBe(standardEntity.name); + expect(result.description).toBe(standardEntity.description); + expect(result.entityType).toBe(standardEntity.entityType); + expect(result.memberOf).toBe(standardEntity.memberOf); + expect(result.rootCollection).toBe(standardEntity.rootCollection); + expect(result.metadataLicenseId).toBe(standardEntity.metadataLicenseId); + expect(result.contentLicenseId).toBe(standardEntity.contentLicenseId); + }); + it('should not add contentAuthorizationUrl for public access', () => { + const standardEntity = { + id: 'http://example.com/entity/public', + name: 'Public Entity', + description: 'Fully public', + entityType: 'http://pcdm.org/models#Collection', + memberOf: null, + rootCollection: null, + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + }; + const result = AllPublicAccessTransformer(standardEntity); + expect(result.access.contentAuthorizationUrl).toBeUndefined(); + }); +}); +//# sourceMappingURL=default.test.js.map \ No newline at end of file diff --git a/dist/transformers/default.test.js.map b/dist/transformers/default.test.js.map new file mode 100644 index 0000000..84cdacd --- /dev/null +++ b/dist/transformers/default.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"default.test.js","sourceRoot":"","sources":["../../src/transformers/default.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/transformers/transformer.test.d.ts b/dist/transformers/transformer.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/transformers/transformer.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/transformers/transformer.test.js b/dist/transformers/transformer.test.js new file mode 100644 index 0000000..a828fb1 --- /dev/null +++ b/dist/transformers/transformer.test.js @@ -0,0 +1,191 @@ +import { describe, expect, it } from 'vitest'; +import { AllPublicAccessTransformer, baseEntityTransformer, } from './default.js'; +describe('Entity Transformers', () => { + const mockContext = { + request: {}, + fastify: {}, + }; + const mockStandardEntity = { + id: 'http://example.com/entity/123', + name: 'Test Entity', + description: 'A test entity', + entityType: 'http://schema.org/Person', + memberOf: { id: 'http://example.com/collection', name: 'Test Collection' }, + rootCollection: { id: 'http://example.com/root', name: 'Root Collection' }, + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + }; + const mockAuthorisedEntity = { + ...mockStandardEntity, + access: { + metadata: true, + content: true, + }, + }; + describe('baseEntityTransformer', () => { + it('should transform raw entity to base shape with unresolved references', () => { + const rawEntity = { + id: 1, + rocrateId: 'http://example.com/entity/123', + name: 'Test Entity', + description: 'A test entity', + entityType: 'http://schema.org/Person', + fileId: null, + memberOf: 'http://example.com/collection', + rootCollection: 'http://example.com/root', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + meta: {}, + createdAt: new Date(), + updatedAt: new Date(), + }; + const result = baseEntityTransformer(rawEntity); + const expectedBaseEntity = { + id: 'http://example.com/entity/123', + name: 'Test Entity', + description: 'A test entity', + entityType: 'http://schema.org/Person', + memberOf: 'http://example.com/collection', + rootCollection: 'http://example.com/root', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + }; + expect(result).toEqual(expectedBaseEntity); + }); + }); + describe('AllPublicAccessTransformer', () => { + it('should add full access to standard entity', () => { + const result = AllPublicAccessTransformer(mockStandardEntity); + expect(result).toEqual({ + ...mockStandardEntity, + access: { + metadata: true, + content: true, + }, + }); + }); + it('should grant metadata and content access', () => { + const result = AllPublicAccessTransformer(mockStandardEntity); + expect(result.access.metadata).toBe(true); + expect(result.access.content).toBe(true); + expect(result.access.contentAuthorizationUrl).toBeUndefined(); + }); + }); + describe('Custom transformers', () => { + it('should allow custom transformer to add computed fields', () => { + const customTransformer = (entity) => ({ + id: entity.id, + displayName: entity.name.toUpperCase(), + uri: entity.id, + }); + const result = customTransformer(mockAuthorisedEntity, mockContext); + expect(result).toEqual({ + id: 'http://example.com/entity/123', + displayName: 'TEST ENTITY', + uri: 'http://example.com/entity/123', + }); + }); + it('should support async transformers for fetching related data', async () => { + const asyncTransformer = async (entity) => { + await new Promise((resolve) => setTimeout(resolve, 1)); + return { + id: entity.id, + name: entity.name, + relatedData: 'fetched-data', + }; + }; + const result = await asyncTransformer(mockAuthorisedEntity, mockContext); + expect(result).toEqual({ + id: 'http://example.com/entity/123', + name: 'Test Entity', + relatedData: 'fetched-data', + }); + }); + it('should allow transformer to access context', async () => { + const contextAwareTransformer = async (entity, context) => ({ + id: entity.id, + name: entity.name, + requestUrl: context.request.url || 'unknown', + }); + const contextWithUrl = { + request: { url: '/test-url' }, + fastify: {}, + }; + const result = await contextAwareTransformer(mockAuthorisedEntity, contextWithUrl); + expect(result).toEqual({ + id: 'http://example.com/entity/123', + name: 'Test Entity', + requestUrl: '/test-url', + }); + }); + it('should support transformer pipeline', async () => { + const addDisplayName = (entity) => ({ + ...entity, + displayName: `${entity.name} [${entity.entityType.split('/').pop()}]`, + }); + const addUpperCase = (entity) => ({ + ...entity, + upperName: entity.name.toUpperCase(), + }); + let result = mockAuthorisedEntity; + result = await addDisplayName(result, mockContext); + result = await addUpperCase(result, mockContext); + expect(result).toMatchObject({ + id: 'http://example.com/entity/123', + name: 'Test Entity', + displayName: 'Test Entity [Person]', + upperName: 'TEST ENTITY', + }); + }); + it('should demonstrate full pipeline: base -> resolve -> access -> custom', async () => { + const rawEntity = { + id: 1, + rocrateId: 'http://example.com/entity/123', + name: 'Test Entity', + description: 'A test entity', + entityType: 'http://schema.org/Person', + fileId: null, + memberOf: 'http://example.com/collection', + rootCollection: 'http://example.com/root', + metadataLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + contentLicenseId: 'https://creativecommons.org/licenses/by/4.0/', + meta: {}, + createdAt: new Date(), + updatedAt: new Date(), + }; + const customAccessTransformer = (entity) => ({ + ...entity, + access: { + metadata: true, + content: false, + contentAuthorizationUrl: 'https://example.com/auth', + }, + }); + const addMetadata = (entity) => ({ + ...entity, + processed: true, + }); + const base = baseEntityTransformer(rawEntity); + const standard = { + ...base, + memberOf: base.memberOf ? { id: base.memberOf, name: 'Parent Collection' } : null, + rootCollection: base.rootCollection ? { id: base.rootCollection, name: 'Root Collection' } : null, + }; + const authorised = await customAccessTransformer(standard, mockContext); + const final = await addMetadata(authorised, mockContext); + expect(final).toMatchObject({ + id: 'http://example.com/entity/123', + name: 'Test Entity', + memberOf: { id: 'http://example.com/collection', name: 'Parent Collection' }, + rootCollection: { id: 'http://example.com/root', name: 'Root Collection' }, + access: { + metadata: true, + content: false, + contentAuthorizationUrl: 'https://example.com/auth', + }, + processed: true, + }); + }); + }); +}); +//# sourceMappingURL=transformer.test.js.map \ No newline at end of file diff --git a/dist/transformers/transformer.test.js.map b/dist/transformers/transformer.test.js.map new file mode 100644 index 0000000..3c573e4 --- /dev/null +++ b/dist/transformers/transformer.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"transformer.test.js","sourceRoot":"","sources":["../../src/transformers/transformer.test.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EACL,0BAA0B,EAG1B,qBAAqB,GAEtB,MAAM,cAAc,CAAC;AAEtB,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,MAAM,WAAW,GAAuB;QACtC,OAAO,EAAE,EAAoB;QAC7B,OAAO,EAAE,EAAqB;KAC/B,CAAC;IAEF,MAAM,kBAAkB,GAAmB;QACzC,EAAE,EAAE,+BAA+B;QACnC,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,eAAe;QAC5B,UAAU,EAAE,0BAA0B;QACtC,QAAQ,EAAE,EAAE,EAAE,EAAE,+BAA+B,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAC1E,cAAc,EAAE,EAAE,EAAE,EAAE,yBAAyB,EAAE,IAAI,EAAE,iBAAiB,EAAE;QAC1E,iBAAiB,EAAE,8CAA8C;QACjE,gBAAgB,EAAE,8CAA8C;KACjE,CAAC;IAEF,MAAM,oBAAoB,GAAqB;QAC7C,GAAG,kBAAkB;QACrB,MAAM,EAAE;YACN,QAAQ,EAAE,IAAI;YACd,OAAO,EAAE,IAAI;SACd;KACF,CAAC;IAEF,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;YAC9E,MAAM,SAAS,GAAG;gBAChB,EAAE,EAAE,CAAC;gBACL,SAAS,EAAE,+BAA+B;gBAC1C,IAAI,EAAE,aAAa;gBACnB,WAAW,EAAE,eAAe;gBAC5B,UAAU,EAAE,0BAA0B;gBACtC,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,+BAA+B;gBACzC,cAAc,EAAE,yBAAyB;gBACzC,iBAAiB,EAAE,8CAA8C;gBACjE,gBAAgB,EAAE,8CAA8C;gBAChE,IAAI,EAAE,EAAE;gBACR,SAAS,EAAE,IAAI,IAAI,EAAE;gBACrB,SAAS,EAAE,IAAI,IAAI,EAAE;aACtB,CAAC;YAEF,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;YAGhD,MAAM,kBAAkB,GAAe;gBACrC,EAAE,EAAE,+BAA+B;gBACnC,IAAI,EAAE,aAAa;gBACnB,WAAW,EAAE,eAAe;gBAC5B,UAAU,EAAE,0BAA0B;gBACtC,QAAQ,EAAE,+BAA+B;gBACzC,cAAc,EAAE,yBAAyB;gBACzC,iBAAiB,EAAE,8CAA8C;gBACjE,gBAAgB,EAAE,8CAA8C;aACjE,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;QAC1C,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACnD,MAAM,MAAM,GAAG,0BAA0B,CAAC,kBAAkB,CAAC,CAAC;YAE9D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBACrB,GAAG,kBAAkB;gBACrB,MAAM,EAAE;oBACN,QAAQ,EAAE,IAAI;oBACd,OAAO,EAAE,IAAI;iBACd;aACF,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;YAClD,MAAM,MAAM,GAAG,0BAA0B,CAAC,kBAAkB,CAAC,CAAC;YAE9D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,aAAa,EAAE,CAAC;QAChE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;YAChE,MAAM,iBAAiB,GAA0F,CAC/G,MAAM,EACN,EAAE,CAAC,CAAC;gBACJ,EAAE,EAAE,MAAM,CAAC,EAAE;gBACb,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;gBACtC,GAAG,EAAE,MAAM,CAAC,EAAE;aACf,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,iBAAiB,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;YAEpE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBACrB,EAAE,EAAE,+BAA+B;gBACnC,WAAW,EAAE,aAAa;gBAC1B,GAAG,EAAE,+BAA+B;aACrC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;YAC3E,MAAM,gBAAgB,GAGlB,KAAK,EAAE,MAAM,EAAE,EAAE;gBAEnB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;gBAEvD,OAAO;oBACL,EAAE,EAAE,MAAM,CAAC,EAAE;oBACb,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,WAAW,EAAE,cAAc;iBAC5B,CAAC;YACJ,CAAC,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;YAEzE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBACrB,EAAE,EAAE,+BAA+B;gBACnC,IAAI,EAAE,aAAa;gBACnB,WAAW,EAAE,cAAc;aAC5B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YAC1D,MAAM,uBAAuB,GAOzB,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;gBAC9B,EAAE,EAAE,MAAM,CAAC,EAAE;gBACb,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,SAAS;aAC7C,CAAC,CAAC;YAEH,MAAM,cAAc,GAAuB;gBACzC,OAAO,EAAE,EAAE,GAAG,EAAE,WAAW,EAAoB;gBAC/C,OAAO,EAAE,EAAqB;aAC/B,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,uBAAuB,CAAC,oBAAoB,EAAE,cAAc,CAAC,CAAC;YAEnF,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBACrB,EAAE,EAAE,+BAA+B;gBACnC,IAAI,EAAE,aAAa;gBACnB,UAAU,EAAE,WAAW;aACxB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;YACnD,MAAM,cAAc,GAAoF,CACtG,MAAM,EACN,EAAE,CAAC,CAAC;gBACJ,GAAG,MAAM;gBACT,WAAW,EAAE,GAAG,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG;aACtE,CAAC,CAAC;YAEH,MAAM,YAAY,GAGd,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBACf,GAAG,MAAM;gBACT,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;aACrC,CAAC,CAAC;YAIH,IAAI,MAAM,GAAQ,oBAAoB,CAAC;YACvC,MAAM,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;YACnD,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;YAEjD,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC;gBAC3B,EAAE,EAAE,+BAA+B;gBACnC,IAAI,EAAE,aAAa;gBACnB,WAAW,EAAE,sBAAsB;gBACnC,SAAS,EAAE,aAAa;aACzB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;YACrF,MAAM,SAAS,GAAG;gBAChB,EAAE,EAAE,CAAC;gBACL,SAAS,EAAE,+BAA+B;gBAC1C,IAAI,EAAE,aAAa;gBACnB,WAAW,EAAE,eAAe;gBAC5B,UAAU,EAAE,0BAA0B;gBACtC,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,+BAA+B;gBACzC,cAAc,EAAE,yBAAyB;gBACzC,iBAAiB,EAAE,8CAA8C;gBACjE,gBAAgB,EAAE,8CAA8C;gBAChE,IAAI,EAAE,EAAE;gBACR,SAAS,EAAE,IAAI,IAAI,EAAE;gBACrB,SAAS,EAAE,IAAI,IAAI,EAAE;aACtB,CAAC;YAEF,MAAM,uBAAuB,GAAsB,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBAC9D,GAAG,MAAM;gBACT,MAAM,EAAE;oBACN,QAAQ,EAAE,IAAI;oBACd,OAAO,EAAE,KAAK;oBACd,uBAAuB,EAAE,0BAA0B;iBACpD;aACF,CAAC,CAAC;YAEH,MAAM,WAAW,GAAmF,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBAC/G,GAAG,MAAM;gBACT,SAAS,EAAE,IAAI;aAChB,CAAC,CAAC;YAGH,MAAM,IAAI,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;YAG9C,MAAM,QAAQ,GAAmB;gBAC/B,GAAG,IAAI;gBACP,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAC,IAAI;gBACjF,cAAc,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,IAAI;aAClG,CAAC;YAEF,MAAM,UAAU,GAAG,MAAM,uBAAuB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;YACxE,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;YAEzD,MAAM,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC;gBAC1B,EAAE,EAAE,+BAA+B;gBACnC,IAAI,EAAE,aAAa;gBACnB,QAAQ,EAAE,EAAE,EAAE,EAAE,+BAA+B,EAAE,IAAI,EAAE,mBAAmB,EAAE;gBAC5E,cAAc,EAAE,EAAE,EAAE,EAAE,yBAAyB,EAAE,IAAI,EAAE,iBAAiB,EAAE;gBAC1E,MAAM,EAAE;oBACN,QAAQ,EAAE,IAAI;oBACd,OAAO,EAAE,KAAK;oBACd,uBAAuB,EAAE,0BAA0B;iBACpD;gBACD,SAAS,EAAE,IAAI;aAChB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/types/fileHandlers.d.ts b/dist/types/fileHandlers.d.ts new file mode 100644 index 0000000..9295ee2 --- /dev/null +++ b/dist/types/fileHandlers.d.ts @@ -0,0 +1,41 @@ +import type { Readable } from 'node:stream'; +import type { FastifyInstance, FastifyRequest } from 'fastify'; +import type { Entity, File } from '../generated/prisma/client.js'; +export type FileHandlerContext = { + request: FastifyRequest; + fastify: FastifyInstance; +}; +export type FileMetadata = { + contentType: string; + contentLength: number; + etag?: string; + lastModified?: Date; +}; +export type FileRedirectResult = { + type: 'redirect'; + url: string; +}; +export type FileStreamResult = { + type: 'stream'; + stream: Readable; + metadata: FileMetadata; +}; +export type FilePathResult = { + type: 'file'; + path: string; + metadata: FileMetadata; + accelPath?: string; +}; +export type FileResult = FileRedirectResult | FileStreamResult | FilePathResult; +export type GetFileHandler = (file: File, context: FileHandlerContext) => Promise | FileResult | false; +export type HeadFileHandler = (file: File, context: FileHandlerContext) => Promise | FileMetadata | false; +export type FileHandler = { + get: GetFileHandler; + head: HeadFileHandler; +}; +export type GetRoCrateHandler = (entity: Entity, context: FileHandlerContext) => Promise | FileResult | false; +export type HeadRoCrateHandler = (entity: Entity, context: FileHandlerContext) => Promise | FileMetadata | false; +export type RoCrateHandler = { + get: GetRoCrateHandler; + head: HeadRoCrateHandler; +}; diff --git a/dist/types/fileHandlers.js b/dist/types/fileHandlers.js new file mode 100644 index 0000000..ee154bf --- /dev/null +++ b/dist/types/fileHandlers.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=fileHandlers.js.map \ No newline at end of file diff --git a/dist/types/fileHandlers.js.map b/dist/types/fileHandlers.js.map new file mode 100644 index 0000000..43b9c63 --- /dev/null +++ b/dist/types/fileHandlers.js.map @@ -0,0 +1 @@ +{"version":3,"file":"fileHandlers.js","sourceRoot":"","sources":["../../src/types/fileHandlers.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/types/transformers.d.ts b/dist/types/transformers.d.ts new file mode 100644 index 0000000..4ded63f --- /dev/null +++ b/dist/types/transformers.d.ts @@ -0,0 +1,10 @@ +import type { FastifyInstance, FastifyRequest } from 'fastify'; +import type { AuthorisedEntity, AuthorisedFile, StandardEntity, StandardFile } from '../transformers/default.js'; +export type TransformerContext = { + request: FastifyRequest; + fastify: FastifyInstance; +}; +export type AccessTransformer = (entity: StandardEntity, context: TransformerContext) => Promise | AuthorisedEntity; +export type EntityTransformer = (entity: TInput, context: TransformerContext) => Promise | TOutput; +export type FileAccessTransformer = (file: StandardFile, context: TransformerContext) => Promise | AuthorisedFile; +export type FileTransformer = (file: TInput, context: TransformerContext) => Promise | TOutput; diff --git a/dist/types/transformers.js b/dist/types/transformers.js new file mode 100644 index 0000000..a3068e4 --- /dev/null +++ b/dist/types/transformers.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=transformers.js.map \ No newline at end of file diff --git a/dist/types/transformers.js.map b/dist/types/transformers.js.map new file mode 100644 index 0000000..57dfcd4 --- /dev/null +++ b/dist/types/transformers.js.map @@ -0,0 +1 @@ +{"version":3,"file":"transformers.js","sourceRoot":"","sources":["../../src/types/transformers.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/utils/errors.d.ts b/dist/utils/errors.d.ts new file mode 100644 index 0000000..c6b24e0 --- /dev/null +++ b/dist/utils/errors.d.ts @@ -0,0 +1,23 @@ +declare const ERROR_CODES: { + readonly VALIDATION_ERROR: "VALIDATION_ERROR"; + readonly NOT_FOUND: "NOT_FOUND"; + readonly RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED"; + readonly INTERNAL_ERROR: "INTERNAL_ERROR"; + readonly INVALID_REQUEST: "INVALID_REQUEST"; + readonly INVALID_ENTITY_TYPE: "INVALID_ENTITY_TYPE"; +}; +type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES]; +type ErrorDetails = { + [key: string]: unknown; +}; +export type StandardErrorResponse = { + error: { + code: ErrorCode; + message: string; + details?: ErrorDetails; + }; +}; +export declare const createValidationError: (message: string, issues: string[]) => StandardErrorResponse; +export declare const createNotFoundError: (message: string, entityId?: string) => StandardErrorResponse; +export declare const createInternalError: (message?: string) => StandardErrorResponse; +export {}; diff --git a/dist/utils/errors.js b/dist/utils/errors.js new file mode 100644 index 0000000..3020846 --- /dev/null +++ b/dist/utils/errors.js @@ -0,0 +1,21 @@ +const ERROR_CODES = { + VALIDATION_ERROR: 'VALIDATION_ERROR', + NOT_FOUND: 'NOT_FOUND', + RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED', + INTERNAL_ERROR: 'INTERNAL_ERROR', + INVALID_REQUEST: 'INVALID_REQUEST', + INVALID_ENTITY_TYPE: 'INVALID_ENTITY_TYPE', +}; +const createErrorResponse = (code, message, details) => ({ + error: { + code, + message, + details, + }, +}); +export const createValidationError = (message, issues) => createErrorResponse(ERROR_CODES.VALIDATION_ERROR, message, { issues }); +export const createNotFoundError = (message, entityId) => createErrorResponse(ERROR_CODES.NOT_FOUND, message, entityId ? { entityId } : undefined); +export const createInternalError = (message = 'Internal server error') => { + return createErrorResponse(ERROR_CODES.INTERNAL_ERROR, message); +}; +//# sourceMappingURL=errors.js.map \ No newline at end of file diff --git a/dist/utils/errors.js.map b/dist/utils/errors.js.map new file mode 100644 index 0000000..e9d4f45 --- /dev/null +++ b/dist/utils/errors.js.map @@ -0,0 +1 @@ +{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,GAAG;IAClB,gBAAgB,EAAE,kBAAkB;IACpC,SAAS,EAAE,WAAW;IACtB,mBAAmB,EAAE,qBAAqB;IAC1C,cAAc,EAAE,gBAAgB;IAChC,eAAe,EAAE,iBAAiB;IAClC,mBAAmB,EAAE,qBAAqB;CAClC,CAAC;AAgBX,MAAM,mBAAmB,GAAG,CAAC,IAAe,EAAE,OAAe,EAAE,OAAsB,EAAyB,EAAE,CAAC,CAAC;IAChH,KAAK,EAAE;QACL,IAAI;QACJ,OAAO;QACP,OAAO;KACR;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,OAAe,EAAE,MAAgB,EAAyB,EAAE,CAChG,mBAAmB,CAAC,WAAW,CAAC,gBAAgB,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;AAEzE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAAe,EAAE,QAAiB,EAAyB,EAAE,CAC/F,mBAAmB,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAO3F,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAAO,GAAG,uBAAuB,EAAyB,EAAE;IAC9F,OAAO,mBAAmB,CAAC,WAAW,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;AAClE,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/utils/errors.test.d.ts b/dist/utils/errors.test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/utils/errors.test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/utils/errors.test.js b/dist/utils/errors.test.js new file mode 100644 index 0000000..3716fc6 --- /dev/null +++ b/dist/utils/errors.test.js @@ -0,0 +1,66 @@ +import { describe, expect, it } from 'vitest'; +import { createInternalError, createNotFoundError } from './errors.js'; +describe('Error Utilities', () => { + describe('createInternalError', () => { + it('should create a default internal error', () => { + const error = createInternalError(); + expect(error).toEqual({ + error: { + code: 'INTERNAL_ERROR', + message: 'Internal server error', + details: undefined, + }, + }); + }); + it('should create an internal error with custom message', () => { + const customMessage = 'Database connection failed'; + const error = createInternalError(customMessage); + expect(error).toEqual({ + error: { + code: 'INTERNAL_ERROR', + message: customMessage, + details: undefined, + }, + }); + }); + }); + describe('createNotFoundError', () => { + it('should create a not found error with message', () => { + const message = 'Entity not found'; + const error = createNotFoundError(message); + expect(error).toEqual({ + error: { + code: 'NOT_FOUND', + message, + details: undefined, + }, + }); + }); + it('should create a not found error with message and entityId', () => { + const message = 'Entity not found'; + const entityId = 'http://example.com/entity/123'; + const error = createNotFoundError(message, entityId); + expect(error).toEqual({ + error: { + code: 'NOT_FOUND', + message, + details: { + entityId, + }, + }, + }); + }); + it('should handle undefined entityId', () => { + const message = 'Entity not found'; + const error = createNotFoundError(message, undefined); + expect(error).toEqual({ + error: { + code: 'NOT_FOUND', + message, + details: undefined, + }, + }); + }); + }); +}); +//# sourceMappingURL=errors.test.js.map \ No newline at end of file diff --git a/dist/utils/errors.test.js.map b/dist/utils/errors.test.js.map new file mode 100644 index 0000000..450ebe9 --- /dev/null +++ b/dist/utils/errors.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"errors.test.js","sourceRoot":"","sources":["../../src/utils/errors.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvE,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;YAEpC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;gBACpB,KAAK,EAAE;oBACL,IAAI,EAAE,gBAAgB;oBACtB,OAAO,EAAE,uBAAuB;oBAChC,OAAO,EAAE,SAAS;iBACnB;aACF,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;YAC7D,MAAM,aAAa,GAAG,4BAA4B,CAAC;YACnD,MAAM,KAAK,GAAG,mBAAmB,CAAC,aAAa,CAAC,CAAC;YAEjD,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;gBACpB,KAAK,EAAE;oBACL,IAAI,EAAE,gBAAgB;oBACtB,OAAO,EAAE,aAAa;oBACtB,OAAO,EAAE,SAAS;iBACnB;aACF,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,OAAO,GAAG,kBAAkB,CAAC;YACnC,MAAM,KAAK,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;YAE3C,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;gBACpB,KAAK,EAAE;oBACL,IAAI,EAAE,WAAW;oBACjB,OAAO;oBACP,OAAO,EAAE,SAAS;iBACnB;aACF,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;YACnE,MAAM,OAAO,GAAG,kBAAkB,CAAC;YACnC,MAAM,QAAQ,GAAG,+BAA+B,CAAC;YACjD,MAAM,KAAK,GAAG,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAErD,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;gBACpB,KAAK,EAAE;oBACL,IAAI,EAAE,WAAW;oBACjB,OAAO;oBACP,OAAO,EAAE;wBACP,QAAQ;qBACT;iBACF;aACF,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,OAAO,GAAG,kBAAkB,CAAC;YACnC,MAAM,KAAK,GAAG,mBAAmB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAEtD,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;gBACpB,KAAK,EAAE;oBACL,IAAI,EAAE,WAAW;oBACjB,OAAO;oBACP,OAAO,EAAE,SAAS;iBACnB;aACF,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/utils/queryBuilder.d.ts b/dist/utils/queryBuilder.d.ts new file mode 100644 index 0000000..305d453 --- /dev/null +++ b/dist/utils/queryBuilder.d.ts @@ -0,0 +1,25 @@ +import { SortOrder } from '@opensearch-project/opensearch/api/_types/_common.js'; +import type { Search_RequestBody } from '@opensearch-project/opensearch/api/index.js'; +type Aggregations = Required['aggs']; +export type QueryBuilderOptions = { + aggregations?: Aggregations; +}; +export declare class OpensearchQueryBuilder { + aggregations: Aggregations; + constructor(opts?: QueryBuilderOptions); + buildQuery(searchType: string, query: string, filters: any, boundingBox: any): { + bool: { + must: import("@opensearch-project/opensearch/api/_types/_common.query_dsl.js").QueryContainer[]; + filter: import("@opensearch-project/opensearch/api/_types/_common.query_dsl.js").QueryContainer[]; + }; + }; + buildAggregations(geohashPrecision: number, boundingBox: any): { + [x: string]: import("@opensearch-project/opensearch/api/_types/_common.aggregations.js").AggregationContainer; + }; + buildSort(sort: string, order: SortOrder): { + 'name.keyword': SortOrder; + }[] | { + [x: string]: SortOrder; + }[] | undefined; +} +export {}; diff --git a/dist/utils/queryBuilder.js b/dist/utils/queryBuilder.js new file mode 100644 index 0000000..354f21e --- /dev/null +++ b/dist/utils/queryBuilder.js @@ -0,0 +1,95 @@ +export class OpensearchQueryBuilder { + aggregations; + constructor(opts) { + this.aggregations = opts?.aggregations || + Object.fromEntries(['inLanguage', 'mediaType', 'communicationMode', 'entityType'].map(name => [name, { terms: { field: name + '.keyword', size: 20 } }])); + } + buildQuery(searchType, query, filters, boundingBox) { + const must = []; + const filter = []; + if (searchType === 'basic') { + must.push({ + multi_match: { + query, + fields: ['name^2', 'description'], + type: 'best_fields', + fuzziness: 'AUTO', + zero_terms_query: 'all', + }, + }); + } + else { + must.push({ + query_string: { + query, + fields: ['name^2', 'description'], + default_operator: 'AND', + }, + }); + } + if (filters) { + Object.entries(filters).forEach(([field, values]) => { + filter.push({ + terms: { + [field]: values, + }, + }); + }); + } + if (boundingBox) { + filter.push({ + geo_bounding_box: { + location: { + top_left: { + lat: boundingBox.topRight.lat, + lon: boundingBox.bottomLeft.lng, + }, + bottom_right: { + lat: boundingBox.bottomLeft.lat, + lon: boundingBox.topRight.lng, + }, + }, + }, + }); + } + return { + bool: { + must, + filter, + }, + }; + } + buildAggregations(geohashPrecision, boundingBox) { + const aggs = { ...this.aggregations }; + if (geohashPrecision && boundingBox) { + aggs.geohash_grid = { + geohash_grid: { + field: 'location', + precision: geohashPrecision, + bounds: { + top_left: { + lat: boundingBox.topRight.lat, + lon: boundingBox.bottomLeft.lng, + }, + bottom_right: { + lat: boundingBox.bottomLeft.lat, + lon: boundingBox.topRight.lng, + }, + }, + }, + }; + } + return aggs; + } + buildSort(sort, order) { + if (sort === 'relevance') { + return; + } + const sortField = sort === 'id' ? 'rocrateId' : sort; + if (sortField === 'name') { + return [{ 'name.keyword': order }]; + } + return [{ [sortField]: order }]; + } +} +//# sourceMappingURL=queryBuilder.js.map \ No newline at end of file diff --git a/dist/utils/queryBuilder.js.map b/dist/utils/queryBuilder.js.map new file mode 100644 index 0000000..d01b135 --- /dev/null +++ b/dist/utils/queryBuilder.js.map @@ -0,0 +1 @@ +{"version":3,"file":"queryBuilder.js","sourceRoot":"","sources":["../../src/utils/queryBuilder.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index 4b06796..d847002 100644 --- a/src/app.ts +++ b/src/app.ts @@ -19,6 +19,7 @@ import type { FileTransformer, } from './types/transformers.js'; import { createValidationError } from './utils/errors.js'; +import { OpensearchQueryBuilder, QueryBuilderOptions } from './utils/queryBuilder.js'; export type { AuthorisedEntity, AuthorisedFile, StandardEntity, StandardFile } from './transformers/default.js'; export { AllPublicAccessTransformer, AllPublicFileAccessTransformer } from './transformers/default.js'; @@ -43,6 +44,8 @@ export type { FileTransformer, TransformerContext, } from './types/transformers.js'; +export { OpensearchQueryBuilder }; +export type { QueryBuilderOptions }; declare module 'fastify' { interface FastifyInstance { @@ -97,6 +100,8 @@ const setupSearch = async (fastify: FastifyInstance, opensearch: Client) => { export type Options = { prisma: PrismaClient; opensearch: Client; + queryBuilderClass?: typeof OpensearchQueryBuilder; + queryBuilderOptions?: QueryBuilderOptions; disableCors?: boolean; accessTransformer: AccessTransformer; entityTransformers?: EntityTransformer[]; @@ -109,6 +114,8 @@ const app: FastifyPluginAsync = async (fastify, options) => { const { prisma, opensearch, + queryBuilderClass, + queryBuilderOptions, disableCors = false, accessTransformer, entityTransformers, @@ -142,6 +149,7 @@ const app: FastifyPluginAsync = async (fastify, options) => { throw new Error('roCrateHandler is required'); } + fastify.register(sensible); if (!disableCors) { fastify.register(cors); @@ -155,7 +163,7 @@ const app: FastifyPluginAsync = async (fastify, options) => { fastify.register(files, { fileAccessTransformer, fileTransformers }); fastify.register(file, { fileHandler }); fastify.register(crate, { roCrateHandler }); - fastify.register(search, { accessTransformer, entityTransformers }); + fastify.register(search, { accessTransformer, entityTransformers, queryBuilderClass, queryBuilderOptions }); }; export default fp(app); diff --git a/src/routes/search.ts b/src/routes/search.ts index f7e67d1..423955b 100644 --- a/src/routes/search.ts +++ b/src/routes/search.ts @@ -1,12 +1,12 @@ import type { MultiBucketAggregateBaseFiltersBucket } from '@opensearch-project/opensearch/api/_types/_common.aggregations.js'; -import type { BoolQuery } from '@opensearch-project/opensearch/api/_types/_common.query_dsl.js'; -import type { Search_Request, Search_RequestBody } from '@opensearch-project/opensearch/api/index.js'; +import type { Search_Request } from '@opensearch-project/opensearch/api/index.js'; import type { FastifyPluginAsync } from 'fastify'; import type { ZodTypeProvider } from 'fastify-type-provider-zod'; import { z } from 'zod/v4'; import { baseEntityTransformer, resolveEntityReferences } from '../transformers/default.js'; import type { AccessTransformer, EntityTransformer } from '../types/transformers.js'; import { createInternalError } from '../utils/errors.js'; +import { OpensearchQueryBuilder, QueryBuilderOptions } from '../utils/queryBuilder.js'; const boundingBoxSchema = z.object({ topRight: z.object({ @@ -30,148 +30,17 @@ const searchParamsSchema = z.object({ sort: z.enum(['id', 'name', 'createdAt', 'updatedAt', 'relevance']).default('relevance'), order: z.enum(['asc', 'desc']).default('asc'), }); -type SearchParams = z.infer; - -const buildQuery = ( - searchType: SearchParams['searchType'], - query: SearchParams['query'], - filters: SearchParams['filters'], - boundingBox: SearchParams['boundingBox'], -) => { - const must: BoolQuery['must'] = []; - const filter: BoolQuery['filter'] = []; - - if (searchType === 'basic') { - must.push({ - multi_match: { - query, - fields: ['name^2', 'description'], - type: 'best_fields', - fuzziness: 'AUTO', - zero_terms_query: 'all', - }, - }); - } else { - must.push({ - query_string: { - query, - fields: ['name^2', 'description'], - default_operator: 'AND', - }, - }); - } - - if (filters) { - Object.entries(filters).forEach(([field, values]) => { - filter.push({ - terms: { - [field]: values, - }, - }); - }); - } - - if (boundingBox) { - filter.push({ - geo_bounding_box: { - location: { - top_left: { - lat: boundingBox.topRight.lat, - lon: boundingBox.bottomLeft.lng, - }, - bottom_right: { - lat: boundingBox.bottomLeft.lat, - lon: boundingBox.topRight.lng, - }, - }, - }, - }); - } - - return { - bool: { - must, - filter, - }, - }; -}; - -// TODO: Pull these from a config file -const buildAggregations = ( - geohashPrecision: SearchParams['geohashPrecision'], - boundingBox: SearchParams['boundingBox'], -) => { - const aggs: Search_RequestBody['aggs'] = { - inLanguage: { - terms: { - field: 'inLanguage.keyword', - size: 20, - }, - }, - mediaType: { - terms: { - field: 'mediaType.keyword', - size: 20, - }, - }, - communicationMode: { - terms: { - field: 'communicationMode.keyword', - size: 20, - }, - }, - entityType: { - terms: { - field: 'entityType.keyword', - size: 20, - }, - }, - }; - - // Add geohash aggregation if precision is specified - if (geohashPrecision && boundingBox) { - aggs.geohash_grid = { - geohash_grid: { - field: 'location', - precision: geohashPrecision, - bounds: { - top_left: { - lat: boundingBox.topRight.lat, - lon: boundingBox.bottomLeft.lng, - }, - bottom_right: { - lat: boundingBox.bottomLeft.lat, - lon: boundingBox.topRight.lng, - }, - }, - }, - }; - } - - return aggs; -}; - -const buildSort = (sort: SearchParams['sort'], order: SearchParams['order']) => { - if (sort === 'relevance') { - return; - } - - const sortField = sort === 'id' ? 'rocrateId' : sort; - - if (sortField === 'name') { - return [{ 'name.keyword': order }]; - } - - return [{ [sortField]: order }]; -}; type SearchRouteOptions = { accessTransformer: AccessTransformer; entityTransformers?: EntityTransformer[]; + queryBuilderClass?: typeof OpensearchQueryBuilder; + queryBuilderOptions?: QueryBuilderOptions; }; const search: FastifyPluginAsync = async (fastify, opts) => { - const { accessTransformer, entityTransformers = [] } = opts; + const { accessTransformer, entityTransformers = [], queryBuilderClass = OpensearchQueryBuilder, queryBuilderOptions } = opts; + const queryBuilder = new queryBuilderClass(queryBuilderOptions); fastify.withTypeProvider().post( '/search', { @@ -186,20 +55,20 @@ const search: FastifyPluginAsync = async (fastify, opts) => const opensearchQuery: Search_Request = { index: 'entities', body: { - query: buildQuery(searchType, query, filters, boundingBox), - aggs: buildAggregations(geohashPrecision, boundingBox), + query: queryBuilder.buildQuery(searchType, query, filters, boundingBox), + aggs: queryBuilder.buildAggregations(geohashPrecision, boundingBox), highlight: { fields: { name: {}, description: {}, }, }, - sort: buildSort(sort, order), + sort: queryBuilder.buildSort(sort, order), from: offset, size: limit, }, }; - + console.log(JSON.stringify(opensearchQuery, null, 2)); const response = await fastify.opensearch.search(opensearchQuery); if (!response.body?.hits?.hits) { diff --git a/src/utils/queryBuilder.ts b/src/utils/queryBuilder.ts new file mode 100644 index 0000000..65f9b08 --- /dev/null +++ b/src/utils/queryBuilder.ts @@ -0,0 +1,120 @@ +import { SortOrder } from '@opensearch-project/opensearch/api/_types/_common.js'; +import type { BoolQuery } from '@opensearch-project/opensearch/api/_types/_common.query_dsl.js'; +import type { Search_RequestBody } from '@opensearch-project/opensearch/api/index.js'; + +type Aggregations = Required['aggs']; + +export type QueryBuilderOptions = { + aggregations?: Aggregations +}; + +export class OpensearchQueryBuilder { + aggregations: Aggregations; + + constructor(opts?: QueryBuilderOptions) { + this.aggregations = opts?.aggregations || + Object.fromEntries(['inLanguage', 'mediaType', 'communicationMode', 'entityType'].map( + name => [name, { terms: { field: name + '.keyword', size: 20 } }] + )); + } + + buildQuery(searchType: string, query: string, filters: any, boundingBox: any) { + const must: BoolQuery['must'] = []; + const filter: BoolQuery['filter'] = []; + + if (searchType === 'basic') { + must.push({ + multi_match: { + query, + fields: ['name^2', 'description'], + type: 'best_fields', + fuzziness: 'AUTO', + zero_terms_query: 'all', + }, + }); + } else { + must.push({ + query_string: { + query, + fields: ['name^2', 'description'], + default_operator: 'AND', + }, + }); + } + + if (filters) { + Object.entries(filters).forEach(([field, values]) => { + filter.push({ + terms: { + [field]: values, + }, + }); + }); + } + + if (boundingBox) { + filter.push({ + geo_bounding_box: { + location: { + top_left: { + lat: boundingBox.topRight.lat, + lon: boundingBox.bottomLeft.lng, + }, + bottom_right: { + lat: boundingBox.bottomLeft.lat, + lon: boundingBox.topRight.lng, + }, + }, + }, + }); + } + + return { + bool: { + must, + filter, + }, + }; + } + + buildAggregations(geohashPrecision: number, boundingBox: any) { + const aggs = { ...this.aggregations }; + + // Add geohash aggregation if precision is specified + if (geohashPrecision && boundingBox) { + aggs.geohash_grid = { + geohash_grid: { + field: 'location', + precision: geohashPrecision, + bounds: { + top_left: { + lat: boundingBox.topRight.lat, + lon: boundingBox.bottomLeft.lng, + }, + bottom_right: { + lat: boundingBox.bottomLeft.lat, + lon: boundingBox.topRight.lng, + }, + }, + }, + }; + } + + return aggs; + } + + buildSort(sort: string, order: SortOrder) { + if (sort === 'relevance') { + return; + } + + const sortField = sort === 'id' ? 'rocrateId' : sort; + + if (sortField === 'name') { + return [{ 'name.keyword': order }]; + } + + return [{ [sortField]: order }]; + } + +} \ No newline at end of file