diff --git a/packages/functions/transform/src/set-forge-users.test.ts b/packages/functions/transform/src/set-forge-users.test.ts new file mode 100644 index 000000000..a5ee3b7b1 --- /dev/null +++ b/packages/functions/transform/src/set-forge-users.test.ts @@ -0,0 +1,114 @@ +import { describe, expect, test } from "@jest/globals"; + +import { drizzle } from "drizzle-orm/libsql"; +import { migrate } from "drizzle-orm/libsql/migrator"; +import { createClient } from "@libsql/client"; +import { and, eq, inArray } from "drizzle-orm"; +import fs from "fs"; + +import * as extract from "@acme/extract-schema"; +import * as transform from "@acme/transform-schema"; +import type { Context } from "./config"; +import type { SetForgeUsersExtractEntities, SetForgeUsersTransformEntities } from "./set-forge-users"; +import { setForgeUsers } from "./set-forge-users"; + +let extractSqlite: ReturnType; +let extractDb: ReturnType; +let transformSqlite: ReturnType; +let transformDb: ReturnType; +let context: Context; + +const extractDbName = 'extract-set-forge-users'; +const transformDbName = 'transform-set-forge-users'; + +beforeAll(async () => { + extractSqlite = createClient({ + url: `file:${extractDbName}`, + }); + extractDb = drizzle(extractSqlite); + + transformSqlite = createClient({ + url: `file:${transformDbName}`, + }); + transformDb = drizzle(transformSqlite); + + await migrate(extractDb, { migrationsFolder: "../../../migrations/extract" }); + await migrate(transformDb, { migrationsFolder: "../../../migrations/transform" }); + + context = { + extract: { + db: extractDb, + entities: { + members: extract.members, + mergeRequestNotes: extract.mergeRequestNotes + } + }, + transform: { + db: transformDb, + entities: { + forgeUsers: transform.forgeUsers + } + } + }; +}); + +afterAll(() => { + extractSqlite.close(); + transformSqlite.close(); + fs.unlinkSync(extractDbName); + fs.unlinkSync(transformDbName); +}); + +beforeEach(async () => { + await extractDb.insert(context.extract.entities.members).values([ + { + id: 1, username: 'deki', externalId: 1000, forgeType: 'github', name: 'Deki', email: null, + }, + { + id: 2, username: 'deki', externalId: 1001, forgeType: 'github', name: null, email: null, + }, + { + id: 3, username: 'deki', externalId: 1002, forgeType: 'github', name: 'Deki', email: null, + }, + ]).run(); +}); + +afterEach(async () => { + await extractDb.delete(context.extract.entities.members).run(); + await transformDb.delete(context.transform.entities.forgeUsers).run(); +}); + +describe('set-forge-users', () => { + describe('setMergeRequests', () => { + test('should insert values into db', async () => { + const extractMemberIds = [1, 2, 3]; + await setForgeUsers({ extractMemberIds }, context); + + const transformMergeRequestRows = await transformDb + .select({ + externalId: context.transform.entities.forgeUsers.externalId, + name: context.transform.entities.forgeUsers.name, + }) + .from(context.transform.entities.forgeUsers) + .where( + and( + inArray(context.transform.entities.forgeUsers.externalId, [1000, 1001, 1002]), + eq(context.transform.entities.forgeUsers.forgeType, "github") + ) + ).all(); + + + expect(transformMergeRequestRows.find((row) => row.externalId === 1000)).toEqual({ + externalId: 1000, name: 'Deki', + }); + + expect(transformMergeRequestRows.find((row) => row.externalId === 1001)).toEqual({ + externalId: 1001, name: 'deki', + }); + + expect(transformMergeRequestRows.find((row) => row.externalId === 1002)).toEqual({ + externalId: 1002, name: 'Deki', + }); + }); + }); +}); diff --git a/packages/functions/transform/src/set-forge-users.ts b/packages/functions/transform/src/set-forge-users.ts new file mode 100644 index 000000000..8886c8be9 --- /dev/null +++ b/packages/functions/transform/src/set-forge-users.ts @@ -0,0 +1,52 @@ +import { inArray } from "drizzle-orm"; +import type { ExtractEntities, TransformEntities, TransformFunction } from "./config"; +import type { NewForgeUser } from "@acme/transform-schema"; + +export type SetForgeUsersInput = { + extractMemberIds?: number[]; +} +export type SetForgeUsersOutput = void; +export type SetForgeUsersExtractEntities = Pick; +export type SetForgeUsersTransformEntities = Pick; + +export type SetForgeUsersFunction = TransformFunction; + +export const setForgeUsers: SetForgeUsersFunction = async ( + { extractMemberIds }, + { extract, transform } +) => { + + if (!extractMemberIds) return; + + const extractForgeUsers = (await extract.db.select({ + externalId: extract.entities.members.externalId, + forgeType: extract.entities.members.forgeType, + name: extract.entities.members.name, + username: extract.entities.members.username, + }).from(extract.entities.members) + .where(inArray(extract.entities.members.id, extractMemberIds)) + .all()).map(user => ({ + ...user, + name: user.name || user.username, + })) satisfies NewForgeUser[]; + + if (extractForgeUsers.length === 0) { + console.error(new Error(`No extracted forge users found for ids: ${extractMemberIds}`)); + return; + } + + const queries = extractForgeUsers.map( + forgeUser => transform.db.insert(transform.entities.forgeUsers) + .values(forgeUser) + .onConflictDoUpdate({ + target: [transform.entities.forgeUsers.externalId, transform.entities.forgeUsers.forgeType], + set: { name: forgeUser.name } + }) + ); + + type Query = typeof queries[number]; + + await transform.db.batch( + queries as [Query, ...Query[]] + ); +}