Make ConnectionProvider a true SyncableEntity (#20232)
## Summary PR #20181 left `ConnectionProvider` in the `SyncableEntity` enum but bypassing the standard sync pipeline — manifest sync called the bespoke `ApplicationOAuthProviderService.upsertManyFromManifest()` instead of going through the workspace-migration orchestrator like every other SyncableEntity. Anything that assumed *"all SyncableEntity values flow through the same pipeline"* (dev UI sync tracking, verification tooling) was wrong about ConnectionProvider — that's the inconsistency this PR closes. This PR follows the `.cursor/skills/syncable-entity-*` guides religiously, all six steps. ## What changes **Step 1 — Types & Constants** (`@syncable-entity-types-and-constants`) - Add `connectionProvider` to `ALL_METADATA_NAME` (twenty-shared) - Make `ApplicationOAuthProviderEntity` extend `SyncableEntity` (drops the ad-hoc columns since the base class provides them, adds `deletedAt`, drops the old `(applicationId, universalIdentifier)` unique in favour of SyncableEntity's `(workspaceId, universalIdentifier)`) - `FlatConnectionProvider`, `FlatConnectionProviderMaps`, `FLAT_CONNECTION_PROVIDER_EDITABLE_PROPERTIES`, `UniversalFlatConnectionProvider`, six action types - Register in **all** the central registries: `AllFlatEntityTypesByMetadataName`, `ALL_METADATA_ENTITY_BY_METADATA_NAME`, `ALL_ENTITY_PROPERTIES_CONFIGURATION`, `ALL_MANY_TO_ONE_*`, `ALL_ONE_TO_MANY_*`, `ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION`, `ALL_METADATA_SERIALIZED_RELATION`, `ALL_JSONB_PROPERTIES_WITH_SERIALIZED_RELATION`, `WORKSPACE_CACHE_KEYS_V2` (`flatConnectionProviderMaps`), `METADATA_EVENTS_TO_EMIT` - `case 'connectionProvider':` in seven discriminated-union switches (`derive-metadata-events-*`, `optimistically-apply-*`, `enrich-create-*`) **Step 2 — Cache & Transform** (`@syncable-entity-cache-and-transform`) - `WorkspaceFlatConnectionProviderMapCacheService` (extends `WorkspaceCacheProvider`, decorated with `@WorkspaceCache`, soft-delete-aware) - `fromConnectionProviderEntityToFlatConnectionProvider` util - `fromConnectionProviderManifestToUniversalFlatConnectionProvider` util - `FlatConnectionProviderModule` wires the cache service - Wired the manifest converter into `compute-application-manifest-all-universal-flat-entity-maps` **Step 3 — Builder & Validation** (`@syncable-entity-builder-and-validation`) - `FlatConnectionProviderValidatorService` — never throws, returns error arrays; uses indexed `byUniversalIdentifier` for the (name, applicationUniversalIdentifier) uniqueness check (no `Object.values().find()` on the hot path) - `WorkspaceMigrationConnectionProviderActionsBuilderService` - Registered in both validators-module + builder-module - **Wired into the orchestrator** (the most-commonly-forgotten step per the rule) — constructor inject, destructure `flatConnectionProviderMaps`, `validateAndBuild`, append actions to the final migration **Step 4 — Runner & Actions** (`@syncable-entity-runner-and-actions`) - Three handlers (create / update / delete) using the canonical `WorkspaceMigrationRunnerActionHandler` mixin - Registered in `WorkspaceSchemaMigrationRunnerActionHandlersModule` **Step 5 — Integration** (`@syncable-entity-integration`) - Delete the `upsertManyFromManifest` bypass on `ApplicationOAuthProviderService` - Remove the bypass call from `ApplicationSyncService` — manifest sync now flows through the standard pipeline - Drop `ApplicationOAuthProviderModule` from `ApplicationManifestModule` (no longer needed) - Import `FlatConnectionProviderModule` from `ApplicationOAuthProviderModule` to keep the cache discoverable - 3 new exception codes: `INVALID_CONNECTION_PROVIDER_INPUT`, `CONNECTION_PROVIDER_NOT_FOUND`, `CONNECTION_PROVIDER_NAME_ALREADY_EXISTS` **Migration** - Generated via `database:migrate:generate` (instance command `1777896012579`): drops the old `(applicationId, universalIdentifier)` unique constraint, adds `deletedAt` column, adds the `(workspaceId, universalIdentifier)` unique index that `SyncableEntity` requires. - Verified clean — a second `migrate:generate` pass produces zero drift. **Step 6 — Tests** (`@syncable-entity-testing`) - 3 new specs for the manifest converter (defaults, optional fields, all-fields) - All 32 existing OAuth-provider tests still pass - ConnectionProvider has no end-user GraphQL CRUD (it's manifest-driven only), so the GraphQL integration suite that other SyncableEntities ship doesn't apply here **Codegen** - Regenerated GraphQL artifacts (twenty-front + twenty-client-sdk) against the live schema ## Why this matters Before: - `ConnectionProvider` claimed to be a `SyncableEntity` (in the enum) - But the entity didn't extend `SyncableEntity` - And the manifest sync bypassed the standard pipeline - → Verification tooling, dev UI sync tracking, anything iterating over `ALL_METADATA_NAME` got inconsistent behaviour After: - `ConnectionProvider` is a `SyncableEntity` end-to-end - Single sync path through the workspace-migration orchestrator (same as `agent`, `skill`, `frontComponent`, `webhook`, …) - One mental model ## Out of scope (deliberate) - **Renaming the table** from `applicationOAuthProvider` to `connectionProvider` — the `metadataName` is `connectionProvider` (what consumers see in code); the table name is internal. A rename would balloon this PR with mechanical churn unrelated to the sync-pipeline wiring. Worth doing as a follow-up. - **`applicationVariable` SyncableEntity conversion** — the other manifest-sync holdout. Tracked in #20215. ## Test plan - [ ] Migration up/down clean against fresh DB - [ ] Install an app whose manifest declares connection providers — providers appear in the workspace - [ ] Re-deploy the app with one provider added, one removed, one renamed → all reconciled correctly via the sync pipeline - [ ] Verify the dev-UI sync-tracking page shows ConnectionProvider entries the same way it shows agents/skills/etc - [ ] OAuth flow still works (existing connections, new connections, reconnect, list/get from SDK) — should be unchanged since the runtime code path didn't move 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AppOAuthRefreshModule } from 'src/engine/core-modules/application/application-oauth-provider/refresh/app-oauth-refresh.module';
|
||||
import { AppOAuthRefreshModule } from 'src/engine/core-modules/application/connection-provider/refresh/app-oauth-refresh.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { CalendarChannelEntity } from 'src/engine/metadata-modules/calendar-channel/entities/calendar-channel.entity';
|
||||
import { ConnectedAccountMetadataService } from 'src/engine/metadata-modules/connected-account/connected-account-metadata.service';
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { AppOAuthRevokeService } from 'src/engine/core-modules/application/application-oauth-provider/refresh/services/app-oauth-revoke.service';
|
||||
import { AppOAuthRevokeService } from 'src/engine/core-modules/application/connection-provider/refresh/services/app-oauth-revoke.service';
|
||||
import {
|
||||
ConnectedAccountException,
|
||||
ConnectedAccountExceptionCode,
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ export class ConnectedAccountDTO {
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
applicationConnectionProviderId: string | null;
|
||||
connectionProviderId: string | null;
|
||||
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
|
||||
+7
-12
@@ -14,21 +14,16 @@ import {
|
||||
import { type ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { ApplicationOAuthProviderEntity } from 'src/engine/core-modules/application/application-oauth-provider/application-oauth-provider.entity';
|
||||
import { ConnectionProviderEntity } from 'src/engine/core-modules/application/connection-provider/connection-provider.entity';
|
||||
import { type ImapSmtpCaldavParams } from 'src/engine/core-modules/imap-smtp-caldav-connection/types/imap-smtp-caldav-connection.type';
|
||||
import { type CalendarChannelEntity } from 'src/engine/metadata-modules/calendar-channel/entities/calendar-channel.entity';
|
||||
import { type MessageChannelEntity } from 'src/engine/metadata-modules/message-channel/entities/message-channel.entity';
|
||||
import { WorkspaceRelatedEntity } from 'src/engine/workspace-manager/types/workspace-related-entity';
|
||||
|
||||
// Distinguishes who can use this credential. Named `visibility` (not
|
||||
// `scope`) so it doesn't clash with the OAuth `scopes` array on the same
|
||||
// row — those are unrelated concepts that used to differ by one letter.
|
||||
export type ConnectedAccountVisibility = 'user' | 'workspace';
|
||||
|
||||
@Entity({ name: 'connectedAccount', schema: 'core' })
|
||||
@Index('IDX_CONNECTED_ACCOUNT_APP_OAUTH_PROVIDER_ID', [
|
||||
'applicationConnectionProviderId',
|
||||
])
|
||||
@Index('IDX_CONNECTED_ACCOUNT_CONNECTION_PROVIDER_ID', ['connectionProviderId'])
|
||||
@Index('IDX_CONNECTED_ACCOUNT_APPLICATION_ID', ['applicationId'])
|
||||
export class ConnectedAccountEntity extends WorkspaceRelatedEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
@@ -70,15 +65,15 @@ export class ConnectedAccountEntity extends WorkspaceRelatedEntity {
|
||||
@Column({ type: 'uuid', nullable: false })
|
||||
userWorkspaceId: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'applicationOAuthProviderId' })
|
||||
applicationConnectionProviderId: string | null;
|
||||
@Column({ type: 'uuid', nullable: true })
|
||||
connectionProviderId: string | null;
|
||||
|
||||
@ManyToOne(() => ApplicationOAuthProviderEntity, {
|
||||
@ManyToOne(() => ConnectionProviderEntity, {
|
||||
onDelete: 'CASCADE',
|
||||
nullable: true,
|
||||
})
|
||||
@JoinColumn({ name: 'applicationOAuthProviderId' })
|
||||
applicationConnectionProvider: Relation<ApplicationOAuthProviderEntity> | null;
|
||||
@JoinColumn({ name: 'connectionProviderId' })
|
||||
connectionProvider: Relation<ConnectionProviderEntity> | null;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true })
|
||||
applicationId: string | null;
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { type MetadataEntityPropertyName } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
|
||||
|
||||
export const FLAT_CONNECTION_PROVIDER_EDITABLE_PROPERTIES = [
|
||||
'displayName',
|
||||
'type',
|
||||
'oauthConfig',
|
||||
] as const satisfies MetadataEntityPropertyName<'connectionProvider'>[];
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { ConnectionProviderEntity } from 'src/engine/core-modules/application/connection-provider/connection-provider.entity';
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { WorkspaceFlatConnectionProviderMapCacheService } from 'src/engine/metadata-modules/flat-connection-provider/services/workspace-flat-connection-provider-map-cache.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([ApplicationEntity, ConnectionProviderEntity]),
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
],
|
||||
providers: [WorkspaceFlatConnectionProviderMapCacheService],
|
||||
exports: [WorkspaceFlatConnectionProviderMapCacheService],
|
||||
})
|
||||
export class FlatConnectionProviderModule {}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service';
|
||||
|
||||
import { ConnectionProviderEntity } from 'src/engine/core-modules/application/connection-provider/connection-provider.entity';
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
|
||||
import { type FlatConnectionProviderMaps } from 'src/engine/metadata-modules/flat-connection-provider/types/flat-connection-provider-maps.type';
|
||||
import { fromConnectionProviderEntityToFlatConnectionProvider } from 'src/engine/metadata-modules/flat-connection-provider/utils/from-connection-provider-entity-to-flat-connection-provider.util';
|
||||
import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator';
|
||||
import { createIdToUniversalIdentifierMap } from 'src/engine/workspace-cache/utils/create-id-to-universal-identifier-map.util';
|
||||
import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceCache('flatConnectionProviderMaps')
|
||||
export class WorkspaceFlatConnectionProviderMapCacheService extends WorkspaceCacheProvider<FlatConnectionProviderMaps> {
|
||||
constructor(
|
||||
@InjectRepository(ConnectionProviderEntity)
|
||||
private readonly connectionProviderRepository: Repository<ConnectionProviderEntity>,
|
||||
@InjectRepository(ApplicationEntity)
|
||||
private readonly applicationRepository: Repository<ApplicationEntity>,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async computeForCache(
|
||||
workspaceId: string,
|
||||
): Promise<FlatConnectionProviderMaps> {
|
||||
const [connectionProviders, applications] = await Promise.all([
|
||||
this.connectionProviderRepository.find({
|
||||
where: { workspaceId },
|
||||
}),
|
||||
this.applicationRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'universalIdentifier'],
|
||||
}),
|
||||
]);
|
||||
|
||||
const applicationIdToUniversalIdentifierMap =
|
||||
createIdToUniversalIdentifierMap(applications);
|
||||
|
||||
const flatConnectionProviderMaps = createEmptyFlatEntityMaps();
|
||||
|
||||
for (const connectionProviderEntity of connectionProviders) {
|
||||
const flatConnectionProvider =
|
||||
fromConnectionProviderEntityToFlatConnectionProvider({
|
||||
entity: connectionProviderEntity,
|
||||
applicationIdToUniversalIdentifierMap,
|
||||
});
|
||||
|
||||
addFlatEntityToFlatEntityMapsThroughMutationOrThrow({
|
||||
flatEntity: flatConnectionProvider,
|
||||
flatEntityMapsToMutate: flatConnectionProviderMaps,
|
||||
});
|
||||
}
|
||||
|
||||
return flatConnectionProviderMaps;
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type FlatConnectionProvider } from 'src/engine/metadata-modules/flat-connection-provider/types/flat-connection-provider.type';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
|
||||
export type FlatConnectionProviderMaps = FlatEntityMaps<FlatConnectionProvider>;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type ConnectionProviderEntity } from 'src/engine/core-modules/application/connection-provider/connection-provider.entity';
|
||||
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
|
||||
|
||||
export type FlatConnectionProvider = FlatEntityFrom<ConnectionProviderEntity>;
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
FlatEntityMapsException,
|
||||
FlatEntityMapsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
|
||||
import { type FlatConnectionProvider } from 'src/engine/metadata-modules/flat-connection-provider/types/flat-connection-provider.type';
|
||||
import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type';
|
||||
|
||||
export const fromConnectionProviderEntityToFlatConnectionProvider = ({
|
||||
entity: connectionProviderEntity,
|
||||
applicationIdToUniversalIdentifierMap,
|
||||
}: FromEntityToFlatEntityArgs<'connectionProvider'>): FlatConnectionProvider => {
|
||||
const applicationUniversalIdentifier =
|
||||
applicationIdToUniversalIdentifierMap.get(
|
||||
connectionProviderEntity.applicationId,
|
||||
);
|
||||
|
||||
if (!isDefined(applicationUniversalIdentifier)) {
|
||||
throw new FlatEntityMapsException(
|
||||
`Application with id ${connectionProviderEntity.applicationId} not found for connection provider ${connectionProviderEntity.id}`,
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
id: connectionProviderEntity.id,
|
||||
universalIdentifier: connectionProviderEntity.universalIdentifier,
|
||||
applicationId: connectionProviderEntity.applicationId,
|
||||
workspaceId: connectionProviderEntity.workspaceId,
|
||||
name: connectionProviderEntity.name,
|
||||
displayName: connectionProviderEntity.displayName,
|
||||
type: connectionProviderEntity.type,
|
||||
oauthConfig: connectionProviderEntity.oauthConfig,
|
||||
createdAt: connectionProviderEntity.createdAt.toISOString(),
|
||||
updatedAt: connectionProviderEntity.updatedAt.toISOString(),
|
||||
applicationUniversalIdentifier,
|
||||
};
|
||||
};
|
||||
+11
@@ -39,6 +39,17 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
|
||||
"hotKeys",
|
||||
],
|
||||
},
|
||||
"connectionProvider": {
|
||||
"propertiesToCompare": [
|
||||
"name",
|
||||
"displayName",
|
||||
"type",
|
||||
"oauthConfig",
|
||||
],
|
||||
"propertiesToStringify": [
|
||||
"oauthConfig",
|
||||
],
|
||||
},
|
||||
"fieldMetadata": {
|
||||
"propertiesToCompare": [
|
||||
"defaultValue",
|
||||
|
||||
+24
@@ -1542,6 +1542,30 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
universalProperty: undefined,
|
||||
},
|
||||
},
|
||||
connectionProvider: {
|
||||
name: { toCompare: true, toStringify: false, universalProperty: undefined },
|
||||
displayName: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
type: { toCompare: true, toStringify: false, universalProperty: undefined },
|
||||
oauthConfig: {
|
||||
toCompare: true,
|
||||
toStringify: true,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
createdAt: {
|
||||
toCompare: false,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
updatedAt: {
|
||||
toCompare: false,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
},
|
||||
} as const satisfies {
|
||||
[P in AllMetadataName]: MetadataEntityPropertyConfiguration<P>;
|
||||
};
|
||||
|
||||
+4
@@ -285,6 +285,10 @@ export const ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY = {
|
||||
foreignKey: 'viewId',
|
||||
},
|
||||
},
|
||||
connectionProvider: {
|
||||
workspace: null,
|
||||
application: null,
|
||||
},
|
||||
} as const satisfies ManyToOneMetadataRelationsProperties;
|
||||
|
||||
// satisfies with complex mapped types involving nested generics doesn't always catch missing required keys
|
||||
|
||||
+4
@@ -505,6 +505,10 @@ export const ALL_MANY_TO_ONE_METADATA_RELATIONS = {
|
||||
metadataName: 'view',
|
||||
},
|
||||
},
|
||||
connectionProvider: {
|
||||
workspace: null,
|
||||
application: null,
|
||||
},
|
||||
} as const satisfies ManyToOneMetadataRelationsProperties;
|
||||
|
||||
// satisfies with complex mapped types involving nested generics doesn't always catch missing required keys
|
||||
|
||||
+2
@@ -1,6 +1,7 @@
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
import { type EntityTarget, type ObjectLiteral } from 'typeorm';
|
||||
|
||||
import { ConnectionProviderEntity } from 'src/engine/core-modules/application/connection-provider/connection-provider.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { CommandMenuItemEntity } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
@@ -57,4 +58,5 @@ export const ALL_METADATA_ENTITY_BY_METADATA_NAME = {
|
||||
permissionFlag: PermissionFlagEntity,
|
||||
webhook: WebhookEntity,
|
||||
viewSort: ViewSortEntity,
|
||||
connectionProvider: ConnectionProviderEntity,
|
||||
} as const satisfies Record<AllMetadataName, EntityTarget<ObjectLiteral>>;
|
||||
|
||||
+1
@@ -112,4 +112,5 @@ export const ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION = {
|
||||
},
|
||||
frontComponent: {},
|
||||
webhook: {},
|
||||
connectionProvider: {},
|
||||
} as const satisfies MetadataRequiredForValidation;
|
||||
|
||||
+1
@@ -50,6 +50,7 @@ export const ALL_METADATA_SERIALIZED_RELATION = {
|
||||
viewSort: {},
|
||||
frontComponent: {},
|
||||
webhook: {},
|
||||
connectionProvider: {},
|
||||
} as const satisfies MetadataSerializedRelationProperties;
|
||||
|
||||
// satisfies with complex mapped types involving nested generics doesn't always catch missing required keys
|
||||
|
||||
+1
@@ -244,6 +244,7 @@ export const ALL_ONE_TO_MANY_METADATA_RELATIONS = {
|
||||
frontComponent: {},
|
||||
webhook: {},
|
||||
viewSort: {},
|
||||
connectionProvider: {},
|
||||
} as const satisfies OneToManyMetadataRelationsProperties;
|
||||
|
||||
// satisfies with complex mapped types involving nested generics doesn't always catch missing required keys
|
||||
|
||||
+27
@@ -2,6 +2,8 @@ import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-meta
|
||||
import { type FlatAgentMaps } from 'src/engine/metadata-modules/flat-agent/types/flat-agent-maps.type';
|
||||
import { type FlatAgent } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type';
|
||||
import { type FlatCommandMenuItemMaps } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item-maps.type';
|
||||
import { type FlatConnectionProviderMaps } from 'src/engine/metadata-modules/flat-connection-provider/types/flat-connection-provider-maps.type';
|
||||
import { type FlatConnectionProvider } from 'src/engine/metadata-modules/flat-connection-provider/types/flat-connection-provider.type';
|
||||
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type';
|
||||
@@ -53,6 +55,7 @@ import { type FlatRowLevelPermissionPredicateMaps } from 'src/engine/metadata-mo
|
||||
import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type';
|
||||
import { type UniversalFlatAgent } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-agent.type';
|
||||
import { type UniversalFlatCommandMenuItem } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-command-menu-item.type';
|
||||
import { type UniversalFlatConnectionProvider } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-connection-provider.type';
|
||||
import { type UniversalFlatEntityFrom } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
|
||||
import { type UniversalFlatFieldPermission } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-permission.type';
|
||||
import { type UniversalFlatFrontComponent } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-front-component.type';
|
||||
@@ -94,6 +97,14 @@ import {
|
||||
type UniversalDeleteCommandMenuItemAction,
|
||||
type UniversalUpdateCommandMenuItemAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/command-menu-item/types/workspace-migration-command-menu-item-action.type';
|
||||
import {
|
||||
type FlatCreateConnectionProviderAction,
|
||||
type FlatDeleteConnectionProviderAction,
|
||||
type FlatUpdateConnectionProviderAction,
|
||||
type UniversalCreateConnectionProviderAction,
|
||||
type UniversalDeleteConnectionProviderAction,
|
||||
type UniversalUpdateConnectionProviderAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/connection-provider/types/workspace-migration-connection-provider-action.type';
|
||||
import {
|
||||
type FlatCreateFieldPermissionAction,
|
||||
type FlatDeleteFieldPermissionAction,
|
||||
@@ -731,4 +742,20 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
universalFlatEntity: UniversalFlatWebhook;
|
||||
entity: MetadataEntity<'webhook'>;
|
||||
};
|
||||
connectionProvider: {
|
||||
flatEntityMaps: FlatConnectionProviderMaps;
|
||||
universalActions: {
|
||||
create: UniversalCreateConnectionProviderAction;
|
||||
update: UniversalUpdateConnectionProviderAction;
|
||||
delete: UniversalDeleteConnectionProviderAction;
|
||||
};
|
||||
flatActions: {
|
||||
create: FlatCreateConnectionProviderAction;
|
||||
update: FlatUpdateConnectionProviderAction;
|
||||
delete: FlatDeleteConnectionProviderAction;
|
||||
};
|
||||
flatEntity: FlatConnectionProvider;
|
||||
universalFlatEntity: UniversalFlatConnectionProvider;
|
||||
entity: MetadataEntity<'connectionProvider'>;
|
||||
};
|
||||
};
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`getMetadataRelatedMetadataNames should return related metadata names for agent 1`] = `[]`;
|
||||
|
||||
@@ -10,6 +10,8 @@ exports[`getMetadataRelatedMetadataNames should return related metadata names fo
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`getMetadataRelatedMetadataNames should return related metadata names for connectionProvider 1`] = `[]`;
|
||||
|
||||
exports[`getMetadataRelatedMetadataNames should return related metadata names for fieldMetadata 1`] = `
|
||||
[
|
||||
"objectMetadata",
|
||||
|
||||
+1
@@ -18,6 +18,7 @@ exports[`sortMetadataNamesChildrenFirst should return metadata names sorted with
|
||||
"rowLevelPermissionPredicateGroup",
|
||||
"viewGroup",
|
||||
"agent",
|
||||
"connectionProvider",
|
||||
"frontComponent",
|
||||
"logicFunction",
|
||||
"pageLayoutTab",
|
||||
|
||||
Reference in New Issue
Block a user