Skip to content

Commit

Permalink
refactor: api handler cache (#1107)
Browse files Browse the repository at this point in the history
* fieldOpt

* geo

* misc
  • Loading branch information
JoeKarow authored Feb 15, 2024
1 parent fc041c6 commit 2168fa0
Show file tree
Hide file tree
Showing 23 changed files with 253 additions and 220 deletions.
147 changes: 56 additions & 91 deletions packages/api/router/fieldOpt/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,10 @@
import { defineRouter, publicProcedure } from '~api/lib/trpc'
import { defineRouter, importHandler, publicProcedure } from '~api/lib/trpc'

import * as schema from './schemas'

const HandlerCache: Partial<FieldOptHandlerCache> = {}
const NAMESPACE = 'fieldOpt'

type FieldOptHandlerCache = {
govDistsByCountry: typeof import('./query.govDistsByCountry.handler').govDistsByCountry
govDistsByCountryNoSub: typeof import('./query.govDistsByCountryNoSub.handler').govDistsByCountryNoSub
phoneTypes: typeof import('./query.phoneTypes.handler').phoneTypes
attributesByCategory: typeof import('./query.attributesByCategory.handler').attributesByCategory
attributeCategories: typeof import('./query.attributeCategories.handler').attributeCategories
languages: typeof import('./query.languages.handler').languages
countries: typeof import('./query.countries.handler').countries
userTitle: typeof import('./query.userTitle.handler').userTitle
countryGovDistMap: typeof import('./query.countryGovDistMap.handler').countryGovDistMap
getSubDistricts: typeof import('./query.getSubDistricts.handler').getSubDistricts
govDists: typeof import('./query.govDists.handler').govDists
orgBadges: typeof import('./query.orgBadges.handler').orgBadges
}
const namespaced = (s: string) => `${NAMESPACE}.${s}`
export const fieldOptRouter = defineRouter({
/** All government districts by country (active for org listings). Gives up to 2 levels of sub-districts */
govDistsByCountry: publicProcedure
Expand All @@ -26,97 +13,75 @@ export const fieldOptRouter = defineRouter({
'All government districts by country (active for org listings). Gives 2 levels of sub-districts',
})
.input(schema.ZGovDistsByCountrySchema)
.query(async ({ ctx, input }) => {
if (!HandlerCache.govDistsByCountry)
HandlerCache.govDistsByCountry = await import('./query.govDistsByCountry.handler').then(
(mod) => mod.govDistsByCountry
)
if (!HandlerCache.govDistsByCountry) throw new Error('Failed to load handler')
return HandlerCache.govDistsByCountry({ ctx, input })
.query(async (opts) => {
const handler = await importHandler(
namespaced('govDistsByCountry'),
() => import('./query.govDistsByCountry.handler')
)
return handler(opts)
}),
govDistsByCountryNoSub: publicProcedure
.meta({
description: 'All government districts by country (active for org listings).',
})
.input(schema.ZGovDistsByCountryNoSubSchema)
.query(async ({ ctx, input }) => {
if (!HandlerCache.govDistsByCountryNoSub)
HandlerCache.govDistsByCountryNoSub = await import('./query.govDistsByCountryNoSub.handler').then(
(mod) => mod.govDistsByCountryNoSub
)
if (!HandlerCache.govDistsByCountryNoSub) throw new Error('Failed to load handler')
return HandlerCache.govDistsByCountryNoSub({ ctx, input })
.query(async (opts) => {
const handler = await importHandler(
namespaced('govDistsByCountryNoSub'),
() => import('./query.govDistsByCountryNoSub.handler')
)
return handler(opts)
}),
phoneTypes: publicProcedure.query(async () => {
if (!HandlerCache.phoneTypes)
HandlerCache.phoneTypes = await import('./query.phoneTypes.handler').then((mod) => mod.phoneTypes)
if (!HandlerCache.phoneTypes) throw new Error('Failed to load handler')
return HandlerCache.phoneTypes()
const handler = await importHandler(namespaced('phoneTypes'), () => import('./query.phoneTypes.handler'))
return handler()
}),
attributesByCategory: publicProcedure
.input(schema.ZAttributesByCategorySchema)
.query(async ({ ctx, input }) => {
if (!HandlerCache.attributesByCategory)
HandlerCache.attributesByCategory = await import('./query.attributesByCategory.handler').then(
(mod) => mod.attributesByCategory
)
if (!HandlerCache.attributesByCategory) throw new Error('Failed to load handler')
return HandlerCache.attributesByCategory({ ctx, input })
}),
attributeCategories: publicProcedure
.input(schema.ZAttributeCategoriesSchema)
.query(async ({ ctx, input }) => {
if (!HandlerCache.attributeCategories)
HandlerCache.attributeCategories = await import('./query.attributeCategories.handler').then(
(mod) => mod.attributeCategories
)
if (!HandlerCache.attributeCategories) throw new Error('Failed to load handler')
return HandlerCache.attributeCategories({ ctx, input })
}),
languages: publicProcedure.input(schema.ZLanguagesSchema).query(async ({ ctx, input }) => {
if (!HandlerCache.languages)
HandlerCache.languages = await import('./query.languages.handler').then((mod) => mod.languages)
if (!HandlerCache.languages) throw new Error('Failed to load handler')
return HandlerCache.languages({ ctx, input })
attributesByCategory: publicProcedure.input(schema.ZAttributesByCategorySchema).query(async (opts) => {
const handler = await importHandler(
namespaced('attributesByCategory'),
() => import('./query.attributesByCategory.handler')
)
return handler(opts)
}),
countries: publicProcedure.input(schema.ZCountriesSchema).query(async ({ ctx, input }) => {
if (!HandlerCache.countries)
HandlerCache.countries = await import('./query.countries.handler').then((mod) => mod.countries)
if (!HandlerCache.countries) throw new Error('Failed to load handler')
return HandlerCache.countries({ ctx, input })
attributeCategories: publicProcedure.input(schema.ZAttributeCategoriesSchema).query(async (opts) => {
const handler = await importHandler(
namespaced('attributeCategories'),
() => import('./query.attributeCategories.handler')
)
return handler(opts)
}),
languages: publicProcedure.input(schema.ZLanguagesSchema).query(async (opts) => {
const handler = await importHandler(namespaced('languages'), () => import('./query.languages.handler'))
return handler(opts)
}),
countries: publicProcedure.input(schema.ZCountriesSchema).query(async (opts) => {
const handler = await importHandler(namespaced('countries'), () => import('./query.countries.handler'))
return handler(opts)
}),
userTitle: publicProcedure.query(async () => {
if (!HandlerCache.userTitle)
HandlerCache.userTitle = await import('./query.userTitle.handler').then((mod) => mod.userTitle)
if (!HandlerCache.userTitle) throw new Error('Failed to load handler')
return HandlerCache.userTitle()
const handler = await importHandler(namespaced('userTitle'), () => import('./query.userTitle.handler'))
return handler()
}),
countryGovDistMap: publicProcedure.query(async () => {
if (!HandlerCache.countryGovDistMap)
HandlerCache.countryGovDistMap = await import('./query.countryGovDistMap.handler').then(
(mod) => mod.countryGovDistMap
)
if (!HandlerCache.countryGovDistMap) throw new Error('Failed to load handler')
return HandlerCache.countryGovDistMap()
const handler = await importHandler(
namespaced('countryGovDistMap'),
() => import('./query.countryGovDistMap.handler')
)
return handler()
}),
getSubDistricts: publicProcedure.input(schema.ZGetSubDistrictsSchema).query(async ({ ctx, input }) => {
if (!HandlerCache.getSubDistricts)
HandlerCache.getSubDistricts = await import('./query.getSubDistricts.handler').then(
(mod) => mod.getSubDistricts
)
if (!HandlerCache.getSubDistricts) throw new Error('Failed to load handler')
return HandlerCache.getSubDistricts({ ctx, input })
getSubDistricts: publicProcedure.input(schema.ZGetSubDistrictsSchema).query(async (opts) => {
const handler = await importHandler(
namespaced('getSubDistricts'),
() => import('./query.getSubDistricts.handler')
)
return handler(opts)
}),
govDists: publicProcedure.input(schema.ZGovDistsSchema).query(async ({ ctx, input }) => {
if (!HandlerCache.govDists)
HandlerCache.govDists = await import('./query.govDists.handler').then((mod) => mod.govDists)
if (!HandlerCache.govDists) throw new Error('Failed to load handler')
return HandlerCache.govDists({ ctx, input })
govDists: publicProcedure.input(schema.ZGovDistsSchema).query(async (opts) => {
const handler = await importHandler(namespaced('govDists'), () => import('./query.govDists.handler'))
return handler(opts)
}),
orgBadges: publicProcedure.input(schema.ZOrgBadgesSchema).query(async ({ ctx, input }) => {
if (!HandlerCache.orgBadges)
HandlerCache.orgBadges = await import('./query.orgBadges.handler').then((mod) => mod.orgBadges)
if (!HandlerCache.orgBadges) throw new Error('Failed to load handler')
return HandlerCache.orgBadges({ ctx, input })
orgBadges: publicProcedure.input(schema.ZOrgBadgesSchema).query(async (opts) => {
const handler = await importHandler(namespaced('orgBadges'), () => import('./query.orgBadges.handler'))
return handler(opts)
}),
})
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export const attributeCategories = async ({ input }: TRPCHandlerParams<TAttribut
})
return results
}
export default attributeCategories
38 changes: 20 additions & 18 deletions packages/api/router/fieldOpt/query.attributesByCategory.handler.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
import flush from 'just-flush'
import { type SetOptional } from 'type-fest'

import { prisma } from '@weareinreach/db'
import { type AttributesByCategory } from '@weareinreach/db/client'
import { type FieldAttributes } from '@weareinreach/db/zod_util/attributeSupplement'
import { type TRPCHandlerParams } from '~api/types/handler'

import { type TAttributesByCategorySchema } from './query.attributesByCategory.schema'
import { fieldAttributesSchema, type TAttributesByCategorySchema } from './query.attributesByCategory.schema'

export const attributesByCategory = async ({ input }: TRPCHandlerParams<TAttributesByCategorySchema>) => {
const where = Array.isArray(input)
? { categoryName: { in: input } }
: typeof input === 'string'
? { categoryName: input }
: undefined
console.log(input)
const result = await prisma.attributesByCategory.findMany({
where,
where: {
categoryName: Array.isArray(input?.categoryName) ? { in: input.categoryName } : input?.categoryName,
canAttachTo: input?.canAttachTo?.length ? { hasSome: input.canAttachTo } : undefined,
},
orderBy: [{ categoryName: 'asc' }, { attributeName: 'asc' }],
})

const flushedResults = result.map((item) =>
flush<FlushedAttributesByCategory>(item)
) as FlushedAttributesByCategory[]
const flushedResults = result.map((item) => {
const { dataSchema, ...rest } = item

const parsedDataSchema = fieldAttributesSchema.safeParse(dataSchema)

return {
...rest,
dataSchema: parsedDataSchema.success
? (parsedDataSchema.data as FieldAttributes[] | FieldAttributes[][])
: null,
}
})
return flushedResults
}
type FlushedAttributesByCategory = SetOptional<
AttributesByCategory,
'interpolationValues' | 'icon' | 'iconBg' | 'badgeRender' | 'dataSchema' | 'dataSchemaName'
>
export default attributesByCategory
53 changes: 50 additions & 3 deletions packages/api/router/fieldOpt/query.attributesByCategory.schema.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,55 @@
import { z } from 'zod'

import { FieldType } from '@weareinreach/db/zod_util/attributeSupplement'

export const ZAttributesByCategorySchema = z
.string()
.or(z.string().array())
.object({
categoryName: z.string().or(z.string().array()).optional().describe('categoryName'),
canAttachTo: z.enum(['LOCATION', 'ORGANIZATION', 'SERVICE', 'USER']).array().optional(),
})
.optional()
.describe('categoryName')
export type TAttributesByCategorySchema = z.infer<typeof ZAttributesByCategorySchema>

const fieldTypeSchema = z.nativeEnum(FieldType)
const baseFieldAttributesSchema = z.object({
key: z.string(),
label: z.string(),
name: z.string(),
type: fieldTypeSchema,
required: z.boolean().optional(),
})

const textFieldAttributesSchema = baseFieldAttributesSchema.extend({
type: z.literal(FieldType.text),
})

const selectFieldAttributesSchema = baseFieldAttributesSchema.extend({
type: z.literal(FieldType.select),
options: z.array(
z.object({
value: z.string(),
label: z.string(),
})
),
})

const numberFieldAttributesSchema = baseFieldAttributesSchema.extend({
type: z.literal(FieldType.number),
})

const currencyFieldAttributesSchema = baseFieldAttributesSchema.extend({
type: z.literal(FieldType.currency),
})

const fieldAttributesObject = z
.union([
textFieldAttributesSchema,
selectFieldAttributesSchema,
numberFieldAttributesSchema,
currencyFieldAttributesSchema,
])
.brand('FieldAttributes')
.array()
export const fieldAttributesSchema = fieldAttributesObject.or(fieldAttributesObject.array())

export type TFieldAttributesSchema = z.infer<typeof fieldAttributesSchema>
1 change: 1 addition & 0 deletions packages/api/router/fieldOpt/query.countries.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ export const countries = async ({ input }: TRPCHandlerParams<TCountriesSchema>)
type CountryResult = (typeof result)[number][]
return result as CountryResult
}
export default countries
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ interface CountryGovDistMapItem {
children: CountryGovDistMapItemBasic[]
parent?: CountryGovDistMapItemBasic & { parent?: CountryGovDistMapItemBasic }
}
export default countryGovDistMap
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ export const getSubDistricts = async ({ input }: TRPCHandlerParams<TGetSubDistri
handleError(error)
}
}
export default getSubDistricts
1 change: 1 addition & 0 deletions packages/api/router/fieldOpt/query.govDists.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export const govDists = async ({ input }: TRPCHandlerParams<TGovDistsSchema>) =>
handleError(error)
}
}
export default govDists
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ export const govDistsByCountry = async ({ input }: TRPCHandlerParams<TGovDistsBy
})
return data
}
export default govDistsByCountry
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ export const govDistsByCountryNoSub = async ({ input }: TRPCHandlerParams<TGovDi
})
return data
}
export default govDistsByCountryNoSub
1 change: 1 addition & 0 deletions packages/api/router/fieldOpt/query.languages.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ export const languages = async ({ input }: TRPCHandlerParams<TLanguagesSchema>)
})
return results
}
export default languages
3 changes: 2 additions & 1 deletion packages/api/router/fieldOpt/query.orgBadges.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { type TRPCHandlerParams } from '~api/types/handler'

import { type TOrgBadgesSchema } from './query.orgBadges.schema'

export const orgBadges = async ({ ctx, input }: TRPCHandlerParams<TOrgBadgesSchema>) => {
export const orgBadges = async ({ input }: TRPCHandlerParams<TOrgBadgesSchema>) => {
try {
const badges = await prisma.attribute.findMany({
where: {
Expand All @@ -24,3 +24,4 @@ export const orgBadges = async ({ ctx, input }: TRPCHandlerParams<TOrgBadgesSche
handleError(error)
}
}
export default orgBadges
1 change: 1 addition & 0 deletions packages/api/router/fieldOpt/query.phoneTypes.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export const phoneTypes = async () => {
})
return result
}
export default phoneTypes
1 change: 1 addition & 0 deletions packages/api/router/fieldOpt/query.userTitle.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export const userTitle = async () => {
})
return results
}
export default userTitle
31 changes: 15 additions & 16 deletions packages/api/router/geo/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { defineRouter, publicProcedure } from '~api/lib/trpc'
import { defineRouter, importHandler, publicProcedure } from '~api/lib/trpc'

import * as schema from './schemas'

const HandlerCache: Partial<GeoHandlerCache> = {}
const NAMESPACE = 'geo'

type GeoHandlerCache = {
autocomplete: typeof import('./query.autocomplete.handler').autocomplete
geoByPlaceId: typeof import('./query.geoByPlaceId.handler').geoByPlaceId
}
const namespaced = (s: string) => `${NAMESPACE}.${s}`

export const geoRouter = defineRouter({
autocomplete: publicProcedure.input(schema.ZAutocompleteSchema).query(async ({ ctx, input }) => {
if (!HandlerCache.autocomplete)
HandlerCache.autocomplete = await import('./query.autocomplete.handler').then((mod) => mod.autocomplete)
if (!HandlerCache.autocomplete) throw new Error('Failed to load handler')
return HandlerCache.autocomplete({ ctx, input })
autocomplete: publicProcedure.input(schema.ZAutocompleteSchema).query(async (opts) => {
const handler = await importHandler(
namespaced('autocomplete'),
() => import('./query.autocomplete.handler')
)
return handler(opts)
}),
geoByPlaceId: publicProcedure.input(schema.ZGeoByPlaceIdSchema).query(async ({ ctx, input }) => {
if (!HandlerCache.geoByPlaceId)
HandlerCache.geoByPlaceId = await import('./query.geoByPlaceId.handler').then((mod) => mod.geoByPlaceId)
if (!HandlerCache.geoByPlaceId) throw new Error('Failed to load handler')
return HandlerCache.geoByPlaceId({ ctx, input })
geoByPlaceId: publicProcedure.input(schema.ZGeoByPlaceIdSchema).query(async (opts) => {
const handler = await importHandler(
namespaced('geoByPlaceId'),
() => import('./query.geoByPlaceId.handler')
)
return handler(opts)
}),
})
1 change: 1 addition & 0 deletions packages/api/router/geo/query.autocomplete.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ export const autocomplete = async ({ input }: TRPCHandlerParams<TAutocompleteSch
const parsedData = autocompleteResponse.parse(data)
return googleAPIResponseHandler(parsedData, data)
}
export default autocomplete
Loading

0 comments on commit 2168fa0

Please sign in to comment.