f4248bf20d
* feat: implement FeatureOptInService WIP * clean up * feat: consolidate feature repositories and add updateFeatureForUser - Implement updateFeatureForUser in FeaturesRepository (similar to updateFeatureForTeam) - Move getUserFeatureState and getTeamFeatureState from PrismaFeatureOptInRepository to FeaturesRepository - Update FeatureOptInService to use only FeaturesRepository - Add setUserFeatureState and setTeamFeatureState methods to FeatureOptInService - Update _router.ts to remove PrismaFeatureOptInRepository usage - Remove PrismaFeatureOptInRepository.ts and FeatureOptInRepositoryInterface.ts - Update features.repository.interface.ts and features.repository.mock.ts - Add integration tests for updateFeatureForUser, getUserFeatureState, getTeamFeatureState - Update service.integration-test.ts to use FeaturesRepository Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: rename updateFeatureForUser to setUserFeatureState Rename to match the convention used for setTeamFeatureState Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: return FeatureState type from getUserFeatureState and getTeamFeatureState * fix integration tests * clean up logics * update services and router * refactor: change getUserFeatureState and getTeamFeatureState to accept featureIds array - Renamed getUserFeatureState to getUserFeatureStates - Renamed getTeamFeatureState to getTeamFeatureStates - Changed parameter from featureId: string to featureIds: string[] - Changed return type from FeatureState to Record<string, FeatureState> - Updated FeatureOptInService to use the new batch methods - Added tests for querying multiple features in a single call - Optimized listFeaturesForTeam to fetch all feature states in one query Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * feat: add getFeatureStateForTeams for batch querying multiple teams - Added getFeatureStateForTeams method to query a single feature across multiple teams in one call - Updated FeatureOptInService.resolveFeatureStateAcrossTeams to use the new batch method - Replaces N+1 queries with a single database query for team states - Added comprehensive integration tests for the new method Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: combine org and team state queries into single call - Include orgId in the teamIds array passed to getFeatureStateForTeams - Extract org state and team states from the combined result - Reduces database queries from 3 to 2 in resolveFeatureStateAcrossTeams Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: use team.isOrganization and clarify computeEffectiveState comment Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: use MembershipRepository.findAllByUserId with isOrganization Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * feat: add featureId validation using isOptInFeature type guard Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * less queries * add fallback value * fix type error * move files * add autoOptInFeatures column * use autoOptInFeatures flag within FeatureOptInService * add setUserAutoOptIn and setTeamAutoOptIn * fix computeEffectiveState logic * rewrite computeEffectiveState * clean up integration tests * clean up in afterEach * fix type error * refactor: use FeaturesRepository methods instead of direct Prisma calls Replace all manual userFeatures and teamFeatures Prisma operations with the new setUserFeatureState and setTeamFeatureState repository methods. Changes include: - Admin handlers (assignFeatureToTeam, unassignFeatureFromTeam) - Test fixtures and integration tests - Playwright fixtures - Development scripts This ensures consistent feature flag management through the repository pattern and supports the new tri-state semantics (enabled/disabled/inherit). Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * clean up * fix the logic * extract some logic into applyAutoOptIn() * remove wrong code * refactor: convert setUserFeatureState and setTeamFeatureState to object params with discriminated union - Convert multiple positional parameters to single object parameter - Use discriminated union types: assignedBy required for enabled/disabled, omitted for inherit - Update all callers across repository, service, handlers, fixtures, and tests * fix type error * use Promise.all * fix --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
143 lines
4.5 KiB
JavaScript
143 lines
4.5 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* This script is used to prepare local environment for delegation credentials testing.
|
|
* It prepares Acme organization and its owner user with email owner1-acme@example.com to test Delegation Credentials with Calendar Cache
|
|
*/
|
|
const { PrismaClient } = require("@prisma/client");
|
|
const prisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
// Dynamic import for ES module
|
|
const { FeaturesRepository } = await import("@calcom/features/flags/features.repository");
|
|
const featuresRepository = new FeaturesRepository(prisma);
|
|
// Parse newEmail from args
|
|
const newEmail = process.argv[2] || "hariom@cal.com";
|
|
console.log(`Using newEmail: ${newEmail}`);
|
|
|
|
// 1. Update user email
|
|
let user = await prisma.user.findUnique({
|
|
where: { email: "owner1-acme@example.com" },
|
|
});
|
|
if (!user) {
|
|
// Check if user with newEmail exists
|
|
user = await prisma.user.findUnique({ where: { email: newEmail } });
|
|
if (user) {
|
|
console.log(`User with newEmail (${newEmail}) already exists. Skipping email update step.`);
|
|
} else {
|
|
console.error(
|
|
"User with email owner1-acme@example.com not found, and user with newEmail also not found."
|
|
);
|
|
process.exit(1);
|
|
}
|
|
} else {
|
|
if (user.email !== newEmail) {
|
|
await prisma.user.update({
|
|
where: { id: user.id },
|
|
data: { email: newEmail },
|
|
});
|
|
console.log(`Updated user email to ${newEmail}`);
|
|
} else {
|
|
console.log("User email already set to newEmail, skipping update.");
|
|
}
|
|
}
|
|
|
|
// 2. Find organization (Team)
|
|
const org = await prisma.team.findFirst({
|
|
where: { slug: "acme", isOrganization: true },
|
|
});
|
|
if (!org) {
|
|
console.error("Organization (Team) with slug=acme and isOrganization=true not found.");
|
|
process.exit(1);
|
|
}
|
|
console.log(`Found organization: id=${org.id}, slug=${org.slug}`);
|
|
|
|
// 3. Ensure TeamFeatures: delegation-credential
|
|
const delegationFeature = await prisma.teamFeatures.findUnique({
|
|
where: {
|
|
teamId_featureId: {
|
|
teamId: org.id,
|
|
featureId: "delegation-credential",
|
|
},
|
|
enabled: true,
|
|
},
|
|
});
|
|
if (!delegationFeature) {
|
|
await featuresRepository.setTeamFeatureState({
|
|
teamId: org.id,
|
|
featureId: "delegation-credential",
|
|
state: "enabled",
|
|
assignedBy: "prepare-local-script",
|
|
});
|
|
console.log("Created TeamFeatures: delegation-credential");
|
|
} else {
|
|
console.log("TeamFeatures: delegation-credential already exists, skipping.");
|
|
}
|
|
|
|
// 4. Ensure TeamFeatures: calendar-cache
|
|
const calendarCacheFeature = await prisma.teamFeatures.findUnique({
|
|
where: {
|
|
teamId_featureId: {
|
|
teamId: org.id,
|
|
featureId: "calendar-cache",
|
|
},
|
|
enabled: true,
|
|
},
|
|
});
|
|
if (!calendarCacheFeature) {
|
|
await featuresRepository.setTeamFeatureState({
|
|
teamId: org.id,
|
|
featureId: "calendar-cache",
|
|
state: "enabled",
|
|
assignedBy: "prepare-local-script",
|
|
});
|
|
console.log("Created TeamFeatures: calendar-cache");
|
|
} else {
|
|
console.log("TeamFeatures: calendar-cache already exists, skipping.");
|
|
}
|
|
|
|
// 5. Add WorkspacePlatform record
|
|
const workspacePlatform = await prisma.workspacePlatform.findUnique({
|
|
where: { slug: "google" },
|
|
});
|
|
if (!workspacePlatform) {
|
|
await prisma.workspacePlatform.create({
|
|
data: {
|
|
slug: "google",
|
|
name: "Google",
|
|
enabled: true,
|
|
description: "Google Workspace Platform",
|
|
defaultServiceAccountKey: {}, // Empty object, update as needed
|
|
},
|
|
});
|
|
console.log("Created WorkspacePlatform: google");
|
|
} else {
|
|
console.log("WorkspacePlatform: google already exists, skipping.");
|
|
}
|
|
|
|
// 6. Enable Feature records for 'calendar-cache' and 'delegation-credential'
|
|
const featureSlugs = ["calendar-cache", "delegation-credential"];
|
|
for (const slug of featureSlugs) {
|
|
const feature = await prisma.feature.findUnique({ where: { slug } });
|
|
if (!feature) {
|
|
console.error(`Feature with slug ${slug} not found.`);
|
|
process.exit(1);
|
|
}
|
|
if (!feature.enabled) {
|
|
await prisma.feature.update({ where: { slug }, data: { enabled: true } });
|
|
console.log(`Enabled Feature: ${slug}`);
|
|
} else {
|
|
console.log(`Feature: ${slug} already enabled, skipping.`);
|
|
}
|
|
}
|
|
console.log(`Now you can sign in with ${newEmail} and create a new Delegation Credential.`);
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|