mirror of
https://github.com/logto-io/logto.git
synced 2025-03-31 22:51:25 -05:00
feat(core): add get email-templates api (#7016)
add get email-templates api
This commit is contained in:
parent
dd8cd13c1f
commit
bf7f399844
5 changed files with 163 additions and 4 deletions
|
@ -4,13 +4,19 @@ import {
|
|||
EmailTemplates,
|
||||
type CreateEmailTemplate,
|
||||
} from '@logto/schemas';
|
||||
import { type CommonQueryMethods } from '@silverhand/slonik';
|
||||
import { sql, type CommonQueryMethods } from '@silverhand/slonik';
|
||||
|
||||
import SchemaQueries from '#src/utils/SchemaQueries.js';
|
||||
|
||||
import { type WellKnownCache } from '../caches/well-known.js';
|
||||
import { buildInsertIntoWithPool } from '../database/insert-into.js';
|
||||
import { convertToIdentifiers, type OmitAutoSetFields } from '../utils/sql.js';
|
||||
import { expandFields } from '../database/utils.js';
|
||||
import {
|
||||
conditionalSql,
|
||||
convertToIdentifiers,
|
||||
manyRows,
|
||||
type OmitAutoSetFields,
|
||||
} from '../utils/sql.js';
|
||||
|
||||
export default class EmailTemplatesQueries extends SchemaQueries<
|
||||
EmailTemplateKeys,
|
||||
|
@ -50,4 +56,34 @@ export default class EmailTemplatesQueries extends SchemaQueries<
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all email templates
|
||||
*
|
||||
* @param where - Optional where clause to filter email templates by language tag and template type
|
||||
* @param where.languageTag - The language tag of the email template
|
||||
* @param where.templateType - The type of the email template
|
||||
*/
|
||||
async findAllWhere(
|
||||
where?: Partial<Pick<EmailTemplate, 'languageTag' | 'templateType'>>
|
||||
): Promise<readonly EmailTemplate[]> {
|
||||
const { fields, table } = convertToIdentifiers(EmailTemplates);
|
||||
|
||||
return manyRows(
|
||||
this.pool.query<EmailTemplate>(sql`
|
||||
select ${expandFields(EmailTemplates)}
|
||||
from ${table}
|
||||
${conditionalSql(where && Object.keys(where).length > 0 && where, (where) => {
|
||||
return sql`where ${sql.join(
|
||||
Object.entries(where).map(
|
||||
// eslint-disable-next-line no-restricted-syntax -- Object.entries can not infer the key type properly.
|
||||
([key, value]) => sql`${fields[key as keyof EmailTemplate]} = ${value}`
|
||||
),
|
||||
sql` and `
|
||||
)}`;
|
||||
})}
|
||||
order by ${fields.languageTag}, ${fields.templateType}
|
||||
`)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
"items": {
|
||||
"properties": {
|
||||
"languageTag": {
|
||||
"description": "The language tag of the email template, e.g., `en` or `zh-CN`."
|
||||
"description": "The language tag of the email template, e.g., `en` or `fr`."
|
||||
},
|
||||
"templateType": {
|
||||
"description": "The type of the email template, e.g. `SignIn` or `ForgotPassword`"
|
||||
|
@ -61,6 +61,27 @@
|
|||
"description": "The list of newly created or replaced email templates."
|
||||
}
|
||||
}
|
||||
},
|
||||
"get": {
|
||||
"summary": "Get email templates",
|
||||
"description": "Get the list of email templates.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "languageTag",
|
||||
"in": "query",
|
||||
"description": "The language tag of the email template, e.g., `en` or `fr`."
|
||||
},
|
||||
{
|
||||
"name": "templateType",
|
||||
"in": "query",
|
||||
"description": "The type of the email template, e.g. `SignIn` or `ForgotPassword`"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The list of matched email templates. Returns empty list, if no email template is found."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/email-templates/{id}": {
|
||||
|
@ -75,6 +96,18 @@
|
|||
"description": "The email template was not found."
|
||||
}
|
||||
}
|
||||
},
|
||||
"get": {
|
||||
"summary": "Get email template by ID",
|
||||
"description": "Get the email template by its ID.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The email template."
|
||||
},
|
||||
"404": {
|
||||
"description": "The email template was not found."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,14 +31,49 @@ export default function emailTemplateRoutes<T extends ManagementApiRouter>(
|
|||
}),
|
||||
async (ctx, next) => {
|
||||
const { body } = ctx.guard;
|
||||
|
||||
ctx.body = await emailTemplatesQueries.upsertMany(
|
||||
body.templates.map((template) => ({
|
||||
id: generateStandardId(),
|
||||
...template,
|
||||
}))
|
||||
);
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
pathPrefix,
|
||||
koaGuard({
|
||||
query: EmailTemplates.guard
|
||||
.pick({
|
||||
languageTag: true,
|
||||
templateType: true,
|
||||
})
|
||||
.partial(),
|
||||
response: EmailTemplates.guard.array(),
|
||||
status: [200],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { query } = ctx.guard;
|
||||
ctx.body = await emailTemplatesQueries.findAllWhere(query);
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
`${pathPrefix}/:id`,
|
||||
koaGuard({
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: EmailTemplates.guard,
|
||||
status: [200, 404],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
params: { id },
|
||||
} = ctx.guard;
|
||||
ctx.body = await emailTemplatesQueries.findById(id);
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
|
|
@ -12,4 +12,14 @@ export class EmailTemplatesApi {
|
|||
async delete(id: string): Promise<void> {
|
||||
await authedAdminApi.delete(`${path}/${id}`);
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<EmailTemplate> {
|
||||
return authedAdminApi.get(`${path}/${id}`).json<EmailTemplate>();
|
||||
}
|
||||
|
||||
async findAll(
|
||||
where?: Partial<Pick<EmailTemplate, 'languageTag' | 'templateType'>>
|
||||
): Promise<EmailTemplate[]> {
|
||||
return authedAdminApi.get(path, { searchParams: where }).json<EmailTemplate[]>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import { TemplateType } from '@logto/connector-kit';
|
||||
|
||||
import { mockEmailTemplates } from '#src/__mocks__/email-templates.js';
|
||||
import { EmailTemplatesApiTest } from '#src/helpers/email-templates.js';
|
||||
import { expectRejects } from '#src/helpers/index.js';
|
||||
import { devFeatureTest } from '#src/utils.js';
|
||||
|
||||
devFeatureTest.describe('email templates', () => {
|
||||
|
@ -35,4 +38,46 @@ devFeatureTest.describe('email templates', () => {
|
|||
expect(template.details.content).toBe(updatedTemplates[index]!.details.content);
|
||||
}
|
||||
});
|
||||
|
||||
it('should get email templates with query search successfully', async () => {
|
||||
await emailTemplatesApi.create(mockEmailTemplates);
|
||||
|
||||
const templates = await emailTemplatesApi.findAll();
|
||||
expect(templates).toHaveLength(3);
|
||||
|
||||
for (const mockTemplate of mockEmailTemplates) {
|
||||
const template = templates.find(
|
||||
({ languageTag, templateType }) =>
|
||||
languageTag === mockTemplate.languageTag && templateType === mockTemplate.templateType
|
||||
);
|
||||
|
||||
expect(template).toBeDefined();
|
||||
expect(template!.details).toEqual(mockTemplate.details);
|
||||
}
|
||||
|
||||
// Search by language tag
|
||||
const enTemplates = await emailTemplatesApi.findAll({ languageTag: 'en' });
|
||||
expect(enTemplates).toHaveLength(
|
||||
mockEmailTemplates.filter(({ languageTag }) => languageTag === 'en').length
|
||||
);
|
||||
|
||||
// Search by template type
|
||||
const signInTemplates = await emailTemplatesApi.findAll({ templateType: TemplateType.SignIn });
|
||||
expect(signInTemplates).toHaveLength(
|
||||
mockEmailTemplates.filter(({ templateType }) => templateType === TemplateType.SignIn).length
|
||||
);
|
||||
});
|
||||
|
||||
it('should get email template by ID successfully', async () => {
|
||||
const [template] = await emailTemplatesApi.create(mockEmailTemplates);
|
||||
const found = await emailTemplatesApi.findById(template!.id);
|
||||
expect(found).toEqual(template);
|
||||
});
|
||||
|
||||
it('should throw 404 error when email template not found by ID', async () => {
|
||||
await expectRejects(emailTemplatesApi.findById('invalid-id'), {
|
||||
code: 'entity.not_exists_with_id',
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue