[Apps] Content + permission tabs for marketplace and installed apps (#17888)
In this PR - Content tab for marketplace apps and installed apps: complies with [figma](https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?node-id=34845-126856); addition of field section showing fields added to standard objects; initiative: arrow opens a sub-table of fields rows. (suggested because 1/ for marketplace apps we cannot redirect to the actual object page in settings since the object does not exist yet in the workspace 2/ since we dont have an quick "go back to application page" option, it can be annoying to be redirected to a different setting page when we just want to look at the content of the app objects) - Permission tab for marketplace apps and installed apps - left to do - settings tab (in another PR) There are breaking changes but it's behind a feature flag not exposed (access to marketplace) so not problematic marketplace apps https://github.com/user-attachments/assets/4c660101-50fc-47ce-b90a-8d6f17db5e74 installed apps https://github.com/user-attachments/assets/c9229ee1-e75f-4cad-8766-758b2c5b37b4
This commit is contained in:
+210
-1
@@ -1,13 +1,202 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
import { Field, Int, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
IsArray,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
MaxLength,
|
||||
} from 'class-validator';
|
||||
|
||||
@ObjectType('MarketplaceAppField')
|
||||
export class MarketplaceAppFieldDTO {
|
||||
@IsString()
|
||||
@Field()
|
||||
name: string;
|
||||
|
||||
@IsString()
|
||||
@Field()
|
||||
type: string;
|
||||
|
||||
@IsString()
|
||||
@Field()
|
||||
label: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
description?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
icon?: string;
|
||||
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
objectUniversalIdentifier: string;
|
||||
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
universalIdentifier: string;
|
||||
}
|
||||
|
||||
@ObjectType('MarketplaceAppObject')
|
||||
export class MarketplaceAppObjectDTO {
|
||||
@IsString()
|
||||
@Field()
|
||||
universalIdentifier: string;
|
||||
|
||||
@IsString()
|
||||
@Field()
|
||||
nameSingular: string;
|
||||
|
||||
@IsString()
|
||||
@Field()
|
||||
namePlural: string;
|
||||
|
||||
@IsString()
|
||||
@Field()
|
||||
labelSingular: string;
|
||||
|
||||
@IsString()
|
||||
@Field()
|
||||
labelPlural: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
description?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
icon?: string;
|
||||
|
||||
@IsArray()
|
||||
@Field(() => [MarketplaceAppFieldDTO])
|
||||
fields: MarketplaceAppFieldDTO[];
|
||||
}
|
||||
|
||||
@ObjectType('MarketplaceAppLogicFunction')
|
||||
export class MarketplaceAppLogicFunctionDTO {
|
||||
@IsString()
|
||||
@Field()
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
description?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Field(() => Int, { nullable: true })
|
||||
timeoutSeconds?: number;
|
||||
}
|
||||
|
||||
@ObjectType('MarketplaceAppFrontComponent')
|
||||
export class MarketplaceAppFrontComponentDTO {
|
||||
@IsString()
|
||||
@Field()
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
description?: string;
|
||||
}
|
||||
|
||||
@ObjectType('MarketplaceAppRoleObjectPermission')
|
||||
export class MarketplaceAppRoleObjectPermissionDTO {
|
||||
@IsString()
|
||||
@Field()
|
||||
objectUniversalIdentifier: string;
|
||||
|
||||
@IsOptional()
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
canReadObjectRecords?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
canUpdateObjectRecords?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
canSoftDeleteObjectRecords?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
canDestroyObjectRecords?: boolean;
|
||||
}
|
||||
|
||||
@ObjectType('MarketplaceAppRoleFieldPermission')
|
||||
export class MarketplaceAppRoleFieldPermissionDTO {
|
||||
@IsString()
|
||||
@Field()
|
||||
objectUniversalIdentifier: string;
|
||||
|
||||
@IsString()
|
||||
@Field()
|
||||
fieldUniversalIdentifier: string;
|
||||
|
||||
@IsOptional()
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
canReadFieldValue?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
canUpdateFieldValue?: boolean;
|
||||
}
|
||||
|
||||
@ObjectType('MarketplaceAppDefaultRole')
|
||||
export class MarketplaceAppDefaultRoleDTO {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
id: string;
|
||||
|
||||
@IsString()
|
||||
@Field()
|
||||
label: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
description?: string;
|
||||
|
||||
@Field(() => Boolean)
|
||||
canReadAllObjectRecords: boolean;
|
||||
|
||||
@Field(() => Boolean)
|
||||
canUpdateAllObjectRecords: boolean;
|
||||
|
||||
@Field(() => Boolean)
|
||||
canSoftDeleteAllObjectRecords: boolean;
|
||||
|
||||
@Field(() => Boolean)
|
||||
canDestroyAllObjectRecords: boolean;
|
||||
|
||||
@Field(() => Boolean)
|
||||
canUpdateAllSettings: boolean;
|
||||
|
||||
@Field(() => Boolean)
|
||||
canAccessAllTools: boolean;
|
||||
|
||||
@IsArray()
|
||||
@Field(() => [MarketplaceAppRoleObjectPermissionDTO])
|
||||
objectPermissions: MarketplaceAppRoleObjectPermissionDTO[];
|
||||
|
||||
@IsArray()
|
||||
@Field(() => [MarketplaceAppRoleFieldPermissionDTO])
|
||||
fieldPermissions: MarketplaceAppRoleFieldPermissionDTO[];
|
||||
|
||||
@IsArray()
|
||||
@Field(() => [String])
|
||||
permissionFlags: string[];
|
||||
}
|
||||
|
||||
@ObjectType('MarketplaceApp')
|
||||
export class MarketplaceAppDTO {
|
||||
@IsString()
|
||||
@@ -67,4 +256,24 @@ export class MarketplaceAppDTO {
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
termsUrl?: string;
|
||||
|
||||
@IsArray()
|
||||
@Field(() => [MarketplaceAppObjectDTO])
|
||||
objects: MarketplaceAppObjectDTO[];
|
||||
|
||||
@IsArray()
|
||||
@Field(() => [MarketplaceAppFieldDTO])
|
||||
fields: MarketplaceAppFieldDTO[];
|
||||
|
||||
@IsArray()
|
||||
@Field(() => [MarketplaceAppLogicFunctionDTO])
|
||||
logicFunctions: MarketplaceAppLogicFunctionDTO[];
|
||||
|
||||
@IsArray()
|
||||
@Field(() => [MarketplaceAppFrontComponentDTO])
|
||||
frontComponents: MarketplaceAppFrontComponentDTO[];
|
||||
|
||||
@IsOptional()
|
||||
@Field(() => MarketplaceAppDefaultRoleDTO, { nullable: true })
|
||||
defaultRole?: MarketplaceAppDefaultRoleDTO;
|
||||
}
|
||||
|
||||
+122
-34
@@ -5,7 +5,15 @@ import { lowerCase, upperFirst } from 'lodash';
|
||||
import { type Manifest } from 'twenty-shared/application';
|
||||
import { PackageJson } from 'type-fest';
|
||||
|
||||
import { MarketplaceAppDTO } from 'src/engine/core-modules/application/dtos/marketplace-app.dto';
|
||||
import {
|
||||
MarketplaceAppDTO,
|
||||
MarketplaceAppDefaultRoleDTO,
|
||||
MarketplaceAppFieldDTO,
|
||||
MarketplaceAppFrontComponentDTO,
|
||||
MarketplaceAppLogicFunctionDTO,
|
||||
MarketplaceAppObjectDTO,
|
||||
} from 'src/engine/core-modules/application/dtos/marketplace-app.dto';
|
||||
import { MOCKED_MARKETPLACE_APP } from 'src/engine/core-modules/application/services/mocked-marketplace-app.constant';
|
||||
|
||||
type GitHubContent = {
|
||||
name: string;
|
||||
@@ -52,7 +60,7 @@ export class MarketplaceService {
|
||||
private async fetchAllMarketplaceApps(): Promise<MarketplaceAppDTO[]> {
|
||||
const apps: MarketplaceAppDTO[] = [];
|
||||
|
||||
apps.push(this.loadMockApp());
|
||||
apps.push(MOCKED_MARKETPLACE_APP); // To remove once we have apps on marketplace
|
||||
|
||||
try {
|
||||
const appDirs = await this.getAppDirectoriesFromGitHub();
|
||||
@@ -82,6 +90,10 @@ export class MarketplaceService {
|
||||
providers: [],
|
||||
websiteUrl: '',
|
||||
termsUrl: '',
|
||||
objects: [],
|
||||
fields: [],
|
||||
logicFunctions: [],
|
||||
frontComponents: [],
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -99,44 +111,12 @@ export class MarketplaceService {
|
||||
return apps;
|
||||
}
|
||||
|
||||
private loadMockApp(): MarketplaceAppDTO {
|
||||
// SVG logo as data URL
|
||||
const logoSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="#1a2744"><ellipse cx="38" cy="20" rx="28" ry="10"/><rect x="10" y="20" width="56" height="50"/><ellipse cx="38" cy="70" rx="28" ry="10"/><ellipse cx="38" cy="35" rx="28" ry="10" fill="none" stroke="#fff" stroke-width="3"/><ellipse cx="38" cy="52" rx="28" ry="10" fill="none" stroke="#fff" stroke-width="3"/><circle cx="72" cy="62" r="22" fill="#1a2744"/><circle cx="72" cy="62" r="18" fill="#fff"/><path d="M72 50 L72 74 M62 58 L72 48 L82 58" stroke="#1a2744" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>`;
|
||||
const logoDataUrl = `data:image/svg+xml,${encodeURIComponent(logoSvg)}`;
|
||||
|
||||
return {
|
||||
id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
||||
name: 'Data Enrichment',
|
||||
description: 'Enrich your data easily. Choose your provider.',
|
||||
icon: 'IconSparkles',
|
||||
version: '1.0.0',
|
||||
author: 'Cosmos Labs',
|
||||
category: 'Data',
|
||||
logo: logoDataUrl,
|
||||
screenshots: [
|
||||
'https://placehold.co/800x400/f5f5f5/666?text=Screenshot+1',
|
||||
'https://placehold.co/800x400/f5f5f5/666?text=Screenshot+2',
|
||||
'https://placehold.co/800x400/f5f5f5/666?text=Screenshot+3',
|
||||
],
|
||||
aboutDescription:
|
||||
'Enhance your workspace with automated data intelligence. This app monitors your new records and automatically populates missing details such as job titles, company size, social profiles, and industry insights.',
|
||||
providers: ['Clearbit', 'Apollo', 'Hunter.io'],
|
||||
websiteUrl: 'https://google.com',
|
||||
termsUrl: 'https://google.com',
|
||||
};
|
||||
}
|
||||
|
||||
private async getAppDirectoriesFromGitHub(): Promise<string[]> {
|
||||
const directories: string[] = [];
|
||||
|
||||
const rootContents = await this.fetchGitHubDirectory('');
|
||||
|
||||
for (const entry of rootContents) {
|
||||
// if (entry.type !== 'dir') continue;
|
||||
// if (entry.name.startsWith('.')) continue;
|
||||
// if (entry.name === 'node_modules') continue;
|
||||
// if (entry.name === 'internal') continue;
|
||||
|
||||
if (entry.name === 'community') {
|
||||
const communityContents = await this.fetchGitHubDirectory('community');
|
||||
|
||||
@@ -217,6 +197,61 @@ export class MarketplaceService {
|
||||
return null;
|
||||
}
|
||||
|
||||
const objects: MarketplaceAppObjectDTO[] = (manifest.objects ?? []).map(
|
||||
(manifestObject) => ({
|
||||
universalIdentifier: manifestObject.universalIdentifier,
|
||||
nameSingular: manifestObject.nameSingular,
|
||||
namePlural: manifestObject.namePlural,
|
||||
labelSingular: manifestObject.labelSingular,
|
||||
labelPlural: manifestObject.labelPlural,
|
||||
description: manifestObject.description,
|
||||
icon: manifestObject.icon,
|
||||
fields: (manifestObject.fields ?? []).map((field) => ({
|
||||
name: field.name ?? '',
|
||||
type: field.type,
|
||||
label: field.label ?? '',
|
||||
description: field.description,
|
||||
icon: field.icon,
|
||||
universalIdentifier: field.universalIdentifier,
|
||||
objectUniversalIdentifier: manifestObject.universalIdentifier,
|
||||
})),
|
||||
}),
|
||||
);
|
||||
|
||||
const fields: MarketplaceAppFieldDTO[] = (manifest.fields ?? []).map(
|
||||
(manifestField) => {
|
||||
return {
|
||||
name: manifestField.name,
|
||||
type: manifestField.type,
|
||||
label: manifestField.label,
|
||||
description: manifestField.description,
|
||||
icon: manifestField.icon,
|
||||
objectUniversalIdentifier: manifestField.objectUniversalIdentifier,
|
||||
universalIdentifier: manifestField.universalIdentifier,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const logicFunctions: MarketplaceAppLogicFunctionDTO[] = (
|
||||
manifest.logicFunctions ?? []
|
||||
).map((manifestLogicFunction) => ({
|
||||
name: manifestLogicFunction.name ?? '',
|
||||
description: manifestLogicFunction.description,
|
||||
timeoutSeconds: manifestLogicFunction.timeoutSeconds,
|
||||
}));
|
||||
|
||||
const frontComponents: MarketplaceAppFrontComponentDTO[] = (
|
||||
manifest.frontComponents ?? []
|
||||
).map((manifestFrontComponent) => ({
|
||||
name: manifestFrontComponent.name ?? '',
|
||||
description: manifestFrontComponent.description,
|
||||
}));
|
||||
|
||||
const defaultRole = this.resolveDefaultRole(
|
||||
manifest,
|
||||
application.defaultRoleUniversalIdentifier,
|
||||
);
|
||||
|
||||
return {
|
||||
id: application.universalIdentifier,
|
||||
name: application.displayName,
|
||||
@@ -231,6 +266,59 @@ export class MarketplaceService {
|
||||
providers: marketplaceData.providers ?? [],
|
||||
websiteUrl: marketplaceData.websiteUrl,
|
||||
termsUrl: marketplaceData.termsUrl,
|
||||
objects,
|
||||
fields,
|
||||
logicFunctions,
|
||||
frontComponents,
|
||||
defaultRole,
|
||||
};
|
||||
}
|
||||
|
||||
private resolveDefaultRole(
|
||||
manifest: Manifest,
|
||||
defaultRoleUniversalIdentifier: string,
|
||||
): MarketplaceAppDefaultRoleDTO | undefined {
|
||||
const roleManifest = manifest.roles?.find(
|
||||
(role) => role.universalIdentifier === defaultRoleUniversalIdentifier,
|
||||
);
|
||||
|
||||
if (!roleManifest) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
id: roleManifest.universalIdentifier,
|
||||
label: roleManifest.label,
|
||||
description: roleManifest.description,
|
||||
canReadAllObjectRecords: roleManifest.canReadAllObjectRecords ?? false,
|
||||
canUpdateAllObjectRecords:
|
||||
roleManifest.canUpdateAllObjectRecords ?? false,
|
||||
canSoftDeleteAllObjectRecords:
|
||||
roleManifest.canSoftDeleteAllObjectRecords ?? false,
|
||||
canDestroyAllObjectRecords:
|
||||
roleManifest.canDestroyAllObjectRecords ?? false,
|
||||
canUpdateAllSettings: roleManifest.canUpdateAllSettings ?? false,
|
||||
canAccessAllTools: roleManifest.canAccessAllTools ?? false,
|
||||
objectPermissions: (roleManifest.objectPermissions ?? []).map(
|
||||
(permission) => ({
|
||||
objectUniversalIdentifier: permission.objectUniversalIdentifier,
|
||||
canReadObjectRecords: permission.canReadObjectRecords,
|
||||
canUpdateObjectRecords: permission.canUpdateObjectRecords,
|
||||
canSoftDeleteObjectRecords: permission.canSoftDeleteObjectRecords,
|
||||
canDestroyObjectRecords: permission.canDestroyObjectRecords,
|
||||
}),
|
||||
),
|
||||
fieldPermissions: (roleManifest.fieldPermissions ?? []).map(
|
||||
(permission) => ({
|
||||
objectUniversalIdentifier: permission.objectUniversalIdentifier,
|
||||
fieldUniversalIdentifier: permission.fieldUniversalIdentifier,
|
||||
canReadFieldValue: permission.canReadFieldValue,
|
||||
canUpdateFieldValue: permission.canUpdateFieldValue,
|
||||
}),
|
||||
),
|
||||
permissionFlags: (roleManifest.permissionFlags ?? []).map((flag) =>
|
||||
flag.toString(),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
import { type MarketplaceAppDTO } from 'src/engine/core-modules/application/dtos/marketplace-app.dto';
|
||||
|
||||
// Readable mock identifiers for the Data Enrichment app
|
||||
const MOCK_APP_ID = 'a1b2c3d4-0000-0000-0000-000000000001';
|
||||
const MOCK_ROLE_ID = 'a1b2c3d4-0000-0000-0000-000000000010';
|
||||
const MOCK_ENRICHMENT_JOB_UNIVERSAL_ID = 'a1b2c3d4-0000-0000-0000-000000000100';
|
||||
|
||||
// Standard object universalIdentifiers from STANDARD_OBJECTS
|
||||
const COMPANY_UNIVERSAL_ID = '20202020-b374-4779-a561-80086cb2e17f';
|
||||
const PERSON_UNIVERSAL_ID = '20202020-e674-48e5-a542-72570eee7213';
|
||||
|
||||
// Standard field universalIdentifiers
|
||||
const COMPANY_NAME_FIELD_UNIVERSAL_ID = '20202020-4d99-4e2e-a84c-4a27837b1ece';
|
||||
|
||||
// Enrichment job field universalIdentifiers
|
||||
const MOCK_ENRICHMENT_JOB_STATUS_FIELD_UNIVERSAL_ID =
|
||||
'a1b2c3d4-0000-0000-0000-000000000101';
|
||||
const MOCK_ENRICHMENT_JOB_PROVIDER_FIELD_UNIVERSAL_ID =
|
||||
'a1b2c3d4-0000-0000-0000-000000000102';
|
||||
const MOCK_ENRICHMENT_JOB_ENRICHED_AT_FIELD_UNIVERSAL_ID =
|
||||
'a1b2c3d4-0000-0000-0000-000000000103';
|
||||
const MOCK_ENRICHMENT_JOB_RECORD_ID_FIELD_UNIVERSAL_ID =
|
||||
'a1b2c3d4-0000-0000-0000-000000000104';
|
||||
|
||||
// App-provided field universalIdentifiers on standard objects
|
||||
const COMPANY_INDUSTRY_FIELD_UNIVERSAL_ID =
|
||||
'a1b2c3d4-0000-0000-0000-000000000201';
|
||||
const COMPANY_EMPLOYEE_COUNT_FIELD_UNIVERSAL_ID =
|
||||
'a1b2c3d4-0000-0000-0000-000000000202';
|
||||
const PERSON_LINKEDIN_URL_FIELD_UNIVERSAL_ID =
|
||||
'a1b2c3d4-0000-0000-0000-000000000203';
|
||||
const PERSON_JOB_TITLE_FIELD_UNIVERSAL_ID =
|
||||
'a1b2c3d4-0000-0000-0000-000000000204';
|
||||
|
||||
const MOCK_LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="#1a2744"><ellipse cx="38" cy="20" rx="28" ry="10"/><rect x="10" y="20" width="56" height="50"/><ellipse cx="38" cy="70" rx="28" ry="10"/><ellipse cx="38" cy="35" rx="28" ry="10" fill="none" stroke="#fff" stroke-width="3"/><ellipse cx="38" cy="52" rx="28" ry="10" fill="none" stroke="#fff" stroke-width="3"/><circle cx="72" cy="62" r="22" fill="#1a2744"/><circle cx="72" cy="62" r="18" fill="#fff"/><path d="M72 50 L72 74 M62 58 L72 48 L82 58" stroke="#1a2744" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>`;
|
||||
|
||||
export const MOCKED_MARKETPLACE_APP: MarketplaceAppDTO = {
|
||||
id: MOCK_APP_ID,
|
||||
name: 'Data Enrichment',
|
||||
description: 'Enrich your data easily. Choose your provider.',
|
||||
icon: 'IconSparkles',
|
||||
version: '1.0.0',
|
||||
author: 'Cosmos Labs',
|
||||
category: 'Data',
|
||||
logo: `data:image/svg+xml,${encodeURIComponent(MOCK_LOGO_SVG)}`,
|
||||
screenshots: [
|
||||
'https://placehold.co/800x400/f5f5f5/666?text=Screenshot+1',
|
||||
'https://placehold.co/800x400/f5f5f5/666?text=Screenshot+2',
|
||||
'https://placehold.co/800x400/f5f5f5/666?text=Screenshot+3',
|
||||
],
|
||||
aboutDescription:
|
||||
'Enhance your workspace with automated data intelligence. This app monitors your new records and automatically populates missing details such as job titles, company size, social profiles, and industry insights.',
|
||||
providers: ['Clearbit', 'Apollo', 'Hunter.io'],
|
||||
websiteUrl: 'https://google.com',
|
||||
termsUrl: 'https://google.com',
|
||||
objects: [
|
||||
{
|
||||
universalIdentifier: MOCK_ENRICHMENT_JOB_UNIVERSAL_ID,
|
||||
nameSingular: 'enrichmentJob',
|
||||
namePlural: 'enrichmentJobs',
|
||||
labelSingular: 'Enrichment Job',
|
||||
labelPlural: 'Enrichment Jobs',
|
||||
description: 'Tracks data enrichment requests and their status',
|
||||
icon: 'IconSparkles',
|
||||
fields: [
|
||||
{
|
||||
name: 'status',
|
||||
type: 'SELECT',
|
||||
label: 'Status',
|
||||
description: 'Current status of the enrichment job',
|
||||
icon: 'IconProgressCheck',
|
||||
universalIdentifier: MOCK_ENRICHMENT_JOB_STATUS_FIELD_UNIVERSAL_ID,
|
||||
objectUniversalIdentifier: MOCK_ENRICHMENT_JOB_UNIVERSAL_ID,
|
||||
},
|
||||
{
|
||||
name: 'provider',
|
||||
type: 'TEXT',
|
||||
label: 'Provider',
|
||||
description: 'Enrichment provider used',
|
||||
icon: 'IconCloud',
|
||||
universalIdentifier: MOCK_ENRICHMENT_JOB_PROVIDER_FIELD_UNIVERSAL_ID,
|
||||
objectUniversalIdentifier: MOCK_ENRICHMENT_JOB_UNIVERSAL_ID,
|
||||
},
|
||||
{
|
||||
name: 'enrichedAt',
|
||||
type: 'DATE_TIME',
|
||||
label: 'Enriched At',
|
||||
description: 'When the enrichment was completed',
|
||||
icon: 'IconCalendar',
|
||||
universalIdentifier:
|
||||
MOCK_ENRICHMENT_JOB_ENRICHED_AT_FIELD_UNIVERSAL_ID,
|
||||
objectUniversalIdentifier: MOCK_ENRICHMENT_JOB_UNIVERSAL_ID,
|
||||
},
|
||||
{
|
||||
name: 'recordId',
|
||||
type: 'TEXT',
|
||||
label: 'Record ID',
|
||||
description: 'ID of the enriched record',
|
||||
icon: 'IconKey',
|
||||
universalIdentifier: MOCK_ENRICHMENT_JOB_RECORD_ID_FIELD_UNIVERSAL_ID,
|
||||
objectUniversalIdentifier: MOCK_ENRICHMENT_JOB_UNIVERSAL_ID,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: 'industry',
|
||||
type: 'TEXT',
|
||||
label: 'Industry',
|
||||
description: 'Company industry from enrichment',
|
||||
icon: 'IconBuildingFactory2',
|
||||
objectUniversalIdentifier: COMPANY_UNIVERSAL_ID,
|
||||
universalIdentifier: COMPANY_INDUSTRY_FIELD_UNIVERSAL_ID,
|
||||
},
|
||||
{
|
||||
name: 'employeeCount',
|
||||
type: 'NUMBER',
|
||||
label: 'Employee Count',
|
||||
description: 'Number of employees from enrichment',
|
||||
icon: 'IconUsers',
|
||||
objectUniversalIdentifier: COMPANY_UNIVERSAL_ID,
|
||||
universalIdentifier: COMPANY_EMPLOYEE_COUNT_FIELD_UNIVERSAL_ID,
|
||||
},
|
||||
{
|
||||
name: 'linkedInUrl',
|
||||
type: 'LINKS',
|
||||
label: 'LinkedIn URL',
|
||||
description: 'LinkedIn profile URL from enrichment',
|
||||
icon: 'IconBrandLinkedin',
|
||||
objectUniversalIdentifier: PERSON_UNIVERSAL_ID,
|
||||
universalIdentifier: PERSON_LINKEDIN_URL_FIELD_UNIVERSAL_ID,
|
||||
},
|
||||
{
|
||||
name: 'jobTitle',
|
||||
type: 'TEXT',
|
||||
label: 'Job Title',
|
||||
description: 'Job title from enrichment',
|
||||
icon: 'IconBriefcase',
|
||||
objectUniversalIdentifier: PERSON_UNIVERSAL_ID,
|
||||
universalIdentifier: PERSON_JOB_TITLE_FIELD_UNIVERSAL_ID,
|
||||
},
|
||||
],
|
||||
logicFunctions: [
|
||||
{
|
||||
name: 'enrich-on-create',
|
||||
description: 'Automatically enriches new records when they are created',
|
||||
timeoutSeconds: 30,
|
||||
},
|
||||
],
|
||||
frontComponents: [],
|
||||
defaultRole: {
|
||||
id: MOCK_ROLE_ID,
|
||||
label: 'Data Enrichment default role',
|
||||
description: 'Default permissions for the Data Enrichment app',
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: true,
|
||||
canSoftDeleteAllObjectRecords: true,
|
||||
canDestroyAllObjectRecords: true,
|
||||
canUpdateAllSettings: false,
|
||||
canAccessAllTools: false,
|
||||
objectPermissions: [
|
||||
{
|
||||
// Company: revoke soft-delete
|
||||
objectUniversalIdentifier: COMPANY_UNIVERSAL_ID,
|
||||
canReadObjectRecords: true,
|
||||
canUpdateObjectRecords: true,
|
||||
canSoftDeleteObjectRecords: false,
|
||||
canDestroyObjectRecords: false,
|
||||
},
|
||||
{
|
||||
// Person: no overrides (matches defaults)
|
||||
objectUniversalIdentifier: PERSON_UNIVERSAL_ID,
|
||||
canReadObjectRecords: true,
|
||||
canUpdateObjectRecords: true,
|
||||
canSoftDeleteObjectRecords: true,
|
||||
canDestroyObjectRecords: false,
|
||||
},
|
||||
{
|
||||
// Enrichment Job (app-custom object): revoke soft-delete
|
||||
objectUniversalIdentifier: MOCK_ENRICHMENT_JOB_UNIVERSAL_ID,
|
||||
canReadObjectRecords: true,
|
||||
canUpdateObjectRecords: true,
|
||||
canSoftDeleteObjectRecords: false,
|
||||
canDestroyObjectRecords: false,
|
||||
},
|
||||
],
|
||||
fieldPermissions: [
|
||||
{
|
||||
objectUniversalIdentifier: COMPANY_UNIVERSAL_ID,
|
||||
fieldUniversalIdentifier: COMPANY_NAME_FIELD_UNIVERSAL_ID,
|
||||
canReadFieldValue: true,
|
||||
canUpdateFieldValue: false,
|
||||
},
|
||||
{
|
||||
objectUniversalIdentifier: PERSON_UNIVERSAL_ID,
|
||||
fieldUniversalIdentifier: PERSON_JOB_TITLE_FIELD_UNIVERSAL_ID,
|
||||
canReadFieldValue: true,
|
||||
canUpdateFieldValue: false,
|
||||
},
|
||||
],
|
||||
permissionFlags: ['DATA_MODEL', 'API_KEYS_AND_WEBHOOKS'],
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user