Skip to content

Commit

Permalink
refactor: update code according to CR
Browse files Browse the repository at this point in the history
  • Loading branch information
darcyYe committed Jul 21, 2024
1 parent ae9b5ec commit fcae3dc
Show file tree
Hide file tree
Showing 11 changed files with 45 additions and 27 deletions.
33 changes: 22 additions & 11 deletions packages/core/src/libraries/quota.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,7 @@ export const createQuotaLibrary = (
}
};

// `SubscriptionQuota` and `SubscriptionUsage` are sharing keys.
const newGuardKey = async (key: keyof SubscriptionQuota) => {
const guardTenantUsageByKey = async (key: keyof SubscriptionQuota) => {
const { isCloud, isIntegrationTest } = EnvSet.values;

// Cloud only feature, skip in non-cloud environments
Expand All @@ -160,6 +159,8 @@ export const createQuotaLibrary = (
const { quota: fullQuota, usage: fullUsage } = await getTenantSubscriptionQuotaAndUsage(
cloudConnection
);

// Type `SubscriptionQuota` and type `SubscriptionUsage` are sharing keys, this design helps us to compare the usage with the quota limit in a easier way.
const { [key]: limit } = fullQuota;
const { [key]: usage } = fullUsage;

Expand All @@ -178,7 +179,10 @@ export const createQuotaLibrary = (
},
})
);
} else if (typeof limit === 'number') {
return;
}

if (typeof limit === 'number') {
// See the definition of `SubscriptionQuota` and `SubscriptionUsage` in `types.ts`, this should never happen.
assertThat(
typeof usage === 'number',
Expand All @@ -197,12 +201,14 @@ export const createQuotaLibrary = (
},
})
);
} else {
throw new TypeError('Unsupported subscription quota type');

return;
}

throw new TypeError('Unsupported subscription quota type');
};

