From c521da6713af499ecf4560812339e718db8ac0cf Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Mon, 2 Dec 2024 08:57:02 +0000 Subject: [PATCH] Events v2: Connect country config event configuration so that it can be read in client (#8100) Co-authored-by: Markus Co-authored-by: Markus Laurila --- docker-compose.yml | 13 +- infrastructure/nginx-deploy-config.sh | 2 + packages/client/Dockerfile | 1 + packages/client/graphql.schema.json | 30 +- packages/client/nginx.conf | 8 + packages/client/package.json | 9 +- packages/client/src/App.tsx | 38 ++- packages/client/src/forms/index.ts | 2 +- packages/client/src/utils/gateway.ts | 30 +- packages/client/src/v2-events/.eslintrc.js | 7 +- .../{PublishEvent.tsx => EventFormWizard.tsx} | 39 ++- .../features/events/EventSelection.tsx | 106 +++++++ .../src/v2-events/features/events/fixtures.ts | 1 + .../events/registered-fields/RadioGroup.tsx | 33 +++ .../events/registered-fields/index.ts | 1 + .../src/v2-events/features/events/useEvent.ts | 50 +--- .../v2-events/features/events/useEventForm.ts | 41 +++ .../src/v2-events/features/events/utils.ts | 22 ++ .../v2-events/features/workqueues/index.tsx | 110 +++++++- packages/client/src/v2-events/index.ts | 1 + packages/client/src/v2-events/routes/index.ts | 14 + packages/client/src/v2-events/trcp.tsx | 60 ++++ packages/client/src/views/OfficeHome/utils.ts | 1 + .../src/views/RecordAudit/ActionMenu.tsx | 1 - packages/client/tsconfig.paths.json | 6 +- packages/client/vite.config.ts | 5 + packages/commons/src/events/FieldConfig.ts | 18 +- packages/commons/src/events/FormConfig.ts | 15 +- .../components/PanDraggable.tsx | 2 +- .../components/src/FormWizard/FormWizard.tsx | 4 +- packages/events/notebook.ipynb | 259 +++++++++++++----- packages/events/src/environment.ts | 3 +- packages/events/src/index.test.ts | 3 +- packages/events/src/index.ts | 4 +- packages/events/src/router.ts | 20 +- packages/events/src/service/config/config.ts | 28 ++ .../src/service/indexing/indexing.test.ts | 9 +- .../events/src/service/indexing/indexing.ts | 29 +- .../src/storage/__mocks__/elasticsearch.ts | 7 +- .../events/src/storage/__mocks__/mongodb.ts | 12 +- packages/events/src/storage/elasticsearch.ts | 3 +- packages/events/vitest.config.ts | 4 +- packages/gateway/src/config/routes.ts | 4 +- .../src/features/metrics/root-resolvers.ts | 4 +- packages/gateway/src/graphql/config.ts | 4 +- .../src/v2-events/event-config/routes.ts | 31 +++ .../src/v2-events/events/root-resolvers.ts | 26 +- .../src/v2-events/events/router.ts} | 3 +- .../gateway/src/v2-events/events/service.ts | 29 ++ yarn.lock | 58 ++-- 50 files changed, 935 insertions(+), 275 deletions(-) rename packages/client/src/v2-events/features/events/{PublishEvent.tsx => EventFormWizard.tsx} (70%) create mode 100644 packages/client/src/v2-events/features/events/EventSelection.tsx create mode 100644 packages/client/src/v2-events/features/events/registered-fields/RadioGroup.tsx create mode 100644 packages/client/src/v2-events/features/events/useEventForm.ts create mode 100644 packages/client/src/v2-events/features/events/utils.ts create mode 100644 packages/client/src/v2-events/routes/index.ts create mode 100644 packages/client/src/v2-events/trcp.tsx create mode 100644 packages/events/src/service/config/config.ts create mode 100644 packages/gateway/src/v2-events/event-config/routes.ts rename packages/{client/src/v2-events/routes/routes.ts => gateway/src/v2-events/events/router.ts} (82%) create mode 100644 packages/gateway/src/v2-events/events/service.ts diff --git a/docker-compose.yml b/docker-compose.yml index c3f3c146099..72a894ac8a3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,6 +26,7 @@ services: - base environment: - COUNTRY_CONFIG_URL_INTERNAL=http://countryconfig:3040 + - GATEWAY_URL_INTERNAL=http://gateway:7070 dashboards: image: opencrvs/ocrvs-dashboards:${VERSION} #platform: linux/amd64 @@ -48,16 +49,6 @@ services: environment: - COUNTRY_CONFIG_URL_INTERNAL=http://countryconfig:3040 - events: - image: opencrvs/ocrvs-events:${VERSION} - #platform: linux/amd64 - build: - context: . - dockerfile: ./packages/events/Dockerfile - restart: unless-stopped - depends_on: - - base - gateway: image: opencrvs/ocrvs-gateway:${VERSION} #platform: linux/amd64 @@ -87,6 +78,7 @@ services: - CHECK_INVALID_TOKEN=true - MINIO_BUCKET=ocrvs - DOCUMENTS_URL=http://documents:9050 + events: image: opencrvs/ocrvs-events:${VERSION} #platform: linux/amd64 @@ -99,6 +91,7 @@ services: environment: - MONGO_URL=mongodb://mongo1/events - ES_HOST=elasticsearch:9200 + - COUNTRY_CONFIG_URL=http://countryconfig:3040/ # User facing services workflow: diff --git a/infrastructure/nginx-deploy-config.sh b/infrastructure/nginx-deploy-config.sh index ffc4a7d6e7a..0480f686cc9 100755 --- a/infrastructure/nginx-deploy-config.sh +++ b/infrastructure/nginx-deploy-config.sh @@ -10,5 +10,7 @@ set -e sed -i s~{{COUNTRY_CONFIG_URL_INTERNAL}}~$COUNTRY_CONFIG_URL_INTERNAL~g /etc/nginx/conf.d/default.conf sed -i s~{{COUNTRY_CONFIG_URL_INTERNAL}}~$COUNTRY_CONFIG_URL_INTERNAL~g /usr/share/nginx/html/index.html +sed -i s~{{GATEWAY_URL_INTERNAL}}~$GATEWAY_URL_INTERNAL~g /etc/nginx/conf.d/default.conf +sed -i s~{{GATEWAY_URL_INTERNAL}}~$GATEWAY_URL_INTERNAL~g /usr/share/nginx/html/index.html sed -i s~{{CONTENT_SECURITY_POLICY_WILDCARD}}~$CONTENT_SECURITY_POLICY_WILDCARD~g /etc/nginx/conf.d/default.conf nginx -g 'daemon off;' diff --git a/packages/client/Dockerfile b/packages/client/Dockerfile index 205042cb324..f06fbbbaff1 100644 --- a/packages/client/Dockerfile +++ b/packages/client/Dockerfile @@ -12,6 +12,7 @@ COPY --chown=node:node packages/components /app/packages/components RUN yarn install --frozen-lockfile && yarn build ENV CONTENT_SECURITY_POLICY_WILDCARD "{{CONTENT_SECURITY_POLICY_WILDCARD}}" ENV COUNTRY_CONFIG_URL_INTERNAL "{{COUNTRY_CONFIG_URL_INTERNAL}}" +ENV GATEWAY_URL_INTERNAL "{{GATEWAY_URL_INTERNAL}}" WORKDIR /app/packages/client COPY --chown=node:node packages/client /app/packages/client RUN yarn install --frozen-lockfile && yarn build diff --git a/packages/client/graphql.schema.json b/packages/client/graphql.schema.json index 55c72656d55..f48994196f9 100644 --- a/packages/client/graphql.schema.json +++ b/packages/client/graphql.schema.json @@ -2101,7 +2101,7 @@ "fields": null, "inputFields": [ { - "name": "fields", + "name": "data", "description": null, "type": { "kind": "NON_NULL", @@ -3806,7 +3806,7 @@ "fields": null, "inputFields": [ { - "name": "fields", + "name": "data", "description": null, "type": { "kind": "NON_NULL", @@ -4709,7 +4709,7 @@ "deprecationReason": null }, { - "name": "fields", + "name": "data", "description": null, "args": [], "type": { @@ -5625,7 +5625,7 @@ "deprecationReason": null }, { - "name": "fields", + "name": "data", "description": null, "args": [], "type": { @@ -5693,7 +5693,7 @@ "fields": null, "inputFields": [ { - "name": "fields", + "name": "data", "description": null, "type": { "kind": "NON_NULL", @@ -7991,7 +7991,7 @@ "fields": null, "inputFields": [ { - "name": "fields", + "name": "data", "description": null, "type": { "kind": "NON_NULL", @@ -12507,7 +12507,7 @@ "deprecationReason": null }, { - "name": "fields", + "name": "data", "description": null, "args": [], "type": { @@ -12559,7 +12559,7 @@ "fields": null, "inputFields": [ { - "name": "fields", + "name": "data", "description": null, "type": { "kind": "NON_NULL", @@ -16477,7 +16477,7 @@ "deprecationReason": null }, { - "name": "fields", + "name": "data", "description": null, "args": [], "type": { @@ -16545,7 +16545,7 @@ "fields": null, "inputFields": [ { - "name": "fields", + "name": "data", "description": null, "type": { "kind": "NON_NULL", @@ -17513,7 +17513,7 @@ "fields": null, "inputFields": [ { - "name": "fields", + "name": "data", "description": null, "type": { "kind": "NON_NULL", @@ -17587,7 +17587,7 @@ "fields": null, "inputFields": [ { - "name": "fields", + "name": "data", "description": null, "type": { "kind": "NON_NULL", @@ -18390,7 +18390,7 @@ "fields": null, "inputFields": [ { - "name": "fields", + "name": "data", "description": null, "type": { "kind": "NON_NULL", @@ -18452,7 +18452,7 @@ "fields": null, "inputFields": [ { - "name": "fields", + "name": "data", "description": null, "type": { "kind": "NON_NULL", @@ -18487,7 +18487,7 @@ "fields": null, "inputFields": [ { - "name": "fields", + "name": "data", "description": null, "type": { "kind": "NON_NULL", diff --git a/packages/client/nginx.conf b/packages/client/nginx.conf index 005f2f175ef..53f718058fa 100644 --- a/packages/client/nginx.conf +++ b/packages/client/nginx.conf @@ -69,6 +69,14 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } + location /api/ { + rewrite ^/api/(.*)$ /$1 break; + proxy_pass {{GATEWAY_URL_INTERNAL}}; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } location / { root /usr/share/nginx/html; diff --git a/packages/client/package.json b/packages/client/package.json index 770846101f1..38a88612e7b 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -16,7 +16,7 @@ "open:cov": "yarn test && opener coverage/index.html", "lint": "yarn lint:css && yarn lint:ts", "lint:css": "stylelint 'src/**/*.{ts,tsx}'", - "lint:ts": "eslint --fix './src/**/*.{ts,tsx}' --max-warnings=349", + "lint:ts": "eslint --fix './src/**/*.{ts,tsx}' --max-warnings=353", "test:compilation": "tsc --noEmit", "extract:translations": "bash extract-translations.sh", "generate-gateway-types": "NODE_OPTIONS=--dns-result-order=ipv4first graphql-codegen --config codegen.ts && prettier --write src/utils/gateway.ts", @@ -36,9 +36,14 @@ "@craco/craco": "^6.4.3", "@opencrvs/commons": "^1.5.0", "@opencrvs/components": "^1.3.0", + "@opencrvs/gateway": "1.5.0", "@reduxjs/toolkit": "^1.8.3", "@sentry/react": "^7.12.1", "@sentry/tracing": "^7.12.1", + "@tanstack/react-query": "^5.61.5", + "@trpc/client": "^11.0.0-rc.648", + "@trpc/react-query": "^11.0.0-rc.648", + "@trpc/server": "^11.0.0-rc.648", "@types/bcryptjs": "^2.4.2", "@types/history": "^4.6.2", "@types/html-to-pdfmake": "^2.4.4", @@ -65,6 +70,7 @@ "formik": "^2.2.9", "google-libphonenumber": "^3.2.32", "graphql": "^15.0.0", + "graphql-tag": "^2.12.6", "graphql-tools": "^4.0.7", "handlebars": "^4.7.6", "history": "^4.7.2", @@ -95,6 +101,7 @@ "redux-sentry-middleware": "^0.2.0", "sanitize-html": "^2.4.0", "styled-components": "^5.2.0", + "superjson": "1.9.0-0", "tsconfig-paths": "^3.13.0", "vite": "^5.4.8", "vite-plugin-svgr": "^0.6.0", diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx index b4b84ef792a..281f1b8b9d5 100644 --- a/packages/client/src/App.tsx +++ b/packages/client/src/App.tsx @@ -69,9 +69,15 @@ import { ReviewCorrection } from './views/ReviewCorrection/ReviewCorrection' import { ReviewCertificate } from './views/PrintCertificate/ReviewCertificateAction' import AllUserEmail from './views/SysAdmin/Communications/AllUserEmail/AllUserEmail' import { ReloadModal } from './views/Modals/ReloadModal' -import { V2_EVENT_ROUTE, V2_ROOT_ROUTE } from './v2-events/routes/routes' +import { + V2_EVENT_ROUTE, + V2_EVENTS_ROUTE, + V2_ROOT_ROUTE +} from './v2-events/routes' import { Workqueues } from './v2-events/features/workqueues' -import { PublishEvent } from './v2-events/features/events/PublishEvent' +import { EventFormWizardIndex } from './v2-events/features/events/EventFormWizard' +import { TRPCProvider } from './v2-events/trcp' +import { Events } from './v2-events/features/events/EventSelection' interface IAppProps { client?: ApolloClient @@ -96,6 +102,7 @@ const GlobalStyle = createGlobalStyle` export function App(props: IAppProps) { const { client } = useApolloClient(props.store) + return ( @@ -522,16 +529,23 @@ export function App(props: IAppProps) { path={routes.PRINT_RECORD} component={PrintRecord} /> - - + + + + + ) diff --git a/packages/client/src/forms/index.ts b/packages/client/src/forms/index.ts index 57055868232..2218acf6489 100644 --- a/packages/client/src/forms/index.ts +++ b/packages/client/src/forms/index.ts @@ -125,7 +125,7 @@ export interface IDynamicOptions { options?: { [key: string]: ISelectOption[] } } -export interface IDispatchOptions { +interface IDispatchOptions { action: string payloadKey: string } diff --git a/packages/client/src/utils/gateway.ts b/packages/client/src/utils/gateway.ts index 67c67822aa9..1b27447390f 100644 --- a/packages/client/src/utils/gateway.ts +++ b/packages/client/src/utils/gateway.ts @@ -238,7 +238,7 @@ export type AdvancedSearchParametersInput = { } export type ApproveCorrectionActionInput = { - fields: Array + data: Array } export type AssignmentData = { @@ -415,7 +415,7 @@ export type CertificationMetric = { } export type CertifyActionInput = { - fields: Array + data: Array } export type Comment = { @@ -507,7 +507,7 @@ export type CreateAction = { __typename?: 'CreateAction' createdAt: Scalars['DateTime'] createdBy: Scalars['String'] - fields: Array + data: Array type: Scalars['String'] } @@ -598,13 +598,13 @@ export type DeclareAction = { __typename?: 'DeclareAction' createdAt: Scalars['DateTime'] createdBy: Scalars['String'] - fields: Array + data: Array identifiers: Identifiers type: Scalars['String'] } export type DeclareActionInput = { - fields: Array + data: Array } export type Dummy = { @@ -858,7 +858,7 @@ export enum IntegratingSystemType { } export type IssueActionInput = { - fields: Array + data: Array } export type LabelInput = { @@ -1404,12 +1404,12 @@ export type NotifyAction = { __typename?: 'NotifyAction' createdAt: Scalars['DateTime'] createdBy: Scalars['String'] - fields: Array + data: Array type: Scalars['String'] } export type NotifyActionInput = { - fields: Array + data: Array } export type ObservationFhirids = { @@ -1865,13 +1865,13 @@ export type RegisterAction = { __typename?: 'RegisterAction' createdAt: Scalars['DateTime'] createdBy: Scalars['String'] - fields: Array + data: Array identifiers: Identifiers type: Scalars['String'] } export type RegisterActionInput = { - fields: Array + data: Array } export type Registration = { @@ -1963,7 +1963,7 @@ export enum RegistrationType { } export type ReinstateActionInput = { - fields: Array + data: Array } export type Reinstated = { @@ -1973,7 +1973,7 @@ export type Reinstated = { } export type RejectCorrectionActionInput = { - fields: Array + data: Array } export type RejectRegistrationInput = { @@ -2044,7 +2044,7 @@ export type RemoveBookmarkedSeachInput = { } export type RequestCorrectionActionInput = { - fields: Array + data: Array } export type Response = { @@ -2053,11 +2053,11 @@ export type Response = { } export type RevokeActionInput = { - fields: Array + data: Array } export type RevokeCorrectionActionInput = { - fields: Array + data: Array } export type Role = { diff --git a/packages/client/src/v2-events/.eslintrc.js b/packages/client/src/v2-events/.eslintrc.js index bef662819b6..28379e1f257 100644 --- a/packages/client/src/v2-events/.eslintrc.js +++ b/packages/client/src/v2-events/.eslintrc.js @@ -13,7 +13,12 @@ module.exports = { 'no-restricted-imports': [ 'error', { - patterns: ['@client/*', '!@client/v2-events', '!@client/components'] + patterns: [ + '@client/*', + '!@client/v2-events', + '!@client/components', + '!@client/utils' + ] } ] } diff --git a/packages/client/src/v2-events/features/events/PublishEvent.tsx b/packages/client/src/v2-events/features/events/EventFormWizard.tsx similarity index 70% rename from packages/client/src/v2-events/features/events/PublishEvent.tsx rename to packages/client/src/v2-events/features/events/EventFormWizard.tsx index 3de83d70306..b01e02115da 100644 --- a/packages/client/src/v2-events/features/events/PublishEvent.tsx +++ b/packages/client/src/v2-events/features/events/EventFormWizard.tsx @@ -21,12 +21,34 @@ import { import React from 'react' import { useEvent } from './useEvent' import { useParams } from 'react-router-dom' -import { TextField, Paragraph, DateField } from './registered-fields' +import { useEventForm } from './useEventForm' +import { EventConfig } from '@opencrvs/commons/client' +import { + TextField, + Paragraph, + DateField, + RadioGroup +} from './registered-fields' -export function PublishEvent() { +export function EventFormWizardIndex() { const { eventType } = useParams<{ eventType: string }>() - const { title, event, exit, saveAndExit, previous, next, page } = - useEvent(eventType) + + const { event, isLoading } = useEvent(eventType) + + if (isLoading) { + return
Loading...
+ } + + if (!event) { + throw new Error('Event not found') + } + + return +} + +export function EventFormWizard({ event }: { event: EventConfig }) { + const { title, pages, exit, saveAndExit, previous, next, currentPageIndex } = + useEventForm(event) return ( console.log(values)} + onSubmit={() => {}} /> diff --git a/packages/client/src/v2-events/features/events/EventSelection.tsx b/packages/client/src/v2-events/features/events/EventSelection.tsx new file mode 100644 index 00000000000..8ccc0bf56d3 --- /dev/null +++ b/packages/client/src/v2-events/features/events/EventSelection.tsx @@ -0,0 +1,106 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +import React from 'react' + +import { + Frame, + AppBar, + Button, + Icon, + Content, + ContentSize, + FormWizard, + Values, + Spinner +} from '@opencrvs/components' +import { V2_EVENT_ROUTE } from '@client/v2-events/routes' + +import { trpc } from '@client/v2-events/trcp' +import { formatUrl } from './utils' +import { useHistory } from 'react-router-dom' +import { RadioGroup } from './registered-fields/RadioGroup' + +export const Events = () => { + const history = useHistory() + const { data, isLoading } = trpc.config.get.useQuery() + + const events = data ?? [] + + const onSubmit = ({ eventType }: Values) => { + if (eventType) { + history.push( + formatUrl(V2_EVENT_ROUTE, { + eventType + }) + ) + } + } + + return ( + {}}> + + Exit + + } + /> + } + skipToContentText="Skip to main content" + > + + + + {isLoading ? ( + + ) : ( + ({ + value: event.id, + label: event.label.defaultMessage + })) + } + ] + } + ]} + components={{ + RADIO_GROUP: RadioGroup + }} + defaultValues={{ + eventType: events[0]?.id + }} + onSubmit={onSubmit} + /> + )} + + + + + ) +} diff --git a/packages/client/src/v2-events/features/events/fixtures.ts b/packages/client/src/v2-events/features/events/fixtures.ts index 882b630c11a..aa86b27bdb1 100644 --- a/packages/client/src/v2-events/features/events/fixtures.ts +++ b/packages/client/src/v2-events/features/events/fixtures.ts @@ -10,6 +10,7 @@ */ import { EventConfig } from '@opencrvs/commons/client' +/** @knipignore */ export const tennisClubMembershipEvent = { id: 'TENNIS_CLUB_MEMBERSHIP', label: { diff --git a/packages/client/src/v2-events/features/events/registered-fields/RadioGroup.tsx b/packages/client/src/v2-events/features/events/registered-fields/RadioGroup.tsx new file mode 100644 index 00000000000..07bbf4cb585 --- /dev/null +++ b/packages/client/src/v2-events/features/events/registered-fields/RadioGroup.tsx @@ -0,0 +1,33 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +import React from 'react' +import { + InputField, + useFormContext, + RadioGroup as RadioGroupComponent +} from '@opencrvs/components' +import { FieldProps } from '@opencrvs/commons' + +export const RadioGroup = ({ name, options }: FieldProps<'RADIO_GROUP'>) => { + const { setValue, watch } = useFormContext() + const value = watch(name) + + return ( + + setValue(name, val)} + value={value} + /> + + ) +} diff --git a/packages/client/src/v2-events/features/events/registered-fields/index.ts b/packages/client/src/v2-events/features/events/registered-fields/index.ts index 6231aaf3f71..ed52c2c04ef 100644 --- a/packages/client/src/v2-events/features/events/registered-fields/index.ts +++ b/packages/client/src/v2-events/features/events/registered-fields/index.ts @@ -11,3 +11,4 @@ export * from './TextField' export * from './DateField' export * from './Paragraph' +export * from './RadioGroup' diff --git a/packages/client/src/v2-events/features/events/useEvent.ts b/packages/client/src/v2-events/features/events/useEvent.ts index 53aede4f56b..4c216a75e72 100644 --- a/packages/client/src/v2-events/features/events/useEvent.ts +++ b/packages/client/src/v2-events/features/events/useEvent.ts @@ -8,45 +8,25 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ +import { trpc } from '@client/v2-events/trcp' -import { tennisClubMembershipEvent } from './fixtures' -import { useIntl } from 'react-intl' -import { usePagination } from './usePagination' -import { FieldConfig } from '@opencrvs/commons/client' - -const eventTypes = { - 'tennis-club-membership': tennisClubMembershipEvent -} - -export function useEvent(anyEventType: string) { - const intl = useIntl() - - if (!eventTypes[anyEventType as keyof typeof eventTypes]) { - throw new Error(`Event type ${anyEventType} not found`) - } - - const type = anyEventType as keyof typeof eventTypes - const event = eventTypes[type] - const { pages, label } = eventTypes[type].actions[0].forms[0] - - const { next, previous, page } = usePagination(pages.length) +/** + * Fetches configures events and finds a matching event + * @param eventIdentifier e.g. 'birth', 'death', 'marriage' or any configured event + * @returns event configuration + */ +export function useEvent(eventIdentifier: string) { + const hook = trpc.config.get.useQuery() + const { error, data, isFetching } = hook - const exit = () => alert('exit') - const saveAndExit = () => alert('save and exit') - const finish = () => alert('finish') + const event = data?.find((event) => event.id === eventIdentifier) - const title = intl.formatMessage(label) + const noMatchingEvent = !isFetching && !event return { - type, - title, - exit, - saveAndExit, - previous, - next, - finish, - page, - form: pages, - event + // We hide the distinction between fetching (all calls) and loading (initial call) from the caller. + isLoading: isFetching, + error: noMatchingEvent ? 'Event not found' : error?.message, + event: event } } diff --git a/packages/client/src/v2-events/features/events/useEventForm.ts b/packages/client/src/v2-events/features/events/useEventForm.ts new file mode 100644 index 00000000000..3ac14fb7107 --- /dev/null +++ b/packages/client/src/v2-events/features/events/useEventForm.ts @@ -0,0 +1,41 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +import { useIntl } from 'react-intl' +import { usePagination } from './usePagination' +import { EventConfig } from '@opencrvs/commons' + +/** + * Creates form methods (e.g. pagination) based on the event configuration + */ +export function useEventForm(event: EventConfig) { + const intl = useIntl() + + const pages = event.actions[0].forms[0].pages + + const { next, previous, page } = usePagination(pages.length) + + const exit = () => alert('exit') + const saveAndExit = () => alert('save and exit') + const finish = () => alert('finish') + + return { + title: intl.formatMessage(event.label), + eventId: event.id, + exit, + saveAndExit, + previous, + next, + finish, + pages, + currentPageIndex: page + } +} diff --git a/packages/client/src/v2-events/features/events/utils.ts b/packages/client/src/v2-events/features/events/utils.ts new file mode 100644 index 00000000000..6b99c845f91 --- /dev/null +++ b/packages/client/src/v2-events/features/events/utils.ts @@ -0,0 +1,22 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +/** + * Copied over from core. Used until react-router-dom is updated to v6 + * @deprecated + */ +export function formatUrl(url: string, props: { [key: string]: string }) { + const formattedUrl = Object.keys(props).reduce( + (str, key) => str.replace(`:${key}`, props[key]), + url + ) + return formattedUrl.endsWith('?') ? formattedUrl.slice(0, -1) : formattedUrl +} diff --git a/packages/client/src/v2-events/features/workqueues/index.tsx b/packages/client/src/v2-events/features/workqueues/index.tsx index 8065a50ecc7..281f414278c 100644 --- a/packages/client/src/v2-events/features/workqueues/index.tsx +++ b/packages/client/src/v2-events/features/workqueues/index.tsx @@ -8,8 +8,116 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ +import { push } from 'connected-react-router' + import React from 'react' +import { + Frame, + AppBar, + Stack, + Button, + Icon, + Content, + LeftNavigation, + NavigationGroup, + NavigationItem, + ContentSize, + SearchTool, + Text +} from '@opencrvs/components' +import { DeclarationIconSmall } from '@opencrvs/components/lib/icons/DeclarationIconSmall' +import { Plus } from '@opencrvs/components/src/icons' +import { V2_EVENTS_ROUTE } from '@client/v2-events/routes' +import { useDispatch } from 'react-redux' + export const Workqueues = () => { - return
Workqueues
+ const dispatch = useDispatch() + return ( + + + } + label="In progress" + /> + } + label="Ready for review" + /> + } + label="Requires updates" + /> + } + label="Sent for approval" + /> + } + label="Ready to print" + /> + } + label="Ready to issue" + /> + + + } + label="Organisation" + /> + } + label="Team" + /> + } + label="Performance" + /> + + + } + skipToContentText="skip" + header={ + + + + , + placeHolderText: 'Search' + } + ]} + searchHandler={() => {}} + /> + + } + /> + } + > + + + 🚧🚧🚧🚧🚧🚧🚧🚧👷‍♂️👷👷🏻👷🏻‍♀️👷‍♂️👷‍♂️🚧🚧🚧🚧🚧🚧🚧🚧 + + + + ) } diff --git a/packages/client/src/v2-events/index.ts b/packages/client/src/v2-events/index.ts index 9634905e3f9..09295dc64ec 100644 --- a/packages/client/src/v2-events/index.ts +++ b/packages/client/src/v2-events/index.ts @@ -9,4 +9,5 @@ * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ +/** @knipignore */ export function noop() {} diff --git a/packages/client/src/v2-events/routes/index.ts b/packages/client/src/v2-events/routes/index.ts new file mode 100644 index 00000000000..469b9c226d7 --- /dev/null +++ b/packages/client/src/v2-events/routes/index.ts @@ -0,0 +1,14 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +export const V2_ROOT_ROUTE = '/v2' +export const V2_EVENTS_ROUTE = `${V2_ROOT_ROUTE}/event` +export const V2_EVENT_ROUTE = `${V2_EVENTS_ROUTE}/event/:eventType` diff --git a/packages/client/src/v2-events/trcp.tsx b/packages/client/src/v2-events/trcp.tsx new file mode 100644 index 00000000000..5aa97de59db --- /dev/null +++ b/packages/client/src/v2-events/trcp.tsx @@ -0,0 +1,60 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +import React from 'react' +import { createTRPCReact } from '@trpc/react-query' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { httpBatchLink } from '@trpc/client' +import superjson from 'superjson' +import type { AppRouter } from '@gateway/v2-events/events/router' +import { getToken } from '@client/utils/authUtils' + +export const trpc = createTRPCReact() + +let queryClient: QueryClient +let trpcClient: ReturnType + +const getTrpcClient = () => { + if (!trpcClient) { + trpcClient = trpc.createClient({ + links: [ + httpBatchLink({ + url: '/api/events', + transformer: superjson, + async headers() { + return { + authorization: `Bearer ${getToken()}` + } + } + }) + ] + }) + } + + return trpcClient +} + +const getQueryClient = () => { + if (!queryClient) { + queryClient = new QueryClient() + } + + return queryClient +} + +export function TRPCProvider({ children }: { children: React.ReactNode }) { + const trpcClient = getTrpcClient() + const queryClient = getQueryClient() + return ( + + {children} + + ) +} diff --git a/packages/client/src/views/OfficeHome/utils.ts b/packages/client/src/views/OfficeHome/utils.ts index 819fa1f9649..f543d269747 100644 --- a/packages/client/src/views/OfficeHome/utils.ts +++ b/packages/client/src/views/OfficeHome/utils.ts @@ -12,6 +12,7 @@ import { COLUMNS, SORT_ORDER } from '@opencrvs/components/lib/Workqueue' import { orderBy } from 'lodash' import { ITaskHistory } from '@client/declarations' +// eslint-disable-next-line @typescript-eslint/no-explicit-any export const getSortedItems = ( items: T[], sortedCol: COLUMNS, diff --git a/packages/client/src/views/RecordAudit/ActionMenu.tsx b/packages/client/src/views/RecordAudit/ActionMenu.tsx index a6f9cedaef2..02b56461f2d 100644 --- a/packages/client/src/views/RecordAudit/ActionMenu.tsx +++ b/packages/client/src/views/RecordAudit/ActionMenu.tsx @@ -52,7 +52,6 @@ import { client } from '@client/utils/apolloClient' import { EventType } from '@client/utils/gateway' import { GQLAssignmentData } from '@client/utils/gateway-deprecated-do-not-use' import { DeleteModal } from '@client/views/RegisterForm/RegisterForm' -import { EVENT } from '@opencrvs/commons/client' import { Icon, ResponsiveModal } from '@opencrvs/components' import { DangerButton, diff --git a/packages/client/tsconfig.paths.json b/packages/client/tsconfig.paths.json index 1ba62e5c28c..42ed613a0b1 100644 --- a/packages/client/tsconfig.paths.json +++ b/packages/client/tsconfig.paths.json @@ -1,5 +1,9 @@ { "compilerOptions": { - "paths": { "@client/*": ["./*"] } + "paths": { + "@client/*": ["./*"], + "@gateway/*": ["../../gateway/src/*"], + "@events/*": ["../../events/src/*"] + } } } diff --git a/packages/client/vite.config.ts b/packages/client/vite.config.ts index cc2736c8ba1..8fd33d19f14 100644 --- a/packages/client/vite.config.ts +++ b/packages/client/vite.config.ts @@ -139,6 +139,11 @@ export default defineConfig(({ mode }) => { target: 'http://localhost:3040', changeOrigin: true, rewrite: (path) => path.replace(/^\/api\/countryconfig/, '') + }, + '/api/': { + target: 'http://localhost:7070', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, '') } } } diff --git a/packages/commons/src/events/FieldConfig.ts b/packages/commons/src/events/FieldConfig.ts index f37e17a52ce..1da2e65a1a5 100644 --- a/packages/commons/src/events/FieldConfig.ts +++ b/packages/commons/src/events/FieldConfig.ts @@ -41,10 +41,26 @@ const Paragraph = z .object({ type: z.literal('PARAGRAPH'), label: TranslationConfig }) .describe('A read-only HTML

