## Add API client generation to SDK dev mode and refactor orchestrator into step-based pipeline ### Why The SDK dev mode lacked typed API client generation, forcing developers to work without auto-generated GraphQL types when building applications. Additionally, the orchestrator was a monolithic class that mixed watcher management, token handling, and sync logic — making it difficult to extend with new steps like client generation. ### How - **Refactored the orchestrator** into a step-based pipeline with dedicated classes: `CheckServer`, `EnsureValidTokens`, `ResolveApplication`, `BuildManifest`, `UploadFiles`, `GenerateApiClient`, `SyncApplication`, and `StartWatchers`. Each step has typed input/output/status, managed by a new `OrchestratorState` class. - **Added `GenerateApiClientOrchestratorStep`** that detects object/field schema changes and regenerates a typed GraphQL client (via `@genql/cli`) into `node_modules/twenty-sdk/generated` for seamless imports. - **Replaced `checkApplicationExist`** with `findOneApplication` on both server resolver and SDK API service, returning the entity data instead of a boolean. - **Added application token pair mutations** (`generateApplicationToken`, `renewApplicationToken`) to the API service, with the server now returning `ApplicationTokenPairDTO` containing both access and refresh tokens. - **Restructured the dev UI** into `dev/ui/components/` with dedicated panel, section, and event log components. - **Simplified `AppDevCommand`** from ~180 lines of watcher management down to ~40 lines that delegate entirely to the orchestrator.
66 lines
2.4 KiB
TypeScript
66 lines
2.4 KiB
TypeScript
import { findManyApplications } from 'test/integration/graphql/utils/find-many-applications.util';
|
|
import { generateApplicationToken } from 'test/integration/metadata/suites/application/utils/generate-application-token.util';
|
|
|
|
describe('generateApplicationToken', () => {
|
|
let applicationId: string;
|
|
|
|
beforeAll(async () => {
|
|
const { data } = await findManyApplications({
|
|
expectToFail: false,
|
|
});
|
|
|
|
const application = data.findManyApplications[0];
|
|
|
|
expect(application).toBeDefined();
|
|
|
|
applicationId = application.id;
|
|
});
|
|
|
|
it('should generate an application token with admin access token', async () => {
|
|
const { data } = await generateApplicationToken({
|
|
applicationId,
|
|
expectToFail: false,
|
|
});
|
|
|
|
const tokenPair = data.generateApplicationToken;
|
|
|
|
expect(tokenPair).toBeDefined();
|
|
expect(tokenPair.applicationAccessToken).toBeDefined();
|
|
expect(typeof tokenPair.applicationAccessToken.token).toBe('string');
|
|
expect(tokenPair.applicationAccessToken.token.length).toBeGreaterThan(0);
|
|
expect(tokenPair.applicationAccessToken.expiresAt).toBeDefined();
|
|
expect(tokenPair.applicationRefreshToken).toBeDefined();
|
|
expect(typeof tokenPair.applicationRefreshToken.token).toBe('string');
|
|
expect(tokenPair.applicationRefreshToken.expiresAt).toBeDefined();
|
|
});
|
|
|
|
it('should generate an application token with API key access token', async () => {
|
|
const { data } = await generateApplicationToken({
|
|
applicationId,
|
|
expectToFail: false,
|
|
token: API_KEY_ACCESS_TOKEN,
|
|
});
|
|
|
|
const tokenPair = data.generateApplicationToken;
|
|
|
|
expect(tokenPair).toBeDefined();
|
|
expect(tokenPair.applicationAccessToken).toBeDefined();
|
|
expect(typeof tokenPair.applicationAccessToken.token).toBe('string');
|
|
expect(tokenPair.applicationAccessToken.token.length).toBeGreaterThan(0);
|
|
expect(tokenPair.applicationAccessToken.expiresAt).toBeDefined();
|
|
expect(tokenPair.applicationRefreshToken).toBeDefined();
|
|
expect(typeof tokenPair.applicationRefreshToken.token).toBe('string');
|
|
expect(tokenPair.applicationRefreshToken.expiresAt).toBeDefined();
|
|
});
|
|
|
|
it('should fail with a non-existent application id', async () => {
|
|
const { errors } = await generateApplicationToken({
|
|
applicationId: '00000000-0000-0000-0000-000000000000',
|
|
expectToFail: true,
|
|
});
|
|
|
|
expect(errors).toBeDefined();
|
|
expect(errors.length).toBeGreaterThan(0);
|
|
});
|
|
});
|