const scopesGuardKey = async (entityName: 'resources' | 'roles', entityId: string) => {
const guardEntityScopesUsage = async (entityName: 'resources' | 'roles', entityId: string) => {
const { isCloud, isIntegrationTest } = EnvSet.values;

// Cloud only feature, skip in non-cloud environments
Expand All @@ -215,10 +221,15 @@ export const createQuotaLibrary = (
return;
}

const {
quota: { scopesPerResourceLimit, scopesPerRoleLimit },
} = await getTenantSubscriptionQuotaAndUsage(cloudConnection);
const scopeUsages = await getTenantSubscriptionScopeUsage(cloudConnection, entityName);
const [
{
quota: { scopesPerResourceLimit, scopesPerRoleLimit },
},
scopeUsages,
] = await Promise.all([
getTenantSubscriptionQuotaAndUsage(cloudConnection),
getTenantSubscriptionScopeUsage(cloudConnection, entityName),
]);
const usage = scopeUsages[entityId] ?? 0;

if (entityName === 'resources') {
Expand Down Expand Up @@ -251,5 +262,5 @@ export const createQuotaLibrary = (
);
};

return { guardKey, newGuardKey, scopesGuardKey };
return { guardKey, guardTenantUsageByKey, guardEntityScopesUsage };
};
2 changes: 1 addition & 1 deletion packages/core/src/middleware/koa-quota-guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function newKoaQuotaGuard<StateT, ContextT, ResponseBodyT>({
return async (ctx, next) => {
// eslint-disable-next-line no-restricted-syntax
if (!methods || methods.includes(ctx.method.toUpperCase() as Method)) {
await quota.newGuardKey(key);
await quota.guardTenantUsageByKey(key);
}
return next();
};
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/routes/applications/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,19 +171,19 @@ export default function applicationRoutes<T extends ManagementApiRouter>(
// When creating a m2m app, should check both m2m limit and application limit.
if (rest.type === ApplicationType.MachineToMachine) {
await (EnvSet.values.isDevFeaturesEnabled
? quota.newGuardKey('machineToMachineLimit')
? quota.guardTenantUsageByKey('machineToMachineLimit')
: quota.guardKey('machineToMachineLimit'));
}

// Guard third party application limit
if (rest.isThirdParty) {
await (EnvSet.values.isDevFeaturesEnabled
? quota.newGuardKey('thirdPartyApplicationsLimit')
? quota.guardTenantUsageByKey('thirdPartyApplicationsLimit')
: quota.guardKey('thirdPartyApplicationsLimit'));
}

await (EnvSet.values.isDevFeaturesEnabled
? quota.newGuardKey('applicationsLimit')
? quota.guardTenantUsageByKey('applicationsLimit')
: quota.guardKey('applicationsLimit'));

assertThat(
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/routes/connector/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const guardConnectorsQuota = async (
) => {
if (factory.type === ConnectorType.Social) {
await (EnvSet.values.isDevFeaturesEnabled
? quota.newGuardKey('socialConnectorsLimit')
? quota.guardTenantUsageByKey('socialConnectorsLimit')
: quota.guardKey('socialConnectorsLimit'));
}
};
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/routes/resource.scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export default function resourceScopeRoutes<T extends ManagementApiRouter>(
} = ctx.guard;

await (EnvSet.values.isDevFeaturesEnabled
? quota.scopesGuardKey('resources', resourceId)
? quota.guardEntityScopesUsage('resources', resourceId)
: quota.guardKey('scopesPerResourceLimit', resourceId));

assertThat(!/\s/.test(body.name), 'scope.name_with_space');
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/routes/role.scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export default function roleScopeRoutes<T extends ManagementApiRouter>(
} = ctx.guard;

await (EnvSet.values.isDevFeaturesEnabled
? quota.scopesGuardKey('roles', id)
? quota.guardEntityScopesUsage('roles', id)
: quota.guardKey('scopesPerRoleLimit', id));

await validateRoleScopeAssignment(scopeIds, id);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/routes/role.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export default function roleRoutes<T extends ManagementApiRouter>(
// We have optional `type` when creating a new role, if `type` is not provided, use `User` as default.
// `machineToMachineRolesLimit` is the limit of machine to machine roles, and is independent to `rolesLimit`.
await (EnvSet.values.isDevFeaturesEnabled
? quota.newGuardKey(
? quota.guardTenantUsageByKey(
roleBody.type === RoleType.MachineToMachine
? 'machineToMachineRolesLimit'
: // In new pricing model, we rename `rolesLimit` to `userRolesLimit`, which is easier to be distinguished from `machineToMachineRolesLimit`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { object, z } from 'zod';
import { EnvSet } from '#src/env-set/index.js';
import RequestError from '#src/errors/RequestError/index.js';
import koaGuard from '#src/middleware/koa-guard.js';
import koaQuotaGuard from '#src/middleware/koa-quota-guard.js';
import koaQuotaGuard, { newKoaQuotaGuard } from '#src/middleware/koa-quota-guard.js';
import SystemContext from '#src/tenants/SystemContext.js';
import assertThat from '#src/utils/assert-that.js';
import { getConsoleLogFromContext } from '#src/utils/console.js';
Expand All @@ -35,7 +35,11 @@ export default function customUiAssetsRoutes<T extends ManagementApiRouter>(

router.post(
'/sign-in-exp/default/custom-ui-assets',
koaQuotaGuard({ key: 'bringYourUiEnabled', quota }),
// Manually add this to avoid the case that the dev feature guard is removed but the quota guard is not being updated accordingly.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
EnvSet.values.isDevFeaturesEnabled
? newKoaQuotaGuard({ key: 'bringYourUiEnabled', quota })
: koaQuotaGuard({ key: 'bringYourUiEnabled', quota }),
koaGuard({
files: object({
file: uploadFileGuard.array().min(1).max(1),
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/routes/sign-in-experience/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function signInExperiencesRoutes<T extends ManagementApiRouter>(
const { deleteConnectorById } = queries.connectors;
const {
signInExperiences: { validateLanguageInfo },
quota: { guardKey, newGuardKey },
quota: { guardKey, guardTenantUsageByKey },
} = libraries;
const { getLogtoConnectors } = connectors;

Expand Down Expand Up @@ -92,7 +92,7 @@ export default function signInExperiencesRoutes<T extends ManagementApiRouter>(
if (mfa) {
if (mfa.factors.length > 0) {
await (EnvSet.values.isDevFeaturesEnabled
? newGuardKey('mfaEnabled')
? guardTenantUsageByKey('mfaEnabled')
: guardKey('mfaEnabled'));
}
validateMfa(mfa);
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/routes/subject-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { addSeconds } from 'date-fns';
import { object, string } from 'zod';

import { subjectTokenExpiresIn, subjectTokenPrefix } from '#src/constants/index.js';
import { EnvSet } from '#src/env-set/index.js';
import koaGuard from '#src/middleware/koa-guard.js';
import { newKoaQuotaGuard } from '#src/middleware/koa-quota-guard.js';
import koaQuotaGuard, { newKoaQuotaGuard } from '#src/middleware/koa-quota-guard.js';

import { type RouterInitArgs, type ManagementApiRouter } from './types.js';

Expand All @@ -25,7 +26,9 @@ export default function subjectTokenRoutes<T extends ManagementApiRouter>(

router.post(
'/subject-tokens',
newKoaQuotaGuard({ key: 'subjectTokenEnabled', quota }),
EnvSet.values.isDevFeaturesEnabled
? newKoaQuotaGuard({ key: 'subjectTokenEnabled', quota })
: koaQuotaGuard({ key: 'subjectTokenEnabled', quota }),
koaGuard({
body: object({
userId: string(),
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/test-utils/quota.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { jest } = import.meta;
export const createMockQuotaLibrary = (): QuotaLibrary => {
return {
guardKey: jest.fn(),
newGuardKey: jest.fn(),
scopesGuardKey: jest.fn(),
guardTenantUsageByKey: jest.fn(),
guardEntityScopesUsage: jest.fn(),
};
};

0 comments on commit fcae3dc

Please sign in to comment.