Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add workflow and version as standard object #6412

Merged
merged 1 commit into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { viewPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/view';
import { workflowPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/workflow';
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';

// TODO: implement dry-run
Expand Down Expand Up @@ -124,6 +125,7 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
dataSourceMetadata.schema,
workspaceId,
);
await workflowPrefillData(entityManager, dataSourceMetadata.schema);

if (workspaceId === SEED_APPLE_WORKSPACE_ID) {
await seedMessageThread(entityManager, dataSourceMetadata.schema);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ export const seedFeatureFlags = async (
workspaceId: workspaceId,
value: true,
},
{
key: FeatureFlagKeys.IsWorkflowEnabled,
workspaceId: workspaceId,
value: false,
},
])
.execute();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export enum FeatureFlagKeys {
IsMessagingAliasFetchingEnabled = 'IS_MESSAGING_ALIAS_FETCHING_ENABLED',
IsGoogleCalendarSyncV2Enabled = 'IS_GOOGLE_CALENDAR_SYNC_V2_ENABLED',
IsFreeAccessEnabled = 'IS_FREE_ACCESS_ENABLED',
IsWorkflowEnabled = 'IS_WORKFLOW_ENABLED',
}

@Entity({ name: 'featureFlag', schema: 'core' })
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { DataSource, EntityManager } from 'typeorm';

