mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
chore: add response guard and integration tests for application APIs (#3771)
This commit is contained in:
parent
d2e6e1fd5b
commit
0e46ddacca
4 changed files with 47 additions and 16 deletions
|
@ -169,6 +169,7 @@ describe('application route', () => {
|
||||||
'http://127.0.0.1',
|
'http://127.0.0.1',
|
||||||
'http://localhost:3002',
|
'http://localhost:3002',
|
||||||
],
|
],
|
||||||
|
postLogoutRedirectUris: [],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
).resolves.toHaveProperty('status', 200);
|
).resolves.toHaveProperty('status', 200);
|
||||||
|
@ -179,6 +180,7 @@ describe('application route', () => {
|
||||||
applicationRequest.patch('/applications/foo').send({
|
applicationRequest.patch('/applications/foo').send({
|
||||||
oidcClientMetadata: {
|
oidcClientMetadata: {
|
||||||
redirectUris: ['www.example.com', 'com.example://callback'],
|
redirectUris: ['www.example.com', 'com.example://callback'],
|
||||||
|
postLogoutRedirectUris: [],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
).resolves.toHaveProperty('status', 400);
|
).resolves.toHaveProperty('status', 400);
|
||||||
|
@ -194,6 +196,7 @@ describe('application route', () => {
|
||||||
'com.example://callback',
|
'com.example://callback',
|
||||||
'io.logto://Abc123',
|
'io.logto://Abc123',
|
||||||
],
|
],
|
||||||
|
postLogoutRedirectUris: [],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
).resolves.toHaveProperty('status', 200);
|
).resolves.toHaveProperty('status', 200);
|
||||||
|
@ -205,6 +208,7 @@ describe('application route', () => {
|
||||||
type: ApplicationType.Native,
|
type: ApplicationType.Native,
|
||||||
oidcClientMetadata: {
|
oidcClientMetadata: {
|
||||||
redirectUris: ['https://www.example.com', 'com.example/callback'],
|
redirectUris: ['https://www.example.com', 'com.example/callback'],
|
||||||
|
postLogoutRedirectUris: [],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
).resolves.toHaveProperty('status', 400);
|
).resolves.toHaveProperty('status', 400);
|
||||||
|
|
|
@ -35,20 +35,25 @@ export default function applicationRoutes<T extends AuthedRouter>(
|
||||||
queries.applicationsRoles;
|
queries.applicationsRoles;
|
||||||
const { findRoleByRoleName } = queries.roles;
|
const { findRoleByRoleName } = queries.roles;
|
||||||
|
|
||||||
router.get('/applications', koaPagination(), async (ctx, next) => {
|
router.get(
|
||||||
const { limit, offset } = ctx.pagination;
|
'/applications',
|
||||||
|
koaPagination(),
|
||||||
|
koaGuard({ response: z.array(Applications.guard), status: 200 }),
|
||||||
|
async (ctx, next) => {
|
||||||
|
const { limit, offset } = ctx.pagination;
|
||||||
|
|
||||||
const [{ count }, applications] = await Promise.all([
|
const [{ count }, applications] = await Promise.all([
|
||||||
findTotalNumberOfApplications(),
|
findTotalNumberOfApplications(),
|
||||||
findAllApplications(limit, offset),
|
findAllApplications(limit, offset),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Return totalCount to pagination middleware
|
// Return totalCount to pagination middleware
|
||||||
ctx.pagination.totalCount = count;
|
ctx.pagination.totalCount = count;
|
||||||
ctx.body = applications;
|
ctx.body = applications;
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/applications',
|
'/applications',
|
||||||
|
@ -57,6 +62,8 @@ export default function applicationRoutes<T extends AuthedRouter>(
|
||||||
.omit({ id: true, createdAt: true })
|
.omit({ id: true, createdAt: true })
|
||||||
.partial()
|
.partial()
|
||||||
.merge(Applications.createGuard.pick({ name: true, type: true })),
|
.merge(Applications.createGuard.pick({ name: true, type: true })),
|
||||||
|
response: Applications.guard,
|
||||||
|
status: [200, 422],
|
||||||
}),
|
}),
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
const { oidcClientMetadata, ...rest } = ctx.guard.body;
|
const { oidcClientMetadata, ...rest } = ctx.guard.body;
|
||||||
|
@ -77,6 +84,7 @@ export default function applicationRoutes<T extends AuthedRouter>(
|
||||||
koaGuard({
|
koaGuard({
|
||||||
params: object({ id: string().min(1) }),
|
params: object({ id: string().min(1) }),
|
||||||
response: Applications.guard.merge(z.object({ isAdmin: z.boolean() })),
|
response: Applications.guard.merge(z.object({ isAdmin: z.boolean() })),
|
||||||
|
status: [200, 404],
|
||||||
}),
|
}),
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
const {
|
const {
|
||||||
|
@ -114,6 +122,8 @@ export default function applicationRoutes<T extends AuthedRouter>(
|
||||||
isAdmin: boolean().optional(),
|
isAdmin: boolean().optional(),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
response: Applications.guard,
|
||||||
|
status: [200, 404, 500],
|
||||||
}),
|
}),
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
const {
|
const {
|
||||||
|
@ -135,7 +145,11 @@ export default function applicationRoutes<T extends AuthedRouter>(
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
internalAdminRole,
|
internalAdminRole,
|
||||||
new RequestError('entity.not_exists', { name: InternalRole.Admin })
|
new RequestError({
|
||||||
|
code: 'entity.not_exists',
|
||||||
|
status: 500,
|
||||||
|
data: { name: InternalRole.Admin },
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isAdmin && !usedToBeAdmin) {
|
if (isAdmin && !usedToBeAdmin) {
|
||||||
|
@ -155,7 +169,11 @@ export default function applicationRoutes<T extends AuthedRouter>(
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
'/applications/:id',
|
'/applications/:id',
|
||||||
koaGuard({ params: object({ id: string().min(1) }) }),
|
koaGuard({
|
||||||
|
params: object({ id: string().min(1) }),
|
||||||
|
response: z.undefined(),
|
||||||
|
status: [204, 404],
|
||||||
|
}),
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
const { id } = ctx.guard.params;
|
const { id } = ctx.guard.params;
|
||||||
// Note: will need delete cascade when application is joint with other tables
|
// Note: will need delete cascade when application is joint with other tables
|
||||||
|
|
|
@ -17,6 +17,8 @@ export const createApplication = async (name: string, type: ApplicationType) =>
|
||||||
})
|
})
|
||||||
.json<Application>();
|
.json<Application>();
|
||||||
|
|
||||||
|
export const getApplications = async () => authedAdminApi.get('applications').json<Application[]>();
|
||||||
|
|
||||||
export const getApplication = async (applicationId: string) =>
|
export const getApplication = async (applicationId: string) =>
|
||||||
authedAdminApi.get(`applications/${applicationId}`).json<Application>();
|
authedAdminApi.get(`applications/${applicationId}`).json<Application>();
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,13 @@ import {
|
||||||
getApplication,
|
getApplication,
|
||||||
updateApplication,
|
updateApplication,
|
||||||
deleteApplication,
|
deleteApplication,
|
||||||
|
getApplications,
|
||||||
} from '#src/api/index.js';
|
} from '#src/api/index.js';
|
||||||
|
|
||||||
describe('admin console application', () => {
|
describe('admin console application', () => {
|
||||||
it('should create application successfully', async () => {
|
it('should create application successfully', async () => {
|
||||||
const applicationName = 'test-create-app';
|
const applicationName = 'test-create-app';
|
||||||
const applicationType = ApplicationType.SPA;
|
const applicationType = ApplicationType.SPA;
|
||||||
|
|
||||||
const application = await createApplication(applicationName, applicationType);
|
const application = await createApplication(applicationName, applicationType);
|
||||||
|
|
||||||
expect(application.name).toBe(applicationName);
|
expect(application.name).toBe(applicationName);
|
||||||
|
@ -25,7 +25,7 @@ describe('admin console application', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update application details successfully', async () => {
|
it('should update application details successfully', async () => {
|
||||||
const application = await createApplication('test-update-app', ApplicationType.SPA);
|
const application = await createApplication('test-update-app', ApplicationType.Traditional);
|
||||||
|
|
||||||
const newApplicationDescription = `new_${application.description ?? ''}`;
|
const newApplicationDescription = `new_${application.description ?? ''}`;
|
||||||
|
|
||||||
|
@ -46,8 +46,15 @@ describe('admin console application', () => {
|
||||||
expect(updatedApplication.oidcClientMetadata.redirectUris).toEqual(newRedirectUris);
|
expect(updatedApplication.oidcClientMetadata.redirectUris).toEqual(newRedirectUris);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fetch all applications created above', async () => {
|
||||||
|
const applications = await getApplications();
|
||||||
|
const applicationNames = applications.map(({ name }) => name);
|
||||||
|
expect(applicationNames).toContain('test-create-app');
|
||||||
|
expect(applicationNames).toContain('test-update-app');
|
||||||
|
});
|
||||||
|
|
||||||
it('should delete application successfully', async () => {
|
it('should delete application successfully', async () => {
|
||||||
const application = await createApplication('test-delete-app', ApplicationType.SPA);
|
const application = await createApplication('test-delete-app', ApplicationType.Native);
|
||||||
|
|
||||||
await deleteApplication(application.id);
|
await deleteApplication(application.id);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue