Skip to content

Commit

Permalink
❇️ Template language (bluesky-social#2780)
Browse files Browse the repository at this point in the history
* ✨ Throw specific error for duplicate template name

* 🧹 Cleanup console

* ✨ Throw duplicate template name error from update too

* ✨ Add language to templates

* 📝 Add changeset

* ✨ Add missing event type

* ✨ Add language format in lexicon and error checker in util

* 🚨 fix linter issues
  • Loading branch information
foysalit authored Sep 4, 2024
1 parent 8252c65 commit e4d41d6
Show file tree
Hide file tree
Showing 28 changed files with 229 additions and 30 deletions.
6 changes: 6 additions & 0 deletions .changeset/forty-apricots-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@atproto/ozone": patch
"@atproto/api": patch
---

Add language property to communication templates
8 changes: 7 additions & 1 deletion lexicons/tools/ozone/communication/createTemplate.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
"type": "string",
"description": "Subject of the message, used in emails."
},
"lang": {
"type": "string",
"format": "language",
"description": "Message language."
},
"createdBy": {
"type": "string",
"format": "did",
Expand All @@ -37,7 +42,8 @@
"type": "ref",
"ref": "tools.ozone.communication.defs#templateView"
}
}
},
"errors": [{ "name": "DuplicateTemplateName" }]
}
}
}
5 changes: 5 additions & 0 deletions lexicons/tools/ozone/communication/defs.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
"description": "Subject of the message, used in emails."
},
"disabled": { "type": "boolean" },
"lang": {
"type": "string",
"format": "language",
"description": "Message language."
},
"lastUpdatedBy": {
"type": "string",
"format": "did",
Expand Down
8 changes: 7 additions & 1 deletion lexicons/tools/ozone/communication/updateTemplate.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
"type": "string",
"description": "Name of the template."
},
"lang": {
"type": "string",
"format": "language",
"description": "Message language."
},
"contentMarkdown": {
"type": "string",
"description": "Content of the template, markdown supported, can contain variable placeholders."
Expand All @@ -44,7 +49,8 @@
"type": "ref",
"ref": "tools.ozone.communication.defs#templateView"
}
}
},
"errors": [{ "name": "DuplicateTemplateName" }]
}
}
}
1 change: 1 addition & 0 deletions lexicons/tools/ozone/moderation/emitEvent.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"tools.ozone.moderation.defs#modEventMuteReporter",
"tools.ozone.moderation.defs#modEventUnmuteReporter",
"tools.ozone.moderation.defs#modEventReverseTakedown",
"tools.ozone.moderation.defs#modEventResolveAppeal",
"tools.ozone.moderation.defs#modEventEmail",
"tools.ozone.moderation.defs#modEventTag"
]
Expand Down
22 changes: 10 additions & 12 deletions packages/api/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3415,12 +3415,11 @@ export class ToolsOzoneCommunicationNS {
data?: ToolsOzoneCommunicationCreateTemplate.InputSchema,
opts?: ToolsOzoneCommunicationCreateTemplate.CallOptions,
): Promise<ToolsOzoneCommunicationCreateTemplate.Response> {
return this._client.call(
'tools.ozone.communication.createTemplate',
opts?.qp,
data,
opts,
)
return this._client
.call('tools.ozone.communication.createTemplate', opts?.qp, data, opts)
.catch((e) => {
throw ToolsOzoneCommunicationCreateTemplate.toKnownErr(e)
})
}

deleteTemplate(
Expand Down Expand Up @@ -3451,12 +3450,11 @@ export class ToolsOzoneCommunicationNS {
data?: ToolsOzoneCommunicationUpdateTemplate.InputSchema,
opts?: ToolsOzoneCommunicationUpdateTemplate.CallOptions,
): Promise<ToolsOzoneCommunicationUpdateTemplate.Response> {
return this._client.call(
'tools.ozone.communication.updateTemplate',
opts?.qp,
data,
opts,
)
return this._client
.call('tools.ozone.communication.updateTemplate', opts?.qp, data, opts)
.catch((e) => {
throw ToolsOzoneCommunicationUpdateTemplate.toKnownErr(e)
})
}
}

