@ -20,7 +20,7 @@ const documents = {
|
|||||||
"\n mutation UpdateOneObjectMetadataItem(\n $idToUpdate: ID!\n $updatePayload: UpdateObjectInput!\n ) {\n updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n }\n }\n": types.UpdateOneObjectMetadataItemDocument,
|
"\n mutation UpdateOneObjectMetadataItem(\n $idToUpdate: ID!\n $updatePayload: UpdateObjectInput!\n ) {\n updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n }\n }\n": types.UpdateOneObjectMetadataItemDocument,
|
||||||
"\n mutation DeleteOneObjectMetadataItem($idToDelete: ID!) {\n deleteOneObject(input: { id: $idToDelete }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n }\n }\n": types.DeleteOneObjectMetadataItemDocument,
|
"\n mutation DeleteOneObjectMetadataItem($idToDelete: ID!) {\n deleteOneObject(input: { id: $idToDelete }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n }\n }\n": types.DeleteOneObjectMetadataItemDocument,
|
||||||
"\n mutation DeleteOneFieldMetadataItem($idToDelete: ID!) {\n deleteOneField(input: { id: $idToDelete }) {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n": types.DeleteOneFieldMetadataItemDocument,
|
"\n mutation DeleteOneFieldMetadataItem($idToDelete: ID!) {\n deleteOneField(input: { id: $idToDelete }) {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n": types.DeleteOneFieldMetadataItemDocument,
|
||||||
"\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n": types.ObjectMetadataItemsDocument,
|
"\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n": types.ObjectMetadataItemsDocument,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,7 +68,7 @@ export function graphql(source: "\n mutation DeleteOneFieldMetadataItem($idToDe
|
|||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n"): (typeof documents)["\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n"];
|
export function graphql(source: "\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n"): (typeof documents)["\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n"];
|
||||||
|
|
||||||
export function graphql(source: string) {
|
export function graphql(source: string) {
|
||||||
return (documents as any)[source] ?? {};
|
return (documents as any)[source] ?? {};
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -46,6 +46,7 @@ export const FIND_MANY_OBJECT_METADATA_ITEMS = gql`
|
|||||||
dataSourceId
|
dataSourceId
|
||||||
nameSingular
|
nameSingular
|
||||||
namePlural
|
namePlural
|
||||||
|
isSystem
|
||||||
}
|
}
|
||||||
toFieldMetadataId
|
toFieldMetadataId
|
||||||
}
|
}
|
||||||
@ -57,6 +58,7 @@ export const FIND_MANY_OBJECT_METADATA_ITEMS = gql`
|
|||||||
dataSourceId
|
dataSourceId
|
||||||
nameSingular
|
nameSingular
|
||||||
namePlural
|
namePlural
|
||||||
|
isSystem
|
||||||
}
|
}
|
||||||
fromFieldMetadataId
|
fromFieldMetadataId
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,6 +54,26 @@ export const useMapFieldMetadataToGraphQLQuery = () => {
|
|||||||
)
|
)
|
||||||
.join('\n')}
|
.join('\n')}
|
||||||
}`;
|
}`;
|
||||||
|
} else if (
|
||||||
|
fieldType === 'RELATION' &&
|
||||||
|
field.toRelationMetadata?.relationType === 'ONE_TO_ONE'
|
||||||
|
) {
|
||||||
|
const relationMetadataItem = objectMetadataItems.find(
|
||||||
|
(objectMetadataItem) =>
|
||||||
|
objectMetadataItem.id ===
|
||||||
|
(field.toRelationMetadata as any)?.fromObjectMetadata?.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
return `${field.name}
|
||||||
|
{
|
||||||
|
id
|
||||||
|
${(relationMetadataItem?.fields ?? [])
|
||||||
|
.filter((field) => field.type !== 'RELATION')
|
||||||
|
.map((field) =>
|
||||||
|
mapFieldMetadataToGraphQLQuery(field, maxDepthForRelations - 1),
|
||||||
|
)
|
||||||
|
.join('\n')}
|
||||||
|
}`;
|
||||||
} else if (
|
} else if (
|
||||||
fieldType === 'RELATION' &&
|
fieldType === 'RELATION' &&
|
||||||
field.fromRelationMetadata?.relationType === 'ONE_TO_MANY'
|
field.fromRelationMetadata?.relationType === 'ONE_TO_MANY'
|
||||||
|
|||||||
@ -9,7 +9,7 @@ export type FieldMetadataItem = Omit<
|
|||||||
| (Pick<Relation, 'id' | 'toFieldMetadataId' | 'relationType'> & {
|
| (Pick<Relation, 'id' | 'toFieldMetadataId' | 'relationType'> & {
|
||||||
toObjectMetadata: Pick<
|
toObjectMetadata: Pick<
|
||||||
Relation['toObjectMetadata'],
|
Relation['toObjectMetadata'],
|
||||||
'id' | 'nameSingular' | 'namePlural'
|
'id' | 'nameSingular' | 'namePlural' | 'isSystem'
|
||||||
>;
|
>;
|
||||||
})
|
})
|
||||||
| null;
|
| null;
|
||||||
@ -17,7 +17,7 @@ export type FieldMetadataItem = Omit<
|
|||||||
| (Pick<Relation, 'id' | 'fromFieldMetadataId' | 'relationType'> & {
|
| (Pick<Relation, 'id' | 'fromFieldMetadataId' | 'relationType'> & {
|
||||||
fromObjectMetadata: Pick<
|
fromObjectMetadata: Pick<
|
||||||
Relation['fromObjectMetadata'],
|
Relation['fromObjectMetadata'],
|
||||||
'id' | 'nameSingular' | 'namePlural'
|
'id' | 'nameSingular' | 'namePlural' | 'isSystem'
|
||||||
>;
|
>;
|
||||||
})
|
})
|
||||||
| null;
|
| null;
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
export enum StandardObjectNameSingular {
|
|
||||||
Company = 'company',
|
|
||||||
Person = 'person',
|
|
||||||
Opportunity = 'opportunity',
|
|
||||||
}
|
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
|
|
||||||
|
export const isObjectMetadataAvailableForRelation = (
|
||||||
|
objectMetadataItem: Pick<ObjectMetadataItem, 'isSystem' | 'nameSingular'>,
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
!objectMetadataItem.isSystem ||
|
||||||
|
objectMetadataItem.nameSingular === CoreObjectNameSingular.WorkspaceMember
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,11 +0,0 @@
|
|||||||
import { StandardObjectNameSingular } from '@/object-metadata/types/StandardObjectNameSingular';
|
|
||||||
|
|
||||||
export const isStandardObject = (objectNameSingular: string) => {
|
|
||||||
const standardObjectNames = [
|
|
||||||
StandardObjectNameSingular.Company,
|
|
||||||
StandardObjectNameSingular.Person,
|
|
||||||
StandardObjectNameSingular.Opportunity,
|
|
||||||
] as string[];
|
|
||||||
|
|
||||||
return standardObjectNames.includes(objectNameSingular);
|
|
||||||
};
|
|
||||||
@ -1,9 +1,10 @@
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
|
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
|
||||||
|
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
|
||||||
import { parseFieldRelationType } from '@/object-metadata/utils/parseFieldRelationType';
|
import { parseFieldRelationType } from '@/object-metadata/utils/parseFieldRelationType';
|
||||||
import { parseFieldType } from '@/object-metadata/utils/parseFieldType';
|
import { parseFieldType } from '@/object-metadata/utils/parseFieldType';
|
||||||
import {
|
import {
|
||||||
@ -62,7 +63,7 @@ export const RecordShowPage = () => {
|
|||||||
|
|
||||||
const { favorites, createFavorite, deleteFavorite } = useFavorites();
|
const { favorites, createFavorite, deleteFavorite } = useFavorites();
|
||||||
|
|
||||||
const [, setEntityFields] = useRecoilState(
|
const setEntityFields = useSetRecoilState(
|
||||||
entityFieldsFamilyState(objectRecordId ?? ''),
|
entityFieldsFamilyState(objectRecordId ?? ''),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -274,8 +275,21 @@ export const RecordShowPage = () => {
|
|||||||
)}
|
)}
|
||||||
</PropertyBox>
|
</PropertyBox>
|
||||||
{isRelationFieldCardEnabled &&
|
{isRelationFieldCardEnabled &&
|
||||||
relationFieldMetadataItems.map(
|
relationFieldMetadataItems
|
||||||
(fieldMetadataItem, index) => (
|
.filter((item) => {
|
||||||
|
const relationObjectMetadataItem =
|
||||||
|
item.toRelationMetadata
|
||||||
|
? item.toRelationMetadata.fromObjectMetadata
|
||||||
|
: item.fromRelationMetadata?.toObjectMetadata;
|
||||||
|
|
||||||
|
if (!relationObjectMetadataItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isObjectMetadataAvailableForRelation(
|
||||||
|
relationObjectMetadataItem,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.map((fieldMetadataItem, index) => (
|
||||||
<FieldContext.Provider
|
<FieldContext.Provider
|
||||||
key={record.id + fieldMetadataItem.id}
|
key={record.id + fieldMetadataItem.id}
|
||||||
value={{
|
value={{
|
||||||
@ -294,8 +308,7 @@ export const RecordShowPage = () => {
|
|||||||
>
|
>
|
||||||
<RecordRelationFieldCardSection />
|
<RecordRelationFieldCardSection />
|
||||||
</FieldContext.Provider>
|
</FieldContext.Provider>
|
||||||
),
|
))}
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</ShowPageLeftContainer>
|
</ShowPageLeftContainer>
|
||||||
|
|||||||
@ -6,7 +6,9 @@ import { useRelationField } from '../../hooks/useRelationField';
|
|||||||
export const RelationFieldDisplay = () => {
|
export const RelationFieldDisplay = () => {
|
||||||
const { fieldValue, fieldDefinition } = useRelationField();
|
const { fieldValue, fieldDefinition } = useRelationField();
|
||||||
|
|
||||||
const { identifiersMapper } = useRelationPicker();
|
const { identifiersMapper } = useRelationPicker({
|
||||||
|
relationPickerScopeId: 'relation-picker',
|
||||||
|
});
|
||||||
|
|
||||||
if (!fieldValue || !fieldDefinition || !identifiersMapper) {
|
if (!fieldValue || !fieldDefinition || !identifiersMapper) {
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|||||||
@ -19,7 +19,9 @@ export const useChipField = () => {
|
|||||||
|
|
||||||
const record = useRecoilValue<any | null>(entityFieldsFamilyState(entityId));
|
const record = useRecoilValue<any | null>(entityFieldsFamilyState(entityId));
|
||||||
|
|
||||||
const { identifiersMapper } = useRelationPicker();
|
const { identifiersMapper } = useRelationPicker({
|
||||||
|
relationPickerScopeId: 'relation-picker',
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
basePathToShowPage,
|
basePathToShowPage,
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import { useUpsertRecordFromState } from '@/object-record/hooks/useUpsertRecordF
|
|||||||
import { RecordRelationFieldCardContent } from '@/object-record/record-relation-card/components/RecordRelationFieldCardContent';
|
import { RecordRelationFieldCardContent } from '@/object-record/record-relation-card/components/RecordRelationFieldCardContent';
|
||||||
import { SingleEntitySelectMenuItemsWithSearch } from '@/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch';
|
import { SingleEntitySelectMenuItemsWithSearch } from '@/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch';
|
||||||
import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker';
|
import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker';
|
||||||
|
import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope';
|
||||||
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
||||||
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
||||||
import { IconForbid, IconPlus } from '@/ui/display/icon';
|
import { IconForbid, IconPlus } from '@/ui/display/icon';
|
||||||
@ -153,12 +154,10 @@ export const RecordRelationFieldCardSection = () => {
|
|||||||
|
|
||||||
const { closeDropdown, isDropdownOpen } = useDropdown(dropdownId);
|
const { closeDropdown, isDropdownOpen } = useDropdown(dropdownId);
|
||||||
|
|
||||||
const {
|
const { relationPickerSearchFilter, setRelationPickerSearchFilter } =
|
||||||
identifiersMapper,
|
useRelationPicker({ relationPickerScopeId: dropdownId });
|
||||||
relationPickerSearchFilter,
|
|
||||||
searchQuery,
|
const { identifiersMapper, searchQuery } = useRelationPicker();
|
||||||
setRelationPickerSearchFilter,
|
|
||||||
} = useRelationPicker();
|
|
||||||
|
|
||||||
const entities = useFilteredSearchEntityQuery({
|
const entities = useFilteredSearchEntityQuery({
|
||||||
filters: [
|
filters: [
|
||||||
@ -225,53 +224,55 @@ export const RecordRelationFieldCardSection = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Section>
|
<Section>
|
||||||
<StyledHeader isDropdownOpen={isDropdownOpen}>
|
<RelationPickerScope relationPickerScopeId={dropdownId}>
|
||||||
<StyledTitle>
|
<StyledHeader isDropdownOpen={isDropdownOpen}>
|
||||||
<StyledTitleLabel>{fieldDefinition.label}</StyledTitleLabel>
|
<StyledTitle>
|
||||||
{parseFieldRelationType(relationFieldMetadataItem) ===
|
<StyledTitleLabel>{fieldDefinition.label}</StyledTitleLabel>
|
||||||
'TO_ONE_OBJECT' && (
|
{parseFieldRelationType(relationFieldMetadataItem) ===
|
||||||
<StyledLink to={filterLinkHref}>
|
'TO_ONE_OBJECT' && (
|
||||||
All ({relationRecords.length})
|
<StyledLink to={filterLinkHref}>
|
||||||
</StyledLink>
|
All ({relationRecords.length})
|
||||||
)}
|
</StyledLink>
|
||||||
</StyledTitle>
|
)}
|
||||||
<DropdownScope dropdownScopeId={dropdownId}>
|
</StyledTitle>
|
||||||
<StyledAddDropdown
|
<DropdownScope dropdownScopeId={dropdownId}>
|
||||||
dropdownId={dropdownId}
|
<StyledAddDropdown
|
||||||
dropdownPlacement="right-start"
|
dropdownId={dropdownId}
|
||||||
onClose={handleCloseRelationPickerDropdown}
|
dropdownPlacement="right-start"
|
||||||
clickableComponent={
|
onClose={handleCloseRelationPickerDropdown}
|
||||||
<LightIconButton
|
clickableComponent={
|
||||||
className="displayOnHover"
|
<LightIconButton
|
||||||
Icon={IconPlus}
|
className="displayOnHover"
|
||||||
accent="tertiary"
|
Icon={IconPlus}
|
||||||
/>
|
accent="tertiary"
|
||||||
}
|
/>
|
||||||
dropdownComponents={
|
}
|
||||||
<SingleEntitySelectMenuItemsWithSearch
|
dropdownComponents={
|
||||||
EmptyIcon={IconForbid}
|
<SingleEntitySelectMenuItemsWithSearch
|
||||||
entitiesToSelect={entities.entitiesToSelect}
|
EmptyIcon={IconForbid}
|
||||||
loading={entities.loading}
|
entitiesToSelect={entities.entitiesToSelect}
|
||||||
onEntitySelected={handleRelationPickerEntitySelected}
|
loading={entities.loading}
|
||||||
/>
|
onEntitySelected={handleRelationPickerEntitySelected}
|
||||||
}
|
/>
|
||||||
dropdownHotkeyScope={{
|
}
|
||||||
scope: dropdownId,
|
dropdownHotkeyScope={{
|
||||||
}}
|
scope: dropdownId,
|
||||||
/>
|
}}
|
||||||
</DropdownScope>
|
|
||||||
</StyledHeader>
|
|
||||||
{!!relationRecords.length && (
|
|
||||||
<Card>
|
|
||||||
{relationRecords.slice(0, 5).map((relationRecord, index) => (
|
|
||||||
<RecordRelationFieldCardContent
|
|
||||||
key={`${relationRecord.id}${relationLabelIdentifierFieldMetadata?.id}`}
|
|
||||||
divider={index < relationRecords.length - 1}
|
|
||||||
relationRecord={relationRecord}
|
|
||||||
/>
|
/>
|
||||||
))}
|
</DropdownScope>
|
||||||
</Card>
|
</StyledHeader>
|
||||||
)}
|
{!!relationRecords.length && (
|
||||||
|
<Card>
|
||||||
|
{relationRecords.slice(0, 5).map((relationRecord, index) => (
|
||||||
|
<RecordRelationFieldCardContent
|
||||||
|
key={`${relationRecord.id}${relationLabelIdentifierFieldMetadata?.id}`}
|
||||||
|
divider={index < relationRecords.length - 1}
|
||||||
|
relationRecord={relationRecord}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</RelationPickerScope>
|
||||||
</Section>
|
</Section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { expect, userEvent, within } from '@storybook/test';
|
|||||||
import { IconUserCircle } from '@/ui/display/icon';
|
import { IconUserCircle } from '@/ui/display/icon';
|
||||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||||
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
||||||
|
import { RelationPickerDecorator } from '~/testing/decorators/RelationPickerDecorator';
|
||||||
import { mockedPeopleData } from '~/testing/mock-data/people';
|
import { mockedPeopleData } from '~/testing/mock-data/people';
|
||||||
import { sleep } from '~/testing/sleep';
|
import { sleep } from '~/testing/sleep';
|
||||||
|
|
||||||
@ -19,7 +20,11 @@ const entities = mockedPeopleData.map<EntityForSelect>((person) => ({
|
|||||||
const meta: Meta<typeof SingleEntitySelect> = {
|
const meta: Meta<typeof SingleEntitySelect> = {
|
||||||
title: 'UI/Input/RelationPicker/SingleEntitySelect',
|
title: 'UI/Input/RelationPicker/SingleEntitySelect',
|
||||||
component: SingleEntitySelect,
|
component: SingleEntitySelect,
|
||||||
decorators: [ComponentDecorator, ComponentWithRecoilScopeDecorator],
|
decorators: [
|
||||||
|
ComponentDecorator,
|
||||||
|
ComponentWithRecoilScopeDecorator,
|
||||||
|
RelationPickerDecorator,
|
||||||
|
],
|
||||||
argTypes: {
|
argTypes: {
|
||||||
selectedEntity: {
|
selectedEntity: {
|
||||||
options: entities.map(({ name }) => name),
|
options: entities.map(({ name }) => name),
|
||||||
|
|||||||
@ -7,9 +7,7 @@ export const useEntitySelectSearch = () => {
|
|||||||
setRelationPickerPreselectedId,
|
setRelationPickerPreselectedId,
|
||||||
relationPickerSearchFilter,
|
relationPickerSearchFilter,
|
||||||
setRelationPickerSearchFilter,
|
setRelationPickerSearchFilter,
|
||||||
} = useRelationPicker({
|
} = useRelationPicker();
|
||||||
relationPickerScopeId: 'relation-picker',
|
|
||||||
});
|
|
||||||
|
|
||||||
const debouncedSetSearchFilter = debounce(
|
const debouncedSetSearchFilter = debounce(
|
||||||
setRelationPickerSearchFilter,
|
setRelationPickerSearchFilter,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
||||||
|
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
|
||||||
import { validateMetadataLabel } from '@/object-metadata/utils/validateMetadataLabel';
|
import { validateMetadataLabel } from '@/object-metadata/utils/validateMetadataLabel';
|
||||||
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
||||||
import { IconPicker } from '@/ui/input/components/IconPicker';
|
import { IconPicker } from '@/ui/input/components/IconPicker';
|
||||||
@ -74,13 +75,13 @@ export const SettingsObjectFieldRelationForm = ({
|
|||||||
fullWidth
|
fullWidth
|
||||||
disabled={disableRelationEdition}
|
disabled={disableRelationEdition}
|
||||||
value={values.type}
|
value={values.type}
|
||||||
options={Object.entries(relationTypes).map(
|
options={Object.entries(relationTypes)
|
||||||
([value, { label, Icon }]) => ({
|
.filter(([value]) => 'ONE_TO_ONE' !== value)
|
||||||
|
.map(([value, { label, Icon }]) => ({
|
||||||
label,
|
label,
|
||||||
value: value as RelationType,
|
value: value as RelationType,
|
||||||
Icon,
|
Icon,
|
||||||
}),
|
}))}
|
||||||
)}
|
|
||||||
onChange={(value) => onChange({ type: value })}
|
onChange={(value) => onChange({ type: value })}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
@ -90,7 +91,9 @@ export const SettingsObjectFieldRelationForm = ({
|
|||||||
disabled={disableRelationEdition}
|
disabled={disableRelationEdition}
|
||||||
value={values.objectMetadataId}
|
value={values.objectMetadataId}
|
||||||
options={objectMetadataItems
|
options={objectMetadataItems
|
||||||
.filter((objectMetadataItem) => !objectMetadataItem.isSystem)
|
.filter((objectMetadataItem) =>
|
||||||
|
isObjectMetadataAvailableForRelation(objectMetadataItem),
|
||||||
|
)
|
||||||
.map((objectMetadataItem) => ({
|
.map((objectMetadataItem) => ({
|
||||||
label: objectMetadataItem.labelPlural,
|
label: objectMetadataItem.labelPlural,
|
||||||
value: objectMetadataItem.id,
|
value: objectMetadataItem.id,
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { Notes } from '@/activities/notes/components/Notes';
|
|||||||
import { ObjectTasks } from '@/activities/tasks/components/ObjectTasks';
|
import { ObjectTasks } from '@/activities/tasks/components/ObjectTasks';
|
||||||
import { Timeline } from '@/activities/timeline/components/Timeline';
|
import { Timeline } from '@/activities/timeline/components/Timeline';
|
||||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||||
import { isStandardObject } from '@/object-metadata/utils/isStandardObject';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import {
|
import {
|
||||||
IconCheckbox,
|
IconCheckbox,
|
||||||
IconMail,
|
IconMail,
|
||||||
@ -41,7 +41,7 @@ const StyledTabListContainer = styled.div`
|
|||||||
const TAB_LIST_COMPONENT_ID = 'show-page-right-tab-list';
|
const TAB_LIST_COMPONENT_ID = 'show-page-right-tab-list';
|
||||||
|
|
||||||
type ShowPageRightContainerProps = {
|
type ShowPageRightContainerProps = {
|
||||||
targetableObject?: ActivityTargetableObject;
|
targetableObject: ActivityTargetableObject;
|
||||||
timeline?: boolean;
|
timeline?: boolean;
|
||||||
tasks?: boolean;
|
tasks?: boolean;
|
||||||
notes?: boolean;
|
notes?: boolean;
|
||||||
@ -60,11 +60,10 @@ export const ShowPageRightContainer = ({
|
|||||||
const { activeTabIdState } = useTabList(TAB_LIST_COMPONENT_ID);
|
const { activeTabIdState } = useTabList(TAB_LIST_COMPONENT_ID);
|
||||||
const activeTabId = useRecoilValue(activeTabIdState());
|
const activeTabId = useRecoilValue(activeTabIdState());
|
||||||
|
|
||||||
if (!targetableObject) return <></>;
|
const { objectMetadataItem: targetableObjectMetadataItem } =
|
||||||
|
useObjectMetadataItem({
|
||||||
const targetableObjectIsStandardObject = isStandardObject(
|
objectNameSingular: targetableObject.targetObjectNameSingular,
|
||||||
targetableObject.targetObjectNameSingular,
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const TASK_TABS = [
|
const TASK_TABS = [
|
||||||
{
|
{
|
||||||
@ -90,14 +89,14 @@ export const ShowPageRightContainer = ({
|
|||||||
title: 'Files',
|
title: 'Files',
|
||||||
Icon: IconPaperclip,
|
Icon: IconPaperclip,
|
||||||
hide: !notes,
|
hide: !notes,
|
||||||
disabled: !targetableObjectIsStandardObject,
|
disabled: targetableObjectMetadataItem.isCustom,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'emails',
|
id: 'emails',
|
||||||
title: 'Emails',
|
title: 'Emails',
|
||||||
Icon: IconMail,
|
Icon: IconMail,
|
||||||
hide: !emails,
|
hide: !emails,
|
||||||
disabled: !isMessagingEnabled || !targetableObjectIsStandardObject,
|
disabled: !isMessagingEnabled || targetableObjectMetadataItem.isCustom,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -58,6 +58,7 @@ export const mockObjectMetadataItem: ObjectMetadataItem = {
|
|||||||
id: 'fcccc985-5edf-405c-aa2b-80c82b230f35',
|
id: 'fcccc985-5edf-405c-aa2b-80c82b230f35',
|
||||||
nameSingular: 'person',
|
nameSingular: 'person',
|
||||||
namePlural: 'people',
|
namePlural: 'people',
|
||||||
|
isSystem: false,
|
||||||
},
|
},
|
||||||
toFieldMetadataId: 'c756f6ff-8c00-4fe5-a923-c6cfc7b1ac4a',
|
toFieldMetadataId: 'c756f6ff-8c00-4fe5-a923-c6cfc7b1ac4a',
|
||||||
},
|
},
|
||||||
@ -85,6 +86,7 @@ export const mockObjectMetadataItem: ObjectMetadataItem = {
|
|||||||
id: '169e5b21-dc95-44a8-acd0-5e9447dd0784',
|
id: '169e5b21-dc95-44a8-acd0-5e9447dd0784',
|
||||||
nameSingular: 'opportunity',
|
nameSingular: 'opportunity',
|
||||||
namePlural: 'opportunities',
|
namePlural: 'opportunities',
|
||||||
|
isSystem: false,
|
||||||
},
|
},
|
||||||
toFieldMetadataId: '00468e2a-a601-4635-ae9c-a9bb826cc860',
|
toFieldMetadataId: '00468e2a-a601-4635-ae9c-a9bb826cc860',
|
||||||
},
|
},
|
||||||
@ -112,6 +114,7 @@ export const mockObjectMetadataItem: ObjectMetadataItem = {
|
|||||||
id: 'b87c6cac-a8e7-4156-a525-30ec536acd75',
|
id: 'b87c6cac-a8e7-4156-a525-30ec536acd75',
|
||||||
nameSingular: 'activityTarget',
|
nameSingular: 'activityTarget',
|
||||||
namePlural: 'activityTargets',
|
namePlural: 'activityTargets',
|
||||||
|
isSystem: true,
|
||||||
},
|
},
|
||||||
toFieldMetadataId: 'bba19feb-c248-487b-92d7-98df54c51e44',
|
toFieldMetadataId: 'bba19feb-c248-487b-92d7-98df54c51e44',
|
||||||
},
|
},
|
||||||
@ -213,6 +216,7 @@ export const mockObjectMetadataItem: ObjectMetadataItem = {
|
|||||||
id: '77240b4b-6bcf-454d-a102-19bbba181716',
|
id: '77240b4b-6bcf-454d-a102-19bbba181716',
|
||||||
nameSingular: 'attachment',
|
nameSingular: 'attachment',
|
||||||
namePlural: 'attachments',
|
namePlural: 'attachments',
|
||||||
|
isSystem: true,
|
||||||
},
|
},
|
||||||
toFieldMetadataId: '0880dac5-37d2-43a6-b143-722126d4923f',
|
toFieldMetadataId: '0880dac5-37d2-43a6-b143-722126d4923f',
|
||||||
},
|
},
|
||||||
@ -322,6 +326,7 @@ export const mockObjectMetadataItem: ObjectMetadataItem = {
|
|||||||
id: '92c306ce-ad06-4712-99d2-5d0daf13c95f',
|
id: '92c306ce-ad06-4712-99d2-5d0daf13c95f',
|
||||||
nameSingular: 'workspaceMember',
|
nameSingular: 'workspaceMember',
|
||||||
namePlural: 'workspaceMembers',
|
namePlural: 'workspaceMembers',
|
||||||
|
isSystem: true,
|
||||||
},
|
},
|
||||||
fromFieldMetadataId: '0f3e456f-3bb4-4261-a436-95246dc0e159',
|
fromFieldMetadataId: '0f3e456f-3bb4-4261-a436-95246dc0e159',
|
||||||
},
|
},
|
||||||
@ -368,6 +373,7 @@ export const mockObjectMetadataItem: ObjectMetadataItem = {
|
|||||||
id: '1415392e-0ecb-462e-aa67-001e424e6a37',
|
id: '1415392e-0ecb-462e-aa67-001e424e6a37',
|
||||||
nameSingular: 'favorite',
|
nameSingular: 'favorite',
|
||||||
namePlural: 'favorites',
|
namePlural: 'favorites',
|
||||||
|
isSystem: true,
|
||||||
},
|
},
|
||||||
toFieldMetadataId: '8fd8965b-bd4e-4a9b-90e9-c75652dadda1',
|
toFieldMetadataId: '8fd8965b-bd4e-4a9b-90e9-c75652dadda1',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -35,7 +35,7 @@ describe('FilterInputFactory', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
expect(() => service.create(request, objectMetadata)).toThrow(
|
expect(() => service.create(request, objectMetadata)).toThrow(
|
||||||
"field 'wrongField' does not exist in 'testingObject' object",
|
"field 'wrongField' does not exist in 'objectName' object",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -112,7 +112,7 @@ describe('OrderByInputFactory', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
expect(() => service.create(request, objectMetadata)).toThrow(
|
expect(() => service.create(request, objectMetadata)).toThrow(
|
||||||
"field 'wrongField' does not exist in 'testingObject' object",
|
"field 'wrongField' does not exist in 'objectName' object",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
|
|
||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
export const getFieldType = (
|
export const getFieldType = (
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
@ -28,7 +29,9 @@ export const checkFields = (objectMetadataItem, fieldNames): void => {
|
|||||||
.includes(fieldName)
|
.includes(fieldName)
|
||||||
) {
|
) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
`field '${fieldName}' does not exist in '${objectMetadataItem.targetTableName}' object`,
|
`field '${fieldName}' does not exist in '${computeObjectTargetTable(
|
||||||
|
objectMetadataItem,
|
||||||
|
)}' object`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import { Record as IRecord } from 'src/workspace/workspace-query-builder/interfaces/record.interface';
|
import { Record as IRecord } from 'src/workspace/workspace-query-builder/interfaces/record.interface';
|
||||||
|
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||||
|
|
||||||
import { isWorkEmail } from 'src/utils/is-work-email';
|
import { isWorkEmail } from 'src/utils/is-work-email';
|
||||||
import { stringifyWithoutKeyQuote } from 'src/workspace/workspace-query-builder/utils/stringify-without-key-quote.util';
|
import { stringifyWithoutKeyQuote } from 'src/workspace/workspace-query-builder/utils/stringify-without-key-quote.util';
|
||||||
@ -17,7 +18,19 @@ export class QuickActionsService {
|
|||||||
private readonly intelligenceService: IntelligenceService,
|
private readonly intelligenceService: IntelligenceService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async createCompanyFromPerson(id: string, workspaceId: string) {
|
async createCompanyFromPerson(
|
||||||
|
id: string,
|
||||||
|
workspaceId: string,
|
||||||
|
objectMetadataItemCollection: ObjectMetadataInterface[],
|
||||||
|
) {
|
||||||
|
const personObjectMetadata = objectMetadataItemCollection.find(
|
||||||
|
(item) => item.nameSingular === 'person',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!personObjectMetadata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const personRequest =
|
const personRequest =
|
||||||
await this.workspaceQueryRunnunerService.executeAndParse<IRecord>(
|
await this.workspaceQueryRunnunerService.executeAndParse<IRecord>(
|
||||||
`query {
|
`query {
|
||||||
@ -32,7 +45,7 @@ export class QuickActionsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
'person',
|
personObjectMetadata,
|
||||||
'',
|
'',
|
||||||
workspaceId,
|
workspaceId,
|
||||||
);
|
);
|
||||||
@ -47,6 +60,14 @@ export class QuickActionsService {
|
|||||||
const companyName = capitalize(companyDomainName.split('.')[0]);
|
const companyName = capitalize(companyDomainName.split('.')[0]);
|
||||||
let relatedCompanyId = uuidv4();
|
let relatedCompanyId = uuidv4();
|
||||||
|
|
||||||
|
const companyObjectMetadata = objectMetadataItemCollection.find(
|
||||||
|
(item) => item.nameSingular === 'company',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!companyObjectMetadata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const existingCompany =
|
const existingCompany =
|
||||||
await this.workspaceQueryRunnunerService.executeAndParse<IRecord>(
|
await this.workspaceQueryRunnunerService.executeAndParse<IRecord>(
|
||||||
`query {companyCollection(filter: {domainName: {eq: "${companyDomainName}"}}) {
|
`query {companyCollection(filter: {domainName: {eq: "${companyDomainName}"}}) {
|
||||||
@ -58,7 +79,7 @@ export class QuickActionsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
'company',
|
companyObjectMetadata,
|
||||||
'',
|
'',
|
||||||
workspaceId,
|
workspaceId,
|
||||||
);
|
);
|
||||||
@ -105,7 +126,11 @@ export class QuickActionsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeQuickActionOnCompany(id: string, workspaceId: string) {
|
async executeQuickActionOnCompany(
|
||||||
|
id: string,
|
||||||
|
workspaceId: string,
|
||||||
|
objectMetadataItem: ObjectMetadataInterface,
|
||||||
|
) {
|
||||||
const companyQuery = `query {
|
const companyQuery = `query {
|
||||||
companyCollection(filter: {id: {eq: "${id}"}}) {
|
companyCollection(filter: {id: {eq: "${id}"}}) {
|
||||||
edges {
|
edges {
|
||||||
@ -123,7 +148,7 @@ export class QuickActionsService {
|
|||||||
const companyRequest =
|
const companyRequest =
|
||||||
await this.workspaceQueryRunnunerService.executeAndParse<IRecord>(
|
await this.workspaceQueryRunnunerService.executeAndParse<IRecord>(
|
||||||
companyQuery,
|
companyQuery,
|
||||||
'company',
|
objectMetadataItem,
|
||||||
'',
|
'',
|
||||||
workspaceId,
|
workspaceId,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -39,8 +39,6 @@ export class TypeORMService implements OnModuleInit, OnModuleDestroy {
|
|||||||
): Promise<DataSource | undefined> {
|
): Promise<DataSource | undefined> {
|
||||||
const isMultiDatasourceEnabled = false;
|
const isMultiDatasourceEnabled = false;
|
||||||
|
|
||||||
console.log('Data sources number', this.dataSources.size);
|
|
||||||
|
|
||||||
if (isMultiDatasourceEnabled) {
|
if (isMultiDatasourceEnabled) {
|
||||||
// Wait for a bit before trying again if another initialization is in progress
|
// Wait for a bit before trying again if another initialization is in progress
|
||||||
while (this.isDatasourceInitializing.get(dataSource.id)) {
|
while (this.isDatasourceInitializing.get(dataSource.id)) {
|
||||||
|
|||||||
@ -71,6 +71,9 @@ export const currencyObjectDefinition = {
|
|||||||
fields: currencyFields(),
|
fields: currencyFields(),
|
||||||
fromRelations: [],
|
fromRelations: [],
|
||||||
toRelations: [],
|
toRelations: [],
|
||||||
|
isActive: true,
|
||||||
|
isSystem: true,
|
||||||
|
isCustom: false,
|
||||||
} satisfies ObjectMetadataInterface;
|
} satisfies ObjectMetadataInterface;
|
||||||
|
|
||||||
export type CurrencyMetadata = {
|
export type CurrencyMetadata = {
|
||||||
|
|||||||
@ -71,6 +71,9 @@ export const fullNameObjectDefinition = {
|
|||||||
fields: fullNameFields(),
|
fields: fullNameFields(),
|
||||||
fromRelations: [],
|
fromRelations: [],
|
||||||
toRelations: [],
|
toRelations: [],
|
||||||
|
isActive: true,
|
||||||
|
isSystem: true,
|
||||||
|
isCustom: false,
|
||||||
} satisfies ObjectMetadataInterface;
|
} satisfies ObjectMetadataInterface;
|
||||||
|
|
||||||
export type FullNameMetadata = {
|
export type FullNameMetadata = {
|
||||||
|
|||||||
@ -71,6 +71,9 @@ export const linkObjectDefinition = {
|
|||||||
fields: linkFields(),
|
fields: linkFields(),
|
||||||
fromRelations: [],
|
fromRelations: [],
|
||||||
toRelations: [],
|
toRelations: [],
|
||||||
|
isActive: true,
|
||||||
|
isSystem: true,
|
||||||
|
isCustom: false,
|
||||||
} satisfies ObjectMetadataInterface;
|
} satisfies ObjectMetadataInterface;
|
||||||
|
|
||||||
export type LinkMetadata = {
|
export type LinkMetadata = {
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
|||||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||||
import { UpdateFieldInput } from 'src/metadata/field-metadata/dtos/update-field.input';
|
import { UpdateFieldInput } from 'src/metadata/field-metadata/dtos/update-field.input';
|
||||||
import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/workspace-migration.factory';
|
import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/workspace-migration.factory';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
import { FieldMetadataEntity } from './field-metadata.entity';
|
import { FieldMetadataEntity } from './field-metadata.entity';
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
fieldMetadataInput.workspaceId,
|
fieldMetadataInput.workspaceId,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: objectMetadata.targetTableName,
|
name: computeObjectTargetTable(objectMetadata),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: this.workspaceMigrationFactory.createColumnActions(
|
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||||
WorkspaceMigrationColumnActionType.CREATE,
|
WorkspaceMigrationColumnActionType.CREATE,
|
||||||
@ -201,7 +202,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
existingFieldMetadata.workspaceId,
|
existingFieldMetadata.workspaceId,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: objectMetadata.targetTableName,
|
name: computeObjectTargetTable(objectMetadata),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: this.workspaceMigrationFactory.createColumnActions(
|
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||||
WorkspaceMigrationColumnActionType.ALTER,
|
WorkspaceMigrationColumnActionType.ALTER,
|
||||||
|
|||||||
@ -12,4 +12,7 @@ export interface ObjectMetadataInterface {
|
|||||||
fromRelations: RelationMetadataInterface[];
|
fromRelations: RelationMetadataInterface[];
|
||||||
toRelations: RelationMetadataInterface[];
|
toRelations: RelationMetadataInterface[];
|
||||||
fields: FieldMetadataInterface[];
|
fields: FieldMetadataInterface[];
|
||||||
|
isSystem: boolean;
|
||||||
|
isCustom: boolean;
|
||||||
|
isActive: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import {
|
|||||||
RelationMetadataEntity,
|
RelationMetadataEntity,
|
||||||
RelationMetadataType,
|
RelationMetadataType,
|
||||||
} from 'src/metadata/relation-metadata/relation-metadata.entity';
|
} from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||||
import { createCustomColumnName } from 'src/metadata/utils/create-custom-column-name.util';
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
import { ObjectMetadataEntity } from './object-metadata.entity';
|
import { ObjectMetadataEntity } from './object-metadata.entity';
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
const createdObjectMetadata = await super.createOne({
|
const createdObjectMetadata = await super.createOne({
|
||||||
...objectMetadataInput,
|
...objectMetadataInput,
|
||||||
dataSourceId: lastDataSourceMetadata.id,
|
dataSourceId: lastDataSourceMetadata.id,
|
||||||
targetTableName: createCustomColumnName(objectMetadataInput.nameSingular),
|
targetTableName: 'DEPRECATED',
|
||||||
isActive: true,
|
isActive: true,
|
||||||
isCustom: true,
|
isCustom: true,
|
||||||
isSystem: false,
|
isSystem: false,
|
||||||
@ -156,62 +156,74 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
createdObjectMetadata.workspaceId,
|
createdObjectMetadata.workspaceId,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: createdObjectMetadata.targetTableName,
|
name: computeObjectTargetTable(createdObjectMetadata),
|
||||||
action: 'create',
|
action: 'create',
|
||||||
} satisfies WorkspaceMigrationTableAction,
|
} satisfies WorkspaceMigrationTableAction,
|
||||||
// Add activity target relation
|
// Add activity target relation
|
||||||
{
|
{
|
||||||
name: activityTargetObjectMetadata.targetTableName,
|
name: computeObjectTargetTable(activityTargetObjectMetadata),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||||
columnName: `${createdObjectMetadata.targetTableName}Id`,
|
columnName: `${computeObjectTargetTable(
|
||||||
|
createdObjectMetadata,
|
||||||
|
)}Id`,
|
||||||
columnType: 'uuid',
|
columnType: 'uuid',
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
} satisfies WorkspaceMigrationColumnCreate,
|
} satisfies WorkspaceMigrationColumnCreate,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: activityTargetObjectMetadata.targetTableName,
|
name: computeObjectTargetTable(activityTargetObjectMetadata),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
action: WorkspaceMigrationColumnActionType.RELATION,
|
action: WorkspaceMigrationColumnActionType.RELATION,
|
||||||
columnName: `${createdObjectMetadata.targetTableName}Id`,
|
columnName: `${computeObjectTargetTable(
|
||||||
referencedTableName: createdObjectMetadata.targetTableName,
|
createdObjectMetadata,
|
||||||
|
)}Id`,
|
||||||
|
referencedTableName: computeObjectTargetTable(
|
||||||
|
createdObjectMetadata,
|
||||||
|
),
|
||||||
referencedTableColumnName: 'id',
|
referencedTableColumnName: 'id',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// Add favorite relation
|
// Add favorite relation
|
||||||
{
|
{
|
||||||
name: favoriteObjectMetadata.targetTableName,
|
name: computeObjectTargetTable(favoriteObjectMetadata),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||||
columnName: `${createdObjectMetadata.targetTableName}Id`,
|
columnName: `${computeObjectTargetTable(
|
||||||
|
createdObjectMetadata,
|
||||||
|
)}Id`,
|
||||||
columnType: 'uuid',
|
columnType: 'uuid',
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
} satisfies WorkspaceMigrationColumnCreate,
|
} satisfies WorkspaceMigrationColumnCreate,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: favoriteObjectMetadata.targetTableName,
|
name: computeObjectTargetTable(favoriteObjectMetadata),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
action: WorkspaceMigrationColumnActionType.RELATION,
|
action: WorkspaceMigrationColumnActionType.RELATION,
|
||||||
columnName: `${createdObjectMetadata.targetTableName}Id`,
|
columnName: `${computeObjectTargetTable(
|
||||||
referencedTableName: createdObjectMetadata.targetTableName,
|
createdObjectMetadata,
|
||||||
|
)}Id`,
|
||||||
|
referencedTableName: computeObjectTargetTable(
|
||||||
|
createdObjectMetadata,
|
||||||
|
),
|
||||||
referencedTableColumnName: 'id',
|
referencedTableColumnName: 'id',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// This is temporary until we implement mainIdentifier
|
// This is temporary until we implement mainIdentifier
|
||||||
{
|
{
|
||||||
name: createdObjectMetadata.targetTableName,
|
name: computeObjectTargetTable(createdObjectMetadata),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@ -384,7 +396,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||||
targetColumnMap: {
|
targetColumnMap: {
|
||||||
value: `${createdObjectMetadata.targetTableName}Id`,
|
value: `${computeObjectTargetTable(createdObjectMetadata)}Id`,
|
||||||
},
|
},
|
||||||
description: `ActivityTarget ${createdObjectMetadata.labelSingular} id foreign key`,
|
description: `ActivityTarget ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||||
icon: undefined,
|
icon: undefined,
|
||||||
@ -474,7 +486,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||||
targetColumnMap: {
|
targetColumnMap: {
|
||||||
value: `${createdObjectMetadata.targetTableName}Id`,
|
value: `${computeObjectTargetTable(createdObjectMetadata)}Id`,
|
||||||
},
|
},
|
||||||
description: `Favorite ${createdObjectMetadata.labelSingular} id foreign key`,
|
description: `Favorite ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||||
icon: undefined,
|
icon: undefined,
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.en
|
|||||||
import { WorkspaceMigrationColumnActionType } from 'src/metadata/workspace-migration/workspace-migration.entity';
|
import { WorkspaceMigrationColumnActionType } from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||||
import { createCustomColumnName } from 'src/metadata/utils/create-custom-column-name.util';
|
import { createCustomColumnName } from 'src/metadata/utils/create-custom-column-name.util';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
RelationMetadataEntity,
|
RelationMetadataEntity,
|
||||||
@ -201,8 +202,9 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
|||||||
[
|
[
|
||||||
// Create the column
|
// Create the column
|
||||||
{
|
{
|
||||||
name: objectMetadataMap[relationMetadataInput.toObjectMetadataId]
|
name: computeObjectTargetTable(
|
||||||
.targetTableName,
|
objectMetadataMap[relationMetadataInput.toObjectMetadataId],
|
||||||
|
),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@ -215,16 +217,17 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
|||||||
},
|
},
|
||||||
// Create the foreignKey
|
// Create the foreignKey
|
||||||
{
|
{
|
||||||
name: objectMetadataMap[relationMetadataInput.toObjectMetadataId]
|
name: computeObjectTargetTable(
|
||||||
.targetTableName,
|
objectMetadataMap[relationMetadataInput.toObjectMetadataId],
|
||||||
|
),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
action: WorkspaceMigrationColumnActionType.RELATION,
|
action: WorkspaceMigrationColumnActionType.RELATION,
|
||||||
columnName: foreignKeyColumnName,
|
columnName: foreignKeyColumnName,
|
||||||
referencedTableName:
|
referencedTableName: computeObjectTargetTable(
|
||||||
objectMetadataMap[relationMetadataInput.fromObjectMetadataId]
|
objectMetadataMap[relationMetadataInput.fromObjectMetadataId],
|
||||||
.targetTableName,
|
),
|
||||||
referencedTableColumnName: 'id',
|
referencedTableColumnName: 'id',
|
||||||
isUnique:
|
isUnique:
|
||||||
relationMetadataInput.relationType ===
|
relationMetadataInput.relationType ===
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import {
|
|||||||
} from 'src/core/feature-flag/feature-flag.entity';
|
} from 'src/core/feature-flag/feature-flag.entity';
|
||||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||||
import DeleteInactiveWorkspaceEmail from 'src/workspace/cron/clean-inactive-workspaces/delete-inactive-workspaces.email';
|
import DeleteInactiveWorkspaceEmail from 'src/workspace/cron/clean-inactive-workspaces/delete-inactive-workspaces.email';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
const MILLISECONDS_IN_ONE_DAY = 1000 * 3600 * 24;
|
const MILLISECONDS_IN_ONE_DAY = 1000 * 3600 * 24;
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ export class CleanInactiveWorkspaceJob implements MessageQueueJob<undefined> {
|
|||||||
(objectMetadata) =>
|
(objectMetadata) =>
|
||||||
objectMetadata.workspaceId === dataSource.workspaceId,
|
objectMetadata.workspaceId === dataSource.workspaceId,
|
||||||
)
|
)
|
||||||
.map((objectMetadata) => objectMetadata.targetTableName);
|
.map((objectMetadata) => computeObjectTargetTable(objectMetadata));
|
||||||
|
|
||||||
const workspaceDataSource =
|
const workspaceDataSource =
|
||||||
await this.typeORMService.connectToDataSource(dataSource);
|
await this.typeORMService.connectToDataSource(dataSource);
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||||
|
|
||||||
|
export const computeObjectTargetTable = (
|
||||||
|
objectMetadata: ObjectMetadataInterface,
|
||||||
|
) => {
|
||||||
|
const prefix = objectMetadata.isCustom ? '_' : '';
|
||||||
|
|
||||||
|
return `${prefix}${objectMetadata.nameSingular}`;
|
||||||
|
};
|
||||||
@ -9,6 +9,7 @@ import { WorkspaceHealthOptions } from 'src/workspace/workspace-health/interface
|
|||||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||||
import { validName } from 'src/workspace/workspace-health/utils/valid-name.util';
|
import { validName } from 'src/workspace/workspace-health/utils/valid-name.util';
|
||||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ObjectMetadataHealthService {
|
export class ObjectMetadataHealthService {
|
||||||
@ -54,14 +55,17 @@ export class ObjectMetadataHealthService {
|
|||||||
|
|
||||||
// Check if the table exist in database
|
// Check if the table exist in database
|
||||||
const tableExist = await mainDataSource.query(
|
const tableExist = await mainDataSource.query(
|
||||||
`SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = '${schemaName}' AND table_name = '${objectMetadata.targetTableName}')`,
|
`SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = '${schemaName}'
|
||||||
|
AND table_name = '${computeObjectTargetTable(objectMetadata)}')`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!tableExist) {
|
if (!tableExist) {
|
||||||
issues.push({
|
issues.push({
|
||||||
type: WorkspaceHealthIssueType.MISSING_TABLE,
|
type: WorkspaceHealthIssueType.MISSING_TABLE,
|
||||||
objectMetadata,
|
objectMetadata,
|
||||||
message: `Table ${objectMetadata.targetTableName} not found in schema ${schemaName}`,
|
message: `Table ${computeObjectTargetTable(
|
||||||
|
objectMetadata,
|
||||||
|
)} not found in schema ${schemaName}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
return issues;
|
return issues;
|
||||||
@ -80,30 +84,13 @@ export class ObjectMetadataHealthService {
|
|||||||
): WorkspaceHealthIssue[] {
|
): WorkspaceHealthIssue[] {
|
||||||
const issues: WorkspaceHealthIssue[] = [];
|
const issues: WorkspaceHealthIssue[] = [];
|
||||||
|
|
||||||
if (
|
|
||||||
objectMetadata.isCustom &&
|
|
||||||
!objectMetadata.targetTableName.startsWith('_')
|
|
||||||
) {
|
|
||||||
issues.push({
|
|
||||||
type: WorkspaceHealthIssueType.TABLE_NAME_SHOULD_BE_CUSTOM,
|
|
||||||
objectMetadata,
|
|
||||||
message: `Table ${objectMetadata.targetTableName} is marked as custom but doesn't start with "_"`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!objectMetadata.targetTableName) {
|
|
||||||
issues.push({
|
|
||||||
type: WorkspaceHealthIssueType.TABLE_TARGET_TABLE_NAME_NOT_VALID,
|
|
||||||
objectMetadata,
|
|
||||||
message: `Table ${objectMetadata.targetTableName} doesn't have a valid target table name`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!objectMetadata.dataSourceId) {
|
if (!objectMetadata.dataSourceId) {
|
||||||
issues.push({
|
issues.push({
|
||||||
type: WorkspaceHealthIssueType.TABLE_DATA_SOURCE_ID_NOT_VALID,
|
type: WorkspaceHealthIssueType.TABLE_DATA_SOURCE_ID_NOT_VALID,
|
||||||
objectMetadata,
|
objectMetadata,
|
||||||
message: `Table ${objectMetadata.targetTableName} doesn't have a data source`,
|
message: `Table ${computeObjectTargetTable(
|
||||||
|
objectMetadata,
|
||||||
|
)} doesn't have a data source`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +105,9 @@ export class ObjectMetadataHealthService {
|
|||||||
issues.push({
|
issues.push({
|
||||||
type: WorkspaceHealthIssueType.TABLE_NAME_NOT_VALID,
|
type: WorkspaceHealthIssueType.TABLE_NAME_NOT_VALID,
|
||||||
objectMetadata,
|
objectMetadata,
|
||||||
message: `Table ${objectMetadata.targetTableName} doesn't have a valid name or label`,
|
message: `Table ${computeObjectTargetTable(
|
||||||
|
objectMetadata,
|
||||||
|
)} doesn't have a valid name or label`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metad
|
|||||||
import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service';
|
import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service';
|
||||||
import { ObjectMetadataHealthService } from 'src/workspace/workspace-health/services/object-metadata-health.service';
|
import { ObjectMetadataHealthService } from 'src/workspace/workspace-health/services/object-metadata-health.service';
|
||||||
import { FieldMetadataHealthService } from 'src/workspace/workspace-health/services/field-metadata-health.service';
|
import { FieldMetadataHealthService } from 'src/workspace/workspace-health/services/field-metadata-health.service';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkspaceHealthService {
|
export class WorkspaceHealthService {
|
||||||
@ -68,7 +69,7 @@ export class WorkspaceHealthService {
|
|||||||
// Check fields metadata health
|
// Check fields metadata health
|
||||||
const fieldIssues = await this.fieldMetadataHealthService.healthCheck(
|
const fieldIssues = await this.fieldMetadataHealthService.healthCheck(
|
||||||
schemaName,
|
schemaName,
|
||||||
objectMetadata.targetTableName,
|
computeObjectTargetTable(objectMetadata),
|
||||||
objectMetadata.fields,
|
objectMetadata.fields,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,237 +0,0 @@
|
|||||||
// import { GraphQLResolveInfo } from 'graphql';
|
|
||||||
|
|
||||||
// import { FieldMetadataTargetColumnMap } from 'src/metadata/field-metadata/interfaces/field-metadata-target-column-map.interface';
|
|
||||||
|
|
||||||
// import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity';
|
|
||||||
// import {
|
|
||||||
// PGGraphQLQueryBuilder,
|
|
||||||
// PGGraphQLQueryBuilderOptions,
|
|
||||||
// } from 'src/tenant/resolver-builder/pg-graphql/pg-graphql-query-builder';
|
|
||||||
|
|
||||||
// const testUUID = '123e4567-e89b-12d3-a456-426614174001';
|
|
||||||
|
|
||||||
// const normalizeWhitespace = (str) => str.replace(/\s+/g, '');
|
|
||||||
|
|
||||||
// // Mocking dependencies
|
|
||||||
// jest.mock('uuid', () => ({
|
|
||||||
// v4: jest.fn(() => testUUID),
|
|
||||||
// }));
|
|
||||||
|
|
||||||
// jest.mock('graphql-fields', () =>
|
|
||||||
// jest.fn(() => ({
|
|
||||||
// name: true,
|
|
||||||
// age: true,
|
|
||||||
// complexField: {
|
|
||||||
// subField1: true,
|
|
||||||
// subField2: true,
|
|
||||||
// },
|
|
||||||
// })),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// describe('PGGraphQLQueryBuilder', () => {
|
|
||||||
// let queryBuilder;
|
|
||||||
// let mockOptions: PGGraphQLQueryBuilderOptions;
|
|
||||||
|
|
||||||
// beforeEach(() => {
|
|
||||||
// const fieldMetadataCollection = [
|
|
||||||
// {
|
|
||||||
// name: 'name',
|
|
||||||
// targetColumnMap: {
|
|
||||||
// value: 'column_name',
|
|
||||||
// } as FieldMetadataTargetColumnMap,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: 'age',
|
|
||||||
// targetColumnMap: {
|
|
||||||
// value: 'column_age',
|
|
||||||
// } as FieldMetadataTargetColumnMap,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: 'complexField',
|
|
||||||
// targetColumnMap: {
|
|
||||||
// subField1: 'column_subField1',
|
|
||||||
// subField2: 'column_subField2',
|
|
||||||
// } as FieldMetadataTargetColumnMap,
|
|
||||||
// },
|
|
||||||
// ] as FieldMetadata[];
|
|
||||||
|
|
||||||
// mockOptions = {
|
|
||||||
// targetTableName: 'TestTable',
|
|
||||||
// info: {} as GraphQLResolveInfo,
|
|
||||||
// fieldMetadataCollection,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// queryBuilder = new PGGraphQLQueryBuilder(mockOptions);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// test('findMany generates correct query with no arguments', () => {
|
|
||||||
// const query = queryBuilder.findMany();
|
|
||||||
|
|
||||||
// expect(normalizeWhitespace(query)).toBe(
|
|
||||||
// normalizeWhitespace(`
|
|
||||||
// query {
|
|
||||||
// TestTableCollection {
|
|
||||||
// name: column_name
|
|
||||||
// age: column_age
|
|
||||||
// ___complexField_subField1: column_subField1
|
|
||||||
// ___complexField_subField2: column_subField2
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// `),
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// test('findMany generates correct query with filter parameters', () => {
|
|
||||||
// const args = {
|
|
||||||
// filter: {
|
|
||||||
// name: { eq: 'Alice' },
|
|
||||||
// age: { gt: 20 },
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// const query = queryBuilder.findMany(args);
|
|
||||||
|
|
||||||
// expect(normalizeWhitespace(query)).toBe(
|
|
||||||
// normalizeWhitespace(`
|
|
||||||
// query {
|
|
||||||
// TestTableCollection(filter: { column_name: { eq: "Alice" }, column_age: { gt: 20 } }) {
|
|
||||||
// name: column_name
|
|
||||||
// age: column_age
|
|
||||||
// ___complexField_subField1: column_subField1
|
|
||||||
// ___complexField_subField2: column_subField2
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// `),
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// test('findMany generates correct query with combined pagination parameters', () => {
|
|
||||||
// const args = {
|
|
||||||
// first: 5,
|
|
||||||
// after: 'someCursor',
|
|
||||||
// before: 'anotherCursor',
|
|
||||||
// last: 3,
|
|
||||||
// };
|
|
||||||
// const query = queryBuilder.findMany(args);
|
|
||||||
|
|
||||||
// expect(normalizeWhitespace(query)).toBe(
|
|
||||||
// normalizeWhitespace(`
|
|
||||||
// query {
|
|
||||||
// TestTableCollection(
|
|
||||||
// first: 5,
|
|
||||||
// after: "someCursor",
|
|
||||||
// before: "anotherCursor",
|
|
||||||
// last: 3
|
|
||||||
// ) {
|
|
||||||
// name: column_name
|
|
||||||
// age: column_age
|
|
||||||
// ___complexField_subField1: column_subField1
|
|
||||||
// ___complexField_subField2: column_subField2
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// `),
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// test('findOne generates correct query with ID filter', () => {
|
|
||||||
// const args = { filter: { id: { eq: testUUID } } };
|
|
||||||
// const query = queryBuilder.findOne(args);
|
|
||||||
|
|
||||||
// expect(normalizeWhitespace(query)).toBe(
|
|
||||||
// normalizeWhitespace(`
|
|
||||||
// query {
|
|
||||||
// TestTableCollection(filter: { id: { eq: "${testUUID}" } }) {
|
|
||||||
// edges {
|
|
||||||
// node {
|
|
||||||
// name: column_name
|
|
||||||
// age: column_age
|
|
||||||
// ___complexField_subField1: column_subField1
|
|
||||||
// ___complexField_subField2: column_subField2
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// `),
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// test('createMany generates correct mutation with complex and nested fields', () => {
|
|
||||||
// const args = {
|
|
||||||
// data: [
|
|
||||||
// {
|
|
||||||
// name: 'Alice',
|
|
||||||
// age: 30,
|
|
||||||
// complexField: {
|
|
||||||
// subField1: 'data1',
|
|
||||||
// subField2: 'data2',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// };
|
|
||||||
// const query = queryBuilder.createMany(args);
|
|
||||||
|
|
||||||
// expect(normalizeWhitespace(query)).toBe(
|
|
||||||
// normalizeWhitespace(`
|
|
||||||
// mutation {
|
|
||||||
// insertIntoTestTableCollection(objects: [{
|
|
||||||
// id: "${testUUID}",
|
|
||||||
// column_name: "Alice",
|
|
||||||
// column_age: 30,
|
|
||||||
// column_subField1: "data1",
|
|
||||||
// column_subField2: "data2"
|
|
||||||
// }]) {
|
|
||||||
// affectedCount
|
|
||||||
// records {
|
|
||||||
// name: column_name
|
|
||||||
// age: column_age
|
|
||||||
// ___complexField_subField1: column_subField1
|
|
||||||
// ___complexField_subField2: column_subField2
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// `),
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
// test('updateOne generates correct mutation with complex and nested fields', () => {
|
|
||||||
// const args = {
|
|
||||||
// id: '1',
|
|
||||||
// data: {
|
|
||||||
// name: 'Bob',
|
|
||||||
// age: 40,
|
|
||||||
// complexField: {
|
|
||||||
// subField1: 'newData1',
|
|
||||||
// subField2: 'newData2',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// const query = queryBuilder.updateOne(args);
|
|
||||||
|
|
||||||
// expect(normalizeWhitespace(query)).toBe(
|
|
||||||
// normalizeWhitespace(`
|
|
||||||
// mutation {
|
|
||||||
// updateTestTableCollection(
|
|
||||||
// set: {
|
|
||||||
// column_name: "Bob",
|
|
||||||
// column_age: 40,
|
|
||||||
// column_subField1: "newData1",
|
|
||||||
// column_subField2: "newData2"
|
|
||||||
// },
|
|
||||||
// filter: { id: { eq: "1" } }
|
|
||||||
// ) {
|
|
||||||
// affectedCount
|
|
||||||
// records {
|
|
||||||
// name: column_name
|
|
||||||
// age: column_age
|
|
||||||
// ___complexField_subField1: column_subField1
|
|
||||||
// ___complexField_subField2: column_subField2
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// `),
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
it('should pass', () => {
|
|
||||||
expect(true).toBe(true);
|
|
||||||
});
|
|
||||||
@ -7,6 +7,7 @@ import { Record as IRecord } from 'src/workspace/workspace-query-builder/interfa
|
|||||||
import { CreateManyResolverArgs } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
import { CreateManyResolverArgs } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||||
|
|
||||||
import { stringifyWithoutKeyQuote } from 'src/workspace/workspace-query-builder/utils/stringify-without-key-quote.util';
|
import { stringifyWithoutKeyQuote } from 'src/workspace/workspace-query-builder/utils/stringify-without-key-quote.util';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
import { FieldsStringFactory } from './fields-string.factory';
|
import { FieldsStringFactory } from './fields-string.factory';
|
||||||
import { ArgsAliasFactory } from './args-alias.factory';
|
import { ArgsAliasFactory } from './args-alias.factory';
|
||||||
@ -36,9 +37,9 @@ export class CreateManyQueryFactory {
|
|||||||
|
|
||||||
return `
|
return `
|
||||||
mutation {
|
mutation {
|
||||||
insertInto${
|
insertInto${computeObjectTargetTable(
|
||||||
options.targetTableName
|
options.objectMetadataItem,
|
||||||
}Collection(objects: ${stringifyWithoutKeyQuote(
|
)}Collection(objects: ${stringifyWithoutKeyQuote(
|
||||||
computedArgs.data.map((datum) => ({
|
computedArgs.data.map((datum) => ({
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
...datum,
|
...datum,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { WorkspaceQueryBuilderOptions } from 'src/workspace/workspace-query-buil
|
|||||||
import { DeleteManyResolverArgs } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
import { DeleteManyResolverArgs } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||||
|
|
||||||
import { stringifyWithoutKeyQuote } from 'src/workspace/workspace-query-builder/utils/stringify-without-key-quote.util';
|
import { stringifyWithoutKeyQuote } from 'src/workspace/workspace-query-builder/utils/stringify-without-key-quote.util';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
import { FieldsStringFactory } from './fields-string.factory';
|
import { FieldsStringFactory } from './fields-string.factory';
|
||||||
|
|
||||||
@ -23,9 +24,9 @@ export class DeleteManyQueryFactory {
|
|||||||
|
|
||||||
return `
|
return `
|
||||||
mutation {
|
mutation {
|
||||||
deleteFrom${
|
deleteFrom${computeObjectTargetTable(
|
||||||
options.targetTableName
|
options.objectMetadataItem,
|
||||||
}Collection(filter: ${stringifyWithoutKeyQuote(
|
)}Collection(filter: ${stringifyWithoutKeyQuote(
|
||||||
args.filter,
|
args.filter,
|
||||||
)}, atMost: 30) {
|
)}, atMost: 30) {
|
||||||
affectedCount
|
affectedCount
|
||||||
|
|||||||
@ -3,6 +3,8 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||||||
import { WorkspaceQueryBuilderOptions } from 'src/workspace/workspace-query-builder/interfaces/workspace-query-builder-options.interface';
|
import { WorkspaceQueryBuilderOptions } from 'src/workspace/workspace-query-builder/interfaces/workspace-query-builder-options.interface';
|
||||||
import { DeleteOneResolverArgs } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
import { DeleteOneResolverArgs } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||||
|
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
import { FieldsStringFactory } from './fields-string.factory';
|
import { FieldsStringFactory } from './fields-string.factory';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -23,7 +25,9 @@ export class DeleteOneQueryFactory {
|
|||||||
|
|
||||||
return `
|
return `
|
||||||
mutation {
|
mutation {
|
||||||
deleteFrom${options.targetTableName}Collection(filter: { id: { eq: "${args.id}" } }) {
|
deleteFrom${computeObjectTargetTable(
|
||||||
|
options.objectMetadataItem,
|
||||||
|
)}Collection(filter: { id: { eq: "${args.id}" } }) {
|
||||||
affectedCount
|
affectedCount
|
||||||
records {
|
records {
|
||||||
${fieldsString}
|
${fieldsString}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { FindManyResolverArgs } from 'src/workspace/workspace-resolver-builder/i
|
|||||||
|
|
||||||
import { ArgsStringFactory } from './args-string.factory';
|
import { ArgsStringFactory } from './args-string.factory';
|
||||||
import { FieldsStringFactory } from './fields-string.factory';
|
import { FieldsStringFactory } from './fields-string.factory';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FindManyQueryFactory {
|
export class FindManyQueryFactory {
|
||||||
@ -38,7 +39,7 @@ export class FindManyQueryFactory {
|
|||||||
|
|
||||||
return `
|
return `
|
||||||
query {
|
query {
|
||||||
${options.targetTableName}Collection${
|
${computeObjectTargetTable(options.objectMetadataItem)}Collection${
|
||||||
argsString ? `(${argsString})` : ''
|
argsString ? `(${argsString})` : ''
|
||||||
} {
|
} {
|
||||||
${fieldsString}
|
${fieldsString}
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import { WorkspaceQueryBuilderOptions } from 'src/workspace/workspace-query-buil
|
|||||||
import { RecordFilter } from 'src/workspace/workspace-query-builder/interfaces/record.interface';
|
import { RecordFilter } from 'src/workspace/workspace-query-builder/interfaces/record.interface';
|
||||||
import { FindOneResolverArgs } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
import { FindOneResolverArgs } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||||
|
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
import { ArgsStringFactory } from './args-string.factory';
|
import { ArgsStringFactory } from './args-string.factory';
|
||||||
import { FieldsStringFactory } from './fields-string.factory';
|
import { FieldsStringFactory } from './fields-string.factory';
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ export class FindOneQueryFactory {
|
|||||||
|
|
||||||
return `
|
return `
|
||||||
query {
|
query {
|
||||||
${options.targetTableName}Collection${
|
${computeObjectTargetTable(options.objectMetadataItem)}Collection${
|
||||||
argsString ? `(${argsString})` : ''
|
argsString ? `(${argsString})` : ''
|
||||||
} {
|
} {
|
||||||
edges {
|
edges {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {
|
|||||||
} from 'src/workspace/utils/deduce-relation-direction.util';
|
} from 'src/workspace/utils/deduce-relation-direction.util';
|
||||||
import { getFieldArgumentsByKey } from 'src/workspace/workspace-query-builder/utils/get-field-arguments-by-key.util';
|
import { getFieldArgumentsByKey } from 'src/workspace/workspace-query-builder/utils/get-field-arguments-by-key.util';
|
||||||
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
import { FieldsStringFactory } from './fields-string.factory';
|
import { FieldsStringFactory } from './fields-string.factory';
|
||||||
import { ArgsStringFactory } from './args-string.factory';
|
import { ArgsStringFactory } from './args-string.factory';
|
||||||
@ -109,25 +110,27 @@ export class RelationFieldAliasFactory {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
${fieldKey}: ${referencedObjectMetadata.targetTableName}Collection${
|
${fieldKey}: ${computeObjectTargetTable(
|
||||||
argsString ? `(${argsString})` : ''
|
referencedObjectMetadata,
|
||||||
} {
|
)}Collection${argsString ? `(${argsString})` : ''} {
|
||||||
${fieldsString}
|
${fieldsString}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let relationAlias = fieldMetadata.isCustom
|
let relationAlias = fieldMetadata.isCustom
|
||||||
? `${fieldKey}: ${referencedObjectMetadata.targetTableName}`
|
? `${fieldKey}: _${fieldMetadata.name}`
|
||||||
: fieldKey;
|
: fieldKey;
|
||||||
|
|
||||||
// For one to one relations, pg_graphql use the targetTableName on the side that is not storing the foreign key
|
// For one to one relations, pg_graphql use the target TableName on the side that is not storing the foreign key
|
||||||
// so we need to alias it to the field key
|
// so we need to alias it to the field key
|
||||||
if (
|
if (
|
||||||
relationMetadata.relationType === RelationMetadataType.ONE_TO_ONE &&
|
relationMetadata.relationType === RelationMetadataType.ONE_TO_ONE &&
|
||||||
relationDirection === RelationDirection.FROM
|
relationDirection === RelationDirection.FROM
|
||||||
) {
|
) {
|
||||||
relationAlias = `${fieldKey}: ${referencedObjectMetadata.targetTableName}`;
|
relationAlias = `${fieldKey}: ${computeObjectTargetTable(
|
||||||
|
referencedObjectMetadata,
|
||||||
|
)}`;
|
||||||
}
|
}
|
||||||
const fieldsString =
|
const fieldsString =
|
||||||
await this.fieldsStringFactory.createFieldsStringRecursive(
|
await this.fieldsStringFactory.createFieldsStringRecursive(
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { UpdateManyResolverArgs } from 'src/workspace/workspace-resolver-builder
|
|||||||
import { stringifyWithoutKeyQuote } from 'src/workspace/workspace-query-builder/utils/stringify-without-key-quote.util';
|
import { stringifyWithoutKeyQuote } from 'src/workspace/workspace-query-builder/utils/stringify-without-key-quote.util';
|
||||||
import { FieldsStringFactory } from 'src/workspace/workspace-query-builder/factories/fields-string.factory';
|
import { FieldsStringFactory } from 'src/workspace/workspace-query-builder/factories/fields-string.factory';
|
||||||
import { ArgsAliasFactory } from 'src/workspace/workspace-query-builder/factories/args-alias.factory';
|
import { ArgsAliasFactory } from 'src/workspace/workspace-query-builder/factories/args-alias.factory';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UpdateManyQueryFactory {
|
export class UpdateManyQueryFactory {
|
||||||
@ -43,7 +44,7 @@ export class UpdateManyQueryFactory {
|
|||||||
|
|
||||||
return `
|
return `
|
||||||
mutation {
|
mutation {
|
||||||
update${options.targetTableName}Collection(
|
update${computeObjectTargetTable(options.objectMetadataItem)}Collection(
|
||||||
set: ${stringifyWithoutKeyQuote(argsData)},
|
set: ${stringifyWithoutKeyQuote(argsData)},
|
||||||
filter: ${stringifyWithoutKeyQuote(args.filter)},
|
filter: ${stringifyWithoutKeyQuote(args.filter)},
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { Record as IRecord } from 'src/workspace/workspace-query-builder/interfa
|
|||||||
import { UpdateOneResolverArgs } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
import { UpdateOneResolverArgs } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||||
|
|
||||||
import { stringifyWithoutKeyQuote } from 'src/workspace/workspace-query-builder/utils/stringify-without-key-quote.util';
|
import { stringifyWithoutKeyQuote } from 'src/workspace/workspace-query-builder/utils/stringify-without-key-quote.util';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
import { FieldsStringFactory } from './fields-string.factory';
|
import { FieldsStringFactory } from './fields-string.factory';
|
||||||
import { ArgsAliasFactory } from './args-alias.factory';
|
import { ArgsAliasFactory } from './args-alias.factory';
|
||||||
@ -39,9 +40,9 @@ export class UpdateOneQueryFactory {
|
|||||||
|
|
||||||
return `
|
return `
|
||||||
mutation {
|
mutation {
|
||||||
update${
|
update${computeObjectTargetTable(
|
||||||
options.targetTableName
|
options.objectMetadataItem,
|
||||||
}Collection(set: ${stringifyWithoutKeyQuote(
|
)}Collection(set: ${stringifyWithoutKeyQuote(
|
||||||
argsData,
|
argsData,
|
||||||
)}, filter: { id: { eq: "${computedArgs.id}" } }) {
|
)}, filter: { id: { eq: "${computedArgs.id}" } }) {
|
||||||
affectedCount
|
affectedCount
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/f
|
|||||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||||
|
|
||||||
export interface WorkspaceQueryBuilderOptions {
|
export interface WorkspaceQueryBuilderOptions {
|
||||||
targetTableName: string;
|
objectMetadataItem: ObjectMetadataInterface;
|
||||||
info: GraphQLResolveInfo;
|
info: GraphQLResolveInfo;
|
||||||
fieldMetadataCollection: FieldMetadataInterface[];
|
fieldMetadataCollection: FieldMetadataInterface[];
|
||||||
objectMetadataCollection: ObjectMetadataInterface[];
|
objectMetadataCollection: ObjectMetadataInterface[];
|
||||||
|
|||||||
@ -4,9 +4,9 @@ import { FieldMetadataInterface } from 'src/metadata/field-metadata/interfaces/f
|
|||||||
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||||
|
|
||||||
export interface WorkspaceQueryRunnerOptions {
|
export interface WorkspaceQueryRunnerOptions {
|
||||||
targetTableName: string;
|
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
info: GraphQLResolveInfo;
|
info: GraphQLResolveInfo;
|
||||||
|
objectMetadataItem: ObjectMetadataInterface;
|
||||||
fieldMetadataCollection: FieldMetadataInterface[];
|
fieldMetadataCollection: FieldMetadataInterface[];
|
||||||
objectMetadataCollection: ObjectMetadataInterface[];
|
objectMetadataCollection: ObjectMetadataInterface[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||||
|
|
||||||
import { MessageQueueJob } from 'src/integrations/message-queue/interfaces/message-queue-job.interface';
|
import { MessageQueueJob } from 'src/integrations/message-queue/interfaces/message-queue-job.interface';
|
||||||
|
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||||
|
|
||||||
import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service';
|
import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service';
|
||||||
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service';
|
||||||
@ -20,7 +21,7 @@ export enum CallWebhookJobsJobOperation {
|
|||||||
|
|
||||||
export type CallWebhookJobsJobData = {
|
export type CallWebhookJobsJobData = {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
objectNameSingular: string;
|
objectMetadataItem: ObjectMetadataInterface;
|
||||||
recordData: any;
|
recordData: any;
|
||||||
operation: CallWebhookJobsJobOperation;
|
operation: CallWebhookJobsJobOperation;
|
||||||
};
|
};
|
||||||
@ -43,7 +44,7 @@ export class CallWebhookJobsJob
|
|||||||
const objectMetadataItem =
|
const objectMetadataItem =
|
||||||
await this.objectMetadataService.findOneOrFailWithinWorkspace(
|
await this.objectMetadataService.findOneOrFailWithinWorkspace(
|
||||||
data.workspaceId,
|
data.workspaceId,
|
||||||
{ where: { nameSingular: data.objectNameSingular } },
|
{ where: { nameSingular: data.objectMetadataItem.nameSingular } },
|
||||||
);
|
);
|
||||||
const dataSourceMetadata =
|
const dataSourceMetadata =
|
||||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||||
|
|||||||
@ -39,6 +39,8 @@ export const parseResult = (obj: any): any => {
|
|||||||
if (obj.hasOwnProperty(key)) {
|
if (obj.hasOwnProperty(key)) {
|
||||||
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
||||||
result[key] = parseResult(obj[key]);
|
result[key] = parseResult(obj[key]);
|
||||||
|
} else if (key === '__typename') {
|
||||||
|
result[key] = obj[key].replace(/^_*/, '');
|
||||||
} else if (isSpecialKey(key)) {
|
} else if (isSpecialKey(key)) {
|
||||||
handleSpecialKey(result, key, obj[key]);
|
handleSpecialKey(result, key, obj[key]);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import {
|
|||||||
UpdateManyResolverArgs,
|
UpdateManyResolverArgs,
|
||||||
UpdateOneResolverArgs,
|
UpdateOneResolverArgs,
|
||||||
} from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
} from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||||
|
import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/object-metadata.interface';
|
||||||
|
|
||||||
import { WorkspaceQueryBuilderFactory } from 'src/workspace/workspace-query-builder/workspace-query-builder.factory';
|
import { WorkspaceQueryBuilderFactory } from 'src/workspace/workspace-query-builder/workspace-query-builder.factory';
|
||||||
import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service';
|
import { WorkspaceDataSourceService } from 'src/workspace/workspace-datasource/workspace-datasource.service';
|
||||||
@ -35,6 +36,7 @@ import {
|
|||||||
import { parseResult } from 'src/workspace/workspace-query-runner/utils/parse-result.util';
|
import { parseResult } from 'src/workspace/workspace-query-runner/utils/parse-result.util';
|
||||||
import { ExceptionHandlerService } from 'src/integrations/exception-handler/exception-handler.service';
|
import { ExceptionHandlerService } from 'src/integrations/exception-handler/exception-handler.service';
|
||||||
import { handleExceptionAndConvertToGraphQLError } from 'src/filters/utils/global-exception-handler.util';
|
import { handleExceptionAndConvertToGraphQLError } from 'src/filters/utils/global-exception-handler.util';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
import { WorkspaceQueryRunnerOptions } from './interfaces/query-runner-optionts.interface';
|
import { WorkspaceQueryRunnerOptions } from './interfaces/query-runner-optionts.interface';
|
||||||
import {
|
import {
|
||||||
@ -63,7 +65,7 @@ export class WorkspaceQueryRunnerService {
|
|||||||
options: WorkspaceQueryRunnerOptions,
|
options: WorkspaceQueryRunnerOptions,
|
||||||
): Promise<IConnection<Record> | undefined> {
|
): Promise<IConnection<Record> | undefined> {
|
||||||
try {
|
try {
|
||||||
const { workspaceId, targetTableName } = options;
|
const { workspaceId, objectMetadataItem } = options;
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
|
|
||||||
const query = await this.workspaceQueryBuilderFactory.findMany(
|
const query = await this.workspaceQueryBuilderFactory.findMany(
|
||||||
@ -76,7 +78,11 @@ export class WorkspaceQueryRunnerService {
|
|||||||
|
|
||||||
console.log(`query time: ${end - start} ms`);
|
console.log(`query time: ${end - start} ms`);
|
||||||
|
|
||||||
return this.parseResult<IConnection<Record>>(result, targetTableName, '');
|
return this.parseResult<IConnection<Record>>(
|
||||||
|
result,
|
||||||
|
objectMetadataItem,
|
||||||
|
'',
|
||||||
|
);
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
const error = handleExceptionAndConvertToGraphQLError(
|
const error = handleExceptionAndConvertToGraphQLError(
|
||||||
exception,
|
exception,
|
||||||
@ -98,7 +104,7 @@ export class WorkspaceQueryRunnerService {
|
|||||||
if (!args.filter || Object.keys(args.filter).length === 0) {
|
if (!args.filter || Object.keys(args.filter).length === 0) {
|
||||||
throw new BadRequestException('Missing filter argument');
|
throw new BadRequestException('Missing filter argument');
|
||||||
}
|
}
|
||||||
const { workspaceId, targetTableName } = options;
|
const { workspaceId, objectMetadataItem } = options;
|
||||||
const query = await this.workspaceQueryBuilderFactory.findOne(
|
const query = await this.workspaceQueryBuilderFactory.findOne(
|
||||||
args,
|
args,
|
||||||
options,
|
options,
|
||||||
@ -106,7 +112,7 @@ export class WorkspaceQueryRunnerService {
|
|||||||
const result = await this.execute(query, workspaceId);
|
const result = await this.execute(query, workspaceId);
|
||||||
const parsedResult = this.parseResult<IConnection<Record>>(
|
const parsedResult = this.parseResult<IConnection<Record>>(
|
||||||
result,
|
result,
|
||||||
targetTableName,
|
objectMetadataItem,
|
||||||
'',
|
'',
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -126,16 +132,17 @@ export class WorkspaceQueryRunnerService {
|
|||||||
options: WorkspaceQueryRunnerOptions,
|
options: WorkspaceQueryRunnerOptions,
|
||||||
): Promise<Record[] | undefined> {
|
): Promise<Record[] | undefined> {
|
||||||
try {
|
try {
|
||||||
const { workspaceId, targetTableName } = options;
|
const { workspaceId, objectMetadataItem } = options;
|
||||||
const query = await this.workspaceQueryBuilderFactory.createMany(
|
const query = await this.workspaceQueryBuilderFactory.createMany(
|
||||||
args,
|
args,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await this.execute(query, workspaceId);
|
const result = await this.execute(query, workspaceId);
|
||||||
|
|
||||||
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
||||||
result,
|
result,
|
||||||
targetTableName,
|
objectMetadataItem,
|
||||||
'insertInto',
|
'insertInto',
|
||||||
)?.records;
|
)?.records;
|
||||||
|
|
||||||
@ -170,7 +177,7 @@ export class WorkspaceQueryRunnerService {
|
|||||||
options: WorkspaceQueryRunnerOptions,
|
options: WorkspaceQueryRunnerOptions,
|
||||||
): Promise<Record | undefined> {
|
): Promise<Record | undefined> {
|
||||||
try {
|
try {
|
||||||
const { workspaceId, targetTableName } = options;
|
const { workspaceId, objectMetadataItem } = options;
|
||||||
const query = await this.workspaceQueryBuilderFactory.updateOne(
|
const query = await this.workspaceQueryBuilderFactory.updateOne(
|
||||||
args,
|
args,
|
||||||
options,
|
options,
|
||||||
@ -179,7 +186,7 @@ export class WorkspaceQueryRunnerService {
|
|||||||
|
|
||||||
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
||||||
result,
|
result,
|
||||||
targetTableName,
|
objectMetadataItem,
|
||||||
'update',
|
'update',
|
||||||
)?.records;
|
)?.records;
|
||||||
|
|
||||||
@ -205,7 +212,7 @@ export class WorkspaceQueryRunnerService {
|
|||||||
options: WorkspaceQueryRunnerOptions,
|
options: WorkspaceQueryRunnerOptions,
|
||||||
): Promise<Record | undefined> {
|
): Promise<Record | undefined> {
|
||||||
try {
|
try {
|
||||||
const { workspaceId, targetTableName } = options;
|
const { workspaceId, objectMetadataItem } = options;
|
||||||
const query = await this.workspaceQueryBuilderFactory.deleteOne(
|
const query = await this.workspaceQueryBuilderFactory.deleteOne(
|
||||||
args,
|
args,
|
||||||
options,
|
options,
|
||||||
@ -214,7 +221,7 @@ export class WorkspaceQueryRunnerService {
|
|||||||
|
|
||||||
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
||||||
result,
|
result,
|
||||||
targetTableName,
|
objectMetadataItem,
|
||||||
'deleteFrom',
|
'deleteFrom',
|
||||||
)?.records;
|
)?.records;
|
||||||
|
|
||||||
@ -240,7 +247,7 @@ export class WorkspaceQueryRunnerService {
|
|||||||
options: WorkspaceQueryRunnerOptions,
|
options: WorkspaceQueryRunnerOptions,
|
||||||
): Promise<Record[] | undefined> {
|
): Promise<Record[] | undefined> {
|
||||||
try {
|
try {
|
||||||
const { workspaceId, targetTableName } = options;
|
const { workspaceId, objectMetadataItem } = options;
|
||||||
const query = await this.workspaceQueryBuilderFactory.updateMany(
|
const query = await this.workspaceQueryBuilderFactory.updateMany(
|
||||||
args,
|
args,
|
||||||
options,
|
options,
|
||||||
@ -249,7 +256,7 @@ export class WorkspaceQueryRunnerService {
|
|||||||
|
|
||||||
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
||||||
result,
|
result,
|
||||||
targetTableName,
|
objectMetadataItem,
|
||||||
'update',
|
'update',
|
||||||
)?.records;
|
)?.records;
|
||||||
|
|
||||||
@ -278,7 +285,7 @@ export class WorkspaceQueryRunnerService {
|
|||||||
options: WorkspaceQueryRunnerOptions,
|
options: WorkspaceQueryRunnerOptions,
|
||||||
): Promise<Record[] | undefined> {
|
): Promise<Record[] | undefined> {
|
||||||
try {
|
try {
|
||||||
const { workspaceId, targetTableName } = options;
|
const { workspaceId, objectMetadataItem } = options;
|
||||||
const query = await this.workspaceQueryBuilderFactory.deleteMany(
|
const query = await this.workspaceQueryBuilderFactory.deleteMany(
|
||||||
args,
|
args,
|
||||||
options,
|
options,
|
||||||
@ -287,7 +294,7 @@ export class WorkspaceQueryRunnerService {
|
|||||||
|
|
||||||
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
|
||||||
result,
|
result,
|
||||||
targetTableName,
|
objectMetadataItem,
|
||||||
'deleteFrom',
|
'deleteFrom',
|
||||||
)?.records;
|
)?.records;
|
||||||
|
|
||||||
@ -334,18 +341,20 @@ export class WorkspaceQueryRunnerService {
|
|||||||
|
|
||||||
private parseResult<Result>(
|
private parseResult<Result>(
|
||||||
graphqlResult: PGGraphQLResult | undefined,
|
graphqlResult: PGGraphQLResult | undefined,
|
||||||
targetTableName: string,
|
objectMetadataItem: ObjectMetadataInterface,
|
||||||
command: string,
|
command: string,
|
||||||
): Result {
|
): Result {
|
||||||
const entityKey = `${command}${targetTableName}Collection`;
|
const entityKey = `${command}${computeObjectTargetTable(
|
||||||
|
objectMetadataItem,
|
||||||
|
)}Collection`;
|
||||||
const result = graphqlResult?.[0]?.resolve?.data?.[entityKey];
|
const result = graphqlResult?.[0]?.resolve?.data?.[entityKey];
|
||||||
const errors = graphqlResult?.[0]?.resolve?.errors;
|
const errors = graphqlResult?.[0]?.resolve?.errors;
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new InternalServerErrorException(
|
throw new InternalServerErrorException(
|
||||||
`GraphQL errors on ${command}${targetTableName}: ${JSON.stringify(
|
`GraphQL errors on ${command}${
|
||||||
errors,
|
objectMetadataItem.nameSingular
|
||||||
)}`,
|
}: ${JSON.stringify(errors)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,13 +363,13 @@ export class WorkspaceQueryRunnerService {
|
|||||||
|
|
||||||
async executeAndParse<Result>(
|
async executeAndParse<Result>(
|
||||||
query: string,
|
query: string,
|
||||||
targetTableName: string,
|
objectMetadataItem: ObjectMetadataInterface,
|
||||||
command: string,
|
command: string,
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
): Promise<Result> {
|
): Promise<Result> {
|
||||||
const result = await this.execute(query, workspaceId);
|
const result = await this.execute(query, workspaceId);
|
||||||
|
|
||||||
return this.parseResult(result, targetTableName, command);
|
return this.parseResult(result, objectMetadataItem, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
async triggerWebhooks<Record>(
|
async triggerWebhooks<Record>(
|
||||||
@ -378,7 +387,7 @@ export class WorkspaceQueryRunnerService {
|
|||||||
recordData: jobData,
|
recordData: jobData,
|
||||||
workspaceId: options.workspaceId,
|
workspaceId: options.workspaceId,
|
||||||
operation,
|
operation,
|
||||||
objectNameSingular: options.targetTableName,
|
objectMetadataItem: options.objectMetadataItem,
|
||||||
},
|
},
|
||||||
{ retryLimit: 3 },
|
{ retryLimit: 3 },
|
||||||
);
|
);
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export class CreateManyResolverFactory
|
|||||||
|
|
||||||
return (_source, args, context, info) => {
|
return (_source, args, context, info) => {
|
||||||
return this.workspaceQueryRunnerService.createMany(args, {
|
return this.workspaceQueryRunnerService.createMany(args, {
|
||||||
targetTableName: internalContext.targetTableName,
|
objectMetadataItem: internalContext.objectMetadataItem,
|
||||||
workspaceId: internalContext.workspaceId,
|
workspaceId: internalContext.workspaceId,
|
||||||
info,
|
info,
|
||||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export class CreateOneResolverFactory
|
|||||||
|
|
||||||
return (_source, args, context, info) => {
|
return (_source, args, context, info) => {
|
||||||
return this.workspaceQueryRunnerService.createOne(args, {
|
return this.workspaceQueryRunnerService.createOne(args, {
|
||||||
targetTableName: internalContext.targetTableName,
|
objectMetadataItem: internalContext.objectMetadataItem,
|
||||||
workspaceId: internalContext.workspaceId,
|
workspaceId: internalContext.workspaceId,
|
||||||
info,
|
info,
|
||||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export class DeleteManyResolverFactory
|
|||||||
|
|
||||||
return (_source, args, context, info) => {
|
return (_source, args, context, info) => {
|
||||||
return this.workspaceQueryRunnerService.deleteMany(args, {
|
return this.workspaceQueryRunnerService.deleteMany(args, {
|
||||||
targetTableName: internalContext.targetTableName,
|
objectMetadataItem: internalContext.objectMetadataItem,
|
||||||
workspaceId: internalContext.workspaceId,
|
workspaceId: internalContext.workspaceId,
|
||||||
info,
|
info,
|
||||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export class DeleteOneResolverFactory
|
|||||||
|
|
||||||
return (_source, args, context, info) => {
|
return (_source, args, context, info) => {
|
||||||
return this.workspaceQueryRunnerService.deleteOne(args, {
|
return this.workspaceQueryRunnerService.deleteOne(args, {
|
||||||
targetTableName: internalContext.targetTableName,
|
objectMetadataItem: internalContext.objectMetadataItem,
|
||||||
workspaceId: internalContext.workspaceId,
|
workspaceId: internalContext.workspaceId,
|
||||||
info,
|
info,
|
||||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||||
|
|||||||
@ -32,7 +32,7 @@ export class ExecuteQuickActionOnOneResolverFactory
|
|||||||
|
|
||||||
return (_source, args, context, info) => {
|
return (_source, args, context, info) => {
|
||||||
return this.executeQuickActionOnOne(args, {
|
return this.executeQuickActionOnOne(args, {
|
||||||
targetTableName: internalContext.targetTableName,
|
objectMetadataItem: internalContext.objectMetadataItem,
|
||||||
workspaceId: internalContext.workspaceId,
|
workspaceId: internalContext.workspaceId,
|
||||||
info,
|
info,
|
||||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||||
@ -45,11 +45,12 @@ export class ExecuteQuickActionOnOneResolverFactory
|
|||||||
args: DeleteOneResolverArgs,
|
args: DeleteOneResolverArgs,
|
||||||
options: WorkspaceQueryRunnerOptions,
|
options: WorkspaceQueryRunnerOptions,
|
||||||
): Promise<Record | undefined> {
|
): Promise<Record | undefined> {
|
||||||
switch (options.targetTableName) {
|
switch (options.objectMetadataItem.nameSingular) {
|
||||||
case 'company': {
|
case 'company': {
|
||||||
await this.quickActionsService.executeQuickActionOnCompany(
|
await this.quickActionsService.executeQuickActionOnCompany(
|
||||||
args.id,
|
args.id,
|
||||||
options.workspaceId,
|
options.workspaceId,
|
||||||
|
options.objectMetadataItem,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -57,6 +58,7 @@ export class ExecuteQuickActionOnOneResolverFactory
|
|||||||
await this.quickActionsService.createCompanyFromPerson(
|
await this.quickActionsService.createCompanyFromPerson(
|
||||||
args.id,
|
args.id,
|
||||||
options.workspaceId,
|
options.workspaceId,
|
||||||
|
options.objectMetadataCollection,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export class FindManyResolverFactory
|
|||||||
|
|
||||||
return (_source, args, context, info) => {
|
return (_source, args, context, info) => {
|
||||||
return this.workspaceQueryRunnerService.findMany(args, {
|
return this.workspaceQueryRunnerService.findMany(args, {
|
||||||
targetTableName: internalContext.targetTableName,
|
objectMetadataItem: internalContext.objectMetadataItem,
|
||||||
workspaceId: internalContext.workspaceId,
|
workspaceId: internalContext.workspaceId,
|
||||||
info,
|
info,
|
||||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export class FindOneResolverFactory
|
|||||||
|
|
||||||
return (_source, args, context, info) => {
|
return (_source, args, context, info) => {
|
||||||
return this.workspaceQueryRunnerService.findOne(args, {
|
return this.workspaceQueryRunnerService.findOne(args, {
|
||||||
targetTableName: internalContext.targetTableName,
|
objectMetadataItem: internalContext.objectMetadataItem,
|
||||||
workspaceId: internalContext.workspaceId,
|
workspaceId: internalContext.workspaceId,
|
||||||
info,
|
info,
|
||||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export class UpdateManyResolverFactory
|
|||||||
|
|
||||||
return (_source, args, context, info) => {
|
return (_source, args, context, info) => {
|
||||||
return this.workspaceQueryRunnerService.updateMany(args, {
|
return this.workspaceQueryRunnerService.updateMany(args, {
|
||||||
targetTableName: internalContext.targetTableName,
|
objectMetadataItem: internalContext.objectMetadataItem,
|
||||||
workspaceId: internalContext.workspaceId,
|
workspaceId: internalContext.workspaceId,
|
||||||
info,
|
info,
|
||||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export class UpdateOneResolverFactory
|
|||||||
|
|
||||||
return (_source, args, context, info) => {
|
return (_source, args, context, info) => {
|
||||||
return this.workspaceQueryRunnerService.updateOne(args, {
|
return this.workspaceQueryRunnerService.updateOne(args, {
|
||||||
targetTableName: internalContext.targetTableName,
|
objectMetadataItem: internalContext.objectMetadataItem,
|
||||||
workspaceId: internalContext.workspaceId,
|
workspaceId: internalContext.workspaceId,
|
||||||
info,
|
info,
|
||||||
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
fieldMetadataCollection: internalContext.fieldMetadataCollection,
|
||||||
|
|||||||
@ -79,7 +79,7 @@ export class WorkspaceResolverFactory {
|
|||||||
|
|
||||||
resolvers.Query[resolverName] = resolverFactory.create({
|
resolvers.Query[resolverName] = resolverFactory.create({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
targetTableName: objectMetadata.targetTableName,
|
objectMetadataItem: objectMetadata,
|
||||||
fieldMetadataCollection: objectMetadata.fields,
|
fieldMetadataCollection: objectMetadata.fields,
|
||||||
objectMetadataCollection: objectMetadataCollection,
|
objectMetadataCollection: objectMetadataCollection,
|
||||||
});
|
});
|
||||||
@ -102,7 +102,7 @@ export class WorkspaceResolverFactory {
|
|||||||
|
|
||||||
resolvers.Mutation[resolverName] = resolverFactory.create({
|
resolvers.Mutation[resolverName] = resolverFactory.create({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
targetTableName: objectMetadata.targetTableName,
|
objectMetadataItem: objectMetadata,
|
||||||
fieldMetadataCollection: objectMetadata.fields,
|
fieldMetadataCollection: objectMetadata.fields,
|
||||||
objectMetadataCollection: objectMetadataCollection,
|
objectMetadataCollection: objectMetadataCollection,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { ObjectMetadataInterface } from 'src/metadata/field-metadata/interfaces/
|
|||||||
|
|
||||||
export interface WorkspaceSchemaBuilderContext {
|
export interface WorkspaceSchemaBuilderContext {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
targetTableName: string;
|
objectMetadataItem: ObjectMetadataInterface;
|
||||||
fieldMetadataCollection: FieldMetadataInterface[];
|
fieldMetadataCollection: FieldMetadataInterface[];
|
||||||
objectMetadataCollection: ObjectMetadataInterface[];
|
objectMetadataCollection: ObjectMetadataInterface[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export function ObjectMetadata(
|
|||||||
{
|
{
|
||||||
nameSingular: objectName,
|
nameSingular: objectName,
|
||||||
...params,
|
...params,
|
||||||
targetTableName: objectName,
|
targetTableName: 'DEPRECATED',
|
||||||
isSystem,
|
isSystem,
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
description: params.description,
|
description: params.description,
|
||||||
|
|||||||
@ -37,6 +37,7 @@ import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/work
|
|||||||
import { WorkspaceMigrationRunnerService } from 'src/workspace/workspace-migration-runner/workspace-migration-runner.service';
|
import { WorkspaceMigrationRunnerService } from 'src/workspace/workspace-migration-runner/workspace-migration-runner.service';
|
||||||
import { ReflectiveMetadataFactory } from 'src/workspace/workspace-sync-metadata/reflective-metadata.factory';
|
import { ReflectiveMetadataFactory } from 'src/workspace/workspace-sync-metadata/reflective-metadata.factory';
|
||||||
import { FeatureFlagEntity } from 'src/core/feature-flag/feature-flag.entity';
|
import { FeatureFlagEntity } from 'src/core/feature-flag/feature-flag.entity';
|
||||||
|
import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkspaceSyncMetadataService {
|
export class WorkspaceSyncMetadataService {
|
||||||
@ -391,7 +392,7 @@ export class WorkspaceSyncMetadataService {
|
|||||||
objectsToCreate.map((object) => {
|
objectsToCreate.map((object) => {
|
||||||
const migrations = [
|
const migrations = [
|
||||||
{
|
{
|
||||||
name: object.targetTableName,
|
name: computeObjectTargetTable(object),
|
||||||
action: 'create',
|
action: 'create',
|
||||||
} satisfies WorkspaceMigrationTableAction,
|
} satisfies WorkspaceMigrationTableAction,
|
||||||
...Object.values(object.fields)
|
...Object.values(object.fields)
|
||||||
@ -399,7 +400,7 @@ export class WorkspaceSyncMetadataService {
|
|||||||
.map(
|
.map(
|
||||||
(field) =>
|
(field) =>
|
||||||
({
|
({
|
||||||
name: object.targetTableName,
|
name: computeObjectTargetTable(object),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: this.workspaceMigrationFactory.createColumnActions(
|
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||||
WorkspaceMigrationColumnActionType.CREATE,
|
WorkspaceMigrationColumnActionType.CREATE,
|
||||||
@ -433,7 +434,9 @@ export class WorkspaceSyncMetadataService {
|
|||||||
fieldsToCreate.map((field) => {
|
fieldsToCreate.map((field) => {
|
||||||
const migrations = [
|
const migrations = [
|
||||||
{
|
{
|
||||||
name: objectsInDbById[field.objectMetadataId].targetTableName,
|
name: computeObjectTargetTable(
|
||||||
|
objectsInDbById[field.objectMetadataId],
|
||||||
|
),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: this.workspaceMigrationFactory.createColumnActions(
|
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||||
WorkspaceMigrationColumnActionType.CREATE,
|
WorkspaceMigrationColumnActionType.CREATE,
|
||||||
@ -454,7 +457,9 @@ export class WorkspaceSyncMetadataService {
|
|||||||
fieldsToDelete.map((field) => {
|
fieldsToDelete.map((field) => {
|
||||||
const migrations = [
|
const migrations = [
|
||||||
{
|
{
|
||||||
name: objectsInDbById[field.objectMetadataId].targetTableName,
|
name: computeObjectTargetTable(
|
||||||
|
objectsInDbById[field.objectMetadataId],
|
||||||
|
),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@ -519,13 +524,14 @@ export class WorkspaceSyncMetadataService {
|
|||||||
|
|
||||||
const migrations = [
|
const migrations = [
|
||||||
{
|
{
|
||||||
name: toObjectMetadata.targetTableName,
|
name: computeObjectTargetTable(toObjectMetadata),
|
||||||
action: 'alter',
|
action: 'alter',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
action: WorkspaceMigrationColumnActionType.RELATION,
|
action: WorkspaceMigrationColumnActionType.RELATION,
|
||||||
columnName: `${camelCase(toFieldMetadata.name)}Id`,
|
columnName: `${camelCase(toFieldMetadata.name)}Id`,
|
||||||
referencedTableName: fromObjectMetadata.targetTableName,
|
referencedTableName:
|
||||||
|
computeObjectTargetTable(fromObjectMetadata),
|
||||||
referencedTableColumnName: 'id',
|
referencedTableColumnName: 'id',
|
||||||
isUnique:
|
isUnique:
|
||||||
relation.relationType === RelationMetadataType.ONE_TO_ONE,
|
relation.relationType === RelationMetadataType.ONE_TO_ONE,
|
||||||
|
|||||||
Reference in New Issue
Block a user