diff --git a/packages/cloud-alert/cypress/support/constants.ts b/packages/cloud-alert/cypress/support/constants.ts index 9783cdaff3..9b5bc4e98a 100644 --- a/packages/cloud-alert/cypress/support/constants.ts +++ b/packages/cloud-alert/cypress/support/constants.ts @@ -23,7 +23,7 @@ export const webAppsProd = [ ] export const nodeServicesDev = [ - { appName: 'Event Status Service', url: 'https://qgbtloumi8.execute-api.eu-west-2.amazonaws.com/dev' }, + { appName: 'Events Service', url: 'https://kzekq19177.execute-api.eu-west-2.amazonaws.com/dev' }, { appName: 'Mailer Service', url: 'https://ew9zhgy2i1.execute-api.eu-west-2.amazonaws.com/dev' }, { appName: 'Payment Service', url: 'https://cja5ya4nc1.execute-api.eu-west-2.amazonaws.com/dev' }, ] diff --git a/packages/cognito-custom-mail-lambda/src/mailer/templates/index.ts b/packages/cognito-custom-mail-lambda/src/mailer/templates/index.ts index 1d7d60c759..513dcb9234 100644 --- a/packages/cognito-custom-mail-lambda/src/mailer/templates/index.ts +++ b/packages/cognito-custom-mail-lambda/src/mailer/templates/index.ts @@ -28,7 +28,7 @@ export const confirmRegistrationTemplate = ({ userName, url }: ConfirmPasswordTe
Reapit Connect is our single sign on solution which allows you to seamlessly access products and services provided by Reapit Ltd.
Please see below your username and verification code. When you click the verification link, you will be re-directed to a screen where you will be asked to change your password.
Once your account has been verified, you will be redirected to the login page.
Best Regards,
@@ -44,7 +44,7 @@ export const adminUserInviteTemplate = ({ userName, url }: ConfirmPasswordTemplaHi <%= userName %>,
+Hi ${userName},
Welcome to Reapit Connect.
Reapit Connect is our single sign on solution which allows you to seamlessly access products and services provided by Reapit Ltd.
Please see below your username and temporary password. When you click the login link, you will be re-directed to a screen where you will be asked to change your password.
diff --git a/packages/geo-diary/src/graphql/client.ts b/packages/geo-diary/src/graphql/client.ts index 9e0b30eaf9..cd2ad267bc 100644 --- a/packages/geo-diary/src/graphql/client.ts +++ b/packages/geo-diary/src/graphql/client.ts @@ -7,10 +7,11 @@ import { notification } from '@reapit/elements' import { ReapitConnectSession } from '@reapit/connect-session' export const generateRequest = (session: ReapitConnectSession) => async (operation: Operation) => { - const { loginIdentity, accessToken } = session + const { loginIdentity, accessToken, idToken } = session operation.setContext({ headers: { - authorization: accessToken, + authorization: idToken, + 'reapit-connect-token': `Bearer ${accessToken}`, 'reapit-customer': `${loginIdentity.clientId}-${loginIdentity.userCode}`, }, }) diff --git a/packages/graphql-server/README.md b/packages/graphql-server/README.md index ecfbe131b9..9dfdc6afdf 100644 --- a/packages/graphql-server/README.md +++ b/packages/graphql-server/README.md @@ -2,6 +2,38 @@ ![lines](./src/tests/badges/badge-lines.svg) ![functions](./src/tests/badges/badge-functions.svg) ![branches](./src/tests/badges/badge-branches.svg) ![statements](./src/tests/badges/badge-statements.svg) -A GraphQL implementation of the Foundations API. +A GraphQL implementation of the Foundations API. The GraphQL service is a proxy service that sits on top of the Reapit Foundations Platform API. The service exists as a convenience for developers to build web applications quickly and with minimal boilerplate. -Under active development pre-alpha. +**The service under active development - if you are interested in becoming an Alpha tester, please contact Will McVay wmcvay@reapit.com** + +## Basic Usage + +The GraphQL production service is deployed to `https://graphql.reapit.cloud/graphql` + +Loading this Url in your browser will do a get to the service and load the GraphQL playground https://www.apollographql.com/docs/apollo-server/testing/graphql-playground/ where you can experiment with the service, like Swagger but for GraphQL. + +There are two required headers in the service as per below, you will need to obtain your access and id tokens from your Reapit Connect session as normal. These headers need to be added to the http headers section of the playground for you to interact with the service. + +Naturally, the scopes in the access token provided in the reapit-connect-token header will need to be sufficient to map to the platform endpoints downstream from GraphQL. + +```json +{ + "authorization": "id-token-from-reapit-connect", + "reapit-connect-token": "access-token-from-reapit-connect" +} + +``` + +Internally we use Apollo Client to query the Apollo Server backend (this service). An example of how we set this up and add a query provider to a Reapit Scaffolded app can be found here and here. + +## Errors + +Because the service is a platform proxy, it is very likely that 4xx errors are downstream platform issues rather than errors thrown by the service itself. The exception to this rule are 401 errors which are thrown by our API gateway owing to an invalid or missing idToken. See previous point regarding headers. + +For downstream errors, the service will return a 200 with an errors object in the payload. You should inspect this errors list for details of any failures downstream, even if the GraphQL service itself was able to respond correctly. + +## Known Limitations During Alpha + +- The service is already working in production with live customers, powering the Reapit Geo Diary app however, naturally with Alpha software there will likely be some bugs. If you find an issue you believe to be problem with the GraphQL service, please open an issue here https://github.com/reapit/foundations/issues/new?assignees=&labels=bug%2C+needs-triage%2C+graphql-server&template=bug_report.md&title= and we will look at it as soon as possible. +- The objective of the project is to offer the an identical schema to the Foundations API, with no extension, modification or deviation. Objects returned from the service should map 1:1 to those seen in the developer portal swagger document https://developers.reapit.cloud/swagger. That said, there may be some lag between when a property is updated in the main platform. If you find any inconsistencies between the platform API and the service, again please report as an issue - it is likely that the platform has updated and the service is yet to follow. We will be conducting a review of all services before releasing to Beta production to esure full schema compliance. +- The service is lacking two services that exist in the Platform Schema - Meta Data and Meta Data Schema. If there is a requirement to add these services in the near term, please comment on this issue and we will look at prioritising https://github.com/reapit/foundations/issues/3631 \ No newline at end of file diff --git a/packages/graphql-server/serverless.yml b/packages/graphql-server/serverless.yml index b5c19080e8..df6a677042 100644 --- a/packages/graphql-server/serverless.yml +++ b/packages/graphql-server/serverless.yml @@ -39,7 +39,7 @@ resources: Properties: ResponseParameters: gatewayresponse.header.Access-Control-Allow-Origin: "'*'" - gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent,reapit-customer,api-version,if-match'" + gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent,reapit-customer,reapit-connect-token,api-version,if-match'" ResponseType: DEFAULT_4XX RestApiId: Ref: 'ApiGatewayRestApi' @@ -48,7 +48,7 @@ resources: Properties: ResponseParameters: gatewayresponse.header.Access-Control-Allow-Origin: "'*'" - gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent,reapit-customer,api-version,if-match'" + gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent,reapit-customer,reapit-connect-token,api-version,if-match'" ResponseType: DEFAULT_5XX RestApiId: Ref: 'ApiGatewayRestApi' @@ -88,8 +88,11 @@ functions: - X-Amz-Security-Token - X-Amz-User-Agent - reapit-customer + - reapit-connect-token - api-version - if-match + authorizer: + arn: arn:aws:cognito-idp:${self:provider.region}:${self:custom.env.AWS_ACCOUNT_ID}:userpool/${self:custom.env.COGNITO_USERPOOL_ID} - http: path: graphql method: get diff --git a/packages/graphql-server/src/__tests__/index.test.ts b/packages/graphql-server/src/__tests__/index.test.ts index 894975647e..4b0cdb9be4 100644 --- a/packages/graphql-server/src/__tests__/index.test.ts +++ b/packages/graphql-server/src/__tests__/index.test.ts @@ -32,7 +32,7 @@ describe('index.js', () => { const mockParams = { event: { headers: { - Authorization: 'Mock Authorization', + 'reapit-connect-token': 'Mock Authorization', }, }, context: { @@ -45,7 +45,7 @@ describe('index.js', () => { authorization: 'Mock Authorization', functionName: 'Mock Function Name', headers: { - Authorization: 'Mock Authorization', + 'reapit-connect-token': 'Mock Authorization', }, event: mockParams.event, context: mockParams.context, diff --git a/packages/graphql-server/src/errors/errors.ts b/packages/graphql-server/src/errors/errors.ts index 9fdfd98da3..a79ca842d2 100644 --- a/packages/graphql-server/src/errors/errors.ts +++ b/packages/graphql-server/src/errors/errors.ts @@ -2,11 +2,13 @@ import { AuthenticationError, UserInputError, ForbiddenError, ValidationError, A import logger from '../logger' export const errorMessages = { - notAuthorized: '[E4010] Not Authorized', - badRequest: '[E4000] Bad Request', - notFound: '[E4040] Not Found', - forbidden: '[E4030] Forbidden', - internalServer: '[E5000] Internal Server Error', + notAuthorized: '401 - Not Authorized', + badRequest: '400 - Bad Request', + notFound: '404 - Not Found', + forbidden: '403 - Forbidden', + precondion: '412 - Precondition Failed', + unprocessable: '422 - Unprocessable Entity', + internalServer: '500 - Internal Server Error', } export const generateAuthenticationError = (traceId?: string) => { @@ -21,6 +23,18 @@ export const generateUserInputError = (traceId?: string) => { return error } +export const generateUnprocessableError = (traceId?: string) => { + const error = new UserInputError(`${traceId || ''} - ${errorMessages.unprocessable}`) + logger.info('generateUnprocessableError', { traceId, error: JSON.stringify(error) }) + return error +} + +export const generatePreconditionError = (traceId?: string) => { + const error = new UserInputError(`${traceId || ''} - ${errorMessages.precondion}`) + logger.info('generatePreconditionError', { traceId, error: JSON.stringify(error) }) + return error +} + export const generateValidationError = (traceId?: string) => { const error = new ValidationError(`${traceId || ''} - ${errorMessages.badRequest}`) logger.info('generateValidationError', { traceId, error: JSON.stringify(error) }) @@ -44,8 +58,10 @@ export const generateNotFoundError = (traceId?: string) => { const errors = { generateAuthenticationError, - generateUserInputError, + generateUnprocessableError, generateValidationError, + generateUserInputError, + generatePreconditionError, generateForbiddenError, generateInternalServerError, generateNotFoundError, diff --git a/packages/graphql-server/src/index.ts b/packages/graphql-server/src/index.ts index 68392e1bee..5ed33d227b 100644 --- a/packages/graphql-server/src/index.ts +++ b/packages/graphql-server/src/index.ts @@ -42,7 +42,7 @@ export const handleContext = ({ event, context }) => { const newContext = { traceId: traceId, headers: event.headers, - authorization: event?.headers?.Authorization || '', + authorization: event?.headers['reapit-connect-token'] ?? '', functionName: context.functionName, event, context, diff --git a/packages/graphql-server/src/utils/__tests__/handle-error.test.ts b/packages/graphql-server/src/utils/__tests__/handle-error.test.ts index e9ea18c8c7..8f95b13956 100644 --- a/packages/graphql-server/src/utils/__tests__/handle-error.test.ts +++ b/packages/graphql-server/src/utils/__tests__/handle-error.test.ts @@ -11,6 +11,7 @@ jest.mock('apollo-server-lambda', () => { } }) jest.mock('../../logger/') + describe('handleError', () => { it('should return ValidationError', async () => { const input = { @@ -28,6 +29,22 @@ describe('handleError', () => { expect(output).toEqual(errors.generateValidationError('mockTraceId')) }) + it('should return ValidationError', async () => { + const input = { + error: { + response: { + data: {}, + status: 400, + headers: {}, + }, + }, + caller: 'mockCaller', + traceId: 'mockTraceId', + } as HandleErrorParams + const output = await handleError(input) + expect(output).toEqual(errors.generateValidationError('mockTraceId')) + }) + it('should return AuthenticationError', async () => { const input = { error: { @@ -89,7 +106,7 @@ describe('handleError', () => { traceId: 'mockTraceId', } as HandleErrorParams const output = await handleError(input) - expect(output).toEqual(errors.generateUserInputError('mockTraceId')) + expect(output).toEqual(errors.generateUnprocessableError('mockTraceId')) }) it('should return UserInputError', async () => { @@ -105,7 +122,7 @@ describe('handleError', () => { traceId: 'mockTraceId', } as HandleErrorParams const output = await handleError(input) - expect(output).toEqual(errors.generateUserInputError('mockTraceId')) + expect(output).toEqual(errors.generatePreconditionError('mockTraceId')) }) it('should return ApolloError', async () => { @@ -133,6 +150,7 @@ describe('handleError', () => { const output = await handleError(input) expect(output).toEqual(errors.generateInternalServerError('mockTraceId')) }) + it('should return ApolloError', async () => { const input = { error: {}, @@ -142,6 +160,7 @@ describe('handleError', () => { const output = await handleError(input) expect(output).toEqual(errors.generateInternalServerError('mockTraceId')) }) + it('should return ApolloError', async () => { const input = { error: { diff --git a/packages/graphql-server/src/utils/handle-error.ts b/packages/graphql-server/src/utils/handle-error.ts index 407fdf04f1..451ac5bde9 100644 --- a/packages/graphql-server/src/utils/handle-error.ts +++ b/packages/graphql-server/src/utils/handle-error.ts @@ -32,10 +32,10 @@ export const handleError = async ({ error, traceId, caller }: HandleErrorParams) return errors.generateNotFoundError(traceId) } if (error?.response?.status === 412) { - return errors.generateUserInputError(traceId) + return errors.generatePreconditionError(traceId) } if (error?.response?.status === 422) { - return errors.generateUserInputError(traceId) + return errors.generateUnprocessableError(traceId) } return errors.generateInternalServerError(traceId) }