paragraph') +const RadioGroup = z + .object({ + type: z.literal('RADIO_GROUP'), + name: z.string(), + required: z.boolean(), + label: TranslationConfig, + options: z.array( + z.object({ + value: z.string().describe('The value of the option'), + label: z.string().describe('The label of the option') + }) + ) + }) + .describe('Grouped radio options') + export const FieldConfig = z.discriminatedUnion('type', [ TextField, DateField, - Paragraph + Paragraph, + RadioGroup ]) export type FieldConfig = z.infer diff --git a/packages/commons/src/events/FormConfig.ts b/packages/commons/src/events/FormConfig.ts index 989c94f09ad..a984ce7dad3 100644 --- a/packages/commons/src/events/FormConfig.ts +++ b/packages/commons/src/events/FormConfig.ts @@ -12,6 +12,12 @@ import { z } from 'zod' import { FieldConfig } from './FieldConfig' import { TranslationConfig } from './TranslationConfig' +const FormPage = z.object({ + id: z.string().describe('Unique identifier for the page'), + title: TranslationConfig.describe('Header title of the page'), + fields: z.array(FieldConfig).describe('Fields to be rendered on the page') +}) + export const FormConfig = z.object({ label: TranslationConfig.describe('Human readable description of the form'), version: z.object({ @@ -25,13 +31,8 @@ export const FormConfig = z.object({ ) }), active: z.boolean().default(false).describe('Whether the form is active'), - pages: z.array( - z.object({ - id: z.string().describe('Unique identifier for the page'), - title: TranslationConfig.describe('Header title of the page'), - fields: z.array(FieldConfig).describe('Fields to be rendered on the page') - }) - ) + pages: z.array(FormPage) }) +export type FormPage = z.infer export type FormConfig = z.infer diff --git a/packages/components/src/DocumentViewer/components/PanDraggable.tsx b/packages/components/src/DocumentViewer/components/PanDraggable.tsx index 56c10e5e3fd..0ca88349f95 100644 --- a/packages/components/src/DocumentViewer/components/PanDraggable.tsx +++ b/packages/components/src/DocumentViewer/components/PanDraggable.tsx @@ -22,7 +22,7 @@ export interface IDragData { dy: number } -export interface IReactPanZoomStateType { +interface IReactPanZoomStateType { dragging: boolean mouseDown: boolean comesFromDragging: boolean diff --git a/packages/components/src/FormWizard/FormWizard.tsx b/packages/components/src/FormWizard/FormWizard.tsx index b3cea89e5bc..04301a53886 100644 --- a/packages/components/src/FormWizard/FormWizard.tsx +++ b/packages/components/src/FormWizard/FormWizard.tsx @@ -18,7 +18,7 @@ import { Button } from '../Button' * @example * { "informant.name": "John Doe", "informant.address": "123 Main St" } */ -type Values = Record +export type Values = Record /** * Definition of a page of the form wizard @@ -34,7 +34,7 @@ type FormWizardProps = { pages: Page[] defaultValues?: Values /** Callback when the user clicks the "Continue" button */ - onNextPage: (() => void) | undefined + onNextPage?: () => void /** Callback when the user submits the form wizard */ onSubmit: (data: Values) => void } diff --git a/packages/events/notebook.ipynb b/packages/events/notebook.ipynb index ed80d3a6b1c..134969a4a9a 100644 --- a/packages/events/notebook.ipynb +++ b/packages/events/notebook.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 185, + "execution_count": 225, "metadata": {}, "outputs": [], "source": [ @@ -53,7 +53,7 @@ }, { "cell_type": "code", - "execution_count": 186, + "execution_count": 226, "metadata": {}, "outputs": [ { @@ -62,7 +62,7 @@ "text": [ " % Total % Received % Xferd Average Speed Time Time Time Current\n", " Dload Upload Total Spent Left Speed\n", - "100 577 100 299 100 278 686 638 --:--:-- --:--:-- --:--:-- 1323\n" + "100 3609 100 3331 100 278 588k 50289 --:--:-- --:--:-- --:--:-- 704k\n" ] } ], @@ -85,47 +85,69 @@ }, { "cell_type": "code", - "execution_count": 187, + "execution_count": 227, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Event ID: '1e2404d8-0c90-4a34-91c7-fe1f27899b38'\n", - "Token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWdpc3RlciIsInBlcmZvcm1hbmNlIiwiY2VydGlmeSIsImRlbW8iXSwiaWF0IjoxNzMyNzA5MTk0LCJleHAiOjE3MzMzMTM5OTQsImF1ZCI6WyJvcGVuY3J2czphdXRoLXVzZXIiLCJvcGVuY3J2czp1c2VyLW1nbnQtdXNlciIsIm9wZW5jcnZzOmhlYXJ0aC11c2VyIiwib3BlbmNydnM6Z2F0ZXdheS11c2VyIiwib3BlbmNydnM6bm90aWZpY2F0aW9uLXVzZXIiLCJvcGVuY3J2czp3b3JrZmxvdy11c2VyIiwib3BlbmNydnM6c2VhcmNoLXVzZXIiLCJvcGVuY3J2czptZXRyaWNzLXVzZXIiLCJvcGVuY3J2czpjb3VudHJ5Y29uZmlnLXVzZXIiLCJvcGVuY3J2czp3ZWJob29rcy11c2VyIiwib3BlbmNydnM6Y29uZmlnLXVzZXIiLCJvcGVuY3J2czpkb2N1bWVudHMtdXNlciJdLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI2NzMzMzA5ODI3Yjk3ZTY0ODM4NzcxODgifQ.RN47J3mCQjC1ODxdFCRDdA8MURkYvwtbdLURpwRphvi8-MpjFr5oO_38CGLyqsyrR2uLn8R1IBmhxieN1zxqJJNYqTZHhLviAFQuL9wTnYVcNw40xA6A69y-AeEstdLOgzB5kBYBJIBPW83DCpIizmSd25eIvk-Wv8zh69ybDXZ7uFJ3eiOW-kBjpT6SGj9ZX27IEODLrsrSIdM098dz-QQpwiywrjm5Oz2S9VQGmgU1ORQTrcyJDwK1mB7_SuNhbF3i1FHqd5hienYpp388OekhzjisN0M_TbWeeNwm6A__nFISViGwEhtLmYxGtFswxZUBOa3kJjR65psAUch2Ag\n", + "Event ID: 'null'\n", + "Token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWdpc3RlciIsInBlcmZvcm1hbmNlIiwiY2VydGlmeSIsImRlbW8iXSwiaWF0IjoxNzMyNzE0OTM4LCJleHAiOjE3MzMzMTk3MzgsImF1ZCI6WyJvcGVuY3J2czphdXRoLXVzZXIiLCJvcGVuY3J2czp1c2VyLW1nbnQtdXNlciIsIm9wZW5jcnZzOmhlYXJ0aC11c2VyIiwib3BlbmNydnM6Z2F0ZXdheS11c2VyIiwib3BlbmNydnM6bm90aWZpY2F0aW9uLXVzZXIiLCJvcGVuY3J2czp3b3JrZmxvdy11c2VyIiwib3BlbmNydnM6c2VhcmNoLXVzZXIiLCJvcGVuY3J2czptZXRyaWNzLXVzZXIiLCJvcGVuY3J2czpjb3VudHJ5Y29uZmlnLXVzZXIiLCJvcGVuY3J2czp3ZWJob29rcy11c2VyIiwib3BlbmNydnM6Y29uZmlnLXVzZXIiLCJvcGVuY3J2czpkb2N1bWVudHMtdXNlciJdLCJpc3MiOiJvcGVuY3J2czphdXRoLXNlcnZpY2UiLCJzdWIiOiI2NzMzMzA5ODI3Yjk3ZTY0ODM4NzcxODgifQ.wSLoqLpBKjLwVydLkP7vZCb8sDKe4y-DDNMWR-o7hViq87pLuBLUtEEyanqD-VMmHr5nIV6A8aALwR5ydN1b8aokYRDmu5XXSxGp0yylQ3of1GjgMq3IaDR0q483LYgOFdMmxyszw22C9RMa4n92oc3kJchjTsljZZVdWQIk6kNKDJ4f0xBpsN_z4K3PZI35BRF-cTDua5y9pP8NdsIwEQB08z8PNhu0ZMglqUYibfPgdD-0Pn8UqY56L_ZRq6h8IRbsEzNA_u2u5jkAzmtICWYyXAJbwAcRpJOZgJcSTbMzvqWIXBAnD1z4WsScDTj1xXRKGCQACofqPqTKIOrI-A\n", "\n", "{\n", - " \"data\": {\n", - " \"declareEvent\": {\n", - " \"id\": \"1e2404d8-0c90-4a34-91c7-fe1f27899b38\",\n", - " \"type\": \"tennis-club-membership\",\n", - " \"createdAt\": \"2024-11-27T12:06:34.941Z\",\n", - " \"updatedAt\": \"2024-11-27T12:06:34.941Z\",\n", - " \"actions\": [\n", - " {},\n", + " \"errors\": [\n", + " {\n", + " \"message\": \"Cannot query field \\\"id\\\" on type \\\"Field\\\". Did you mean to use an inline fragment on \\\"BaseField\\\", \\\"DateField\\\", \\\"ParagraphField\\\", or \\\"TextField\\\"?\",\n", + " \"locations\": [\n", " {\n", - " \"type\": \"DECLARE\",\n", - " \"createdAt\": \"2024-11-27T12:06:37.275Z\",\n", - " \"createdBy\": \"6733309827b97e6483877188\",\n", - " \"data\": [\n", - " {\n", - " \"id\": \"applicant.firstname\",\n", - " \"value\": \"Riku\"\n", - " },\n", - " {\n", - " \"id\": \"applicant.surname\",\n", - " \"value\": \"Rouvila\"\n", - " },\n", - " {\n", - " \"id\": \"applicant.dob\",\n", - " \"value\": \"2024-11-27T11:56:20.781Z\"\n", - " }\n", - " ]\n", + " \"line\": 1,\n", + " \"column\": 211\n", " }\n", - " ]\n", + " ],\n", + " \"extensions\": {\n", + " \"code\": \"GRAPHQL_VALIDATION_FAILED\",\n", + " \"stacktrace\": [\n", + " \"GraphQLError: Cannot query field \\\"id\\\" on type \\\"Field\\\". Did you mean to use an inline fragment on \\\"BaseField\\\", \\\"DateField\\\", \\\"ParagraphField\\\", or \\\"TextField\\\"?\",\n", + " \" at Object.Field (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/graphql/validation/rules/FieldsOnCorrectTypeRule.js:51:13)\",\n", + " \" at Object.enter (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/graphql/language/visitor.js:301:32)\",\n", + " \" at Object.enter (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/graphql/utilities/TypeInfo.js:391:27)\",\n", + " \" at visit (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/graphql/language/visitor.js:197:21)\",\n", + " \" at validate (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/graphql/validation/validate.js:91:24)\",\n", + " \" at processGraphQLRequest (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/@apollo/server/src/requestPipeline.ts:246:40)\",\n", + " \" at processTicksAndRejections (node:internal/process/task_queues:95:5)\",\n", + " \" at internalExecuteOperation (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/@apollo/server/src/ApolloServer.ts:1331:12)\",\n", + " \" at runHttpQuery (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/@apollo/server/src/runHttpQuery.ts:232:27)\",\n", + " \" at runPotentiallyBatchedHttpQuery (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/@apollo/server/src/httpBatching.ts:85:12)\"\n", + " ]\n", + " }\n", + " },\n", + " {\n", + " \"message\": \"Cannot query field \\\"value\\\" on type \\\"Field\\\".\",\n", + " \"locations\": [\n", + " {\n", + " \"line\": 1,\n", + " \"column\": 214\n", + " }\n", + " ],\n", + " \"extensions\": {\n", + " \"code\": \"GRAPHQL_VALIDATION_FAILED\",\n", + " \"stacktrace\": [\n", + " \"GraphQLError: Cannot query field \\\"value\\\" on type \\\"Field\\\".\",\n", + " \" at Object.Field (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/graphql/validation/rules/FieldsOnCorrectTypeRule.js:51:13)\",\n", + " \" at Object.enter (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/graphql/language/visitor.js:301:32)\",\n", + " \" at Object.enter (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/graphql/utilities/TypeInfo.js:391:27)\",\n", + " \" at visit (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/graphql/language/visitor.js:197:21)\",\n", + " \" at validate (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/graphql/validation/validate.js:91:24)\",\n", + " \" at processGraphQLRequest (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/@apollo/server/src/requestPipeline.ts:246:40)\",\n", + " \" at processTicksAndRejections (node:internal/process/task_queues:95:5)\",\n", + " \" at internalExecuteOperation (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/@apollo/server/src/ApolloServer.ts:1331:12)\",\n", + " \" at runHttpQuery (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/@apollo/server/src/runHttpQuery.ts:232:27)\",\n", + " \" at runPotentiallyBatchedHttpQuery (/Users/riku/Code/OpenCRVS/opencrvs-core/packages/gateway/node_modules/@apollo/server/src/httpBatching.ts:85:12)\"\n", + " ]\n", + " }\n", " }\n", - " }\n", + " ]\n", "}\n" ] }, @@ -135,7 +157,7 @@ "text": [ " % Total % Received % Xferd Average Speed Time Time Time Current\n", " Dload Upload Total Spent Left Speed\n", - "100 1053 100 450 100 603 22114 29632 --:--:-- --:--:-- --:--:-- 52650\n" + "100 3902 100 3331 100 571 619k 106k --:--:-- --:--:-- --:--:-- 762k\n" ] } ], @@ -170,7 +192,7 @@ }, { "cell_type": "code", - "execution_count": 193, + "execution_count": 230, "metadata": {}, "outputs": [ { @@ -179,50 +201,135 @@ "text": [ "{\n", " \"data\": {\n", - " \"getEvent\": {\n", - " \"id\": \"1e2404d8-0c90-4a34-91c7-fe1f27899b38\",\n", - " \"type\": \"tennis-club-membership\",\n", - " \"createdAt\": \"2024-11-27T12:06:34.941Z\",\n", - " \"updatedAt\": \"2024-11-27T12:06:34.941Z\",\n", - " \"actions\": [\n", - " {\n", - " \"type\": \"CREATE\",\n", - " \"createdAt\": \"2024-11-27T12:06:34.941Z\",\n", - " \"createdBy\": \"6733309827b97e6483877188\",\n", - " \"data\": []\n", + " \"getEventConfig\": [\n", + " {\n", + " \"id\": \"TENNIS_CLUB_MEMBERSHIP\",\n", + " \"label\": {\n", + " \"id\": \"event.tennis-club-membership.label\",\n", + " \"defaultMessage\": \"Tennis club membership application\",\n", + " \"description\": \"This is what this event is referred as in the system\"\n", " },\n", - " {\n", - " \"type\": \"DECLARE\",\n", - " \"createdAt\": \"2024-11-27T12:06:37.275Z\",\n", - " \"createdBy\": \"6733309827b97e6483877188\",\n", - " \"data\": [\n", - " {\n", - " \"id\": \"applicant.firstname\",\n", - " \"value\": \"Riku\"\n", - " },\n", - " {\n", - " \"id\": \"applicant.surname\",\n", - " \"value\": \"Rouvila\"\n", + " \"actions\": [\n", + " {\n", + " \"label\": {\n", + " \"id\": \"event.tennis-club-membership.action.declare.label\",\n", + " \"defaultMessage\": \"Send an application\",\n", + " \"description\": \"This is shown as the action name anywhere the user can trigger the action from\"\n", " },\n", - " {\n", - " \"id\": \"applicant.dob\",\n", - " \"value\": \"2024-11-27T11:56:20.781Z\"\n", - " }\n", - " ]\n", - " }\n", - " ]\n", - " }\n", + " \"type\": \"DECLARE\",\n", + " \"forms\": [\n", + " {\n", + " \"label\": {\n", + " \"id\": \"event.tennis-club-membership.action.declare.form.label\",\n", + " \"defaultMessage\": \"Tennis club membership application\",\n", + " \"description\": \"This is what this form is referred as in the system\"\n", + " },\n", + " \"version\": {\n", + " \"id\": \"1.0.0\",\n", + " \"label\": {\n", + " \"id\": \"event.tennis-club-membership.action.declare.form.version.1\",\n", + " \"defaultMessage\": \"Version 1\",\n", + " \"description\": \"This is the first version of the form\"\n", + " }\n", + " },\n", + " \"active\": true,\n", + " \"pages\": [\n", + " {\n", + " \"id\": \"applicant\",\n", + " \"title\": {\n", + " \"id\": \"event.tennis-club-membership.action.declare.form.section.who.title\",\n", + " \"defaultMessage\": \"Who is applying for the membership?\",\n", + " \"description\": \"This is the title of the section\"\n", + " },\n", + " \"fields\": [\n", + " {\n", + " \"id\": \"applicant.firstname\",\n", + " \"required\": true,\n", + " \"label\": {\n", + " \"id\": \"event.tennis-club-membership.action.declare.form.section.who.field.firstname.label\",\n", + " \"defaultMessage\": \"Applicant's first name\",\n", + " \"description\": \"This is the label for the field\"\n", + " },\n", + " \"options\": null\n", + " },\n", + " {\n", + " \"id\": \"applicant.surname\",\n", + " \"required\": true,\n", + " \"label\": {\n", + " \"id\": \"event.tennis-club-membership.action.declare.form.section.who.field.surname.label\",\n", + " \"defaultMessage\": \"Applicant's surname\",\n", + " \"description\": \"This is the label for the field\"\n", + " },\n", + " \"options\": null\n", + " },\n", + " {\n", + " \"id\": \"applicant.dob\",\n", + " \"required\": true,\n", + " \"label\": {\n", + " \"id\": \"event.tennis-club-membership.action.declare.form.section.who.field.dob.label\",\n", + " \"defaultMessage\": \"Applicant's date of birth\",\n", + " \"description\": \"This is the label for the field\"\n", + " },\n", + " \"options\": null\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"id\": \"recommender\",\n", + " \"title\": {\n", + " \"id\": \"event.tennis-club-membership.action.declare.form.section.recommender.title\",\n", + " \"defaultMessage\": \"Who is recommending the applicant?\",\n", + " \"description\": \"This is the title of the section\"\n", + " },\n", + " \"fields\": [\n", + " {\n", + " \"id\": \"recommender.firstname\",\n", + " \"required\": true,\n", + " \"label\": {\n", + " \"id\": \"event.tennis-club-membership.action.declare.form.section.recommender.field.firstname.label\",\n", + " \"defaultMessage\": \"Recommender's first name\",\n", + " \"description\": \"This is the label for the field\"\n", + " },\n", + " \"options\": null\n", + " },\n", + " {\n", + " \"id\": \"recommender.surname\",\n", + " \"required\": true,\n", + " \"label\": {\n", + " \"id\": \"event.tennis-club-membership.action.declare.form.section.recommender.field.surname.label\",\n", + " \"defaultMessage\": \"Recommender's surname\",\n", + " \"description\": \"This is the label for the field\"\n", + " },\n", + " \"options\": null\n", + " },\n", + " {\n", + " \"id\": \"recommender.id\",\n", + " \"required\": true,\n", + " \"label\": {\n", + " \"id\": \"event.tennis-club-membership.action.declare.form.section.recommender.field.id.label\",\n", + " \"defaultMessage\": \"Recommender's membership ID\",\n", + " \"description\": \"This is the label for the field\"\n", + " },\n", + " \"options\": null\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + " }\n", + " ]\n", + " }\n", + " ]\n", " }\n", "}\n" ] } ], "source": [ - "%%bash -s \"$id\" \"$TOKEN\"\n", - "\n", - "id=$(echo \"$1\" | xargs)\n", - "TOKEN=$2\n", + "%%bash -s \"$TOKEN\"\n", "\n", + "TOKEN=$1\n", "\n", "response_data=$(curl --request POST \\\n", " --url \"http://localhost:7070/graphql\" \\\n", @@ -230,13 +337,17 @@ " --header 'Authorization: Bearer '$TOKEN \\\n", " --header 'Content-Type: application/json' \\\n", " -d '{\n", - " \"query\": \"query GetEvent($eventId: ID!) { getEvent(eventId: $eventId) { id type createdAt updatedAt actions { ... on CreateAction { type createdAt createdBy data { id value } } ... on NotifyAction { type createdAt createdBy data { id value } } ... on DeclareAction { type createdAt createdBy data { id value } } ... on RegisterAction { type createdAt createdBy data { id value } } } } }\",\n", - " \"variables\": {\n", - " \"eventId\": \"'${id}'\"\n", - " }\n", + " \"query\": \"query GetEventConfig { getEventConfig { id label { id defaultMessage description } actions { label { id defaultMessage description } type forms { label { id defaultMessage description } version { id label { id defaultMessage description } } active pages { id title { id defaultMessage description } fields { ... on TextField { id required label { id defaultMessage description } options { maxLength } } ... on DateField { id required label { id defaultMessage description } options { notice { id defaultMessage description } } } ... on ParagraphField { id required label { id defaultMessage description } } } } } } } }\"\n", " }')\n", "echo \"$response_data\" | jq" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/packages/events/src/environment.ts b/packages/events/src/environment.ts index 77fc8eab4a1..f1798e1b379 100644 --- a/packages/events/src/environment.ts +++ b/packages/events/src/environment.ts @@ -13,5 +13,6 @@ import { cleanEnv, url } from 'envalid' export const env = cleanEnv(process.env, { MONGO_URL: url({ devDefault: 'mongodb://localhost/events' }), - ES_HOST: url({ devDefault: 'http://localhost:9200' }) + ES_HOST: url({ devDefault: 'http://localhost:9200' }), + COUNTRY_CONFIG_URL: url({ devDefault: 'http://localhost:3040' }) }) diff --git a/packages/events/src/index.test.ts b/packages/events/src/index.test.ts index 3aff1085424..d1a393e4c51 100644 --- a/packages/events/src/index.test.ts +++ b/packages/events/src/index.test.ts @@ -34,7 +34,8 @@ afterEach(() => Promise.all([resetMongoServer(), resetESServer()])) function createClient() { const createCaller = createCallerFactory(appRouter) const caller = createCaller({ - user: { id: '1', primaryOfficeId: '123' } + user: { id: '1', primaryOfficeId: '123' }, + token: 'NOT_A_REAL_TOKEN' }) return caller } diff --git a/packages/events/src/index.ts b/packages/events/src/index.ts index 70bcfaee8ed..8adf8f17247 100644 --- a/packages/events/src/index.ts +++ b/packages/events/src/index.ts @@ -24,6 +24,7 @@ const server = createHTTPServer({ router: appRouter, createContext: function createContext(opts) { const token = opts.req.headers.authorization + if (!token) { throw new TRPCError({ code: 'UNAUTHORIZED' @@ -42,7 +43,8 @@ const server = createHTTPServer({ user: { id: userId, primaryOfficeId: 'ae5be1bb-6c50-4389-a72d-4c78d19ec176' - } + }, + token } } }) diff --git a/packages/events/src/router.ts b/packages/events/src/router.ts index 84c16e9a2d1..2fedec834d9 100644 --- a/packages/events/src/router.ts +++ b/packages/events/src/router.ts @@ -13,6 +13,12 @@ import { initTRPC } from '@trpc/server' import superjson from 'superjson' import { z } from 'zod' +import { + DeclareActionInput, + EventInput, + NotifyActionInput +} from '@events/schema' +import { getEventsConfig } from './service/config/config' import { addAction, createEvent, @@ -20,17 +26,14 @@ import { getEventById, patchEvent } from './service/events' -import { - DeclareActionInput, - NotifyActionInput, - EventInput -} from '@events/schema' +import { EventConfig } from '@opencrvs/commons' const ContextSchema = z.object({ user: z.object({ id: z.string(), primaryOfficeId: z.string() - }) + }), + token: z.string() }) type Context = z.infer @@ -48,6 +51,11 @@ const publicProcedure = t.procedure export type AppRouter = typeof appRouter export const appRouter = router({ + config: router({ + get: publicProcedure.output(z.array(EventConfig)).query(async (options) => { + return getEventsConfig(options.ctx.token) + }) + }), event: router({ create: publicProcedure.input(EventInput).mutation(async (options) => { return createEvent( diff --git a/packages/events/src/service/config/config.ts b/packages/events/src/service/config/config.ts new file mode 100644 index 00000000000..b21d8c5b140 --- /dev/null +++ b/packages/events/src/service/config/config.ts @@ -0,0 +1,28 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ + +import { env } from '@events/environment' +import fetch from 'node-fetch' + +export async function getEventsConfig(token: string) { + const res = await fetch(new URL('/events', env.COUNTRY_CONFIG_URL), { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + } + }) + + if (!res.ok) { + throw new Error('Failed to fetch events config') + } + + return res.json() +} diff --git a/packages/events/src/service/indexing/indexing.test.ts b/packages/events/src/service/indexing/indexing.test.ts index 1f5ecf67e89..16c22f18e12 100644 --- a/packages/events/src/service/indexing/indexing.test.ts +++ b/packages/events/src/service/indexing/indexing.test.ts @@ -13,12 +13,13 @@ import { appRouter, t } from '@events/router' const { createCallerFactory } = t import { vi } from 'vitest' import { indexAllEvents } from './indexing' -import { getOrCreateClient } from '@events/storage/elasticsearch' + import { setupServer as setupMongoServer, resetServer as resetMongoServer } from '@events/storage/__mocks__/mongodb' import { + getOrCreateClient, resetServer as resetESServer, setupServer as setupESServer } from '@events/storage/__mocks__/elasticsearch' @@ -29,12 +30,14 @@ vi.mock('@events/storage/elasticsearch') function createClient() { const createCaller = createCallerFactory(appRouter) const caller = createCaller({ - user: { id: '1', primaryOfficeId: '123' } + user: { id: '1', primaryOfficeId: '123' }, + token: 'FAKE_TOKEN' }) + return caller } -beforeAll(() => Promise.all([setupMongoServer(), setupESServer()]), 100000) +beforeAll(() => Promise.all([setupMongoServer(), setupESServer()]), 200000) afterEach(() => Promise.all([resetMongoServer(), resetESServer()])) const client = createClient() diff --git a/packages/events/src/service/indexing/indexing.ts b/packages/events/src/service/indexing/indexing.ts index c59e9c99fd9..395baf63786 100644 --- a/packages/events/src/service/indexing/indexing.ts +++ b/packages/events/src/service/indexing/indexing.ts @@ -106,23 +106,18 @@ export async function indexAllEvents() { } }) - return esClient.helpers.bulk( - { - retries: 3, - wait: 3000, - datasource: stream.pipe(transformedStreamData), - onDocument: (doc: EventIndex) => ({ - index: { - _index: 'events', - _id: doc.id - } - }), - refresh: 'wait_for' - }, - { - meta: true - } - ) + return esClient.helpers.bulk({ + retries: 3, + wait: 3000, + datasource: stream.pipe(transformedStreamData), + onDocument: (doc: EventIndex) => ({ + index: { + _index: 'events', + _id: doc.id + } + }), + refresh: 'wait_for' + }) } export async function indexEvent(event: EventDocument) { const esClient = getOrCreateClient() diff --git a/packages/events/src/storage/__mocks__/elasticsearch.ts b/packages/events/src/storage/__mocks__/elasticsearch.ts index 8564dbaa1dd..6acd1baa962 100644 --- a/packages/events/src/storage/__mocks__/elasticsearch.ts +++ b/packages/events/src/storage/__mocks__/elasticsearch.ts @@ -16,6 +16,10 @@ import * as elasticsearch from '@elastic/elasticsearch' let server: StartedElasticsearchContainer export async function setupServer() { + if (server) { + return + } + server = await new ElasticsearchContainer('elasticsearch:8.14.3') .withExposedPorts(9200) .withStartupTimeout(120_000) @@ -35,10 +39,11 @@ export async function resetServer() { }) } -/** @public */ +/** @knipignore */ export function getOrCreateClient() { const host = server.getHost() const port = server.getMappedPort(9200) + return new elasticsearch.Client({ node: `http://${host}:${port}` }) diff --git a/packages/events/src/storage/__mocks__/mongodb.ts b/packages/events/src/storage/__mocks__/mongodb.ts index e8bcc3539b5..00ad9499995 100644 --- a/packages/events/src/storage/__mocks__/mongodb.ts +++ b/packages/events/src/storage/__mocks__/mongodb.ts @@ -12,10 +12,15 @@ import { MongoMemoryServer } from 'mongodb-memory-server' import { MongoClient } from 'mongodb' let server: MongoMemoryServer +let client: MongoClient let databaseName = 'events_' + Date.now() export async function setupServer() { - server = await MongoMemoryServer.create() + if (!server) { + server = await MongoMemoryServer.create() + } + + return server } export async function resetServer() { @@ -24,7 +29,10 @@ export async function resetServer() { export async function getClient() { const uri = server.getUri() - const client = new MongoClient(uri) + if (!client) { + client = new MongoClient(uri) + } + await client.connect() return client.db(databaseName) diff --git a/packages/events/src/storage/elasticsearch.ts b/packages/events/src/storage/elasticsearch.ts index 04248211e74..07e3df9be1d 100644 --- a/packages/events/src/storage/elasticsearch.ts +++ b/packages/events/src/storage/elasticsearch.ts @@ -15,9 +15,10 @@ let client: elasticsearch.Client export const getOrCreateClient = () => { if (!client) { - return new elasticsearch.Client({ + client = new elasticsearch.Client({ node: env.ES_HOST }) + return client } return client diff --git a/packages/events/vitest.config.ts b/packages/events/vitest.config.ts index 6e5b83777ab..8a11b6ecf48 100644 --- a/packages/events/vitest.config.ts +++ b/packages/events/vitest.config.ts @@ -13,6 +13,8 @@ import tsconfigPaths from 'vite-tsconfig-paths' export default defineConfig({ plugins: [tsconfigPaths()], test: { - globals: true + globals: true, + testTimeout: 60000, + hookTimeout: 60000 } }) diff --git a/packages/gateway/src/config/routes.ts b/packages/gateway/src/config/routes.ts index 6b065169171..b592f0b57fa 100644 --- a/packages/gateway/src/config/routes.ts +++ b/packages/gateway/src/config/routes.ts @@ -20,6 +20,7 @@ import sendVerifyCodeHandler, { requestSchema, responseSchema } from '@gateway/routes/verifyCode/handler' +import { trpcProxy } from '@gateway/v2-events/event-config/routes' export const getRoutes = () => { const routes: ServerRoute[] = [ @@ -87,7 +88,8 @@ export const getRoutes = () => { authProxy.token, rateLimitedAuthProxy.authenticate, rateLimitedAuthProxy.authenticateSuperUser, - rateLimitedAuthProxy.verifyUser + rateLimitedAuthProxy.verifyUser, + ...trpcProxy ] return routes diff --git a/packages/gateway/src/features/metrics/root-resolvers.ts b/packages/gateway/src/features/metrics/root-resolvers.ts index fd0e41ecaf4..3b981186eb8 100644 --- a/packages/gateway/src/features/metrics/root-resolvers.ts +++ b/packages/gateway/src/features/metrics/root-resolvers.ts @@ -67,7 +67,9 @@ export const resolvers: GQLResolver = { results } } else { - throw new Error('User does not have the scope required for this resource') + throw new Error( + 'User does not have the scope required for this resource' + ) } }, async getTotalPayments( diff --git a/packages/gateway/src/graphql/config.ts b/packages/gateway/src/graphql/config.ts index 64e96b6f752..254f568f9a5 100644 --- a/packages/gateway/src/graphql/config.ts +++ b/packages/gateway/src/graphql/config.ts @@ -18,10 +18,10 @@ import { } from 'graphql' import { ApolloServerOptions } from '@apollo/server' -import { resolvers as bookmarkAdvancedSearchResolvers } from '@gateway/features/bookmarkAdvancedSearch/root-resolvers' -import { resolvers as correctionRootResolvers } from '@gateway/features/correction/root-resolvers' import { resolvers as eventsV2Resolvers } from '@gateway/v2-events/events/root-resolvers' import { eventResolvers as eventsV2TypeResolvers } from '@gateway/v2-events/events/type-resolvers' +import { resolvers as bookmarkAdvancedSearchResolvers } from '@gateway/features/bookmarkAdvancedSearch/root-resolvers' +import { resolvers as correctionRootResolvers } from '@gateway/features/correction/root-resolvers' import { resolvers as locationRootResolvers } from '@gateway/features/location/root-resolvers' import { resolvers as metricsRootResolvers } from '@gateway/features/metrics/root-resolvers' import { typeResolvers as metricsTypeResolvers } from '@gateway/features/metrics/type-resolvers' diff --git a/packages/gateway/src/v2-events/event-config/routes.ts b/packages/gateway/src/v2-events/event-config/routes.ts new file mode 100644 index 00000000000..621e23f6290 --- /dev/null +++ b/packages/gateway/src/v2-events/event-config/routes.ts @@ -0,0 +1,31 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +import { env } from '@gateway/environment' +import { ServerRoute } from '@hapi/hapi' + +export const trpcProxy = [ + { + method: '*', + path: '/events/{path*}', + handler: (req, h) => { + return h.proxy({ + uri: new URL(req.params.path, env.EVENTS_URL).toString(), + passThrough: true + }) + }, + options: { + payload: { + output: 'data', + parse: false + } + } + } +] satisfies Array diff --git a/packages/gateway/src/v2-events/events/root-resolvers.ts b/packages/gateway/src/v2-events/events/root-resolvers.ts index 13da857ab54..6b5d105b1ce 100644 --- a/packages/gateway/src/v2-events/events/root-resolvers.ts +++ b/packages/gateway/src/v2-events/events/root-resolvers.ts @@ -8,36 +8,20 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ -import { env } from '@gateway/environment' import { GQLResolver } from '@gateway/graphql/schema' -import type { AppRouter } from '@opencrvs/events/src/router' -import { createTRPCClient, httpBatchLink, HTTPHeaders } from '@trpc/client' -import superjson from 'superjson' import uuid from 'uuid' - -const trpc = createTRPCClient({ - links: [ - httpBatchLink({ - url: env.EVENTS_URL, - transformer: superjson, - headers({ opList }) { - const headers = opList[0].context?.headers - return headers as HTTPHeaders - } - }) - ] -}) +import { api } from './service' export const resolvers: GQLResolver = { Query: { async getEvent(_, { eventId }, { headers }) { - return trpc.event.get.query(eventId, { context: { headers } }) + return api.event.get.query(eventId, { context: { headers } }) } }, Mutation: { async createEvent(_, { event }, { headers }) { - const createdEvent = await trpc.event.create.mutate( + const createdEvent = await api.event.create.mutate( { type: event.type, transactionId: uuid.v4() @@ -47,7 +31,7 @@ export const resolvers: GQLResolver = { return createdEvent }, async notifyEvent(_, { eventId, input }, { headers }) { - return trpc.event.actions.notify.mutate( + return api.event.actions.notify.mutate( { eventId: eventId, data: Object.fromEntries(input.data.map((d) => [d.id, d.value])), @@ -58,7 +42,7 @@ export const resolvers: GQLResolver = { ) }, async declareEvent(_, { eventId, input }, { headers }) { - const data = await trpc.event.actions.declare.mutate( + const data = await api.event.actions.declare.mutate( { eventId: eventId, data: Object.fromEntries(input.data.map((d) => [d.id, d.value])), diff --git a/packages/client/src/v2-events/routes/routes.ts b/packages/gateway/src/v2-events/events/router.ts similarity index 82% rename from packages/client/src/v2-events/routes/routes.ts rename to packages/gateway/src/v2-events/events/router.ts index 6d7efe2e514..f0d01a3ac0e 100644 --- a/packages/client/src/v2-events/routes/routes.ts +++ b/packages/gateway/src/v2-events/events/router.ts @@ -8,5 +8,4 @@ * * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ -export const V2_ROOT_ROUTE = '/v2' -export const V2_EVENT_ROUTE = `${V2_ROOT_ROUTE}/event/:eventType` +export type { AppRouter } from '@opencrvs/events/src/router' diff --git a/packages/gateway/src/v2-events/events/service.ts b/packages/gateway/src/v2-events/events/service.ts new file mode 100644 index 00000000000..5767b80edde --- /dev/null +++ b/packages/gateway/src/v2-events/events/service.ts @@ -0,0 +1,29 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * OpenCRVS is also distributed under the terms of the Civil Registration + * & Healthcare Disclaimer located at http://opencrvs.org/license. + * + * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + */ +import { AppRouter } from './router' +import { env } from '@gateway/environment' + +import { createTRPCClient, httpBatchLink, HTTPHeaders } from '@trpc/client' + +import superjson from 'superjson' + +export const api = createTRPCClient({ + links: [ + httpBatchLink({ + url: env.EVENTS_URL, + transformer: superjson, + headers({ opList }) { + const headers = opList[0].context?.headers + return headers as HTTPHeaders + } + }) + ] +}) diff --git a/yarn.lock b/yarn.lock index bd1bb8553ee..bd63e49c6fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8132,6 +8132,18 @@ dependencies: defer-to-connect "^2.0.0" +"@tanstack/query-core@5.61.5": + version "5.61.5" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.61.5.tgz#3ba80527f6474b5edbf348c1b0a6254b3cdfe160" + integrity sha512-iG5vqurEOEbv+paP6kW3zPENa99kSIrd1THISJMaTwVlJ+N5yjVDNOUwp9McK2DWqWCXM3v13ubBbAyhxT78UQ== + +"@tanstack/react-query@^5.61.5": + version "5.61.5" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.61.5.tgz#abe26e5f2d7c9c6c0643579abfd9119ac22a88c8" + integrity sha512-rjy8aqPgBBEz/rjJnpnuhi8TVkVTorMUsJlM3lMvrRb5wK6yzfk34Er0fnJ7w/4qyF01SnXsLB/QsTBsLF5PaQ== + dependencies: + "@tanstack/query-core" "5.61.5" + "@testcontainers/elasticsearch@^10.15.0": version "10.15.0" resolved "https://registry.yarnpkg.com/@testcontainers/elasticsearch/-/elasticsearch-10.15.0.tgz#748ebcac81b6aff103370c9df3acd1c0aa093d49" @@ -8159,11 +8171,26 @@ resolved "https://registry.yarnpkg.com/@trpc/client/-/client-11.0.0-rc.638.tgz#5e35d861d905a9e1a17d919889cc5744a6993ef6" integrity sha512-zKOZZwUMcF4cvm04aawG7dP6M9dCs7FEWHuiBy7xmU+RIonsgpolLMRXm7bFie5EhQOGizhLRGnhneGwjNzWMA== +"@trpc/client@^11.0.0-rc.648": + version "11.0.0-rc.648" + resolved "https://registry.yarnpkg.com/@trpc/client/-/client-11.0.0-rc.648.tgz#275cc0b36e08850754d0c03ccbd911735d734976" + integrity sha512-k4FfLKvJwbosUH8KYyZkC50RJHYtIyJECi5WhRXsvaf9a6lgrTlcA+osq815zYcAHo7wEgR9E9UdSTrpLdAQFQ== + +"@trpc/react-query@^11.0.0-rc.648": + version "11.0.0-rc.648" + resolved "https://registry.yarnpkg.com/@trpc/react-query/-/react-query-11.0.0-rc.648.tgz#5725798e4832814d50dd234161e93d10a98f2074" + integrity sha512-U3H6o/aN3umEA2QNDGRsaJb6M7zrffor2NQl2UaHOiLBHuXZ3ISI2fJXay7e32s1l6z5F5PGMGwTQtUedzWI2w== + "@trpc/server@^11.0.0-rc.532": version "11.0.0-rc.604" resolved "https://registry.yarnpkg.com/@trpc/server/-/server-11.0.0-rc.604.tgz#99929ca7840fe1d01f0adefe35536d66778f02bd" integrity sha512-JXn6XbheQwj2tWqKnAlWhGnIo6F7cSYWQ3SgPXdq+MCYKJBuSYrurj5NvreNCVAkVZmftjyUlIV+Hs2/se4GNg== +"@trpc/server@^11.0.0-rc.648": + version "11.0.0-rc.648" + resolved "https://registry.yarnpkg.com/@trpc/server/-/server-11.0.0-rc.648.tgz#500a4ce795ea216059025e7a1ac44e773ff09768" + integrity sha512-nKW7FNM+QZrY/CVGlX3hFNIdUvbw6pwSJ+HzEF8GIeSJDKLHK7Ke1QJGI2mRW6oF9dCKMBXfuLaYY2dXfjfn7Q== + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz" @@ -23316,16 +23343,7 @@ string-similarity@^4.0.1: resolved "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz" integrity sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -23485,7 +23503,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -23506,13 +23524,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" @@ -25826,7 +25837,7 @@ workbox-window@7.1.0, workbox-window@^7.1.0: "@types/trusted-types" "^2.0.2" workbox-core "7.1.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -25853,15 +25864,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz"