Skip to content

Commit

Permalink
tag-descriptions as 1:st class entities
Browse files Browse the repository at this point in the history
  • Loading branch information
jlarsson committed Feb 13, 2025
1 parent 09fcd51 commit aedfe71
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 3 deletions.
4 changes: 3 additions & 1 deletion src/haffa/haffa-gql-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { createSyslogGqlModule } from '../syslog/syslog-gql-module'
import type { GraphQLModule } from '../lib/gdi-api-node'
import type { EntityResolverMap } from '../lib/gdi-api-node/graphql'
import { createPickupLocationsGqlModule } from '../pickup/pickup-locations-gql-module'
import { createTagsGqlModule } from '../tags/tags-gql-module'

export const createStandardGqlModule = (): GraphQLModule => ({
schema: haffaGqlSchema,
Expand All @@ -46,7 +47,8 @@ export const createHaffaGqlModule = (services: Services): GraphQLModule =>
createPickupLocationsGqlModule(services),
createUserMapperGqlModule(services),
createSmsTemplatesGqlModule(services),
createSyslogGqlModule(services)
createSyslogGqlModule(services),
createTagsGqlModule(services)
)

const mergeModules = (...modules: GraphQLModule[]): GraphQLModule => ({
Expand Down
17 changes: 15 additions & 2 deletions src/options/options-gql-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import { normalizeRoles } from '../login'
import { optionsAdapter } from './options-adapter'
import type { GraphQLModule } from '../lib/gdi-api-node'

const validOptions = new Set([
'branding-theme',
'branding-phrases',
'analytics-tagmanager',
'branding-html',
'tag-descriptions',
'label',
])

export const createOptionsGqlModule = ({
settings,
}: Pick<Services, 'settings'>): GraphQLModule => ({
Expand All @@ -14,7 +23,9 @@ export const createOptionsGqlModule = ({
// https://www.graphql-tools.com/docs/resolvers
options: async ({ ctx, args: { name } }) => {
const { user } = ctx
if (!normalizeRoles(user?.roles).canEditTerms) {
if (
!(normalizeRoles(user?.roles).canEditTerms && validOptions.has(name))
) {
ctx.throw(HttpStatusCodes.UNAUTHORIZED)
}
return optionsAdapter(settings).getOptions(name)
Expand All @@ -23,7 +34,9 @@ export const createOptionsGqlModule = ({
Mutation: {
updateOptions: async ({ ctx, args: { input, name } }) => {
const { user } = ctx
if (!normalizeRoles(user?.roles).canEditTerms) {
if (
!(normalizeRoles(user?.roles).canEditTerms && validOptions.has(name))
) {
ctx.throw(HttpStatusCodes.UNAUTHORIZED)
}
return optionsAdapter(settings).updateOptions(name, input)
Expand Down
45 changes: 45 additions & 0 deletions src/tags/mappers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { uniqueBy } from '../lib'
import type { TagDescription } from './types'

const isString = (v: any) => typeof v === 'string'
const isStringOrUndefined = (v: any) =>
typeof v === 'string' || typeof v === 'undefined'

const empty: TagDescription = { tag: '', label: '', description: '' }

// In ancient times, tags descriptions were stored as options as (tag, description) pairs
const tryMigrateFromLegacyOptionsFormat = (v: any): TagDescription | null =>
isString(v?.key) && isString(v?.value)
? {
tag: v.key,
label: '',
description: v.value,
}
: null

const tryNormalizeTagDescription = (
d?: Partial<TagDescription>
): TagDescription | null =>
d &&
isString(d.tag) &&
isStringOrUndefined(d.label) &&
isStringOrUndefined(d.description)
? {
tag: d.tag?.trim() || '',
label: d.label?.trim() || '',
description: d.description?.trim() || '',
}
: null

export const normalizeTagDescriptions = (
descriptions: Partial<TagDescription>[] | null
): TagDescription[] =>
(descriptions || [])
.map(
d =>
tryMigrateFromLegacyOptionsFormat(d) ||
tryNormalizeTagDescription(d) ||
empty
)
.filter(d => d.tag && (d.label || d.description))
.filter(uniqueBy(d => d?.tag))
17 changes: 17 additions & 0 deletions src/tags/tags-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { SettingsService } from '../settings/types'
import { normalizeTagDescriptions } from './mappers'
import type { TagDescription } from './types'

export const tagsAdapter = (settings: SettingsService) => ({
getTagDescriptions: () =>
settings
.getSetting<TagDescription[]>('options-tag-descriptions')
.then(normalizeTagDescriptions),
updateTagDescriptions: (descriptions: Partial<TagDescription>[]) =>
settings
.updateSetting(
'options-tag-descriptions',
normalizeTagDescriptions(descriptions)
)
.then(value => value),
})
27 changes: 27 additions & 0 deletions src/tags/tags-gql-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import HttpStatusCodes from 'http-status-codes'
import type { Services } from '../types'
import { normalizeRoles } from '../login'
import { tagsGqlSchema } from './tags.gql.schema'
import { tagsAdapter } from './tags-adapter'
import type { GraphQLModule } from '../lib/gdi-api-node'

export const createTagsGqlModule = ({
settings,
}: Pick<Services, 'settings'>): GraphQLModule => ({
schema: tagsGqlSchema,
resolvers: {
Query: {
// https://www.graphql-tools.com/docs/resolvers
tagDescriptions: async () => tagsAdapter(settings).getTagDescriptions(),
},
Mutation: {
updateTagDescriptions: async ({ ctx, args: { input } }) => {
const { user } = ctx
if (!normalizeRoles(user?.roles).canEditTerms) {
ctx.throw(HttpStatusCodes.UNAUTHORIZED)
}
return tagsAdapter(settings).updateTagDescriptions(input)
},
},
},
})
21 changes: 21 additions & 0 deletions src/tags/tags.gql.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export const tagsGqlSchema = /* GraphQL */ `
type Query {
tagDescriptions: [TagDescription]
}
type Mutation {
updateTagDescriptions(input: [TagDescriptionInput]!): [TagDescription]
}
type TagDescription {
tag: String!
label: String!
description: String!
}
input TagDescriptionInput {
tag: String!
label: String!
description: String!
}
`
5 changes: 5 additions & 0 deletions src/tags/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface TagDescription {
tag: string
label: string
description: string
}

0 comments on commit aedfe71

Please sign in to comment.