Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: misc data portal #1233

Merged
merged 16 commits into from
Apr 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion InReach.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@
},
"prettier.resolveGlobalModules": false,
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.tsdk": "✨ InReach (root)/node_modules/typescript/lib",
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"typescript.workspaceSymbols.scope": "allOpenProjects",
"sonarlint.connectedMode.project": {
Expand Down
9 changes: 9 additions & 0 deletions packages/api/router/component/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,13 @@ export const componentRouter = defineRouter({
)
return handler(opts)
}),
AttributeEditWrapper: permissionedProcedure('updateOrgService')
.input(schema.ZAttributeEditWrapperSchema)
.mutation(async (opts) => {
const handler = await importHandler(
namespaced('AttributeEditWrapper'),
() => import('./mutation.AttributeEditWrapper.handler')
)
return handler(opts)
}),
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { getAuditedClient } from '@weareinreach/db'
import { handleError } from '~api/lib/errorHandler'
import { type TRPCHandlerParams } from '~api/types/handler'

import { type TAttributeEditWrapperSchema } from './mutation.AttributeEditWrapper.schema'

export const AttributeEditWrapper = async ({
ctx,
input,
}: TRPCHandlerParams<TAttributeEditWrapperSchema, 'protected'>) => {
try {
const prisma = getAuditedClient(ctx.actorId)

const { id, action } = input
if (action === 'delete') {
const deleteResult = await prisma.attributeSupplement.delete({
where: { id },
})
return deleteResult
}

const current = await prisma.attributeSupplement.findUniqueOrThrow({
where: { id },
select: { active: true },
})
const updateResult = await prisma.attributeSupplement.update({
where: { id },
data: { active: !current.active },
})
return updateResult
} catch (error) {
return handleError(error)
}
}
export default AttributeEditWrapper
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { z } from 'zod'

import { prefixedId } from '~api/schemas/idPrefix'

export const ZAttributeEditWrapperSchema = z.object({
id: prefixedId('attributeSupplement'),
action: z.enum(['toggleActive', 'delete']),
})
export type TAttributeEditWrapperSchema = z.infer<typeof ZAttributeEditWrapperSchema>
1 change: 1 addition & 0 deletions packages/api/router/component/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// codegen:start {preset: barrel, include: ./*.schema.ts}
export * from './mutation.AttributeEditWrapper.schema'
export * from './mutation.EditModeBarDelete.schema'
export * from './mutation.EditModeBarPublish.schema'
export * from './mutation.EditModeBarReverify.schema'
Expand Down
6 changes: 6 additions & 0 deletions packages/api/router/orgWebsite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,10 @@ export const orgWebsiteRouter = defineRouter({
)
return handler(opts)
}),
upsert: permissionedProcedure('updateOrgWebsite')
.input(schema.ZUpsertSchema)
.mutation(async (opts) => {
const handler = await importHandler(namespaced('upsert'), () => import('./mutation.upsert.handler'))
return handler(opts)
}),
})
78 changes: 78 additions & 0 deletions packages/api/router/orgWebsite/mutation.upsert.handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
generateId,
generateNestedFreeText,
generateNestedFreeTextUpsert,
getAuditedClient,
Prisma,
} from '@weareinreach/db'
import { handleError } from '~api/lib/errorHandler'
import { connectOne, createOne } from '~api/schemas/nestedOps'
import { type TRPCHandlerParams } from '~api/types/handler'

import { type Create, type TUpsertSchema } from './mutation.upsert.schema'

type CreateData = Pick<Create, 'url' | 'isPrimary' | 'deleted' | 'published' | 'orgLocationOnly'>
export const upsert = async ({ ctx, input }: TRPCHandlerParams<TUpsertSchema, 'protected'>) => {
try {
const prisma = getAuditedClient(ctx.actorId)
const { description: desc, operation, id: passedId, orgLocationId, organizationId, ...data } = input

const isCreateData = (op: 'create' | 'update', inputData: typeof data): inputData is CreateData =>
op === 'create'
const isCreate = operation === 'create'

const id = isCreate ? passedId ?? generateId('orgEmail') : passedId

const generateDescription = ():
| Prisma.FreeTextCreateNestedOneWithoutOrgWebsiteInput
| Prisma.FreeTextUpdateOneWithoutOrgWebsiteNestedInput
| undefined => {
if (!desc || !organizationId) {
return undefined
}
if (isCreateData(operation, data)) {
return Prisma.validator<Prisma.FreeTextCreateNestedOneWithoutOrgWebsiteInput>()(
generateNestedFreeText({
orgId: organizationId,
text: desc,
type: 'websiteDesc',
itemId: id,
})
)
} else {
return Prisma.validator<Prisma.FreeTextUpdateOneWithoutOrgWebsiteNestedInput>()(
generateNestedFreeTextUpsert({
orgId: organizationId,
text: desc,
type: 'websiteDesc',
itemId: id,
})
)
}
}
const description = generateDescription()

const result = isCreateData(operation, data)
? await prisma.orgWebsite.create({
data: {
id,
...(description && { description }),
...data,
locations: createOne(orgLocationId, 'orgLocationId'),
organization: connectOne(organizationId, 'id'),
},
})
: await prisma.orgWebsite.update({
where: { id },
data: {
...(description && { description }),
...data,
},
})

return result
} catch (error) {
return handleError(error)
}
}
export default upsert
34 changes: 34 additions & 0 deletions packages/api/router/orgWebsite/mutation.upsert.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { z } from 'zod'