import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { viewPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/view';
import { companyPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/company';
import { personPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/person';
import { viewPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/view';
import { workflowPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/workflow';

export const standardObjectsPrefillData = async (
workspaceDataSource: DataSource,
Expand Down Expand Up @@ -34,6 +35,7 @@ export const standardObjectsPrefillData = async (
workspaceDataSource.transaction(async (entityManager: EntityManager) => {
await companyPrefillData(entityManager, schemaName);
await personPrefillData(entityManager, schemaName);
await workflowPrefillData(entityManager, schemaName);
await viewPrefillData(entityManager, schemaName, objectMetadataMap);
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WORKFLOW_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';

export const viewWorkflowFields = (
viewId: string,
objectMetadataMap: Record<string, ObjectMetadataEntity>,
) => {
return [
{
fieldMetadataId:
objectMetadataMap[STANDARD_OBJECT_IDS.workflow].fields[
WORKFLOW_STANDARD_FIELD_IDS.name
],
viewId: viewId,
position: 0,
isVisible: true,
size: 210,
},
{
fieldMetadataId:
objectMetadataMap[STANDARD_OBJECT_IDS.workflow].fields[
WORKFLOW_STANDARD_FIELD_IDS.publishedVersionId
],
viewId: viewId,
position: 1,
isVisible: true,
size: 150,
},
];
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { EntityManager } from 'typeorm';

import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { viewCompanyFields } from 'src/engine/workspace-manager/standard-objects-prefill-data/view-company-fields';
import { viewPersonFields } from 'src/engine/workspace-manager/standard-objects-prefill-data/view-person-fields';
import { viewOpportunityFields } from 'src/engine/workspace-manager/standard-objects-prefill-data/view-opportunity-fields';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
import { viewPersonFields } from 'src/engine/workspace-manager/standard-objects-prefill-data/view-person-fields';
import { viewWorkflowFields } from 'src/engine/workspace-manager/standard-objects-prefill-data/view-workflow-fields';
import { OPPORTUNITY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';

export const viewPrefillData = async (
entityManager: EntityManager,
Expand Down Expand Up @@ -64,6 +65,15 @@ export const viewPrefillData = async (
OPPORTUNITY_STANDARD_FIELD_IDS.stage
],
},
{
name: 'All Workflows',
objectMetadataId: objectMetadataMap[STANDARD_OBJECT_IDS.workflow].id,
type: 'table',
key: 'INDEX',
position: 0,
icon: 'IconSettingsAutomation',
kanbanFieldMetadataId: '',
},
])
.returning('*')
.execute();
Expand Down Expand Up @@ -92,6 +102,7 @@ export const viewPrefillData = async (
objectMetadataMap,
),
...viewOpportunityFields(viewIdMap['By Stage'], objectMetadataMap),
...viewWorkflowFields(viewIdMap['All Workflows'], objectMetadataMap),
])
.execute();
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { EntityManager } from 'typeorm';

export const workflowPrefillData = async (
entityManager: EntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}.workflow`, ['name', 'publishedVersionId', 'position'])
.orIgnore()
.values([
{
name: 'Update Subscription Status',
publishedVersionId: null,
position: 1,
},
])
.returning('*')
.execute();
};
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export class AddStandardIdCommand extends CommandRunner {
IS_MESSAGING_ALIAS_FETCHING_ENABLED: true,
IS_GOOGLE_CALENDAR_SYNC_V2_ENABLED: true,
IS_FREE_ACCESS_ENABLED: false,
IS_WORKFLOW_ENABLED: false,
},
);
const standardFieldMetadataCollection = this.standardFieldFactory.create(
Expand All @@ -80,6 +81,7 @@ export class AddStandardIdCommand extends CommandRunner {
IS_MESSAGING_ALIAS_FETCHING_ENABLED: true,
IS_GOOGLE_CALENDAR_SYNC_V2_ENABLED: true,
IS_FREE_ACCESS_ENABLED: false,
IS_WORKFLOW_ENABLED: false,
},
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const ACTIVITY_TARGET_STANDARD_FIELD_IDS = {
person: '20202020-4afd-4ae7-99c2-de57d795a93f',
company: '20202020-7cc0-44a1-8068-f11171fdd02e',
opportunity: '20202020-1fc2-4af1-8c91-7901ee0fd38b',
workflow: '20202020-a63d-40d0-b24d-ddcc1347d583',
custom: '20202020-7f21-442f-94be-32462281b1ca',
};

Expand Down Expand Up @@ -42,6 +43,7 @@ export const ATTACHMENT_STANDARD_FIELD_IDS = {
person: '20202020-0158-4aa2-965c-5cdafe21ffa2',
company: '20202020-ceab-4a28-b546-73b06b4c08d5',
opportunity: '20202020-7374-499d-bea3-9354890755b5',
workflow: '20202020-0906-4dc3-b26f-edc951c7ab82',
custom: '20202020-302d-43b3-9aea-aa4f89282a9f',
};

Expand Down Expand Up @@ -180,6 +182,7 @@ export const TIMELINE_ACTIVITY_STANDARD_FIELD_IDS = {
person: '20202020-c414-45b9-a60a-ac27aa96229f',
company: '20202020-04ad-4221-a744-7a8278a5ce21',
opportunity: '20202020-7664-4a35-a3df-580d389fd527',
workflow: '20202020-9e59-4030-aa27-55abd676c3c8',
custom: '20202020-4a71-41b0-9f83-9cdcca3f8b14',
linkedRecordCachedName: '20202020-cfdb-4bef-bbce-a29f41230934',
linkedRecordId: '20202020-2e0e-48c0-b445-ee6c1e61687d',
Expand All @@ -192,6 +195,7 @@ export const FAVORITE_STANDARD_FIELD_IDS = {
person: '20202020-c428-4f40-b6f3-86091511c41c',
company: '20202020-cff5-4682-8bf9-069169e08279',
opportunity: '20202020-dabc-48e1-8318-2781a2b32aa2',
workflow: '20202020-b11b-4dc8-999a-6bd0a947b463',
custom: '20202020-855a-4bc8-9861-79deef37011f',
};

Expand Down Expand Up @@ -323,6 +327,23 @@ export const WEBHOOK_STANDARD_FIELD_IDS = {
operation: '20202020-15b7-458e-bf30-74770a54410c',
};

export const WORKFLOW_STANDARD_FIELD_IDS = {
name: '20202020-b3d3-478f-acc0-5d901e725b20',
publishedVersionId: '20202020-326a-4fba-8639-3456c0a169e8',
versions: '20202020-9432-416e-8f3c-27ee3153d099',
position: '20202020-39b0-4d8c-8c5f-33c2326deb5f',
favorites: '20202020-c554-4c41-be7a-cf9cd4b0d512',
activityTargets: '20202020-9d65-445a-899d-1c6f1cf3a9ab',
attachments: '20202020-ea95-4d4d-81cd-9921740316b8',
timelineActivities: '20202020-dd79-492a-9d11-58333ed0f71a',
};

export const WORKFLOW_VERSION_STANDARD_FIELD_IDS = {
name: '20202020-a12f-4cca-9937-a2e40cc65509',
workflow: '20202020-afa3-46c3-91b0-0631ca6aa1c8',
trigger: '20202020-4eae-43e7-86e0-212b41a30b48',
};

export const WORKSPACE_MEMBER_STANDARD_FIELD_IDS = {
name: '20202020-e914-43a6-9c26-3603c59065f4',
colorScheme: '20202020-66bc-47f2-adac-f2ef7c598b63',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ export const STANDARD_OBJECT_IDS = {
viewSort: '20202020-e46a-47a8-939a-e5d911f83531',
view: '20202020-722e-4739-8e2c-0c372d661f49',
webhook: '20202020-be4d-4e08-811d-0fffcd13ffd4',
workspaceMember: '20202020-3319-4234-a34c-82d5c0e881a6',
workflow: '20202020-62be-406c-b9ca-8caa50d51392',
workflowVersion: '20202020-d65d-4ab9-9344-d77bfb376a3d',
workspaceMember: '20202020-2632-4659-9540-567498166593',
};
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { ViewFilterWorkspaceEntity } from 'src/modules/view/standard-objects/vie
import { ViewSortWorkspaceEntity } from 'src/modules/view/standard-objects/view-sort.workspace-entity';
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
import { WebhookWorkspaceEntity } from 'src/modules/webhook/standard-objects/webhook.workspace-entity';
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/standard-objects/workflow-version.workspace-entity';
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/standard-objects/workflow.workspace-entity';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';

// TODO: Maybe we should automate this with the DiscoverService of Nest.JS
Expand Down Expand Up @@ -53,6 +55,8 @@ export const standardObjectMetadataDefinitions = [
ViewSortWorkspaceEntity,
ViewWorkspaceEntity,
WebhookWorkspaceEntity,
WorkflowWorkspaceEntity,
WorkflowVersionWorkspaceEntity,
WorkspaceMemberWorkspaceEntity,
MessageThreadWorkspaceEntity,
MessageWorkspaceEntity,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';

import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
import { WorkspaceDynamicRelation } from 'src/engine/twenty-orm/decorators/workspace-dynamic-relation.decorator';
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
import { ACTIVITY_TARGET_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
import { ActivityWorkspaceEntity } from 'src/modules/activity/standard-objects/activity.workspace-entity';
import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/company.workspace-entity';
import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
import { WorkspaceDynamicRelation } from 'src/engine/twenty-orm/decorators/workspace-dynamic-relation.decorator';
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/standard-objects/workflow.workspace-entity';

@WorkspaceEntity({
standardId: STANDARD_OBJECT_IDS.activityTarget,
Expand Down Expand Up @@ -86,6 +87,21 @@ export class ActivityTargetWorkspaceEntity extends BaseWorkspaceEntity {
@WorkspaceJoinColumn('opportunity')
opportunityId: string | null;

@WorkspaceRelation({
standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.workflow,
type: RelationMetadataType.MANY_TO_ONE,
label: 'Workflow',
description: 'ActivityTarget workflow',
icon: 'IconSettingsAutomation',
inverseSideTarget: () => WorkflowWorkspaceEntity,
inverseSideFieldKey: 'activityTargets',
})
@WorkspaceIsNullable()
workflow: Relation<WorkflowWorkspaceEntity> | null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also have to gate the relations


@WorkspaceJoinColumn('workflow')
workflowId: string | null;

@WorkspaceDynamicRelation({
type: RelationMetadataType.MANY_TO_ONE,
argsFactory: (oppositeObjectMetadata) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ActivityWorkspaceEntity } from 'src/modules/activity/standard-objects/a
import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/company.workspace-entity';
import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/standard-objects/workflow.workspace-entity';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';

@WorkspaceEntity({
Expand Down Expand Up @@ -133,6 +134,21 @@ export class AttachmentWorkspaceEntity extends BaseWorkspaceEntity {
@WorkspaceJoinColumn('opportunity')
opportunityId: string | null;

@WorkspaceRelation({
standardId: ATTACHMENT_STANDARD_FIELD_IDS.workflow,
type: RelationMetadataType.MANY_TO_ONE,
label: 'Workflow',
description: 'Attachment workflow',
icon: 'IconSettingsAutomation',
inverseSideTarget: () => WorkflowWorkspaceEntity,
inverseSideFieldKey: 'attachments',
})
@WorkspaceIsNullable()
workflow: Relation<WorkflowWorkspaceEntity> | null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here


@WorkspaceJoinColumn('workflow')
workflowId: string | null;

@WorkspaceDynamicRelation({
type: RelationMetadataType.MANY_TO_ONE,
argsFactory: (oppositeObjectMetadata) => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';

import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
import { WorkspaceDynamicRelation } from 'src/engine/twenty-orm/decorators/workspace-dynamic-relation.decorator';
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator';
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
import { FAVORITE_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity';
import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/company.workspace-entity';
import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/standard-objects/workflow.workspace-entity';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator';
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
import { WorkspaceDynamicRelation } from 'src/engine/twenty-orm/decorators/workspace-dynamic-relation.decorator';
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';

@WorkspaceEntity({
standardId: STANDARD_OBJECT_IDS.favorite,
Expand Down Expand Up @@ -100,6 +101,21 @@ export class FavoriteWorkspaceEntity extends BaseWorkspaceEntity {
@WorkspaceJoinColumn('opportunity')
opportunityId: string;

@WorkspaceRelation({
standardId: FAVORITE_STANDARD_FIELD_IDS.workflow,
type: RelationMetadataType.MANY_TO_ONE,
label: 'Workflow',
description: 'Favorite workflow',
icon: 'IconSettingsAutomation',
inverseSideTarget: () => WorkflowWorkspaceEntity,
inverseSideFieldKey: 'favorites',
})
@WorkspaceIsNullable()
workflow: Relation<WorkflowWorkspaceEntity> | null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here


@WorkspaceJoinColumn('workflow')
workflowId: string;

@WorkspaceDynamicRelation({
type: RelationMetadataType.MANY_TO_ONE,
argsFactory: (oppositeObjectMetadata) => ({
Expand Down
Loading
Loading