diff --git a/package-lock.json b/package-lock.json index 2ea4ec6b..45114b1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10518,6 +10518,12 @@ "path-type": "^4.0.0" } }, + "discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=", + "dev": true + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -13475,6 +13481,12 @@ "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", "dev": true }, + "immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "dev": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -17855,6 +17867,18 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "dev": true + }, + "moo": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", + "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==", + "dev": true + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -17924,6 +17948,26 @@ "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "dev": true }, + "nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "dev": true, + "requires": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -18843,6 +18887,12 @@ } } }, + "object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "dev": true + }, "object-inspect": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", @@ -19558,6 +19608,22 @@ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" }, + "pg-mem": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/pg-mem/-/pg-mem-2.1.7.tgz", + "integrity": "sha512-jDRFdtkyY22kN40vfnVL8OTQ1HnjMxLJ0gSInGgSMpZynM+5J1oDZ5OPme4Kw21V0o753sc+X1FsdpPXIVycFw==", + "dev": true, + "requires": { + "@mikro-orm/core": "^4.5.3", + "@mikro-orm/postgresql": "^4.5.3", + "functional-red-black-tree": "^1.0.1", + "immutable": "^4.0.0-rc.12", + "lru-cache": "^6.0.0", + "moment": "^2.27.0", + "object-hash": "^2.0.3", + "pgsql-ast-parser": "^9.2.1" + } + }, "pg-pool": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.3.0.tgz", @@ -19588,6 +19654,16 @@ "split2": "^3.1.1" } }, + "pgsql-ast-parser": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/pgsql-ast-parser/-/pgsql-ast-parser-9.2.1.tgz", + "integrity": "sha512-poyC5hEwFyo6M1b1MFSH9uCeJ6rzjT/F7E0SZsLZZKvVmfrJ3sr8hW3AA35t4cZ7OuaMpCg4Qnmzspo+5Q/+zg==", + "dev": true, + "requires": { + "moo": "^0.5.1", + "nearley": "^2.19.5" + } + }, "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", @@ -20999,6 +21075,22 @@ "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.2.tgz", "integrity": "sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ==" }, + "railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=", + "dev": true + }, + "randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "dev": true, + "requires": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + } + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", diff --git a/package.json b/package.json index d05ec0cf..5847b57c 100644 --- a/package.json +++ b/package.json @@ -185,6 +185,7 @@ "nodemon": "^2.0.6", "npm-watch": "^0.9.0", "parcel": "2.0.0-beta.2", + "pg-mem": "^2.1.7", "prettier": "^2.4.1", "rimraf": "^3.0.2", "supertest": "^6.1.6", diff --git a/src/backend/db.ts b/src/backend/db.ts deleted file mode 100644 index e3246542..00000000 --- a/src/backend/db.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MikroORM, RequestContext } from '@mikro-orm/core'; -import 'reflect-metadata'; -import config from './mikro-orm.config'; - -type Middleware = ( - ctx: Record, - next: (...args: any[]) => Promise, -) => Promise; - -export async function dbWrapper(): Promise<[MikroORM, Middleware]> { - const orm = await MikroORM.init(config); - const dbMiddleware: Middleware = (_, next) => - RequestContext.createAsync(orm.em, next); - return [orm, dbMiddleware]; -} diff --git a/src/backend/index.ts b/src/backend/index.ts index d167842d..092fcdb5 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -1,5 +1,7 @@ +import { MikroORM } from '@mikro-orm/core'; import { createServer } from 'http'; import config from './config'; +import ormConfig from './mikro-orm.config'; import configServer from './server'; /** @@ -12,7 +14,8 @@ async function bootstrap() { * await sequelize.authenticate() */ config.parse(process.argv); - return createServer(await configServer(config)).listen(config.port); + const orm = await MikroORM.init(ormConfig); + return createServer(await configServer(orm, config)).listen(config.port); } bootstrap() diff --git a/src/backend/scripts/dbInit.ts b/src/backend/scripts/dbInit.ts index 96b7f8aa..fcb16c96 100644 --- a/src/backend/scripts/dbInit.ts +++ b/src/backend/scripts/dbInit.ts @@ -1,9 +1,10 @@ -import { dbWrapper } from '../db'; +import { MikroORM } from '@mikro-orm/core'; +import dbConfig from '../mikro-orm.config'; import { groupModelWrapper } from '../models'; async function main() { try { - const [db] = await dbWrapper(); + const db = await MikroORM.init(dbConfig); const groups = groupModelWrapper(db); const adminGroup = groups.create({ name: 'admins' }); console.log('Created admins group:', adminGroup); diff --git a/src/backend/server.ts b/src/backend/server.ts index 22f0c322..da0ca96d 100644 --- a/src/backend/server.ts +++ b/src/backend/server.ts @@ -1,4 +1,5 @@ // Node modules +import { MikroORM, RequestContext } from '@mikro-orm/core'; import { RequestListener } from 'http'; import path from 'path'; @@ -25,7 +26,6 @@ import serialize from 'serialize-javascript'; // Our modules import { createError } from './utils/http-errors'; -import { dbWrapper } from './db'; // Our middlewares import authWrapper from './middleware/auth.js'; // authorization/user roles @@ -88,6 +88,7 @@ const startTime = Symbol.for('request-received.startTime'); type Config = Record; // TODO add correct type export default async function configServer( + db: MikroORM, config: Config, ): Promise { // Initialize our application server @@ -113,8 +114,7 @@ export default async function configServer( server.use(xRequestId()); // Initialize database - const [db, dbMiddleware] = await dbWrapper(); - server.use(dbMiddleware); + server.use((_, next) => RequestContext.createAsync(db.em, next)); // Setup auth handlers const userModel = userModelWrapper(db); diff --git a/test/backend/preprints.test.ts b/test/backend/preprints.test.ts new file mode 100644 index 00000000..575bb156 --- /dev/null +++ b/test/backend/preprints.test.ts @@ -0,0 +1,19 @@ +import { StatusCodes } from 'http-status-codes'; +import request from 'supertest'; +import { createServer } from '../setup'; + +describe('preprints', () => { + it('returns a 404 when not found', async () => { + const response = await request(await createServer()).get( + '/api/v2/preprints/doi-10.5555-12345678', + ); + + expect(response.status).toBe(StatusCodes.NOT_FOUND); + expect(response.type).toBe('application/json'); + expect(response.body).toStrictEqual({ + message: 'That preprint with ID doi-10.5555-12345678 does not exist.', + status: 'HTTP 404 Error.', + statusCode: 404, + }); + }); +}); diff --git a/test/setup.ts b/test/setup.ts index a7f898ca..584c33cd 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -1,8 +1,31 @@ +import { MikroORM } from '@mikro-orm/core'; +import { PostgreSqlDriver } from '@mikro-orm/postgresql'; import { RequestListener } from 'http'; +import { IBackup, newDb } from 'pg-mem'; +import dbConfig from '../src/backend/mikro-orm.config'; import configServer from '../src/backend/server'; +let orm: MikroORM; +let backup: IBackup; + +global.beforeAll(async () => { + const db = newDb(); + orm = await db.adapters.createMikroOrm(dbConfig); + const generator = orm.getSchemaGenerator(); + + await generator.createSchema(); + + backup = db.backup(); +}); + +global.afterAll(async () => { + await orm.close(true); +}); + export async function createServer(config = {}): Promise { - return await configServer({ + backup.restore(); + + return await configServer(orm, { logLevel: 'off', orcidCallbackUrl: 'http://localhost/', orcidClientId: 'orcid-client-id',