-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[DEV-1309] Update WebinarQuestionsForm to use dynamodb (#573)
- Loading branch information
Showing
13 changed files
with
2,655 additions
and
555 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"nextjs-website": patch | ||
--- | ||
|
||
[DEV-1309] Update WebinarQuestionsForm logic |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { pipe } from 'fp-ts/lib/function'; | ||
import * as E from 'fp-ts/lib/Either'; | ||
import { Config, makeConfig, publicEnv } from './config'; | ||
import { WebinarEnv } from './lib/webinars/webinarQuestions'; | ||
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; | ||
import { makeAwsCredentialsFromCognito } from './lib/makeAwsCredentialsFromCognito'; | ||
import { Auth } from 'aws-amplify'; | ||
|
||
// This type represents the environment of the application. It contains | ||
// configuration as well as other dependencies required by the application. In | ||
// other words contains all runtime configuration and global functions that may | ||
// be mockable | ||
export type AppEnv = { | ||
readonly config: Config; | ||
} & WebinarEnv; | ||
|
||
// given environment variables produce an AppEnv | ||
const makeAppEnv = ( | ||
env: Record<string, undefined | string> | ||
): E.Either<string, AppEnv> => | ||
pipe( | ||
makeConfig(env), | ||
E.map((config) => ({ | ||
config, | ||
questionLifetimeInSeconds: | ||
config.NEXT_PUBLIC_WEBINAR_QUESTION_LIFETIME_IN_SECONDS, | ||
nowDate: () => new Date(), | ||
dynamoDBClient: new DynamoDBClient({ | ||
region: config.NEXT_PUBLIC_COGNITO_REGION, | ||
credentials: makeAwsCredentialsFromCognito( | ||
config, | ||
// passing Auth.currentSession raise an error because | ||
// Auth.currentSession is not able to retrieve all the information | ||
() => Auth.currentSession() | ||
), | ||
}), | ||
})) | ||
); | ||
|
||
// an AppEnv instance ready to be used | ||
export const appEnv = pipe( | ||
makeAppEnv(publicEnv), | ||
E.getOrElseW((errors) => { | ||
// eslint-disable-next-line functional/no-throw-statements | ||
throw errors; | ||
}) | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 0 additions & 53 deletions
53
apps/nextjs-website/src/helpers/webinarQuestions.helpers.ts
This file was deleted.
Oops, something went wrong.
26 changes: 26 additions & 0 deletions
26
apps/nextjs-website/src/lib/makeAwsCredentialsFromCognito.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { Config } from '@/config'; | ||
import { fromCognitoIdentityPool } from '@aws-sdk/credential-providers'; | ||
import { Auth } from 'aws-amplify'; | ||
|
||
// Create a aws credentials provider given a cognito user | ||
export const makeAwsCredentialsFromCognito = ( | ||
config: Config, | ||
getCurrentSession: typeof Auth.currentSession | ||
) => { | ||
const providerName = `cognito-idp.${config.NEXT_PUBLIC_COGNITO_REGION}.amazonaws.com/${config.NEXT_PUBLIC_COGNITO_USER_POOL_ID}`; | ||
// create custom credentials provider | ||
return async () => { | ||
// get session of the user | ||
const session = await getCurrentSession(); | ||
|
||
const credentials = fromCognitoIdentityPool({ | ||
clientConfig: { region: config.NEXT_PUBLIC_COGNITO_REGION }, | ||
identityPoolId: config.NEXT_PUBLIC_COGNITO_IDENTITY_POOL_ID, | ||
logins: { | ||
[providerName]: session.getIdToken().getJwtToken(), | ||
}, | ||
}); | ||
|
||
return await credentials(); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// This file contains application logic adapted to be easily integrated by pages | ||
// or components within the app and components folders, e.g.: provide a valid | ||
// application environment (AppEnv) and transform TaskEither to Promise | ||
import { pipe } from 'fp-ts/function'; | ||
import * as TE from 'fp-ts/TaskEither'; | ||
import { appEnv } from '@/AppEnv'; | ||
import { | ||
InsertWebinarQuestion, | ||
insertWebinarQuestion, | ||
listWebinarQuestions, | ||
} from './webinars/webinarQuestions'; | ||
|
||
const makePromiseFromTE = <E, A>(input: TE.TaskEither<E, A>) => | ||
pipe( | ||
input, | ||
TE.fold( | ||
// eslint-disable-next-line functional/no-promise-reject | ||
(error) => () => Promise.reject(error), | ||
(result) => () => Promise.resolve(result) | ||
) | ||
); | ||
|
||
export const sendWebinarQuestion = (question: InsertWebinarQuestion) => | ||
pipe(insertWebinarQuestion(question)(appEnv), makePromiseFromTE)(); | ||
|
||
export const getWebinarQuestionList = (webinarId: string) => | ||
pipe(listWebinarQuestions(webinarId)(appEnv), makePromiseFromTE)(); |
82 changes: 82 additions & 0 deletions
82
apps/nextjs-website/src/lib/webinars/__tests__/webinarQuestions.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { mock } from 'jest-mock-extended'; | ||
import * as E from 'fp-ts/lib/Either'; | ||
import { | ||
WebinarEnv, | ||
insertWebinarQuestion, | ||
listWebinarQuestions, | ||
} from '../webinarQuestions'; | ||
import { PutItemCommand, QueryCommand } from '@aws-sdk/client-dynamodb'; | ||
import { makeDynamodbItemFromWebinarQuestion } from '../dynamodb/codec'; | ||
|
||
const aWebinarQuestion = { | ||
webinarId: 'aWebinarId', | ||
givenName: 'aGivenName', | ||
familyName: 'aFamilyName', | ||
question: 'aQuestion', | ||
createdAt: new Date(), | ||
expireAt: new Date('2024-01-22T11:36:31.000Z'), | ||
}; | ||
const aDynamoDBItem = makeDynamodbItemFromWebinarQuestion({ | ||
...aWebinarQuestion, | ||
}); | ||
|
||
const makeTestWebinarEnv = () => { | ||
const nowDate = new Date(0); | ||
const dynamoDBClientMock = mock<WebinarEnv['dynamoDBClient']>(); | ||
const nowDateMock = jest.fn(); | ||
// default mock behaviour | ||
dynamoDBClientMock.send.mockImplementation((cmd) => { | ||
if (cmd instanceof PutItemCommand) return Promise.resolve({}); | ||
else if (cmd instanceof QueryCommand) | ||
return Promise.resolve({ Items: [aDynamoDBItem] }); | ||
// eslint-disable-next-line functional/no-promise-reject | ||
else return Promise.reject(new Error('Unsupported command')); | ||
}); | ||
nowDateMock.mockImplementation(() => nowDate); | ||
const env = { | ||
questionLifetimeInSeconds: 1000, | ||
dynamoDBClient: dynamoDBClientMock, | ||
nowDate: nowDateMock, | ||
}; | ||
return { env, dynamoDBClientMock, nowDateMock, nowDate }; | ||
}; | ||
|
||
describe('webinarQuestions', () => { | ||
describe('insertWebinarQuestion', () => { | ||
it('should send dynamodb put command', async () => { | ||
const { env, dynamoDBClientMock, nowDateMock } = makeTestWebinarEnv(); | ||
const actual = await insertWebinarQuestion(aWebinarQuestion)(env)(); | ||
const expected = E.right(undefined); | ||
|
||
expect(dynamoDBClientMock.send).toBeCalledTimes(1); | ||
expect(nowDateMock).toBeCalledTimes(1); | ||
expect(actual).toStrictEqual(expected); | ||
}); | ||
it('should return error if send returns an error', async () => { | ||
const error = new Error(); | ||
const { env, dynamoDBClientMock, nowDateMock } = makeTestWebinarEnv(); | ||
|
||
// override the mock to simulate a rejection | ||
// eslint-disable-next-line functional/no-promise-reject | ||
dynamoDBClientMock.send.mockImplementation(() => Promise.reject(error)); | ||
|
||
const actual = await insertWebinarQuestion(aWebinarQuestion)(env)(); | ||
const expected = E.left(error); | ||
|
||
expect(dynamoDBClientMock.send).toBeCalledTimes(1); | ||
expect(nowDateMock).toBeCalledTimes(1); | ||
expect(actual).toStrictEqual(expected); | ||
}); | ||
}); | ||
|
||
describe('listWebinarQuestion', () => { | ||
it('should return a list of webinar questions', async () => { | ||
const { env } = makeTestWebinarEnv(); | ||
const { webinarId } = aWebinarQuestion; | ||
const actual = await listWebinarQuestions(webinarId)(env)(); | ||
const expected = E.right([aWebinarQuestion]); | ||
|
||
expect(actual).toStrictEqual(expected); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.