Skip to content

Commit

Permalink
Merge pull request #451 from claushaas/dev
Browse files Browse the repository at this point in the history
many migration fixes
  • Loading branch information
claushaas authored Jan 10, 2025
2 parents 8f987da + db59d06 commit 7566616
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 75 deletions.
8 changes: 8 additions & 0 deletions app/cache/memory-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ class MemoryCache {
return Object.keys(MemoryCache.cache);
}

static values() {
return Object.values(MemoryCache.cache);
}

static entries() {
return Object.entries(MemoryCache.cache);
}

static clear() {
for (const key of Object.keys(MemoryCache.cache)) {
delete MemoryCache.cache[key];
Expand Down
2 changes: 1 addition & 1 deletion app/routes/admin.migrate-lesson-activity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const loader = ({request}: LoaderFunctionArgs) => ({

export const action = async () => {
try {
await new MigrationService().migrateSavedAndFavoritedLessons();
await new MigrationService().getUsers();
return {error: undefined, success: 'Atividade Migrada com sucesso'};
} catch (error) {
logger.logError(`Error in Activity Migration: ${(error as Error).message}`);
Expand Down
167 changes: 100 additions & 67 deletions app/services/migration.service.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {DynamoDBDocument, type QueryCommandOutput} from '@aws-sdk/lib-dynamodb';
import {database} from '~/database/database.server';
import {type TServiceReturn} from '~/types/service-return.type';
import {logger} from '~/utils/logger.util';
import {memoryCache} from '~/cache/memory-cache';

export class MigrationService {
private readonly _model: PrismaClient;
Expand All @@ -24,12 +25,8 @@ export class MigrationService {
this._model = model;
}

public async migrateSavedAndFavoritedLessons(): Promise<TServiceReturn<string>> {
public async migrateSavedAndFavoritedLessonsForUsers(users: UserType[]): Promise<TServiceReturn<string>> {
try {
logger.logInfo('Started: Getting users for Saved And Favorited Lessons migration');
const {data: users} = await this.getUsers();
logger.logInfo('Completed: Getting users for Saved And Favorited Lessons migration');

const client = new DynamoDB({
region: process.env.AWS_REGION ?? 'us-east-1',
credentials: fromEnv(),
Expand All @@ -42,49 +39,87 @@ export class MigrationService {

users.forEach(async user => { // eslint-disable-line unicorn/no-array-for-each
const userId = user.Attributes?.find(attribute => attribute.Name === 'custom:id')?.Value ?? user.Attributes!.find(attribute => attribute.Name === 'sub')!.Value!;
const newUserId = user.Attributes!.find(attribute => attribute.Name === 'sub')!.Value!;
logger.logDebug(`User: ${userId} - New User Id: ${newUserId}`);

const {data: favoritedLessons} = await this.getFavoritedLessonsForUser(userId, ddbDocumentClient);
logger.logDebug(`User: ${userId} - Favorited Lessons: ${favoritedLessons?.length}`);

const {data: completedLessons} = await this.getCompletedLessonsForUser(userId, ddbDocumentClient);
logger.logDebug(`User: ${userId} - Completed Lessons: ${completedLessons?.length}`);

if (favoritedLessons && favoritedLessons.length > 0) {
const favoritedLessonsArray = favoritedLessons.map(dynamoLesson => {
const lesson = memoryCache.entries().find(([key, value]) => {
const lesson = JSON.parse(value) as Record<string, any>;

const oldId = lesson?.lesson?.oldId as string || '';

return oldId === dynamoLesson.aula;
})?.[1];

if (!lesson) {
return null;
}

return {
userId: newUserId,
lessonSlug: JSON.parse(lesson).lesson.slug as string,
};
});

// eslint-disable-next-line unicorn/no-array-for-each
favoritedLessons.forEach(async dynamoLesson => {
const newLesson = await this._model.lesson.findUnique({where: {oldId: dynamoLesson.aula as string}});

if (newLesson) {
await this._model.favoritedLessons.create({
data: {
userId,
lessonSlug: newLesson.slug,
favoritedLessonsArray.filter(element => element !== null).forEach(async element => {
await this._model.favoritedLessons.upsert({
create: element,
update: element,
where: {
userId_lessonSlug: { // eslint-disable-line @typescript-eslint/naming-convention
lessonSlug: element.lessonSlug,
userId: element.userId,
},
});
}
},
});
});
}

if (completedLessons && completedLessons.length > 0) {
const completedLessonsArray = completedLessons.map(dynamoLesson => {
const lesson = memoryCache.entries().find(([key, value]) => {
const lesson = JSON.parse(value) as Record<string, any>;

const oldId = lesson?.lesson?.oldId as string || '';

return oldId === dynamoLesson.aula;
})?.[1];

if (!lesson) {
return null;
}

return {
userId: newUserId,
lessonSlug: JSON.parse(lesson).lesson.slug as string,
};
});

// eslint-disable-next-line unicorn/no-array-for-each
completedLessons.forEach(async dynamoLesson => {
const newLesson = await this._model.lesson.findUnique({where: {oldId: dynamoLesson.aula as string}});

if (newLesson) {
await this._model.favoritedLessons.upsert({
create: {
userId,
lessonSlug: newLesson.slug,
},
where: {
userId_lessonSlug: { // eslint-disable-line @typescript-eslint/naming-convention
userId,
lessonSlug: newLesson.slug,
},
completedLessonsArray.filter(element => element !== null).forEach(async element => {
await this._model.completedLessons.upsert({
create: element,
update: element,
where: {
lessonSlug_userId: { // eslint-disable-line @typescript-eslint/naming-convention
lessonSlug: element.lessonSlug,
userId: element.userId,
},
update: {
createdAt: new Date(dynamoLesson.data as string),
},
});
}
},
});
});

// Await this._model.completedLessons.createMany({
// data: completedLessonsArray.filter(element => element !== null),
// });
}
});

Expand All @@ -102,6 +137,37 @@ export class MigrationService {
}
}

public async getUsers(): Promise<TServiceReturn<string>> {
const listUsersCommand = (paginationToken: string | undefined) => new ListUsersCommand({
UserPoolId: process.env.COGNITO_USER_POOL_ID,
Limit: 60,
PaginationToken: paginationToken ?? undefined,
});

const addUsersToList = async (paginationToken: string | undefined, pages = 0) => {
try {
const result = await this._awsClient.send(listUsersCommand(paginationToken));

if (result.Users) {
await this.migrateSavedAndFavoritedLessonsForUsers(result.Users);
}

if (result.PaginationToken && pages < 20) {
await addUsersToList(result.PaginationToken, pages + 1);
}
} catch (error) {
logger.logError((error as Error).message);
}
};

await addUsersToList(undefined);

return {
status: 'SUCCESSFUL',
data: 'Users fetched successfully',
};
}

private async getFavoritedLessonsForUser(userId: string, ddbDocumentClient: DynamoDBDocument): Promise<TServiceReturn<QueryCommandOutput['Items']>> {
const result = await ddbDocumentClient.query({
TableName: 'yem-prod-aulasFavoritas',
Expand Down Expand Up @@ -133,37 +199,4 @@ export class MigrationService {
data: result.Items?.sort((a, b) => Number(a.data) - Number(b.data)) ?? [],
};
}

private async getUsers(): Promise<TServiceReturn<UserType[]>> {
const listUsersCommand = (paginationToken: string | undefined) => new ListUsersCommand({
UserPoolId: process.env.COGNITO_USER_POOL_ID,
Limit: 60,
PaginationToken: paginationToken ?? undefined,
});

const users: UserType[] = [];

const addUsersToList = async (paginationToken: string | undefined, pages = 0) => {
try {
const result = await this._awsClient.send(listUsersCommand(paginationToken));

if (result.Users) {
users.push(...result.Users);
}

if (result.PaginationToken && pages < 20) {
await addUsersToList(result.PaginationToken, pages + 1);
}
} catch (error) {
logger.logError((error as Error).message);
}
};

await addUsersToList(undefined);

return {
status: 'SUCCESSFUL',
data: users,
};
}
}
49 changes: 49 additions & 0 deletions docker-compose-staging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
services:
app:
build:
context: .
dockerfile: Dockerfile
target: dev
container_name: app
working_dir: /app
volumes:
- ./app:/app/app
ports:
- "3001:3001"
- "24678:24678"
environment:
APP_PORT: ${APP_PORT}
DATABASE_URL: ${DATABASE_URL}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
AWS_REGION: ${AWS_REGION}
COGNITO_CLIENT_ID: ${COGNITO_CLIENT_ID}
COGNITO_USER_POOL_ID: ${COGNITO_USER_POOL_ID}
JWT_SECRET: ${JWT_SECRET}
AWS_SECRET_ID: ${AWS_SECRET_ID}
IUGU_API_URL: ${IUGU_API_URL}
IUGU_API_TOKEN: ${IUGU_API_TOKEN}
NEW_RELIC_NO_CONFIG_FILE: ${NEW_RELIC_NO_CONFIG_FILE}
NEW_RELIC_DISTRIBUTED_TRACING_ENABLED: ${NEW_RELIC_DISTRIBUTED_TRACING_ENABLED}
NEW_RELIC_LOG: ${NEW_RELIC_LOG}
NEW_RELIC_LICENSE_KEY: ${NEW_RELIC_LICENSE_KEY}
NEW_RELIC_APP_NAME: ${NEW_RELIC_APP_NAME}
WINSTON_LOG_LEVEL: ${WINSTON_LOG_LEVEL}
HOTMART_API_URL: ${HOTMART_API_URL}
HOTMART_API_BASIC: ${HOTMART_API_BASIC}
HOTMART_API_CLIENT_ID: ${HOTMART_API_CLIENT_ID}
HOTMART_API_SECRET: ${HOTMART_API_SECRET}
BOTMAKER_CLIENT_ID: ${BOTMAKER_CLIENT_ID}
BOTMAKER_CLIENT_SECRET: ${BOTMAKER_CLIENT_SECRET}
BOTMAKER_API_URL: ${BOTMAKER_API_URL}
BOTMAKER_WHATSAPP_CHANNEL_ID: ${BOTMAKER_WHATSAPP_CHANNEL_ID}
MAUTIC_API_USERNAME: ${MAUTIC_API_USERNAME}
MAUTIC_API_PASSWORD: ${MAUTIC_API_PASSWORD}
MAUTIC_API_URL: ${MAUTIC_API_URL}
YEM_API_BASE_URL: ${YEM_API_BASE_URL}
YEM_REMIX_COOKIE_SESSION_SECRET: ${YEM_REMIX_COOKIE_SESSION_SECRET}
SLACK_WEBHOOK_URL: ${SLACK_WEBHOOK_URL}
SLACK_WEBHOOK_URL_FORMATION: ${SLACK_WEBHOOK_URL_FORMATION}
HOTMART_HOTTOK: ${HOTMART_HOTTOK}
HUBLA_TOKEN: ${HUBLA_TOKEN}
restart: always
7 changes: 1 addition & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,7 @@ services:
condition: service_healthy
environment:
APP_PORT: ${APP_PORT}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_DATABASE: ${POSTGRES_DATABASE}
POSTGRES_HOST: db
POSTGRES_PORT: ${POSTGRES_PORT}
DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/${POSTGRES_DATABASE}
DATABASE_URL: ${DATABASE_URL}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
AWS_REGION: ${AWS_REGION}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"typecheck": "tsc --noEmit",
"update": "npx npm-check-updates -i",
"up": "docker compose up -d --build",
"up-staging": "docker compose --env-file .env.staging -f docker-compose-staging.yml up -d --build",
"down": "docker compose down",
"lint": "xo",
"db:reset": "npx prisma migrate reset"
Expand Down Expand Up @@ -146,4 +147,4 @@
"server.js"
]
}
}
}

0 comments on commit 7566616

Please sign in to comment.