Skip to content

Commit e13913d

Browse files
committed
feat: add feature flag for analysis tools and extract shared code to library
1 parent 21d8e29 commit e13913d

40 files changed

Lines changed: 187 additions & 130 deletions

apps/api/src/app/db/feature-flags.db.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,15 @@ export async function checkFeatureFlag({ userId, key }: { userId: string; key: F
5656
const flags = await resolveFeatureFlagsForUser({ userId, teamId });
5757
return flags[key];
5858
}
59+
60+
/**
61+
* Force a flag on for a single user by upserting a user-scoped override. Intended for test setup so
62+
* flag-gated features can be exercised as if rolled out; production rollout is managed via overrides.
63+
*/
64+
export async function enableFeatureFlagForUser({ userId, key }: { userId: string; key: FeatureFlagKey }): Promise<void> {
65+
await prisma.featureFlagOverride.upsert({
66+
where: { uniqueFeatureFlagUser: { key, userId } },
67+
create: { key, userId, enabled: true },
68+
update: { enabled: true },
69+
});
70+
}

apps/api/src/app/db/subscription.db.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@ export const updateUserEntitlements = async (customerId: string, entitlementAcce
4949
});
5050
};
5151

52+
/**
53+
* Force-grant only the Analysis Tools entitlement to a user, leaving the other entitlement flags
54+
* untouched. Intended for test setup so the flag-gated, paid-only Analysis Tools render as usable
55+
* (unlocked) without provisioning a full paid subscription.
56+
*/
57+
export const grantAnalysisToolsEntitlementForUser = async (userId: string): Promise<void> => {
58+
await prisma.entitlement.upsert({
59+
create: { userId, analysisTools: true },
60+
update: { analysisTools: true },
61+
where: { userId },
62+
});
63+
};
64+
5265
export const updateTeamEntitlements = async (customerId: string, entitlementAccessUntrusted: EntitlementsAccess) => {
5366
const entitlementAccess = EntitlementsAccessSchema.parse(entitlementAccessUntrusted);
5467
const team = await prisma.team.findFirstOrThrow({

apps/api/src/app/routes/test.routes.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { ApiConnection, getApiRequestFactoryFn } from '@jetstream/salesforce-api
44
import { salesforceLoginJwtBearer } from '@jetstream/salesforce-oauth';
55
import express, { Router } from 'express';
66
import { initConnectionFromOAuthResponse } from '../controllers/oauth.controller';
7+
import { enableFeatureFlagForUser } from '../db/feature-flags.db';
8+
import { grantAnalysisToolsEntitlementForUser } from '../db/subscription.db';
79
import { NotAllowedError } from '../utils/error-handler';
810
import { sendJson } from '../utils/response.handlers';
911

@@ -65,6 +67,12 @@ routes.post('/e2e-integration-org', async (_: express.Request, res: express.Resp
6567
userId: ENV.EXAMPLE_USER!.id,
6668
});
6769

70+
// Exercise the Analysis Tools in E2E as if fully rolled out + entitled: enable the rollout flag (off by
71+
// default in code) so the cards/nav render, and grant the paid-only entitlement so they render unlocked
72+
// (a locked card renders its links as plain text, which the routing nav tests cannot click).
73+
await enableFeatureFlagForUser({ userId: ENV.EXAMPLE_USER!.id, key: 'analysis-tools' });
74+
await grantAnalysisToolsEntitlementForUser(ENV.EXAMPLE_USER!.id);
75+
6876
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6977
sendJson(res as any, salesforceOrg);
7078
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const baseConfig = require('../../../eslint.config.js');
2+
3+
module.exports = [
4+
...baseConfig,
5+
{
6+
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
7+
rules: {},
8+
},
9+
{
10+
files: ['**/*.ts', '**/*.tsx'],
11+
rules: {},
12+
},
13+
{
14+
files: ['**/*.js', '**/*.jsx'],
15+
rules: {},
16+
},
17+
];
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "features-analysis-shared",
3+
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "libs/features/analysis-shared/src",
5+
"projectType": "library",
6+
"tags": ["scope:browser"],
7+
"targets": {
8+
"test": {
9+
"executor": "@nx/vitest:test",
10+
"outputs": ["{options.reportsDirectory}"],
11+
"options": {
12+
"reportsDirectory": "{projectRoot}/../../../coverage/libs/features/analysis-shared"
13+
}
14+
}
15+
}
16+
}

libs/features/data-analysis/src/field-usage/__tests__/compute-field-usage-where-used.spec.ts renamed to libs/features/analysis-shared/src/field-usage/__tests__/compute-field-usage-where-used.spec.ts

File renamed without changes.

libs/features/data-analysis/src/field-usage/__tests__/run-field-usage.spec.ts renamed to libs/features/analysis-shared/src/field-usage/__tests__/run-field-usage.spec.ts

File renamed without changes.

libs/features/data-analysis/src/field-usage/compute-field-usage-where-used.ts renamed to libs/features/analysis-shared/src/field-usage/compute-field-usage-where-used.ts

File renamed without changes.

libs/features/data-analysis/src/field-usage/run-field-usage.ts renamed to libs/features/analysis-shared/src/field-usage/run-field-usage.ts

File renamed without changes.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Pure, UI-free runner logic for the Analysis Tools (Field Usage + Permission Export). Lives in its
3+
* own lib so the shared job worker (`ui-core`'s JobWorker) can run analysis jobs without depending on
4+
* the feature UI libs — those import UI down from `ui-core`, which would otherwise form a cycle.
5+
*/
6+
export * from './field-usage/compute-field-usage-where-used';
7+
export * from './field-usage/run-field-usage';
8+
export * from './permission-export';

0 commit comments

Comments
 (0)