Skip to content

Commit

Permalink
Merge branch 'dev' into IN-857-user-management
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] authored Apr 10, 2024
2 parents 9cfa76f + e13330f commit a70843e
Show file tree
Hide file tree
Showing 24 changed files with 275 additions and 200 deletions.
2 changes: 1 addition & 1 deletion apps/app/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const nextConfig = {
tunnelRoute: '/monitoring',

// Hides source maps from generated client bundles
hideSourceMaps: true,
hideSourceMaps: !isLocalDev,

// Automatically tree-shake Sentry logger statements to reduce bundle size
disableLogger: isVercelProd || isVercelActiveDev,
Expand Down
8 changes: 4 additions & 4 deletions packages/db/client/extensions/auditContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import { Prisma, prisma } from '~db/client'
import { isIdFor } from '~db/lib/idGen'

function auditedExtension(actorId: string) {
return Prisma.defineExtension((prisma) =>
prisma.$extends({
return Prisma.defineExtension((client) =>
client.$extends({
query: {
$allModels: {
async $allOperations({ args, query }) {
if (!isIdFor('user', actorId)) {
throw new Error('Invalid userId')
}
const [, result] = await prisma.$transaction([
prisma.$executeRaw`SELECT set_config('app.actor_id', ${actorId}, TRUE)`,
const [, result] = await client.$transaction([
client.$executeRaw`SELECT set_config('app.actor_id', ${actorId}, TRUE)`,
query(args),
])
return result
Expand Down
41 changes: 41 additions & 0 deletions packages/db/client/extensions/idGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Prisma } from '~db/client'
import { generateId, idPrefix } from '~db/lib/idGen'

const applicableModels = Object.keys(idPrefix) as (keyof typeof idPrefix)[]

const isApplicableModel = (model: string | undefined): model is keyof typeof idPrefix =>
Boolean(model) && applicableModels.includes(model as keyof typeof idPrefix)

export const idGeneratorExtension = Prisma.defineExtension({
name: 'Id Generator',
query: {
$allOperations({ args, model, operation, query }) {
if (model) {
model = model.charAt(0).toLowerCase() + model.slice(1)
}
if (isApplicableModel(model)) {
switch (operation) {
case 'create': {
if (args.data) {
args.data.id ??= generateId(model)
}
break
}
case 'createMany': {
if (Array.isArray(args.data)) {
args.data.forEach((item: Record<string, unknown>) => {
item.id ??= generateId(model)
})
}
break
}
case 'upsert': {
args.create.id ??= generateId(model)
break
}
}
}
return query(args)
},
},
})
133 changes: 66 additions & 67 deletions packages/db/client/extensions/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,79 +5,78 @@ const deserialize = (data: unknown) => (isSuperJSONResult(data) ? superjson.dese

const processData = <T>(data: T) => (data ? superjson.stringify(data) : data)

export const jsonExtension = Prisma.defineExtension((prisma) => {
return prisma.$extends({
result: {
attributeSupplement: {
data: {
needs: { data: true },
compute({ data }) {
return deserialize(data)
},
export const jsonExtension = Prisma.defineExtension({
name: 'SuperJSON Serializer',
result: {
attributeSupplement: {
data: {
needs: { data: true },
compute({ data }) {
return deserialize(data)
},
},
suggestion: {
data: {
needs: { data: true },
compute({ data }) {
return deserialize(data)
},
},
suggestion: {
data: {
needs: { data: true },
compute({ data }) {
return deserialize(data)
},
},
},
query: {
attributeSupplement: {
create({ args, query }) {
args.data.data = processData(args.data.data)
return query(args)
},
createMany({ args, query }) {
args.data = Array.isArray(args.data) ? args.data : [args.data]
for (const item of args.data) {
item.data = processData(item.data)
}
return query(args)
},
update({ args, query }) {
args.data.data = processData(args.data.data)
return query(args)
},
updateMany({ args, query }) {
args.data.data = processData(args.data.data)
return query(args)
},
upsert({ args, query }) {
args.create.data = processData(args.create.data)
args.update.data = processData(args.update.data)
return query(args)
},
},
query: {
attributeSupplement: {
create({ args, query }) {
args.data.data = processData(args.data.data)
return query(args)
},
suggestion: {
create({ args, query }) {
args.data.data = processData(args.data.data)
return query(args)
},
createMany({ args, query }) {
args.data = Array.isArray(args.data) ? args.data : [args.data]
for (const item of args.data) {
item.data = processData(item.data)
}
return query(args)
},
update({ args, query }) {
args.data.data = processData(args.data.data)
return query(args)
},
updateMany({ args, query }) {
args.data.data = processData(args.data.data)
return query(args)
},
upsert({ args, query }) {
args.create.data = processData(args.create.data)
args.update.data = processData(args.update.data)
return query(args)
},
createMany({ args, query }) {
args.data = Array.isArray(args.data) ? args.data : [args.data]
for (const item of args.data) {
item.data = processData(item.data)
}
return query(args)
},
update({ args, query }) {
args.data.data = processData(args.data.data)
return query(args)
},
updateMany({ args, query }) {
args.data.data = processData(args.data.data)
return query(args)
},
upsert({ args, query }) {
args.create.data = processData(args.create.data)
args.update.data = processData(args.update.data)
return query(args)
},
},
suggestion: {
create({ args, query }) {
args.data.data = processData(args.data.data)
return query(args)
},
createMany({ args, query }) {
args.data = Array.isArray(args.data) ? args.data : [args.data]
for (const item of args.data) {
item.data = processData(item.data)
}
return query(args)
},
update({ args, query }) {
args.data.data = processData(args.data.data)
return query(args)
},
updateMany({ args, query }) {
args.data.data = processData(args.data.data)
return query(args)
},
upsert({ args, query }) {
args.create.data = processData(args.create.data)
args.update.data = processData(args.update.data)
return query(args)
},
},
})
},
})
46 changes: 23 additions & 23 deletions packages/db/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@ import { type Prisma, PrismaClient } from '@prisma/client'
import { createPrismaQueryEventHandler } from 'prisma-query-log'

import { createLoggerInstance } from '@weareinreach/util/logger'
import { idMiddleware } from '~db/lib/idMiddleware'
import { superjsonMiddleware } from '~db/lib/superjsonMiddleware'
import { idGeneratorExtension } from '~db/client/extensions/idGenerator'
import { jsonExtension } from '~db/client/extensions/json'

const log = createLoggerInstance('prisma')
const verboseLogging = Boolean(
process.env.NODE_ENV === 'development' && (!!process.env.NEXT_VERBOSE || !!process.env.PRISMA_VERBOSE)
)

declare global {
// allow global `var` declarations
// eslint-disable-next-line no-var
// eslint-disable-next-line no-var -- allow global `var` declarations
var prisma: PrismaClient<typeof clientOptions> | undefined
}

Expand All @@ -31,31 +30,32 @@ const clientOptions = {
errorFormat: 'pretty',
} satisfies Prisma.PrismaClientOptions

const prisma = global.prisma || new PrismaClient(clientOptions)
const generateClient = () => {
const client = new PrismaClient(clientOptions)

prisma.$use(idMiddleware)
prisma.$use(superjsonMiddleware)

const queryLogger = createPrismaQueryEventHandler({
queryDuration: true,
format: true,
indent: '\t',
// linesBetweenQueries: 2,
language: 'pl/sql',
logger: (data) => log.info(`\n${data}`),
})

if (!global.prisma) {
if (verboseLogging) {
prisma.$on('query', queryLogger)
const queryLogger = createPrismaQueryEventHandler({
queryDuration: true,
format: true,
indent: '\t',
language: 'pl/sql',
logger: (data) => log.info(`\n${data}`),
})
client.$on('query', queryLogger)
} else {
prisma.$on('error', (event) => log.error(event))
prisma.$on('warn', (event) => log.warn(event))
client.$on('error', (event) => log.error(event))
client.$on('warn', (event) => log.warn(event))
}

return client.$extends(jsonExtension).$extends(idGeneratorExtension) as unknown as PrismaClient<
typeof clientOptions
>
}
// prisma.$connect()

const prisma = global.prisma ?? generateClient()

if (process.env.NODE_ENV !== 'production') {
global.prisma = prisma
global.prisma ??= prisma
}
export { prisma }
export type * from '@prisma/client'
Expand Down
33 changes: 29 additions & 4 deletions packages/db/lib/generateData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable node/no-process-env */
import { PrismaClient } from '@prisma/client'
import {
Listr,
type ListrDefaultRenderer,
Expand All @@ -7,19 +8,41 @@ import {
type ListrTaskWrapper,
PRESET_TIMER,
} from 'listr2'
import prettier from 'prettier'

import { prisma } from '~db/client'
import { writeFileSync } from 'fs'
import path from 'path'

import * as job from './generators'

/**
* It takes a filename and some data, and writes it to a file in the `generated` directory
*
* @param {string} filename - The base name of the file to write to, **without extension**.
* @param {string} data - The data to be written to the file.
*/
export const writeOutput = async (filename: string, data: string, isJs = false) => {
const prettierOpts = (await prettier.resolveConfig(__dirname)) ?? undefined
const parser = isJs ? 'babel' : 'typescript'
const outFile = `${path.resolve(__dirname, '../generated')}/${filename}.${isJs ? 'mjs' : 'ts'}`

const formattedOutput = await prettier.format(data, { ...prettierOpts, parser })
writeFileSync(outFile, formattedOutput)
}

const prisma = new PrismaClient()

const rendererOptions = {
bottomBar: 10,
timer: PRESET_TIMER,
}
const defineJob = (title: string, job: (task: ListrTask) => void | Promise<void>): ListrJob => ({
const defineJob = (
title: string,
jobItem: (ctx: Context, task: ListrTask) => void | Promise<void>
): ListrJob => ({
title,
task: async (_ctx, task): Promise<void> => job(task),
rendererOptions,
task: jobItem,
skip: !process.env.DATABASE_URL,
})

Expand All @@ -41,7 +64,7 @@ const tasks = new Listr<Context>(
defineJob('Translation Namespaces', job.generateNamespaces),
defineJob('Attribute Supplement Data Schemas', job.generateDataSchemas),
],
{ concurrent: true }
{ concurrent: true, ctx: { prisma, writeOutput } }
),
},
{
Expand All @@ -68,6 +91,8 @@ tasks.run()

export type Context = {
error?: boolean
prisma: typeof prisma
writeOutput: typeof writeOutput
}
export type ListrTask = ListrTaskWrapper<Context, ListrDefaultRenderer, ListrSimpleRenderer>
type ListrJob = ListrTaskObj<Context, ListrDefaultRenderer>
8 changes: 3 additions & 5 deletions packages/db/lib/generators/allAttributes.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { prisma } from '~db/client'
import { type ListrTask } from '~db/lib/generateData'
import { type Context, type ListrTask } from '~db/lib/generateData'

import { writeOutput } from './common'

export const generateAllAttributes = async (task: ListrTask) => {
export const generateAllAttributes = async (ctx: Context, task: ListrTask) => {
const { prisma, writeOutput } = ctx
const data = await prisma.attribute.findMany({
select: {
id: true,
Expand Down
8 changes: 3 additions & 5 deletions packages/db/lib/generators/attributeCategory.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { prisma } from '~db/client'
import { type ListrTask } from '~db/lib/generateData'
import { type Context, type ListrTask } from '~db/lib/generateData'

import { writeOutput } from './common'

export const generateAttributeCategories = async (task: ListrTask) => {
export const generateAttributeCategories = async (ctx: Context, task: ListrTask) => {
const { prisma, writeOutput } = ctx
const data = await prisma.attributeCategory.findMany({
where: { active: true },
select: {
Expand Down
Loading

0 comments on commit a70843e

Please sign in to comment.