mirror of
https://github.com/graphql-hive/console
synced 2026-04-21 14:37:17 +00:00
fix(billing): only allow PRO plan to self-update their plan limits (#7867)
This commit is contained in:
parent
3330308ef5
commit
ebe8df69fb
4 changed files with 81 additions and 2 deletions
|
|
@ -53,7 +53,12 @@ import {
|
|||
updateTargetValidationSettings,
|
||||
} from './flow';
|
||||
import * as GraphQLSchema from './gql/graphql';
|
||||
import { ProjectType, SchemaPolicyInput, TargetAccessScope } from './gql/graphql';
|
||||
import {
|
||||
ProjectType,
|
||||
SchemaPolicyInput,
|
||||
TargetAccessScope,
|
||||
UpdateOrgRateLimitDocument,
|
||||
} from './gql/graphql';
|
||||
import { execute } from './graphql';
|
||||
import { createOIDCIntegration } from './oidc-integration.js';
|
||||
import {
|
||||
|
|
@ -197,6 +202,31 @@ export function initSeed() {
|
|||
|
||||
return {
|
||||
organization,
|
||||
async overrideOrgPlan(plan: 'PRO' | 'ENTERPRISE' | 'HOBBY') {
|
||||
const pool = await createConnectionPool();
|
||||
|
||||
await pool.query(sql`
|
||||
UPDATE organizations SET plan_name = ${plan} WHERE id = ${organization.id}
|
||||
`);
|
||||
|
||||
await pool.end();
|
||||
},
|
||||
async updateOrgRateLimit(newLimit: number, token = ownerToken) {
|
||||
const result = await execute({
|
||||
document: UpdateOrgRateLimitDocument,
|
||||
variables: {
|
||||
selector: {
|
||||
organizationSlug: organization.slug,
|
||||
},
|
||||
monthlyLimits: {
|
||||
operations: newLimit,
|
||||
},
|
||||
},
|
||||
authToken: token,
|
||||
}).then(r => r.expectNoGraphQLErrors());
|
||||
|
||||
return result.updateOrgRateLimit;
|
||||
},
|
||||
async createOrganizationAccessToken(
|
||||
args: {
|
||||
permissions: Array<string>;
|
||||
|
|
|
|||
38
integration-tests/tests/api/commerce/plan.spec.ts
Normal file
38
integration-tests/tests/api/commerce/plan.spec.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { initSeed } from '../../../testkit/seed';
|
||||
|
||||
test.concurrent(
|
||||
'should not allow HOBBY organization to use updateOrgRateLimit mutation',
|
||||
async ({ expect }) => {
|
||||
const { createOrg } = await initSeed().createOwner();
|
||||
const { updateOrgRateLimit, overrideOrgPlan } = await createOrg();
|
||||
|
||||
await expect(updateOrgRateLimit(50000000)).rejects.toThrowError(
|
||||
'Only PRO organizations can update rate limits via API',
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.concurrent(
|
||||
'should not allow ENTERPRISE organization to use updateOrgRateLimit mutation',
|
||||
async ({ expect }) => {
|
||||
const { createOrg } = await initSeed().createOwner();
|
||||
const { updateOrgRateLimit, overrideOrgPlan } = await createOrg();
|
||||
await overrideOrgPlan('ENTERPRISE');
|
||||
|
||||
await expect(updateOrgRateLimit(50000000)).rejects.toThrowError(
|
||||
'Only PRO organizations can update rate limits via API',
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.concurrent(
|
||||
'should only allow PRO organization to use updateOrgRateLimit mutation',
|
||||
async ({ expect }) => {
|
||||
const { createOrg } = await initSeed().createOwner();
|
||||
const { overrideOrgPlan, updateOrgRateLimit } = await createOrg();
|
||||
|
||||
await overrideOrgPlan('PRO');
|
||||
// Not throwing - should succeed
|
||||
await updateOrgRateLimit(50000000);
|
||||
},
|
||||
);
|
||||
|
|
@ -14,7 +14,8 @@ function filterEmailsByOrg(orgSlug: string, emails: emails.Email[]) {
|
|||
|
||||
test('rate limit approaching and reached for organization', async () => {
|
||||
const { createOrg, ownerToken, ownerEmail } = await initSeed().createOwner();
|
||||
const { createProject, organization } = await createOrg();
|
||||
const { createProject, organization, overrideOrgPlan } = await createOrg();
|
||||
await overrideOrgPlan('PRO');
|
||||
const { createTargetAccessToken, waitForRequestsCollected } = await createProject(
|
||||
ProjectType.Single,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { HiveError } from '../../../../shared/errors';
|
||||
import { OrganizationManager } from '../../../organization/providers/organization-manager';
|
||||
import { IdTranslator } from '../../../shared/providers/id-translator';
|
||||
import { Storage } from '../../../shared/providers/storage';
|
||||
import { USAGE_DEFAULT_LIMITATIONS } from '../../constants';
|
||||
import type { MutationResolvers } from './../../../../__generated__/types';
|
||||
|
||||
|
|
@ -12,6 +14,14 @@ export const updateOrgRateLimit: NonNullable<MutationResolvers['updateOrgRateLim
|
|||
organizationSlug: args.selector.organizationSlug,
|
||||
});
|
||||
|
||||
const organization = await injector.get(Storage).getOrganization({
|
||||
organizationId: organizationId,
|
||||
});
|
||||
|
||||
if (organization.billingPlan !== 'PRO') {
|
||||
throw new HiveError('Only PRO organizations can update rate limits via API');
|
||||
}
|
||||
|
||||
return injector.get(OrganizationManager).updateRateLimits({
|
||||
organizationId: organizationId,
|
||||
monthlyRateLimit: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue