Skip to content

Commit

Permalink
Merge pull request DouglasNeuroInformatics#934 from joshunrau/main
Browse files Browse the repository at this point in the history
  • Loading branch information
joshunrau authored Aug 26, 2024
2 parents e1dffd2 + 9ba6b25 commit e879a4c
Show file tree
Hide file tree
Showing 108 changed files with 1,641 additions and 1,190 deletions.
29 changes: 15 additions & 14 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"lint": "tsc && eslint --fix src",
"start": "NODE_ENV=production env-cmd -f ../../.env node dist/app.mjs",
"start:inspect": "NODE_ENV=production env-cmd -f ../../.env node --inspect dist/app.mjs",
"start:profile": "NODE_ENV=production env-cmd -f ../../.env node --prof dist/app.mjs"
"start:profile": "NODE_ENV=production env-cmd -f ../../.env node --prof dist/app.mjs",
"test": "env-cmd -f ../../.env vitest"
},
"dependencies": {
"@casl/ability": "^6.7.1",
Expand All @@ -22,16 +23,16 @@
"@douglasneuroinformatics/libnest": "^0.1.1",
"@douglasneuroinformatics/libpasswd": "^0.0.3",
"@faker-js/faker": "^8.4.1",
"@nestjs/axios": "^3.0.2",
"@nestjs/common": "^10.3.9",
"@nestjs/config": "^3.2.2",
"@nestjs/core": "^10.3.9",
"@nestjs/axios": "^3.0.3",
"@nestjs/common": "^10.4.1",
"@nestjs/config": "^3.2.3",
"@nestjs/core": "^10.4.1",
"@nestjs/jwt": "^10.2.0",
"@nestjs/mapped-types": "2.0.5",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.3.9",
"@nestjs/swagger": "^7.3.1",
"@nestjs/throttler": "^5.1.2",
"@nestjs/platform-express": "^10.4.1",
"@nestjs/swagger": "^7.4.0",
"@nestjs/throttler": "^6.2.1",
"@opendatacapture/demo": "workspace:*",
"@opendatacapture/instrument-library": "workspace:*",
"@opendatacapture/instrument-utils": "workspace:*",
Expand All @@ -41,10 +42,10 @@
"@opendatacapture/stats": "workspace:*",
"@opendatacapture/subject-utils": "workspace:*",
"@prisma/client": "catalog:",
"axios": "^1.7.2",
"axios": "^1.7.5",
"express": "^4.19.2",
"lodash-es": "workspace:lodash-es__4.x@*",
"mongodb": "^6.7.0",
"mongodb": "^6.8.0",
"passport": "^0.7.0",
"passport-jwt": "4.0.1",
"reflect-metadata": "^0.1.14",
Expand All @@ -53,9 +54,9 @@
"zod": "workspace:zod__3.23.x@*"
},
"devDependencies": {
"@douglasneuroinformatics/esbuild-plugin-native-modules": "^0.0.2",
"@douglasneuroinformatics/esbuild-plugin-prisma": "^0.0.2",
"@nestjs/testing": "^10.3.9",
"@douglasneuroinformatics/esbuild-plugin-native-modules": "^1.0.0",
"@douglasneuroinformatics/esbuild-plugin-prisma": "^1.0.0",
"@nestjs/testing": "^10.4.1",
"@opendatacapture/esbuild-plugin-runtime": "workspace:*",
"@opendatacapture/instrument-stubs": "workspace:*",
"@types/express": "^4.17.21",
Expand All @@ -65,7 +66,7 @@
"concurrently": "^8.2.2",
"esbuild": "catalog:esbuild",
"esbuild-plugin-tsc": "^0.4.0",
"nodemon": "^3.1.3",
"nodemon": "^3.1.4",
"prisma": "catalog:",
"prisma-json-types-generator": "^3.0.4",
"supertest": "^7.0.0",
Expand Down
1 change: 0 additions & 1 deletion apps/api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ enum InstrumentKind {
FORM
INTERACTIVE
SERIES
UNKNOWN
}

type InstrumentInternal {
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/auth/guards/authentication.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class AuthenticationGuard extends AuthGuard('jwt') {
super();
}

canActivate(context: ExecutionContext): boolean | Observable<boolean> | Promise<boolean> {
override canActivate(context: ExecutionContext): boolean | Observable<boolean> | Promise<boolean> {
this.logger.verbose(`Request URL: ${context.switchToHttp().getRequest<Request>().url}`);
return this.isPublicRoute(context) || super.canActivate(context);
}
Expand Down
9 changes: 6 additions & 3 deletions apps/api/src/gateway/gateway.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HybridCrypto } from '@douglasneuroinformatics/libcrypto';
import { HttpService } from '@nestjs/axios';
import { BadGatewayException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { BadGatewayException, HttpStatus, Injectable, Logger, NotImplementedException } from '@nestjs/common';
import { $MutateAssignmentResponseBody, $RemoteAssignment } from '@opendatacapture/schemas/assignment';
import type {
Assignment,
Expand All @@ -21,11 +21,14 @@ export class GatewayService {
) {}

async createRemoteAssignment(assignment: Assignment, publicKey: CryptoKey): Promise<MutateAssignmentResponseBody> {
const instrument = await this.instrumentsService.findById(assignment.instrumentId);
const instrument = await this.instrumentsService.findBundleById(assignment.instrumentId);
if (instrument.kind === 'SERIES') {
throw new NotImplementedException('Cannot create remote assignment for series instrument');
}

const response = await this.httpService.axiosRef.post(`/api/assignments`, {
...assignment,
instrumentBundle: instrument.bundle,
instrumentContainer: instrument,
publicKey: Array.from(await HybridCrypto.serializePublicKey(publicKey))
} satisfies CreateRemoteAssignmentInputData);
if (response.status !== HttpStatus.CREATED) {
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/groups/dto/update-group.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import { CreateGroupDto } from './create-group.dto';
@ValidationSchema($UpdateGroupData)
export class UpdateGroupDto extends PartialType(CreateGroupDto) {
accessibleInstrumentIds?: string[];
name?: string;
override name?: string;
settings?: GroupSettings;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class InstrumentMeasuresService {
computeMeasures(measures: InstrumentMeasures, data: FormInstrument.Data | Json | Prisma.JsonValue) {
const computedMeasures: { [key: string]: InstrumentMeasureValue } = {};
for (const key in measures) {
computedMeasures[key] = this.computeMeasure(measures[key], data);
computedMeasures[key] = this.computeMeasure(measures[key]!, data);
}
return computedMeasures;
}
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/instrument-records/instrument-records.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export class InstrumentRecordsService {
const numericMeasures = pickBy(record.computedMeasures, isNumber);
for (const measure in numericMeasures) {
const x = record.date.getTime();
const y = numericMeasures[measure];
const y = numericMeasures[measure]!;
if (Array.isArray(data[measure])) {
data[measure].push([x, y]);
} else {
Expand All @@ -243,7 +243,7 @@ export class InstrumentRecordsService {

const results: LinearRegressionResults = {};
for (const measure in data) {
results[measure] = linearRegression(data[measure]);
results[measure] = linearRegression(data[measure]!);
}
return results;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,19 +99,19 @@ describe('/instruments', () => {
// });
// });

describe('GET /instruments/:id', () => {
describe('GET /instruments/bundle/:id', () => {
let id: string;
beforeAll(() => {
id = new ObjectId().toHexString();
});
it('should return status code 200 with a valid ID', async () => {
instrumentModel.findFirst.mockResolvedValueOnce({ id, kind: 'FORM' });
const response = await request(server).get(`/instruments/${id}`);
expect(response.status).toBe(HttpStatus.OK);
});
// it('should return status code 200 with a valid ID', async () => {
// instrumentModel.findFirst.mockResolvedValueOnce({ id, kind: 'FORM' });
// const response = await request(server).get(`/instruments/${id}`);
// expect(response.status).toBe(HttpStatus.OK);
// });
it('should throw a not found exception if the instrument does not exist', async () => {
instrumentModel.findFirst.mockResolvedValueOnce(null);
const response = await request(server).get(`/instruments/${id}`);
const response = await request(server).get(`/instruments/bundle/${id}`);
expect(response.status).toBe(HttpStatus.NOT_FOUND);
});
// it('should return the instrument if it exists', async () => {
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/instruments/instruments.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export class InstrumentsService {
return {
bundle: instance.bundle,
id: instance.id,
kind: 'SCALAR'
kind: instance.kind
};
} else if (isSeriesInstrument(instance)) {
return {
Expand Down
24 changes: 11 additions & 13 deletions apps/gateway/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,35 @@
"@opendatacapture/runtime-v1": "workspace:*",
"@opendatacapture/schemas": "workspace:*",
"@prisma/client": "catalog:",
"axios": "^1.7.2",
"axios": "^1.7.5",
"compression": "^1.7.4",
"express": "^4.19.2",
"i18next": "^23.11.5",
"i18next": "catalog:",
"lodash-es": "workspace:lodash-es__4.x@*",
"pino-http": "^10.1.0",
"pino-pretty": "^11.2.0",
"pino-http": "^10.2.0",
"pino-pretty": "^11.2.2",
"react": "workspace:react__18.x@*",
"react-dom": "workspace:react-dom__18.x@*",
"react-i18next": "^14.1.2",
"react-i18next": "catalog:",
"sirv": "^2.0.4",
"sqlite3": "^5.1.7",
"zod": "workspace:zod__3.23.x@*"
},
"devDependencies": {
"@douglasneuroinformatics/esbuild-plugin-native-modules": "^0.0.2",
"@douglasneuroinformatics/esbuild-plugin-prisma": "^0.0.2",
"@douglasneuroinformatics/esbuild-plugin-native-modules": "^1.0.0",
"@douglasneuroinformatics/esbuild-plugin-prisma": "^1.0.0",
"@opendatacapture/tailwindcss": "workspace:*",
"@opendatacapture/vite-plugin-runtime": "workspace:*",
"@types/compression": "^1.7.5",
"@types/express": "^4.17.21",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react-swc": "^3.7.0",
"autoprefixer": "^10.4.19",
"autoprefixer": "^10.4.20",
"esbuild": "catalog:esbuild",
"postcss": "^8.4.38",
"postcss": "^8.4.41",
"prisma": "catalog:",
"tailwindcss": "^3.4.4",
"tailwindcss": "^3.4.10",
"type-fest": "workspace:type-fest__4.x@*",
"vite": "^5.2.13"
"vite": "^5.4.2"
},
"trustedDependencies": [
"prisma",
Expand Down
1 change: 1 addition & 0 deletions apps/gateway/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ model RemoteAssignmentModel {
expiresAt DateTime
groupId String?
instrumentBundle String
instrumentKind String
instrumentId String
rawPublicKey Bytes
symmetricKey Bytes?
Expand Down
11 changes: 6 additions & 5 deletions apps/gateway/src/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { useEffect, useRef } from 'react';
import { NotificationHub } from '@douglasneuroinformatics/libui/components';
import { LanguageToggle, ThemeToggle } from '@douglasneuroinformatics/libui/components';
import { useNotificationsStore } from '@douglasneuroinformatics/libui/hooks';
import { InstrumentRenderer } from '@opendatacapture/instrument-renderer';
import { InstrumentRenderer, type InstrumentSubmitHandler } from '@opendatacapture/instrument-renderer';
import { Branding } from '@opendatacapture/react-core';
import type { InstrumentKind } from '@opendatacapture/runtime-core';
import type { UpdateAssignmentData } from '@opendatacapture/schemas/assignment';
import type { Json } from '@opendatacapture/schemas/core';
import axios from 'axios';

import './services/axios';
Expand All @@ -15,18 +15,19 @@ import './services/i18n';
export type RootProps = {
bundle: string;
id: string;
kind: Exclude<InstrumentKind, 'SERIES'>;
token: string;
};

export const Root = ({ bundle, id, token }: RootProps) => {
export const Root = ({ bundle, id, kind, token }: RootProps) => {
const ref = useRef<HTMLDivElement>(null);
const notifications = useNotificationsStore();

useEffect(() => {
ref.current!.style.display = 'flex';
}, []);

const handleSubmit = async (data: Json) => {
const handleSubmit: InstrumentSubmitHandler = async ({ data }) => {
await axios.patch(
`/api/assignments/${id}`,
{
Expand Down Expand Up @@ -60,7 +61,7 @@ export const Root = ({ bundle, id, token }: RootProps) => {
</div>
</header>
<main className="container flex min-h-0 max-w-3xl flex-grow flex-col pb-16 pt-32 xl:max-w-5xl">
<InstrumentRenderer className="min-h-full w-full" target={{ bundle, kind: 'SCALAR' }} onSubmit={handleSubmit} />
<InstrumentRenderer className="min-h-full w-full" target={{ bundle, id, kind }} onSubmit={handleSubmit} />
</main>
<NotificationHub />
</div>
Expand Down
5 changes: 4 additions & 1 deletion apps/gateway/src/routers/api.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,13 @@ router.post(
if (!result.success) {
throw new HttpException(400, 'Bad Request');
}
const { publicKey, ...assignment } = result.data;
const { instrumentContainer, publicKey, ...assignment } = result.data;
await prisma.remoteAssignmentModel.create({
data: {
...assignment,
instrumentBundle: instrumentContainer.bundle,
instrumentId: instrumentContainer.id,
instrumentKind: instrumentContainer.kind,
rawPublicKey: Buffer.from(publicKey)
}
});
Expand Down
22 changes: 20 additions & 2 deletions apps/gateway/src/routers/root.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const router = Router();
router.get(
'/assignments/:id',
ah(async (req, res, next) => {
const id = req.params.id;
const id = req.params.id!;
const assignment = await prisma.remoteAssignmentModel.findFirst({
where: { id }
});
Expand All @@ -21,8 +21,26 @@ router.get(
.set({ 'Content-Type': 'application/json' })
.json({ error: 'Conflict', message: 'Assignment already completed', statusCode: 409 });
}

const kind = assignment.instrumentKind;
if (!(kind === 'FORM' || kind === 'INTERACTIVE')) {
return res
.status(501)
.set({ 'Content-Type': 'application/json' })
.json({
error: 'Not Implemented',
message: `Cannot render instrument kind '${kind}' on remote gateway`,
statusCode: 501
});
}

const token = generateToken(assignment.id);
const html = res.locals.loadRoot({ bundle: assignment.instrumentBundle, id, token });
const html = res.locals.loadRoot({
bundle: assignment.instrumentBundle,
id,
kind,
token
});
res.status(200).set({ 'Content-Type': 'text/html' }).end(html);
})
);
Expand Down
2 changes: 1 addition & 1 deletion apps/gateway/src/server/server.development.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class DevelopmentServer extends BaseServer {
this.app.use(this.vite.middlewares);
}

protected fixStacktrace(err: Error) {
protected override fixStacktrace(err: Error) {
return this.vite.ssrFixStacktrace(err);
}

Expand Down
2 changes: 1 addition & 1 deletion apps/gateway/src/utils/http-exception.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export class HttpException extends Error {
constructor(
public readonly status: number,
public readonly message: string
message: string
) {
super(message);
}
Expand Down
3 changes: 1 addition & 2 deletions apps/gateway/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
"paths": {
"@/*": ["./src/*"],
"/runtime/v1/*": ["../../runtime/v1/dist/*"]
},
"resolveJsonModule": true
}
},
"include": ["scripts/*", "src/**/*", "*.js", "*.cjs"]
}
4 changes: 2 additions & 2 deletions apps/outreach/astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import path from 'path';
import sitemap from '@astrojs/sitemap';
import starlight from '@astrojs/starlight';
import tailwind from '@astrojs/tailwind';
import { defineConfig, squooshImageService } from 'astro/config';
import { defineConfig, sharpImageService } from 'astro/config';
import { toString } from 'mdast-util-to-string';
import getReadingTime from 'reading-time';

Expand All @@ -23,7 +23,7 @@ export default defineConfig({
},
compressHTML: true,
image: {
service: squooshImageService()
service: sharpImageService()
},
integrations: [
sitemap({
Expand Down
Loading

0 comments on commit e879a4c

Please sign in to comment.