Expand Down
26 changes: 26 additions & 0 deletions packages/api/src/client/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10466,6 +10466,11 @@ export const schemaDict = {
type: 'string',
description: 'Subject of the message, used in emails.',
},
lang: {
type: 'string',
format: 'language',
description: 'Message language.',
},
createdBy: {
type: 'string',
format: 'did',
Expand All @@ -10481,6 +10486,11 @@ export const schemaDict = {
ref: 'lex:tools.ozone.communication.defs#templateView',
},
},
errors: [
{
name: 'DuplicateTemplateName',
},
],
},
},
},
Expand Down Expand Up @@ -10519,6 +10529,11 @@ export const schemaDict = {
disabled: {
type: 'boolean',
},
lang: {
type: 'string',
format: 'language',
description: 'Message language.',
},
lastUpdatedBy: {
type: 'string',
format: 'did',
Expand Down Expand Up @@ -10606,6 +10621,11 @@ export const schemaDict = {
type: 'string',
description: 'Name of the template.',
},
lang: {
type: 'string',
format: 'language',
description: 'Message language.',
},
contentMarkdown: {
type: 'string',
description:
Expand Down Expand Up @@ -10633,6 +10653,11 @@ export const schemaDict = {
ref: 'lex:tools.ozone.communication.defs#templateView',
},
},
errors: [
{
name: 'DuplicateTemplateName',
},
],
},
},
},
Expand Down Expand Up @@ -11432,6 +11457,7 @@ export const schemaDict = {
'lex:tools.ozone.moderation.defs#modEventMuteReporter',
'lex:tools.ozone.moderation.defs#modEventUnmuteReporter',
'lex:tools.ozone.moderation.defs#modEventReverseTakedown',
'lex:tools.ozone.moderation.defs#modEventResolveAppeal',
'lex:tools.ozone.moderation.defs#modEventEmail',
'lex:tools.ozone.moderation.defs#modEventTag',
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export interface InputSchema {
contentMarkdown: string
/** Subject of the message, used in emails. */
subject: string
/** Message language. */
lang?: string
/** DID of the user who is creating the template. */
createdBy?: string
[k: string]: unknown
Expand All @@ -37,6 +39,17 @@ export interface Response {
data: OutputSchema
}

export class DuplicateTemplateNameError extends XRPCError {
constructor(src: XRPCError) {
super(src.status, src.error, src.message, src.headers, { cause: src })
}
}

export function toKnownErr(e: any) {
if (e instanceof XRPCError) {
if (e.error === 'DuplicateTemplateName')
return new DuplicateTemplateNameError(e)
}

return e
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface TemplateView {
/** Subject of the message, used in emails. */
contentMarkdown: string
disabled: boolean
/** Message language. */
lang?: string
/** DID of the user who last updated the template. */
lastUpdatedBy: string
createdAt: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface InputSchema {
id: string
/** Name of the template. */
name?: string
/** Message language. */
lang?: string
/** Content of the template, markdown supported, can contain variable placeholders. */
contentMarkdown?: string
/** Subject of the message, used in emails. */
Expand All @@ -40,6 +42,17 @@ export interface Response {
data: OutputSchema
}

export class DuplicateTemplateNameError extends XRPCError {
constructor(src: XRPCError) {
super(src.status, src.error, src.message, src.headers, { cause: src })
}
}

export function toKnownErr(e: any) {
if (e instanceof XRPCError) {
if (e.error === 'DuplicateTemplateName')
return new DuplicateTemplateNameError(e)
}

return e
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface InputSchema {
| ToolsOzoneModerationDefs.ModEventMuteReporter
| ToolsOzoneModerationDefs.ModEventUnmuteReporter
| ToolsOzoneModerationDefs.ModEventReverseTakedown
| ToolsOzoneModerationDefs.ModEventResolveAppeal
| ToolsOzoneModerationDefs.ModEventEmail
| ToolsOzoneModerationDefs.ModEventTag
| { $type: string; [k: string]: unknown }
Expand Down
32 changes: 23 additions & 9 deletions packages/ozone/src/api/communication/createTemplate.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server'
import { Server } from '../../lexicon'
import AppContext from '../../context'
import { isDuplicateTemplateNameError } from '../../communication-service/util'

export default function (server: Server, ctx: AppContext) {
server.tools.ozone.communication.createTemplate({
auth: ctx.authVerifier.modOrAdminToken,
handler: async ({ input, auth }) => {
const access = auth.credentials
const db = ctx.db
const { createdBy, ...template } = input.body
const { createdBy, lang, ...template } = input.body

if (!access.isModerator) {
throw new AuthRequiredError(
Expand All @@ -22,15 +23,28 @@ export default function (server: Server, ctx: AppContext) {
}

const communicationTemplate = ctx.communicationTemplateService(db)
const newTemplate = await communicationTemplate.create({
...template,
disabled: false,
lastUpdatedBy: createdBy,
})

return {
encoding: 'application/json',
body: communicationTemplate.view(newTemplate),
try {
const newTemplate = await communicationTemplate.create({
...template,
// We are not using ?? here because we want to use null instead of potentially empty string
lang: lang || null,
disabled: false,
lastUpdatedBy: createdBy,
})

return {
encoding: 'application/json',
body: communicationTemplate.view(newTemplate),
}
} catch (err) {
if (isDuplicateTemplateNameError(err)) {
throw new InvalidRequestError(
`${template.name} already exists. Please choose a different name.`,
'DuplicateTemplateName',
)
}
throw err
}
},
})
Expand Down
25 changes: 18 additions & 7 deletions packages/ozone/src/api/communication/updateTemplate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server'
import { Server } from '../../lexicon'
import AppContext from '../../context'
import { isDuplicateTemplateNameError } from '../../communication-service/util'

export default function (server: Server, ctx: AppContext) {
server.tools.ozone.communication.updateTemplate({
Expand All @@ -26,14 +27,24 @@ export default function (server: Server, ctx: AppContext) {
}

const communicationTemplate = ctx.communicationTemplateService(db)
const updatedTemplate = await communicationTemplate.update(Number(id), {
...template,
lastUpdatedBy: updatedBy,
})
try {
const updatedTemplate = await communicationTemplate.update(Number(id), {
...template,
lastUpdatedBy: updatedBy,
})

return {
encoding: 'application/json',
body: communicationTemplate.view(updatedTemplate),
return {
encoding: 'application/json',
body: communicationTemplate.view(updatedTemplate),
}
} catch (err) {
if (isDuplicateTemplateNameError(err)) {
throw new InvalidRequestError(
`${template.name} already exists. Please choose a different name.`,
'DuplicateTemplateName',
)
}
throw err
}
},
})
Expand Down
5 changes: 5 additions & 0 deletions packages/ozone/src/communication-service/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class CommunicationTemplateService {
name,
contentMarkdown,
subject,
lang,
disabled,
updatedAt,
createdAt,
Expand All @@ -44,6 +45,7 @@ export class CommunicationTemplateService {
name,
contentMarkdown,
subject,
lang,
disabled,
lastUpdatedBy,
updatedAt: updatedAt || new Date(),
Expand All @@ -62,6 +64,7 @@ export class CommunicationTemplateService {
contentMarkdown,
subject,
disabled,
lang,
updatedAt,
lastUpdatedBy,
}: Partial<Omit<Selectable<CommunicationTemplate>, 'id' | 'createdAt'>>,
Expand All @@ -73,6 +76,7 @@ export class CommunicationTemplateService {
name,
contentMarkdown,
subject,
lang,
disabled,
lastUpdatedBy,
updatedAt: updatedAt || new Date(),
Expand All @@ -96,6 +100,7 @@ export class CommunicationTemplateService {
name: template.name,
contentMarkdown: template.contentMarkdown,
disabled: template.disabled,
lang: template.lang || undefined,
subject: template.subject || undefined,
createdAt: template.createdAt.toISOString(),
updatedAt: template.updatedAt.toISOString(),
Expand Down
8 changes: 8 additions & 0 deletions packages/ozone/src/communication-service/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Postgresql will throw a specific error code with the constraint when trying to create a template with duplicate name
// see https://www.postgresql.org/docs/current/errcodes-appendix.html
export const isDuplicateTemplateNameError = (err: any) => {
return (
err?.['code'] === '23505' &&
err?.['constraint'] === 'communication_template_unique_name'
)
}
Loading

0 comments on commit e4d41d6

Please sign in to comment.