From a58f938c710a93055491ff1bffdbe95bc77787cd Mon Sep 17 00:00:00 2001 From: Daniel Bradley Date: Mon, 14 Sep 2020 11:39:39 +0100 Subject: [PATCH 1/2] Complete bookings migration - Switch all code to use officeIds and new bookings table. - Require officeId in config. --- CHANGELOG.md | 6 + infrastructure/index.ts | 1 - server/app-config.ts | 9 +- server/app.ts | 2 +- server/bookings/createBooking.ts | 6 +- server/bookings/deleteBooking.ts | 4 +- server/bookings/model.ts | 2 +- server/bookings/queryBookings.ts | 4 +- server/db/bookings.ts | 10 +- server/db/bookingsV2.ts | 137 ------------------ server/db/officeBookings.ts | 25 ++-- server/getOffices.ts | 2 +- server/migrations/1-save-users-to-db.ts | 61 -------- .../2-replace-office-booking-ids.ts | 15 -- server/migrations/3-move-bookings.ts | 33 ----- server/migrations/index.ts | 9 +- server/scripts/create-dynamo-tables.ts | 14 -- server/tests/migrations.test.ts | 103 +------------ 18 files changed, 42 insertions(+), 401 deletions(-) delete mode 100644 server/db/bookingsV2.ts delete mode 100644 server/migrations/1-save-users-to-db.ts delete mode 100644 server/migrations/2-replace-office-booking-ids.ts delete mode 100644 server/migrations/3-move-bookings.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 92fc688..f5e36b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.0.0] - 2020-09-14 + +- Re-enable API write access. +- Use newly migrated DB (using officeId). +- Require officeId to be specified in config (to avoid renaming issues). + ## [1.2.0] - 2020-09-14 - Migrate DB to store all bookings with office ID rather than office name. diff --git a/infrastructure/index.ts b/infrastructure/index.ts index ab35918..3d320b2 100644 --- a/infrastructure/index.ts +++ b/infrastructure/index.ts @@ -414,7 +414,6 @@ const getHttpEnv = (): aws.types.input.lambda.FunctionEnvironment['variables'] = DEFAULT_WEEKLY_QUOTA: defaultWeeklyQuota.toString(), DATA_RETENTION_DAYS: logRetention.toString(), SHOW_TEST_BANNER: showTestBanner.toString(), - READONLY: 'true', }; if (caseSensitiveEmail) { env.CASE_SENSITIVE_EMAIL = 'true'; diff --git a/server/app-config.ts b/server/app-config.ts index 909c1b2..2a023ae 100644 --- a/server/app-config.ts +++ b/server/app-config.ts @@ -2,7 +2,7 @@ import DynamoDB from 'aws-sdk/clients/dynamodb'; import { Request } from 'express'; import { isValidEmail } from './users/model'; import { assert } from 'console'; -import { getOfficeId, isValidOfficeId } from './getOffices'; +import { isValidOfficeId } from './getOffices'; import { Arrays } from 'collection-fns'; import { UserType } from 'aws-sdk/clients/cognitoidentityserviceprovider'; @@ -19,7 +19,7 @@ type CognitoAuthConfig = { region: string; }; -type OfficeQuotaConfig = { id?: string; name: string; quota: number; parkingQuota?: number }; +type OfficeQuotaConfig = { id: string; name: string; quota: number; parkingQuota?: number }; export type OfficeQuota = Required; const isOfficeQuotaConfigs = (input: any): input is OfficeQuotaConfig[] => @@ -30,7 +30,7 @@ const isOfficeQuotaConfigs = (input: any): input is OfficeQuotaConfig[] => o !== null && typeof o.name === 'string' && typeof o.quota === 'number' && - (typeof o.id === 'undefined' || typeof o.id === 'string') && + typeof o.id === 'string' && (typeof o.parkingQuota === 'undefined' || typeof o.parkingQuota === 'number') ); @@ -54,14 +54,13 @@ export type Config = { readonly?: boolean; }; -const parseOfficeQuotas = (OFFICE_QUOTAS: string) => { +const parseOfficeQuotas = (OFFICE_QUOTAS: string): OfficeQuota[] => { const officeQuotaConfigs = JSON.parse(OFFICE_QUOTAS); if (!isOfficeQuotaConfigs(officeQuotaConfigs)) { throw new Error('Invalid office quotas in OFFICE_QUOTAS'); } const officeQuotas = officeQuotaConfigs.map((o) => ({ ...o, - id: o.id ?? getOfficeId(o.name), parkingQuota: o.parkingQuota ?? 0, })); const invalidIds = officeQuotas.map((o) => o.id).filter((id) => !isValidOfficeId(id)); diff --git a/server/app.ts b/server/app.ts index d3635f5..77c96c5 100644 --- a/server/app.ts +++ b/server/app.ts @@ -46,7 +46,7 @@ export const configureApp = (config: Config) => { app.get('/api/config', (req, res, next) => { try { const clientConfig = { - version: '1.2.0', + version: '2.0.0', showTestBanner: config.showTestBanner, auth: config.authConfig.type === 'cognito' diff --git a/server/bookings/createBooking.ts b/server/bookings/createBooking.ts index 727e216..f652ca7 100644 --- a/server/bookings/createBooking.ts +++ b/server/bookings/createBooking.ts @@ -69,7 +69,7 @@ export const createBooking = async ( const newBooking = { id, parking: request.parking ?? false, - office: requestedOffice.name, + officeId: requestedOffice.id, date: request.date, user: request.user, ...('created' in request ? { id: request.id, created: request.created } : {}), @@ -130,7 +130,7 @@ export const createBooking = async ( audit('2.1:DecrementingOfficeBookingCount'); await decrementOfficeBookingCount( config, - requestedOffice.name, + requestedOffice.id, newBooking.date, newBooking.parking ); @@ -151,7 +151,7 @@ export const createBooking = async ( audit('3.1:DecremetingOfficeBookingCount'); await decrementOfficeBookingCount( config, - requestedOffice.name, + requestedOffice.id, newBooking.date, newBooking.parking ); diff --git a/server/bookings/deleteBooking.ts b/server/bookings/deleteBooking.ts index 629de06..af1b7ab 100644 --- a/server/bookings/deleteBooking.ts +++ b/server/bookings/deleteBooking.ts @@ -21,7 +21,7 @@ const audit = (step: string, details?: any) => const canManageBooking = (authUser: User, booking: BookingsModel) => authUser.permissions.canManageAllBookings || authUser.permissions.officesCanManageBookingsFor.find( - (office) => office.name === booking.office + (office) => office.id === booking.officeId ) !== undefined; export const deleteBooking = async ( @@ -63,7 +63,7 @@ export const deleteBooking = async ( try { audit('2:DecrementingOfficeBookingCount'); - await decrementOfficeBookingCount(config, booking.office, booking.date, booking.parking); + await decrementOfficeBookingCount(config, booking.officeId, booking.date, booking.parking); audit('3:DecrementingUserBookingCount'); await decrementUserBookingCount(config, booking.user, startOfWeek); } catch (err) { diff --git a/server/bookings/model.ts b/server/bookings/model.ts index e34224f..2560802 100644 --- a/server/bookings/model.ts +++ b/server/bookings/model.ts @@ -40,7 +40,7 @@ export const mapBooking = (config: Config, booking: DbBooking): Booking => ({ created: booking.created, user: booking.user, date: booking.date, - office: Arrays.get(config.officeQuotas, (office) => office.name === booking.office), + office: Arrays.get(config.officeQuotas, (office) => office.id === booking.officeId), lastCancellation: getBookingLastCancelTime(booking.date), parking: booking.parking, }); diff --git a/server/bookings/queryBookings.ts b/server/bookings/queryBookings.ts index ed24bd0..18ad289 100644 --- a/server/bookings/queryBookings.ts +++ b/server/bookings/queryBookings.ts @@ -26,7 +26,7 @@ export const queryBookings = async (config: Config, currentUser: User, query: Bo } const filterBookings = (booking: BookingsModel) => - (!query.office || booking.office === query.office.name) && + (!query.office || booking.officeId === query.office.id) && (!query.date || booking.date === query.date) && (!query.email || booking.user === query.email); @@ -35,7 +35,7 @@ export const queryBookings = async (config: Config, currentUser: User, query: Bo return mapBookings(config, userBookings.filter(filterBookings)); } else if (query.office !== undefined) { const officeBookings = await queryBookingsDb(config, { - office: query.office.name, + officeId: query.office.id, date: query.date, }); return mapBookings(config, officeBookings.filter(filterBookings)); diff --git a/server/db/bookings.ts b/server/db/bookings.ts index 1b73cfe..37e9793 100644 --- a/server/db/bookings.ts +++ b/server/db/bookings.ts @@ -9,11 +9,11 @@ export interface CreateBookingModel { id: string; user: string; date: string; - office: string; + officeId: string; parking: boolean; } -@table('bookings') +@table('bookings-v2') export class BookingsModel { @rangeKey() id!: string; @@ -22,7 +22,7 @@ export class BookingsModel { @attribute({ indexKeyConfigurations: { 'office-date-bookings': 'RANGE' } }) date!: string; @attribute({ indexKeyConfigurations: { 'office-date-bookings': 'HASH' } }) - office!: string; + officeId!: string; @attribute() parking!: boolean; @attribute() @@ -53,11 +53,11 @@ export const getUserBookings = async ( export const queryBookings = async ( config: Config, - query: { office: string; date?: string } + query: { officeId: string; date?: string } ): Promise => { const mapper = buildMapper(config); const rows: BookingsModel[] = []; - const mapperQuery: Partial = { office: query.office }; + const mapperQuery: Partial = { officeId: query.officeId }; if (query.date !== undefined) { mapperQuery.date = query.date; } diff --git a/server/db/bookingsV2.ts b/server/db/bookingsV2.ts deleted file mode 100644 index e4960c4..0000000 --- a/server/db/bookingsV2.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { Config } from '../app-config'; -import DynamoDB from 'aws-sdk/clients/dynamodb'; -import { attribute, table, hashKey, rangeKey } from '@aws/dynamodb-data-mapper-annotations'; -import { DataMapper } from '@aws/dynamodb-data-mapper'; -import { addDays } from 'date-fns'; -import { AttributePath, FunctionExpression } from '@aws/dynamodb-expressions'; - -export interface CreateBookingModel { - id: string; - user: string; - date: string; - officeId: string; - parking: boolean; -} - -@table('bookings-v2') -export class BookingsModel { - @rangeKey() - id!: string; - @hashKey() - user!: string; - @attribute({ indexKeyConfigurations: { 'office-date-bookings': 'RANGE' } }) - date!: string; - @attribute({ indexKeyConfigurations: { 'office-date-bookings': 'HASH' } }) - officeId!: string; - @attribute() - parking!: boolean; - @attribute() - ttl!: number; - @attribute({ - defaultProvider: () => new Date().toISOString(), - }) - created!: string; -} - -const buildMapper = (config: Config) => - new DataMapper({ - client: new DynamoDB(config.dynamoDB), - tableNamePrefix: config.dynamoDBTablePrefix, - }); - -export const getUserBookings = async ( - config: Config, - userEmail: string -): Promise => { - const mapper = buildMapper(config); - const rows: BookingsModel[] = []; - for await (const item of mapper.query(BookingsModel, { user: userEmail }, { limit: 50 })) { - rows.push(item); - } - return rows; -}; - -export const queryBookings = async ( - config: Config, - query: { officeId: string; date?: string } -): Promise => { - const mapper = buildMapper(config); - const rows: BookingsModel[] = []; - const mapperQuery: Partial = { officeId: query.officeId }; - if (query.date !== undefined) { - mapperQuery.date = query.date; - } - for await (const item of mapper.query(BookingsModel, mapperQuery, { - indexName: 'office-date-bookings', - })) { - rows.push(item); - } - return rows; -}; - -export const getAllBookings = async (config: Config): Promise => { - const mapper = buildMapper(config); - const rows: BookingsModel[] = []; - for await (const item of mapper.scan(BookingsModel)) { - rows.push(item); - } - return rows; -}; - -export const createBooking = async ( - config: Config, - booking: CreateBookingModel -): Promise => { - const mapper = buildMapper(config); - try { - const created = await mapper.put( - Object.assign(new BookingsModel(), booking, { - ttl: addDays(new Date(booking.date), config.dataRetentionDays).getTime(), - }), - { - condition: new FunctionExpression('attribute_not_exists', new AttributePath('id')), - } - ); - return created; - } catch (err) { - if (err.code === 'ConditionalCheckFailedException') { - return undefined; - } - throw err; - } -}; - -export const migrateBookings = async (config: Config, bookings: BookingsModel[]): Promise => { - const mapper = buildMapper(config); - for await (const _result of mapper.batchPut( - bookings.map((booking) => Object.assign(new BookingsModel(), booking)) - )) { - } -}; - -export const deleteBooking = async ( - config: Config, - id: string, - userEmail: string -): Promise => { - const mapper = buildMapper(config); - const deleted = await mapper.delete(Object.assign(new BookingsModel(), { id, user: userEmail })); - return deleted; -}; - -export const getBooking = async ( - config: Config, - id: string, - userEmail: string -): Promise => { - const mapper = buildMapper(config); - try { - return await mapper.get(Object.assign(new BookingsModel(), { id, user: userEmail })); - } catch (err) { - if (err.name === 'ItemNotFoundException') { - return undefined; - } else { - throw err; - } - } -}; diff --git a/server/db/officeBookings.ts b/server/db/officeBookings.ts index ac36f31..ec148d1 100644 --- a/server/db/officeBookings.ts +++ b/server/db/officeBookings.ts @@ -15,7 +15,7 @@ import { import { addDays } from 'date-fns'; export interface OfficeBooking { - name: string; + officeId: string; date: string; bookingCount: number; parkingCount: number; @@ -23,6 +23,7 @@ export interface OfficeBooking { @table('office-bookings') export class OfficeBookingModel { + /** This is still called name, but now contains the officeId since version 3 */ @hashKey() name!: string; @rangeKey() @@ -52,7 +53,7 @@ export const incrementOfficeBookingCount = async ( try { await mapper.put( Object.assign(new OfficeBookingModel(), { - name: office.name, + name: office.id, date, bookingCount: 0, parkingCount: 0, @@ -97,7 +98,7 @@ export const incrementOfficeBookingCount = async ( const checkParkingQuota = includeParking ? `AND parkingCount < ${parkingQuotaValue}` : ''; await client .updateItem({ - Key: { name: { S: office.name }, date: { S: date } }, + Key: { name: { S: office.id }, date: { S: date } }, TableName: (config.dynamoDBTablePrefix || '') + 'office-bookings', UpdateExpression: updateExpression.serialize(attributes), ExpressionAttributeNames: attributes.names, @@ -118,7 +119,7 @@ export const incrementOfficeBookingCount = async ( export const decrementOfficeBookingCount = async ( config: Config, - officeName: string, + officeId: string, date: string, includeParking: boolean ) => { @@ -138,7 +139,7 @@ export const decrementOfficeBookingCount = async ( const client = new DynamoDB(config.dynamoDB); await client .updateItem({ - Key: { name: { S: officeName }, date: { S: date } }, + Key: { name: { S: officeId }, date: { S: date } }, TableName: (config.dynamoDBTablePrefix || '') + 'office-bookings', UpdateExpression: updateExpression.serialize(attributes), ExpressionAttributeNames: attributes.names, @@ -149,14 +150,14 @@ export const decrementOfficeBookingCount = async ( export const getOfficeBookings = async ( config: Config, - officeName: string, + officeId: string, dates: string[] ): Promise => { const mapper = buildMapper(config); const resultKey = (model: OfficeBookingModel) => model.date; const resultsMap = new Map( dates.map((date) => { - const model = Object.assign(new OfficeBookingModel(), { date, name: officeName }); + const model = Object.assign(new OfficeBookingModel(), { date, name: officeId }); return [resultKey(model), model]; }) ); @@ -167,7 +168,7 @@ export const getOfficeBookings = async ( { subject: 'name', type: 'Equals', - object: officeName, + object: officeId, }, { subject: 'date', @@ -194,11 +195,3 @@ export const getOfficeBookings = async ( } return Array.from(resultsMap.values()); }; - -export const setOfficeBookings = async (config: Config, officeBookings: OfficeBookingModel[]) => { - const mapper = buildMapper(config); - for await (const _result of mapper.batchPut( - officeBookings.map((booking) => Object.assign(new OfficeBookingModel(), booking)) - )) { - } -}; diff --git a/server/getOffices.ts b/server/getOffices.ts index a0c51e0..9124c4a 100644 --- a/server/getOffices.ts +++ b/server/getOffices.ts @@ -17,7 +17,7 @@ export const getOffice = async (config: Config, officeId: string) => { if (office === undefined) { throw new NotFound(); } - const officeBookings = await getOfficeBookings(config, office.name, availableDates); + const officeBookings = await getOfficeBookings(config, office.id, availableDates); const indexedBookings = new Map( officeBookings.map((officeBooking) => [officeBooking.date, officeBooking]) ); diff --git a/server/migrations/1-save-users-to-db.ts b/server/migrations/1-save-users-to-db.ts deleted file mode 100644 index a5dd3c7..0000000 --- a/server/migrations/1-save-users-to-db.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Config } from '../app-config'; -import { CognitoIdentityServiceProvider, AWSError } from 'aws-sdk'; -import { PromiseResult } from 'aws-sdk/lib/request'; -import { getAllUsers, setUser } from '../db/users'; - -export type CognitoUser = { - email: string; - created: string; -}; - -const getAllCognitoUsers = async (config: Config): Promise => { - if (config.authConfig.type !== 'cognito') { - return config.authConfig.users.map((u) => ({ - email: u.Username!, - created: u.UserCreateDate?.toISOString()!, - })); - } - const { cognitoUserPoolId } = config.authConfig; - const cognito = new CognitoIdentityServiceProvider(); - const emails: CognitoUser[] = []; - let paginationToken: string | undefined = undefined; - do { - const response: PromiseResult< - CognitoIdentityServiceProvider.ListUsersResponse, - AWSError - > = await cognito - .listUsers({ - UserPoolId: cognitoUserPoolId, - AttributesToGet: ['email'], - PaginationToken: paginationToken, - }) - .promise(); - paginationToken = response.PaginationToken; - for (const user of response.Users ?? []) { - const email = user.Attributes?.find((att) => att.Name === 'email')?.Value; - if ( - email !== undefined && - user.Enabled !== undefined && - user.UserCreateDate && - user.UserLastModifiedDate - ) { - emails.push({ - email, - created: user.UserCreateDate.toISOString(), - }); - } - } - } while (paginationToken !== undefined); - return emails; -}; - -export const saveCognitoUsersToDb = async (config: Config) => { - const allDbUsers = await getAllUsers(config); - const dbUserEmails = new Set(allDbUsers.map((u) => u.email)); - const allCognitoUsers = await getAllCognitoUsers(config); - for (const cognitoUser of allCognitoUsers) { - if (!dbUserEmails.has(cognitoUser.email)) { - await setUser(config, { email: cognitoUser.email, created: cognitoUser.created }); - } - } -}; diff --git a/server/migrations/2-replace-office-booking-ids.ts b/server/migrations/2-replace-office-booking-ids.ts deleted file mode 100644 index c3eca5c..0000000 --- a/server/migrations/2-replace-office-booking-ids.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Config } from '../app-config'; -import { getOfficeBookings, setOfficeBookings } from '../db/officeBookings'; -import { getAvailableDates } from '../availableDates'; - -export const duplicateOfficeBooking = async (config: Config) => { - const availableDates = getAvailableDates(config); - const officeQuotas = config.officeQuotas; - for (const office of officeQuotas) { - console.log('Migrating ', office.name); - const officeBookings = await getOfficeBookings(config, office.name, availableDates); - const bookingsWithIds = officeBookings.map((booking) => ({ ...booking, name: office.id })); - await setOfficeBookings(config, bookingsWithIds); - console.log('Completed migrating ', office.name); - } -}; diff --git a/server/migrations/3-move-bookings.ts b/server/migrations/3-move-bookings.ts deleted file mode 100644 index 457ad42..0000000 --- a/server/migrations/3-move-bookings.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Config } from '../app-config'; -import { getAllBookings } from '../db/bookings'; -import { - BookingsModel, - migrateBookings, - getAllBookings as getAllMigratedBookings, -} from '../db/bookingsV2'; - -export const moveBookingsToV2 = async (config: Config) => { - const officeNameToId = new Map(config.officeQuotas.map((o) => [o.name, o.id])); - const getIdFromNameOrFail = (name: string) => { - const id = officeNameToId.get(name); - if (id === undefined) { - throw new Error('Office ID not found for name ' + name); - } - return id; - }; - const allBookings = await getAllBookings(config); - const allMigratedBookings = await getAllMigratedBookings(config); - const migratedBookingsById = new Set(allMigratedBookings.map((b) => b.id)); - const newBookings: BookingsModel[] = allBookings - .filter((booking) => !migratedBookingsById.has(booking.id)) - .map((booking) => ({ - id: booking.id, - created: booking.created, - date: booking.date, - parking: booking.parking, - user: booking.user, - ttl: booking.ttl, - officeId: getIdFromNameOrFail(booking.office), - })); - await migrateBookings(config, newBookings); -}; diff --git a/server/migrations/index.ts b/server/migrations/index.ts index 0b9daf5..6879a7e 100644 --- a/server/migrations/index.ts +++ b/server/migrations/index.ts @@ -1,9 +1,6 @@ import { options } from 'yargs'; import { SSM } from 'aws-sdk'; import { parseConfigFromEnv, Config } from '../app-config'; -import { saveCognitoUsersToDb } from './1-save-users-to-db'; -import { duplicateOfficeBooking } from './2-replace-office-booking-ids'; -import { moveBookingsToV2 } from './3-move-bookings'; /** Collection all migrations that should be applied to the system */ type Migrations = { @@ -25,13 +22,13 @@ type Migrations = { /** Enter migrations here */ const migrations: Migrations = { '1-save-users-to-db': { - execute: saveCognitoUsersToDb, + reasonToFailPreCheck: 'You must deploy version 1.1.0 first.', }, '2-replace-office-booking-ids': { - execute: duplicateOfficeBooking, + reasonToFailPreCheck: 'You must deploy version 1.2.0 first.', }, '3-move-bookings': { - execute: moveBookingsToV2, + reasonToFailPreCheck: 'You must deploy version 1.2.0 first.', }, }; diff --git a/server/scripts/create-dynamo-tables.ts b/server/scripts/create-dynamo-tables.ts index 9db1622..a180ce5 100644 --- a/server/scripts/create-dynamo-tables.ts +++ b/server/scripts/create-dynamo-tables.ts @@ -3,7 +3,6 @@ import { DataMapper } from '@aws/dynamodb-data-mapper'; import { OfficeBookingModel } from '../db/officeBookings'; import { UserBookingsModel } from '../db/userBookings'; import { BookingsModel } from '../db/bookings'; -import { BookingsModel as BookingsModelV2 } from '../db/bookingsV2'; import { UserModel } from '../db/users'; export const createLocalTables = async ( @@ -16,7 +15,6 @@ export const createLocalTables = async ( await mapper.ensureTableNotExists(OfficeBookingModel); await mapper.ensureTableNotExists(UserBookingsModel); await mapper.ensureTableNotExists(BookingsModel); - await mapper.ensureTableNotExists(BookingsModelV2); await mapper.ensureTableNotExists(UserModel); } await mapper.ensureTableExists(OfficeBookingModel, { @@ -39,18 +37,6 @@ export const createLocalTables = async ( }, }, }); - await mapper.ensureTableExists(BookingsModelV2, { - readCapacityUnits: 1, - writeCapacityUnits: 1, - indexOptions: { - 'office-date-bookings': { - type: 'global', - projection: 'all', - readCapacityUnits: 1, - writeCapacityUnits: 1, - }, - }, - }); await mapper.ensureTableExists(UserModel, { readCapacityUnits: 1, writeCapacityUnits: 1, diff --git a/server/tests/migrations.test.ts b/server/tests/migrations.test.ts index da8f63f..9962833 100644 --- a/server/tests/migrations.test.ts +++ b/server/tests/migrations.test.ts @@ -1,100 +1,7 @@ -import { configureServer, getNormalUser, expectOk } from './test-utils'; -import { format, startOfWeek, addWeeks } from 'date-fns'; -import { duplicateOfficeBooking } from '../migrations/2-replace-office-booking-ids'; -import { getOfficeBookings } from '../db/officeBookings'; -import { saveCognitoUsersToDb } from '../migrations/1-save-users-to-db'; -import { getAllUsers } from '../db/users'; -import { getAllBookings } from '../db/bookingsV2'; -import { moveBookingsToV2 } from '../migrations/3-move-bookings'; +// const { app, resetDb, config } = configureServer('migrations', { +// users: [{ Username: cognitoUsername, UserCreateDate: cognitoUserCreated }], +// }); -const cognitoUsername = getNormalUser(); -const cognitoUserCreated = new Date(); +// beforeEach(resetDb); -const { app, resetDb, config } = configureServer('migrations', { - users: [{ Username: cognitoUsername, UserCreateDate: cognitoUserCreated }], -}); - -const nextMonday = startOfWeek(addWeeks(new Date(), 1), { weekStartsOn: 1 }); -beforeEach(resetDb); - -describe('1 - user migration', async () => { - it(`Creates users in DB`, async () => { - const allUsersBefore = await getAllUsers(config); - expect(allUsersBefore).toEqual([]); - - await saveCognitoUsersToDb(config); - - const allUsersAfter = await getAllUsers(config); - expect(allUsersAfter).toEqual([ - { email: cognitoUsername, created: cognitoUserCreated.toISOString() }, - ]); - }); -}); - -describe('2 - Office Bookings ID Migration', async () => { - it('Duplicates current bookings with ID ', async () => { - const normalUserEmail = getNormalUser(); - const office = config.officeQuotas[0]; - const date = format(nextMonday, 'yyyy-MM-dd'); - const createBookingBody = { - user: normalUserEmail, - office: { id: office.id }, - date, - parking: false, - }; - const createResponse = await app - .post('/api/bookings') - .send(createBookingBody) - .set('bearer', normalUserEmail); - - expect(createResponse.ok).toBe(true); - await duplicateOfficeBooking(config); - - const getCreatedBookingResponse = await app - .get(`/api/bookings?user=${normalUserEmail}`) - .set('bearer', normalUserEmail); - expect(getCreatedBookingResponse.body).toContainEqual(createResponse.body); - - const officeBookingById = await getOfficeBookings(config, office.id, [date]); - expect(officeBookingById[0]).toMatchObject({ - name: office.id, - date, - bookingCount: 1, - parkingCount: 0, - }); - }); -}); - -describe('3 - migrate bookings to use office id', () => { - it('migrates all booking rows', async () => { - const normalUserEmail = getNormalUser(); - const office = config.officeQuotas[0]; - const date = format(nextMonday, 'yyyy-MM-dd'); - const createBookingBody = { - user: normalUserEmail, - office: { id: office.id }, - date, - parking: false, - }; - const createResponse = await app - .post('/api/bookings') - .send(createBookingBody) - .set('bearer', normalUserEmail); - expectOk(createResponse); - - const preMigrationBookings = await getAllBookings(config); - expect(preMigrationBookings).toEqual([]); - - await moveBookingsToV2(config); - - const postMigrationBookings = await getAllBookings(config); - expect(postMigrationBookings.length).toBe(1); - expect(postMigrationBookings[0]).toMatchObject({ - id: createResponse.body.id, - user: normalUserEmail, - officeId: office.id, - date, - parking: false, - }); - }); -}); +test('no-active-migration', () => {}); From 1b501b94b83c3e1028a7bc512f6778728801d405 Mon Sep 17 00:00:00 2001 From: Daniel Bradley Date: Mon, 14 Sep 2020 15:29:35 +0100 Subject: [PATCH 2/2] Disable public Cognito registrations --- CHANGELOG.md | 1 + infrastructure/index.ts | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5e36b3..9dd72c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Re-enable API write access. - Use newly migrated DB (using officeId). - Require officeId to be specified in config (to avoid renaming issues). +- Disable public Cognito registrations. ## [1.2.0] - 2020-09-14 diff --git a/infrastructure/index.ts b/infrastructure/index.ts index 3d320b2..43860dc 100644 --- a/infrastructure/index.ts +++ b/infrastructure/index.ts @@ -159,10 +159,9 @@ const userPool = new aws.cognito.UserPool(`${serviceName}-users`, { preSignUp: preSignUp.arn, verifyAuthChallengeResponse: verifyAuthChallengeResponse.arn, }, - // TODO: Block public sign-ups to the user pool. - // adminCreateUserConfig: { - // allowAdminCreateUserOnly: true, - // }, + adminCreateUserConfig: { + allowAdminCreateUserOnly: true, + }, tags, });