mirror of
https://github.com/logto-io/logto.git
synced 2025-02-03 21:48:55 -05:00
Merge pull request #4662 from logto-io/gao-org-apis-6
feat(core): organization - organization role - user apis
This commit is contained in:
commit
a9d657e37c
36 changed files with 287 additions and 10 deletions
|
@ -8,6 +8,7 @@ import {
|
||||||
OrganizationRoleScopeRelations,
|
OrganizationRoleScopeRelations,
|
||||||
Users,
|
Users,
|
||||||
OrganizationUserRelations,
|
OrganizationUserRelations,
|
||||||
|
OrganizationRoleUserRelations,
|
||||||
} from '@logto/schemas';
|
} from '@logto/schemas';
|
||||||
import { type CommonQueryMethods } from 'slonik';
|
import { type CommonQueryMethods } from 'slonik';
|
||||||
|
|
||||||
|
@ -35,6 +36,14 @@ export default class OrganizationQueries extends SchemaQueries<
|
||||||
),
|
),
|
||||||
/** Queries for organization - user relations. */
|
/** Queries for organization - user relations. */
|
||||||
users: new RelationQueries(this.pool, OrganizationUserRelations.table, Organizations, Users),
|
users: new RelationQueries(this.pool, OrganizationUserRelations.table, Organizations, Users),
|
||||||
|
/** Queries for organization - organization role - user relations. */
|
||||||
|
rolesUsers: new RelationQueries(
|
||||||
|
this.pool,
|
||||||
|
OrganizationRoleUserRelations.table,
|
||||||
|
Organizations,
|
||||||
|
OrganizationRoles,
|
||||||
|
Users
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(pool: CommonQueryMethods) {
|
constructor(pool: CommonQueryMethods) {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import { Organizations } from '@logto/schemas';
|
import { OrganizationRoles, Organizations } from '@logto/schemas';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import RequestError from '#src/errors/RequestError/index.js';
|
||||||
|
import koaGuard from '#src/middleware/koa-guard.js';
|
||||||
import SchemaRouter, { SchemaActions } from '#src/utils/SchemaRouter.js';
|
import SchemaRouter, { SchemaActions } from '#src/utils/SchemaRouter.js';
|
||||||
|
|
||||||
import { type AuthedRouter, type RouterInitArgs } from './types.js';
|
import { type AuthedRouter, type RouterInitArgs } from './types.js';
|
||||||
|
@ -8,7 +11,7 @@ export default function organizationRoutes<T extends AuthedRouter>(
|
||||||
...[
|
...[
|
||||||
originalRouter,
|
originalRouter,
|
||||||
{
|
{
|
||||||
queries: { organizations },
|
queries: { organizations, users },
|
||||||
},
|
},
|
||||||
]: RouterInitArgs<T>
|
]: RouterInitArgs<T>
|
||||||
) {
|
) {
|
||||||
|
@ -16,5 +19,77 @@ export default function organizationRoutes<T extends AuthedRouter>(
|
||||||
|
|
||||||
router.addRelationRoutes(organizations.relations.users);
|
router.addRelationRoutes(organizations.relations.users);
|
||||||
|
|
||||||
|
// Manually add these routes since I don't want to over-engineer the `SchemaRouter`
|
||||||
|
// MARK: Organization - user - organization role relation routes
|
||||||
|
const params = Object.freeze({ id: z.string().min(1), userId: z.string().min(1) } as const);
|
||||||
|
const pathname = '/:id/users/:userId/roles';
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
pathname,
|
||||||
|
koaGuard({
|
||||||
|
params: z.object(params),
|
||||||
|
response: OrganizationRoles.guard.array(),
|
||||||
|
status: [200, 404],
|
||||||
|
}),
|
||||||
|
// TODO: Add pagination
|
||||||
|
async (ctx, next) => {
|
||||||
|
const { id, userId } = ctx.guard.params;
|
||||||
|
|
||||||
|
// Ensure both the organization and the role exist
|
||||||
|
await Promise.all([organizations.findById(id), users.findUserById(userId)]);
|
||||||
|
|
||||||
|
ctx.body = await organizations.relations.rolesUsers.getEntries(OrganizationRoles, {
|
||||||
|
organizationId: id,
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
pathname,
|
||||||
|
koaGuard({
|
||||||
|
params: z.object(params),
|
||||||
|
body: z.object({ roleIds: z.string().min(1).array().nonempty() }),
|
||||||
|
status: [201, 404, 422],
|
||||||
|
}),
|
||||||
|
async (ctx, next) => {
|
||||||
|
const { id, userId } = ctx.guard.params;
|
||||||
|
const { roleIds } = ctx.guard.body;
|
||||||
|
|
||||||
|
// Ensure membership
|
||||||
|
if (!(await organizations.relations.users.exists(id, userId))) {
|
||||||
|
throw new RequestError({ code: 'organization.require_membership', status: 422 });
|
||||||
|
}
|
||||||
|
|
||||||
|
await organizations.relations.rolesUsers.insert(
|
||||||
|
...roleIds.map<[string, string, string]>((roleId) => [id, roleId, userId])
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.status = 201;
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
`${pathname}/:roleId`,
|
||||||
|
koaGuard({
|
||||||
|
params: z.object({ ...params, roleId: z.string().min(1) }),
|
||||||
|
status: [204, 404],
|
||||||
|
}),
|
||||||
|
async (ctx, next) => {
|
||||||
|
const { id, roleId, userId } = ctx.guard.params;
|
||||||
|
|
||||||
|
await organizations.relations.rolesUsers.delete({
|
||||||
|
organizationId: id,
|
||||||
|
organizationRoleId: roleId,
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.status = 204;
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
originalRouter.use(router.routes());
|
originalRouter.use(router.routes());
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,9 @@ export default class RelationQueries<
|
||||||
* Each entry must contain the same number of ids as the number of relations, and
|
* Each entry must contain the same number of ids as the number of relations, and
|
||||||
* the order of the ids must match the order of the relations.
|
* the order of the ids must match the order of the relations.
|
||||||
*
|
*
|
||||||
|
* @param data Entries to insert.
|
||||||
|
* @returns A Promise that resolves to the query result.
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* const userGroupRelations = new RelationQueries(pool, 'user_group_relations', Users, Groups);
|
* const userGroupRelations = new RelationQueries(pool, 'user_group_relations', Users, Groups);
|
||||||
|
@ -91,9 +94,6 @@ export default class RelationQueries<
|
||||||
* ['user-id-2', 'group-id-1']
|
* ['user-id-2', 'group-id-1']
|
||||||
* );
|
* );
|
||||||
* ```
|
* ```
|
||||||
*
|
|
||||||
* @param data Entries to insert.
|
|
||||||
* @returns A Promise that resolves to the query result.
|
|
||||||
*/
|
*/
|
||||||
async insert(...data: ReadonlyArray<string[] & { length: Length }>) {
|
async insert(...data: ReadonlyArray<string[] & { length: Length }>) {
|
||||||
return this.pool.query(sql`
|
return this.pool.query(sql`
|
||||||
|
@ -179,4 +179,32 @@ export default class RelationQueries<
|
||||||
|
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a relation exists.
|
||||||
|
*
|
||||||
|
* @param ids The ids of the entries to check. The order of the ids must match the order of the relations.
|
||||||
|
* @returns A Promise that resolves to `true` if the relation exists, otherwise `false`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const userGroupRelations = new RelationQueries(pool, 'user_group_relations', Users, Groups);
|
||||||
|
*
|
||||||
|
* userGroupRelations.exists('user-id-1', 'group-id-1');
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async exists(...ids: readonly string[] & { length: Length }) {
|
||||||
|
return this.pool.exists(sql`
|
||||||
|
select
|
||||||
|
from ${this.table}
|
||||||
|
where ${sql.join(
|
||||||
|
this.schemas.map(
|
||||||
|
({ tableSingular }, index) =>
|
||||||
|
sql`${sql.identifier([tableSingular + '_id'])} = ${ids[index] ?? sql`null`}`
|
||||||
|
),
|
||||||
|
sql` and `
|
||||||
|
)}
|
||||||
|
limit 1
|
||||||
|
`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -315,8 +315,7 @@ export default class SchemaRouter<
|
||||||
koaGuard({
|
koaGuard({
|
||||||
params: z.object({ id: z.string().min(1) }),
|
params: z.object({ id: z.string().min(1) }),
|
||||||
body: z.object({ [columns.relationSchemaIds]: z.string().min(1).array().nonempty() }),
|
body: z.object({ [columns.relationSchemaIds]: z.string().min(1).array().nonempty() }),
|
||||||
response: relationSchema.guard.array(),
|
status: [201, 404, 422],
|
||||||
status: [200, 404, 422],
|
|
||||||
}),
|
}),
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
const {
|
const {
|
||||||
|
@ -327,8 +326,7 @@ export default class SchemaRouter<
|
||||||
await relationQueries.insert(
|
await relationQueries.insert(
|
||||||
...(relationIds?.map<[string, string]>((relationId) => [id, relationId]) ?? [])
|
...(relationIds?.map<[string, string]>((relationId) => [id, relationId]) ?? [])
|
||||||
);
|
);
|
||||||
|
ctx.status = 201;
|
||||||
ctx.body = await relationQueries.getEntries(relationSchema, { [columns.schemaId]: id });
|
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { type Organization } from '@logto/schemas';
|
import { type Role, type Organization } from '@logto/schemas';
|
||||||
|
|
||||||
import { authedAdminApi } from './api.js';
|
import { authedAdminApi } from './api.js';
|
||||||
import { ApiFactory } from './factory.js';
|
import { ApiFactory } from './factory.js';
|
||||||
|
@ -19,6 +19,18 @@ class OrganizationApi extends ApiFactory<Organization, { name: string; descripti
|
||||||
async deleteUser(id: string, userId: string): Promise<void> {
|
async deleteUser(id: string, userId: string): Promise<void> {
|
||||||
await authedAdminApi.delete(`${this.path}/${id}/users/${userId}`);
|
await authedAdminApi.delete(`${this.path}/${id}/users/${userId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async addUserRoles(id: string, userId: string, roleIds: string[]): Promise<void> {
|
||||||
|
await authedAdminApi.post(`${this.path}/${id}/users/${userId}/roles`, { json: { roleIds } });
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserRoles(id: string, userId: string): Promise<Role[]> {
|
||||||
|
return authedAdminApi.get(`${this.path}/${id}/users/${userId}/roles`).json<Role[]>();
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteUserRole(id: string, userId: string, roleId: string): Promise<void> {
|
||||||
|
await authedAdminApi.delete(`${this.path}/${id}/users/${userId}/roles/${roleId}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** API methods for operating organizations. */
|
/** API methods for operating organizations. */
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
import assert from 'node:assert';
|
||||||
|
|
||||||
import { generateStandardId } from '@logto/shared';
|
import { generateStandardId } from '@logto/shared';
|
||||||
import { HTTPError } from 'got';
|
import { HTTPError } from 'got';
|
||||||
|
|
||||||
import { createUser, deleteUser } from '#src/api/admin-user.js';
|
import { createUser, deleteUser } from '#src/api/admin-user.js';
|
||||||
|
import { roleApi } from '#src/api/organization-role.js';
|
||||||
import { organizationApi } from '#src/api/organization.js';
|
import { organizationApi } from '#src/api/organization.js';
|
||||||
|
|
||||||
const randomId = () => generateStandardId(4);
|
const randomId = () => generateStandardId(4);
|
||||||
|
@ -123,4 +126,37 @@ describe('organization APIs', () => {
|
||||||
await Promise.all([organizationApi.delete(organization.id), deleteUser(user.id)]);
|
await Promise.all([organizationApi.delete(organization.id), deleteUser(user.id)]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('organization - user - organization role relation routes', () => {
|
||||||
|
it("should be able to add and get user's organization roles", async () => {
|
||||||
|
const organization = await organizationApi.create({ name: 'test' });
|
||||||
|
const user = await createUser({ username: 'test' + randomId() });
|
||||||
|
const [role1, role2] = await Promise.all([
|
||||||
|
roleApi.create({ name: 'test' + randomId() }),
|
||||||
|
roleApi.create({ name: 'test' + randomId() }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const response = await organizationApi
|
||||||
|
.addUserRoles(organization.id, user.id, [role1.id, role2.id])
|
||||||
|
.catch((error: unknown) => error);
|
||||||
|
|
||||||
|
assert(response instanceof HTTPError);
|
||||||
|
expect(response.response.statusCode).toBe(422);
|
||||||
|
expect(JSON.parse(String(response.response.body))).toMatchObject(
|
||||||
|
expect.objectContaining({ code: 'organization.require_membership' })
|
||||||
|
);
|
||||||
|
|
||||||
|
await organizationApi.addUsers(organization.id, [user.id]);
|
||||||
|
await organizationApi.addUserRoles(organization.id, user.id, [role1.id, role2.id]);
|
||||||
|
const roles = await organizationApi.getUserRoles(organization.id, user.id);
|
||||||
|
expect(roles).toContainEqual(expect.objectContaining({ id: role1.id }));
|
||||||
|
expect(roles).toContainEqual(expect.objectContaining({ id: role2.id }));
|
||||||
|
await Promise.all([
|
||||||
|
organizationApi.delete(organization.id),
|
||||||
|
deleteUser(user.id),
|
||||||
|
roleApi.delete(role1.id),
|
||||||
|
roleApi.delete(role2.id),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
6
packages/phrases/src/locales/de/errors/organization.ts
Normal file
6
packages/phrases/src/locales/de/errors/organization.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
5
packages/phrases/src/locales/en/errors/organization.ts
Normal file
5
packages/phrases/src/locales/en/errors/organization.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
const organization = {
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
6
packages/phrases/src/locales/es/errors/organization.ts
Normal file
6
packages/phrases/src/locales/es/errors/organization.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
6
packages/phrases/src/locales/fr/errors/organization.ts
Normal file
6
packages/phrases/src/locales/fr/errors/organization.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
6
packages/phrases/src/locales/it/errors/organization.ts
Normal file
6
packages/phrases/src/locales/it/errors/organization.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
6
packages/phrases/src/locales/ja/errors/organization.ts
Normal file
6
packages/phrases/src/locales/ja/errors/organization.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
6
packages/phrases/src/locales/ko/errors/organization.ts
Normal file
6
packages/phrases/src/locales/ko/errors/organization.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
6
packages/phrases/src/locales/ru/errors/organization.ts
Normal file
6
packages/phrases/src/locales/ru/errors/organization.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
|
@ -8,6 +8,7 @@ import hook from './hook.js';
|
||||||
import localization from './localization.js';
|
import localization from './localization.js';
|
||||||
import log from './log.js';
|
import log from './log.js';
|
||||||
import oidc from './oidc.js';
|
import oidc from './oidc.js';
|
||||||
|
import organization from './organization.js';
|
||||||
import password from './password.js';
|
import password from './password.js';
|
||||||
import request from './request.js';
|
import request from './request.js';
|
||||||
import resource from './resource.js';
|
import resource from './resource.js';
|
||||||
|
@ -44,6 +45,7 @@ const errors = {
|
||||||
domain,
|
domain,
|
||||||
subscription,
|
subscription,
|
||||||
application,
|
application,
|
||||||
|
organization,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Object.freeze(errors);
|
export default Object.freeze(errors);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
const organization = {
|
||||||
|
/** UNTRANSLATED */
|
||||||
|
require_membership: 'The user must be a member of the organization to proceed.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Object.freeze(organization);
|
Loading…
Add table
Reference in a new issue