import { prefixedId } from '~api/schemas/idPrefix'

const base = z
.object({
id: prefixedId('orgWebsite'),
url: z.string().url('Invalid URL. Must start with either "https://" or "http://"'),
description: z.string().nullable(),
isPrimary: z.boolean(),
published: z.boolean(),
deleted: z.boolean(),
organizationId: prefixedId('organization'),
orgLocationId: prefixedId('orgLocation').nullable(),
orgLocationOnly: z.boolean(),
})
.partial()

const create = z
.object({
operation: z.literal('create'),
})
.merge(base.required({ url: true, organizationId: true }))
const update = z
.object({
operation: z.literal('update'),
})
.merge(base.required({ id: true }))

export type Create = z.infer<typeof create>
export type Update = z.infer<typeof update>

export const ZUpsertSchema = z.discriminatedUnion('operation', [create, update])
export type TUpsertSchema = z.infer<typeof ZUpsertSchema>
1 change: 1 addition & 0 deletions packages/api/router/orgWebsite/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
export * from './mutation.create.schema'
export * from './mutation.locationLink.schema'
export * from './mutation.update.schema'
export * from './mutation.upsert.schema'
export * from './query.forContactInfo.schema'
export * from './query.forContactInfoEdit.schema'
export * from './query.forEditDrawer.schema'
Expand Down
77 changes: 58 additions & 19 deletions packages/api/schemas/nestedOps.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/consistent-type-assertions */
import compact from 'just-compact'
import invariant from 'tiny-invariant'

const isString = (x: unknown): x is string => typeof x === 'string'

/**
* `*************`
*
Expand Down Expand Up @@ -33,35 +36,54 @@ export const createManyOptional = <T extends Array<any>>(data: T | undefined) =>
}

/** Individual create record */
export const createOne = <T extends Record<string, any>>(data: T | undefined) =>
!data
? undefined
: ({
create: data,
} as const)
export const connectOne = <T extends Record<string, any>>(data: T | undefined) =>
!data
? undefined
: ({
connect: data,
} as const)
export const createOne = <T extends Record<string, any> | string, K extends string = 'id'>(
data: T | null | undefined,
key?: K
): undefined | { create: T extends string ? { [Key in K]: T } : T } => {
if (!data) {
return undefined
}
if (isString(data)) {
return { create: { [key ?? 'id']: data } as T extends string ? { [Key in K]: T } : T }
}
return {
create: data as T extends string ? { [Key in K]: T } : T,
}
}

export const connectOne = <T extends Record<string, any> | string, K extends string = 'id'>(
data: T | null | undefined,
key?: K
): undefined | { connect: T extends string ? { [Key in K]: T } : T } => {
if (!data) {
return undefined
}
if (isString(data)) {
return { connect: { [key ?? 'id']: data } as T extends string ? { [Key in K]: T } : T }
}
return {
connect: data as T extends string ? { [Key in K]: T } : T,
}
}
export const connectOneId = <T extends string>(id: T | undefined | null) =>
!id ? undefined : ({ connect: { id } } as const)

export const connectOneIdRequired = <T extends string>(id: T) => {
invariant(id)
return { connect: { id } }
}
export const connectOrDisconnectId = <T extends string | null | undefined>(id: T) => {
switch (id) {
case null: {
try {
if (id === null) {
return { disconnect: true }
}
case undefined: {
return
}
default: {
if (typeof id === 'string') return { connect: { id } }
if (id === undefined) {
return undefined
}
invariant(id)
return { connect: { id } }
} catch {
return undefined
}
}

Expand Down Expand Up @@ -107,3 +129,20 @@ export const connectOneRequired = <T extends Record<string, any>>(data: T) => {
connect: data,
}
}

export const connectOrCreateOne = <T extends string, K extends string = 'id'>(
id: T | undefined | null,
key?: K
): { connectOrCreate: { where: { [Key in K]: T }; create: { [Key in K]: T } } } | undefined => {
if (!id) {
return undefined
}
const idObj = { [key ?? 'id']: id } as { [Key in K]: T }

return {
connectOrCreate: {
where: idObj,
create: idObj,
},
}
}
5 changes: 4 additions & 1 deletion packages/db/lib/generateFreeText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ export const generateFreeText = <T extends GenerateFreeTextType>({
invariant(itemId)
return createKey([orgId, itemId, 'description'])
}
default: {
return null
}
}
})()
const ns = namespaces.orgData
Expand Down Expand Up @@ -67,7 +70,7 @@ export const generateNestedFreeText = <T extends GenerateFreeTextType>(args: Gen

export const generateNestedFreeTextUpsert = <T extends GenerateFreeTextType>(
args: GenerateFreeTextParams<T>
): Prisma.FreeTextUpdateOneWithoutOrgEmailNestedInput => {
) => {
const { freeText, translationKey } = generateFreeText(args)
return {
upsert: {
Expand Down
Loading
Loading