Fix app:dev CLI by removing deleted createOneApplication mutation (#18460)

## Summary
- The `createOneApplication` GraphQL mutation was removed from the
server during the application architecture refactor (#18432), but the
SDK CLI (`app:dev`, `app:build --sync`) still called it, causing
failures.
- Simplified the SDK to use `syncApplication` (which now internally
creates the `ApplicationEntity` via `ensureApplicationExists`) instead
of a separate create step.
- On first run (clean install), the orchestrator now runs an initial
sync before initializing the file uploader, so file uploads can proceed
(they require the `ApplicationEntity` to exist).

## Test plan
- [x] Typecheck passes for both `twenty-sdk` and `twenty-server`
- [x] `app:dev` tested locally with existing app (finds app, uploads,
syncs)
- [x] `app:dev` tested locally after `app:uninstall` (creates app via
sync, uploads, syncs)
- [x] SDK unit tests pass (23/26 files, 3 pre-existing failures
unrelated)

Made with [Cursor](https://cursor.com)
This commit is contained in:
Félix Malfait
2026-03-06 18:37:54 +01:00
committed by GitHub
parent 2c69102f15
commit 66d93c4d28
53 changed files with 1837 additions and 2170 deletions
@@ -101,61 +101,6 @@ export class ApiService {
}
}
async findOneApplication(
universalIdentifier: string,
): Promise<ApiResponse<{ id: string; universalIdentifier: string } | null>> {
try {
const query = `
query FindOneApplication($universalIdentifier: UUID!) {
findOneApplication(universalIdentifier: $universalIdentifier) {
id
universalIdentifier
}
}
`;
const response = await this.client.post(
'/metadata',
{
query,
variables: { universalIdentifier },
},
{
headers: {
'Content-Type': 'application/json',
Accept: '*/*',
},
},
);
if (response.data.errors) {
const isNotFound = response.data.errors.some(
(error: { extensions?: { code?: string } }) =>
error.extensions?.code === 'NOT_FOUND',
);
if (isNotFound) {
return { success: true, data: null };
}
return {
success: false,
error: response.data.errors[0],
};
}
return {
success: true,
data: response.data.data.findOneApplication,
};
} catch (error) {
return {
success: false,
error,
};
}
}
async generateApplicationToken(applicationId: string): Promise<
ApiResponse<{
applicationAccessToken: { token: string; expiresAt: string };
@@ -383,40 +328,25 @@ export class ApiService {
}
}
async createApplication(
manifest: Manifest,
options?: { applicationRegistrationId?: string },
): Promise<ApiResponse<{ id: string; universalIdentifier: string }>> {
async createDevelopmentApplication(input: {
universalIdentifier: string;
name: string;
}): Promise<ApiResponse<{ id: string; universalIdentifier: string }>> {
try {
const mutation = `
mutation CreateOneApplication($input: CreateApplicationInput!) {
createOneApplication(input: $input) {
mutation CreateDevelopmentApplication($universalIdentifier: String!, $name: String!) {
createDevelopmentApplication(universalIdentifier: $universalIdentifier, name: $name) {
id
universalIdentifier
}
}
`;
const input: Record<string, string> = {
universalIdentifier: manifest.application.universalIdentifier,
name: manifest.application.displayName,
version: '0.0.1',
sourcePath: 'cli-sync',
};
if (options?.applicationRegistrationId) {
input.applicationRegistrationId = options.applicationRegistrationId;
}
const variables = {
input,
};
const response: AxiosResponse = await this.client.post(
const response = await this.client.post(
'/metadata',
{
query: mutation,
variables,
variables: input,
},
{
headers: {
@@ -435,8 +365,7 @@ export class ApiService {
return {
success: true,
data: response.data.data.createOneApplication,
message: `Successfully create application: ${manifest.application.displayName}`,
data: response.data.data.createDevelopmentApplication,
};
} catch (error) {
return {