mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-29 10:13:05 +03:00
Merge branch 'twentyhq:main' into horizontal-nav
This commit is contained in:
commit
b9ddb39a51
@ -1,11 +1,15 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui';
|
||||
import {
|
||||
Checkbox,
|
||||
CheckboxShape,
|
||||
IconCalendar,
|
||||
OverflowingTextWithTooltip,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
|
||||
import { ActivityTargetsInlineCell } from '@/activities/inline-cell/components/ActivityTargetsInlineCell';
|
||||
import { getActivitySummary } from '@/activities/utils/getActivitySummary';
|
||||
import { Checkbox, CheckboxShape } from '@/ui/input/components/Checkbox';
|
||||
import { beautifyExactDate, hasDatePassed } from '~/utils/date-utils';
|
||||
|
||||
import { ActivityRow } from '@/activities/components/ActivityRow';
|
||||
|
@ -115,7 +115,7 @@ export const PageChangeEffect = () => {
|
||||
break;
|
||||
}
|
||||
case isMatchingLocation(AppPath.CreateWorkspace): {
|
||||
setHotkeyScope(PageHotkeyScope.CreateWokspace);
|
||||
setHotkeyScope(PageHotkeyScope.CreateWorkspace);
|
||||
break;
|
||||
}
|
||||
case isMatchingLocation(AppPath.SyncEmails): {
|
||||
|
@ -188,7 +188,9 @@ export const SignInUpForm = () => {
|
||||
</>
|
||||
)}
|
||||
|
||||
<HorizontalSeparator visible={true} />
|
||||
{(authProviders.google ||
|
||||
authProviders.microsoft ||
|
||||
authProviders.sso) && <HorizontalSeparator visible />}
|
||||
|
||||
{authProviders.password &&
|
||||
(signInUpStep === SignInUpStep.Password ||
|
||||
|
@ -17,7 +17,6 @@ import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/
|
||||
import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
import { RecordBoardScrollWrapperContext } from '@/ui/utilities/scroll/contexts/ScrollWrapperContexts';
|
||||
@ -29,6 +28,8 @@ import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import {
|
||||
AnimatedEaseInOut,
|
||||
AvatarChipVariant,
|
||||
Checkbox,
|
||||
CheckboxVariant,
|
||||
ChipSize,
|
||||
IconEye,
|
||||
IconEyeOff,
|
||||
|
@ -4,7 +4,7 @@ import { useCallback, useContext } from 'react';
|
||||
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||
import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd';
|
||||
import { useSetCurrentRowSelected } from '@/object-record/record-table/record-table-row/hooks/useSetCurrentRowSelected';
|
||||
import { Checkbox } from '@/ui/input/components/Checkbox';
|
||||
import { Checkbox } from 'twenty-ui';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
|
@ -3,7 +3,7 @@ import { useContext } from 'react';
|
||||
|
||||
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||
import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd';
|
||||
import { IconListViewGrip } from '@/ui/input/components/IconListViewGrip';
|
||||
import { IconListViewGrip } from 'twenty-ui';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
cursor: grab;
|
||||
|
@ -3,7 +3,7 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { Checkbox } from '@/ui/input/components/Checkbox';
|
||||
import { Checkbox } from 'twenty-ui';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
|
@ -1,8 +1,7 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { Radio } from '@/ui/input/components/Radio';
|
||||
import { Card, CardContent } from 'twenty-ui';
|
||||
import { Card, CardContent, Radio } from 'twenty-ui';
|
||||
|
||||
type SettingsAccountsRadioSettingsCardProps<Option extends { value: string }> =
|
||||
{
|
||||
|
@ -1,7 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { Radio } from '@/ui/input/components/Radio';
|
||||
import { IconComponent, CardContent } from 'twenty-ui';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { CardContent, IconComponent, Radio } from 'twenty-ui';
|
||||
|
||||
const StyledRadioCardContent = styled(CardContent)`
|
||||
display: flex;
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useIcons } from 'twenty-ui';
|
||||
import { Checkbox, useIcons } from 'twenty-ui';
|
||||
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { Checkbox } from '@/ui/input/components/Checkbox';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { Column, FormatterProps, useRowSelection } from 'react-data-grid';
|
||||
|
||||
import { ImportedRow } from '@/spreadsheet-import/types';
|
||||
import { Radio } from '@/ui/input/components/Radio';
|
||||
import { Radio } from 'twenty-ui';
|
||||
|
||||
const SELECT_COLUMN_KEY = 'select-row';
|
||||
|
||||
|
@ -8,8 +8,7 @@ import { SpreadsheetImportStep } from '@/spreadsheet-import/steps/types/Spreadsh
|
||||
import { SpreadsheetImportStepType } from '@/spreadsheet-import/steps/types/SpreadsheetImportStepType';
|
||||
import { exceedsMaxRecords } from '@/spreadsheet-import/utils/exceedsMaxRecords';
|
||||
import { mapWorkbook } from '@/spreadsheet-import/utils/mapWorkbook';
|
||||
import { Radio } from '@/ui/input/components/Radio';
|
||||
import { RadioGroup } from '@/ui/input/components/RadioGroup';
|
||||
import { Radio, RadioGroup } from 'twenty-ui';
|
||||
|
||||
import { Modal } from '@/ui/layout/modal/components/Modal';
|
||||
import { WorkBook } from 'xlsx-ugnis';
|
||||
|
@ -2,11 +2,10 @@ import styled from '@emotion/styled';
|
||||
// @ts-expect-error // Todo: remove usage of react-data-grid
|
||||
import { Column, useRowSelection } from 'react-data-grid';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { AppTooltip, Toggle } from 'twenty-ui';
|
||||
import { AppTooltip, Checkbox, CheckboxVariant, Toggle } from 'twenty-ui';
|
||||
|
||||
import { MatchColumnSelect } from '@/spreadsheet-import/components/MatchColumnSelect';
|
||||
import { Fields, ImportedStructuredRow } from '@/spreadsheet-import/types';
|
||||
import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
export enum PageHotkeyScope {
|
||||
Settings = 'settings',
|
||||
CreateWokspace = 'create-workspace',
|
||||
CreateWorkspace = 'create-workspace',
|
||||
SignInUp = 'sign-in-up',
|
||||
CreateProfile = 'create-profile',
|
||||
InviteTeam = 'invite-team',
|
||||
|
@ -19,13 +19,13 @@ const StyledAddressContainer = styled.div`
|
||||
|
||||
padding: 4px 8px;
|
||||
|
||||
width: 100%;
|
||||
min-width: 260px;
|
||||
width: 344px;
|
||||
> div {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
width: auto;
|
||||
min-width: 100px;
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
@ -36,7 +36,8 @@ const StyledAddressContainer = styled.div`
|
||||
`;
|
||||
|
||||
const StyledHalfRowContainer = styled.div`
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
|
@ -31,6 +31,7 @@ export type SelectProps<Value extends string | number | null> = {
|
||||
disableBlur?: boolean;
|
||||
dropdownId: string;
|
||||
dropdownWidth?: `${string}px` | 'auto' | number;
|
||||
dropdownWidthAuto?: boolean;
|
||||
emptyOption?: SelectOption<Value>;
|
||||
fullWidth?: boolean;
|
||||
label?: string;
|
||||
@ -60,6 +61,7 @@ export const Select = <Value extends string | number | null>({
|
||||
disableBlur = false,
|
||||
dropdownId,
|
||||
dropdownWidth = 176,
|
||||
dropdownWidthAuto = false,
|
||||
emptyOption,
|
||||
fullWidth,
|
||||
label,
|
||||
@ -94,6 +96,11 @@ export const Select = <Value extends string | number | null>({
|
||||
|
||||
const { closeDropdown } = useDropdown(dropdownId);
|
||||
|
||||
const dropDownMenuWidth =
|
||||
dropdownWidthAuto && selectContainerRef.current?.clientWidth
|
||||
? selectContainerRef.current?.clientWidth
|
||||
: dropdownWidth;
|
||||
|
||||
return (
|
||||
<StyledContainer
|
||||
className={className}
|
||||
@ -111,7 +118,7 @@ export const Select = <Value extends string | number | null>({
|
||||
) : (
|
||||
<Dropdown
|
||||
dropdownId={dropdownId}
|
||||
dropdownMenuWidth={dropdownWidth}
|
||||
dropdownMenuWidth={dropDownMenuWidth}
|
||||
dropdownPlacement="bottom-start"
|
||||
clickableComponent={
|
||||
<SelectControl
|
||||
|
@ -1,28 +1,31 @@
|
||||
import { SelectOption } from '@/ui/input/components/Select';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { IconChevronDown } from 'twenty-ui';
|
||||
import {
|
||||
IconChevronDown,
|
||||
isDefined,
|
||||
OverflowingTextWithTooltip,
|
||||
} from 'twenty-ui';
|
||||
|
||||
const StyledControlContainer = styled.div<{ disabled?: boolean }>`
|
||||
const StyledControlContainer = styled.div<{
|
||||
disabled?: boolean;
|
||||
hasIcon: boolean;
|
||||
}>`
|
||||
display: grid;
|
||||
grid-template-columns: ${({ hasIcon }) =>
|
||||
hasIcon ? 'auto 1fr auto' : '1fr auto'};
|
||||
align-items: center;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
box-sizing: border-box;
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
max-width: 100%;
|
||||
padding: 0 ${({ theme }) => theme.spacing(2)};
|
||||
background-color: ${({ theme }) => theme.background.transparent.lighter};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
box-sizing: border-box;
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
color: ${({ disabled, theme }) =>
|
||||
disabled ? theme.font.color.tertiary : theme.font.color.primary};
|
||||
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
justify-content: space-between;
|
||||
padding: 0 ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledControlLabel = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledIconChevronDown = styled(IconChevronDown)<{
|
||||
@ -44,19 +47,18 @@ export const SelectControl = ({
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<StyledControlContainer disabled={isDisabled}>
|
||||
<StyledControlLabel>
|
||||
{!!selectedOption?.Icon && (
|
||||
<selectedOption.Icon
|
||||
color={
|
||||
isDisabled ? theme.font.color.light : theme.font.color.primary
|
||||
}
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
/>
|
||||
)}
|
||||
{selectedOption?.label}
|
||||
</StyledControlLabel>
|
||||
<StyledControlContainer
|
||||
disabled={isDisabled}
|
||||
hasIcon={isDefined(selectedOption.Icon)}
|
||||
>
|
||||
{isDefined(selectedOption.Icon) ? (
|
||||
<selectedOption.Icon
|
||||
color={isDisabled ? theme.font.color.light : theme.font.color.primary}
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
/>
|
||||
) : null}
|
||||
<OverflowingTextWithTooltip text={selectedOption.label} />
|
||||
<StyledIconChevronDown disabled={isDisabled} size={theme.icon.size.md} />
|
||||
</StyledControlContainer>
|
||||
);
|
||||
|
@ -4,9 +4,10 @@ import {
|
||||
FloatingPortal,
|
||||
offset,
|
||||
Placement,
|
||||
size,
|
||||
useFloating,
|
||||
} from '@floating-ui/react';
|
||||
import { MouseEvent, useEffect, useRef } from 'react';
|
||||
import { MouseEvent, useEffect, useRef, useState } from 'react';
|
||||
import { Keys } from 'react-hotkeys-hook';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
@ -21,6 +22,7 @@ import { isDefined } from '~/utils/isDefined';
|
||||
import { useDropdown } from '../hooks/useDropdown';
|
||||
import { useInternalHotkeyScopeManagement } from '../hooks/useInternalHotkeyScopeManagement';
|
||||
|
||||
import { flushSync } from 'react-dom';
|
||||
import { DropdownMenu } from './DropdownMenu';
|
||||
import { DropdownOnToggleEffect } from './DropdownOnToggleEffect';
|
||||
|
||||
@ -63,6 +65,9 @@ export const Dropdown = ({
|
||||
onOpen,
|
||||
}: DropdownProps) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [maxHeight, setMaxHeight] = useState<string | number | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
const {
|
||||
isDropdownOpen,
|
||||
@ -84,7 +89,16 @@ export const Dropdown = ({
|
||||
|
||||
const { refs, floatingStyles, placement } = useFloating({
|
||||
placement: dropdownPlacement,
|
||||
middleware: [flip(), ...offsetMiddlewares],
|
||||
middleware: [
|
||||
flip(),
|
||||
size({
|
||||
padding: 12 + 20, // 12px for padding bottom, 20px for dropdown bottom margin target
|
||||
apply: ({ availableHeight }) => {
|
||||
flushSync(() => setMaxHeight(availableHeight));
|
||||
},
|
||||
}),
|
||||
...offsetMiddlewares,
|
||||
],
|
||||
whileElementsMounted: autoUpdate,
|
||||
strategy: dropdownStrategy,
|
||||
});
|
||||
@ -155,7 +169,7 @@ export const Dropdown = ({
|
||||
width={dropdownMenuWidth ?? dropdownWidth}
|
||||
data-select-disable
|
||||
ref={refs.setFloating}
|
||||
style={floatingStyles}
|
||||
style={{ ...floatingStyles, maxHeight }}
|
||||
>
|
||||
{dropdownComponents}
|
||||
</DropdownMenu>
|
||||
@ -167,7 +181,7 @@ export const Dropdown = ({
|
||||
width={dropdownMenuWidth ?? dropdownWidth}
|
||||
data-select-disable
|
||||
ref={refs.setFloating}
|
||||
style={floatingStyles}
|
||||
style={{ ...floatingStyles, maxHeight }}
|
||||
>
|
||||
{dropdownComponents}
|
||||
</DropdownMenu>
|
||||
|
@ -25,6 +25,8 @@ const StyledDropdownMenu = styled.div<{
|
||||
|
||||
flex-direction: column;
|
||||
z-index: 30;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
width: ${({ width = 160 }) =>
|
||||
typeof width === 'number' ? `${width}px` : width};
|
||||
`;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { IconComponent, Tag, ThemeColor } from 'twenty-ui';
|
||||
import { Checkbox, IconComponent, Tag, ThemeColor } from 'twenty-ui';
|
||||
|
||||
import { Checkbox } from '@/ui/input/components/Checkbox';
|
||||
import { MenuItemLeftContent } from '@/ui/navigation/menu-item/internals/components/MenuItemLeftContent';
|
||||
|
||||
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { ReactNode } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { OverflowingTextWithTooltip } from 'twenty-ui';
|
||||
|
||||
import { Checkbox } from '@/ui/input/components/Checkbox';
|
||||
import { ReactNode } from 'react';
|
||||
import { Checkbox, OverflowingTextWithTooltip } from 'twenty-ui';
|
||||
|
||||
import {
|
||||
StyledMenuItemBase,
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { Tag, ThemeColor } from 'twenty-ui';
|
||||
|
||||
import {
|
||||
Checkbox,
|
||||
CheckboxShape,
|
||||
CheckboxSize,
|
||||
} from '@/ui/input/components/Checkbox';
|
||||
Tag,
|
||||
ThemeColor,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import {
|
||||
StyledMenuItemBase,
|
||||
|
@ -87,6 +87,7 @@ export const WorkflowEditTriggerManualForm = ({
|
||||
...trigger,
|
||||
settings: {
|
||||
objectType: updatedObject,
|
||||
outputSchema: {},
|
||||
},
|
||||
});
|
||||
}}
|
||||
|
@ -6,13 +6,13 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { SearchVariablesDropdownStepItem } from '@/workflow/search-variables/components/SearchVariablesDropdownStepItem';
|
||||
import SearchVariablesDropdownStepSubItem from '@/workflow/search-variables/components/SearchVariablesDropdownStepSubItem';
|
||||
import { SEARCH_VARIABLES_DROPDOWN_ID } from '@/workflow/search-variables/constants/SearchVariablesDropdownId';
|
||||
import { useAvailableVariablesInWorkflowStep } from '@/workflow/search-variables/hooks/useAvailableVariablesInWorkflowStep';
|
||||
import { StepOutputSchema } from '@/workflow/search-variables/types/StepOutputSchema';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Editor } from '@tiptap/react';
|
||||
import { useState } from 'react';
|
||||
import { IconVariable } from 'twenty-ui';
|
||||
import { useAvailableVariablesInWorkflowStep } from '@/workflow/hooks/useAvailableVariablesInWorkflowStep';
|
||||
|
||||
const StyledDropdownVariableButtonContainer = styled(
|
||||
StyledDropdownButtonContainer,
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MenuItemSelect } from '@/ui/navigation/menu-item/components/MenuItemSelect';
|
||||
import { StepOutputSchema } from '@/workflow/search-variables/types/StepOutputSchema';
|
||||
|
||||
@ -10,7 +11,7 @@ export const SearchVariablesDropdownStepItem = ({
|
||||
steps,
|
||||
onSelect,
|
||||
}: SearchVariablesDropdownStepItemProps) => {
|
||||
return (
|
||||
return steps.length > 0 ? (
|
||||
<>
|
||||
{steps.map((item, _index) => (
|
||||
<MenuItemSelect
|
||||
@ -24,5 +25,13 @@ export const SearchVariablesDropdownStepItem = ({
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<MenuItem
|
||||
key="no-steps"
|
||||
onClick={() => {}}
|
||||
text="No variables available"
|
||||
LeftIcon={undefined}
|
||||
hasSubMenu={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { StepOutputSchema } from '@/workflow/search-variables/types/StepOutputSchema';
|
||||
import { getTriggerStepName } from '@/workflow/search-variables/utils/getTriggerStepName';
|
||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||
import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState';
|
||||
import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
import { StepOutputSchema } from '@/workflow/search-variables/types/StepOutputSchema';
|
||||
import { isEmptyObject } from '~/utils/isEmptyObject';
|
||||
|
||||
export const useAvailableVariablesInWorkflowStep = (): StepOutputSchema[] => {
|
||||
const workflowId = useRecoilValue(workflowIdState);
|
||||
@ -41,20 +43,22 @@ export const useAvailableVariablesInWorkflowStep = (): StepOutputSchema[] => {
|
||||
const result = [];
|
||||
|
||||
if (
|
||||
workflow.currentVersion.trigger?.type === 'DATABASE_EVENT' &&
|
||||
isDefined(workflow.currentVersion.trigger?.settings?.outputSchema)
|
||||
isDefined(workflow.currentVersion.trigger) &&
|
||||
isDefined(workflow.currentVersion.trigger?.settings?.outputSchema) &&
|
||||
!isEmptyObject(workflow.currentVersion.trigger?.settings?.outputSchema)
|
||||
) {
|
||||
const [object, action] =
|
||||
workflow.currentVersion.trigger.settings.eventName.split('.');
|
||||
result.push({
|
||||
id: 'trigger',
|
||||
name: `${capitalize(object)} is ${capitalize(action)}`,
|
||||
name: getTriggerStepName(workflow.currentVersion.trigger),
|
||||
outputSchema: workflow.currentVersion.trigger.settings.outputSchema,
|
||||
});
|
||||
}
|
||||
|
||||
previousSteps.forEach((previousStep) => {
|
||||
if (isDefined(previousStep.settings.outputSchema)) {
|
||||
if (
|
||||
isDefined(previousStep.settings.outputSchema) &&
|
||||
!isEmpty(previousStep.settings.outputSchema)
|
||||
) {
|
||||
result.push({
|
||||
id: previousStep.id,
|
||||
name: previousStep.name,
|
@ -267,4 +267,33 @@ describe('parseEditorContent', () => {
|
||||
|
||||
expect(parseEditorContent(input)).toBe('First line\nSecond line');
|
||||
});
|
||||
|
||||
it('should handle spaces between variables correctly', () => {
|
||||
const input: JSONContent = {
|
||||
type: 'doc',
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'variableTag',
|
||||
attrs: { variable: '{{user.firstName}}' },
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: '\u00A0', // NBSP character
|
||||
},
|
||||
{
|
||||
type: 'variableTag',
|
||||
attrs: { variable: '{{user.lastName}}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(parseEditorContent(input)).toBe(
|
||||
'{{user.firstName}} {{user.lastName}}',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,28 @@
|
||||
import {
|
||||
WorkflowDatabaseEventTrigger,
|
||||
WorkflowTrigger,
|
||||
} from '@/workflow/types/Workflow';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const getTriggerStepName = (trigger: WorkflowTrigger): string => {
|
||||
switch (trigger.type) {
|
||||
case 'DATABASE_EVENT':
|
||||
return getDatabaseEventTriggerStepName(trigger);
|
||||
case 'MANUAL':
|
||||
if (!trigger.settings.objectType) {
|
||||
return 'Manual trigger';
|
||||
}
|
||||
|
||||
return 'Manual trigger for ' + capitalize(trigger.settings.objectType);
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const getDatabaseEventTriggerStepName = (
|
||||
trigger: WorkflowDatabaseEventTrigger,
|
||||
): string => {
|
||||
const [object, action] = trigger.settings.eventName.split('.');
|
||||
|
||||
return `${capitalize(object)} is ${capitalize(action)}`;
|
||||
};
|
@ -11,7 +11,8 @@ export const parseEditorContent = (json: JSONContent): string => {
|
||||
}
|
||||
|
||||
if (node.type === 'text') {
|
||||
return node.text || '';
|
||||
// Replace with regular space
|
||||
return node?.text?.replace(/\u00A0/g, ' ') ?? '';
|
||||
}
|
||||
|
||||
if (node.type === 'hardBreak') {
|
||||
|
@ -69,6 +69,7 @@ export type WorkflowManualTrigger = BaseTrigger & {
|
||||
type: 'MANUAL';
|
||||
settings: {
|
||||
objectType?: string;
|
||||
outputSchema: object;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -16,11 +16,13 @@ export const getManualTriggerDefaultSettings = ({
|
||||
case 'EVERYWHERE': {
|
||||
return {
|
||||
objectType: undefined,
|
||||
outputSchema: {},
|
||||
};
|
||||
}
|
||||
case 'WHEN_RECORD_SELECTED': {
|
||||
return {
|
||||
objectType: activeObjectMetadataItems[0].nameSingular,
|
||||
outputSchema: {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,17 @@ import { billingState } from '@/client-config/states/billingState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { CardPicker } from '@/ui/input/components/CardPicker';
|
||||
import styled from '@emotion/styled';
|
||||
import { isNonEmptyString, isNumber } from '@sniptt/guards';
|
||||
import { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { ActionLink, CAL_LINK, Loader, MainButton } from 'twenty-ui';
|
||||
import {
|
||||
ActionLink,
|
||||
CAL_LINK,
|
||||
CardPicker,
|
||||
Loader,
|
||||
MainButton,
|
||||
} from 'twenty-ui';
|
||||
import {
|
||||
ProductPriceEntity,
|
||||
SubscriptionInterval,
|
||||
|
@ -28,6 +28,7 @@ export const DateTimeSettingsDateFormatSelect = ({
|
||||
dropdownWidth={218}
|
||||
label="Date format"
|
||||
fullWidth
|
||||
dropdownWidthAuto
|
||||
value={value}
|
||||
options={[
|
||||
{
|
||||
|
@ -27,6 +27,7 @@ export const DateTimeSettingsTimeFormatSelect = ({
|
||||
dropdownId="datetime-settings-time-format"
|
||||
dropdownWidth={218}
|
||||
label="Time format"
|
||||
dropdownWidthAuto
|
||||
fullWidth
|
||||
value={value}
|
||||
options={[
|
||||
|
@ -19,9 +19,10 @@ export const DateTimeSettingsTimeZoneSelect = ({
|
||||
|
||||
return (
|
||||
<Select
|
||||
disableBlur
|
||||
dropdownId="settings-accounts-calendar-time-zone"
|
||||
dropdownWidth={416}
|
||||
label="Time zone"
|
||||
dropdownWidthAuto
|
||||
fullWidth
|
||||
value={value}
|
||||
options={[
|
||||
|
@ -3,12 +3,9 @@ import { ThemeType } from 'twenty-ui';
|
||||
export { ThemeProvider } from '@emotion/react';
|
||||
export * from 'twenty-ui';
|
||||
export * from './src/modules/ui/input/components/AutosizeTextInput';
|
||||
export * from './src/modules/ui/input/components/Checkbox';
|
||||
export * from './src/modules/ui/input/components/EntityTitleDoubleTextInput';
|
||||
export * from './src/modules/ui/input/components/IconPicker';
|
||||
export * from './src/modules/ui/input/components/ImageInput';
|
||||
export * from './src/modules/ui/input/components/Radio';
|
||||
export * from './src/modules/ui/input/components/RadioGroup';
|
||||
export * from './src/modules/ui/input/components/Select';
|
||||
export * from './src/modules/ui/input/components/TextArea';
|
||||
export * from './src/modules/ui/input/components/TextInput';
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { Args, Mutation, Resolver } from '@nestjs/graphql';
|
||||
import { UseFilters, UseGuards } from '@nestjs/common';
|
||||
import { Args, Mutation, Resolver } from '@nestjs/graphql';
|
||||
|
||||
import graphqlTypeJson from 'graphql-type-json';
|
||||
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
|
||||
import { WorkflowTriggerGraphqlApiExceptionFilter } from 'src/engine/core-modules/workflow/filters/workflow-trigger-graphql-api-exception.filter';
|
||||
import { OutputSchema } from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { ComputeStepOutputSchemaInput } from 'src/engine/core-modules/workflow/dtos/compute-step-output-schema-input.dto';
|
||||
import { WorkflowTriggerGraphqlApiExceptionFilter } from 'src/engine/core-modules/workflow/filters/workflow-trigger-graphql-api-exception.filter';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { WorkflowBuilderService } from 'src/modules/workflow/workflow-builder/workflow-builder.service';
|
||||
import { OutputSchema } from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
|
||||
|
||||
@Resolver()
|
||||
@UseGuards(WorkspaceAuthGuard, UserAuthGuard)
|
||||
|
@ -10,13 +10,15 @@ import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { BeforeUpdateOneObject } from 'src/engine/metadata-modules/object-metadata/hooks/before-update-one-object.hook';
|
||||
import { ObjectMetadataGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/object-metadata/interceptors/object-metadata-graphql-api-exception.interceptor';
|
||||
import { ObjectMetadataResolver } from 'src/engine/metadata-modules/object-metadata/object-metadata.resolver';
|
||||
import { ObjectMetadataMigrationService } from 'src/engine/metadata-modules/object-metadata/services/object-metadata-migration.service';
|
||||
import { ObjectMetadataRelatedRecordsService } from 'src/engine/metadata-modules/object-metadata/services/object-metadata-related-records.service';
|
||||
import { ObjectMetadataRelationService } from 'src/engine/metadata-modules/object-metadata/services/object-metadata-relation.service';
|
||||
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { RemoteTableRelationsModule } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table-relations/remote-table-relations.module';
|
||||
import { SearchModule } from 'src/engine/metadata-modules/search/search.module';
|
||||
@ -46,10 +48,14 @@ import { UpdateObjectPayload } from './dtos/update-object.input';
|
||||
WorkspaceMigrationRunnerModule,
|
||||
WorkspaceMetadataVersionModule,
|
||||
RemoteTableRelationsModule,
|
||||
FeatureFlagModule,
|
||||
SearchModule,
|
||||
],
|
||||
services: [ObjectMetadataService],
|
||||
services: [
|
||||
ObjectMetadataService,
|
||||
ObjectMetadataMigrationService,
|
||||
ObjectMetadataRelationService,
|
||||
ObjectMetadataRelatedRecordsService,
|
||||
],
|
||||
resolvers: [
|
||||
{
|
||||
EntityClass: ObjectMetadataEntity,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,305 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { buildMigrationsForCustomObjectRelations } from 'src/engine/metadata-modules/object-metadata/utils/build-migrations-for-custom-object-relations.util';
|
||||
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { RelationToDelete } from 'src/engine/metadata-modules/relation-metadata/types/relation-to-delete';
|
||||
import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
||||
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
|
||||
import {
|
||||
WorkspaceMigrationColumnActionType,
|
||||
WorkspaceMigrationColumnDrop,
|
||||
WorkspaceMigrationTableAction,
|
||||
WorkspaceMigrationTableActionType,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory';
|
||||
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
|
||||
|
||||
@Injectable()
|
||||
export class ObjectMetadataMigrationService {
|
||||
constructor(
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
@InjectRepository(FieldMetadataEntity, 'metadata')
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
@InjectRepository(RelationMetadataEntity, 'metadata')
|
||||
private readonly relationMetadataRepository: Repository<RelationMetadataEntity>,
|
||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
|
||||
) {}
|
||||
|
||||
public async createObjectMigration(
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
) {
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(`create-${createdObjectMetadata.nameSingular}`),
|
||||
createdObjectMetadata.workspaceId,
|
||||
[
|
||||
{
|
||||
name: computeObjectTargetTable(createdObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.CREATE,
|
||||
} satisfies WorkspaceMigrationTableAction,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public async createFieldMigrations(
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
fieldMetadataCollection: FieldMetadataEntity[],
|
||||
) {
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(
|
||||
`create-${createdObjectMetadata.nameSingular}-fields`,
|
||||
),
|
||||
createdObjectMetadata.workspaceId,
|
||||
[
|
||||
{
|
||||
name: computeObjectTargetTable(createdObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: fieldMetadataCollection.flatMap((fieldMetadata) =>
|
||||
this.workspaceMigrationFactory.createColumnActions(
|
||||
WorkspaceMigrationColumnActionType.CREATE,
|
||||
fieldMetadata,
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public async createRelationMigrations(
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
relatedObjectMetadataCollection: ObjectMetadataEntity[],
|
||||
) {
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(
|
||||
`create-${createdObjectMetadata.nameSingular}-relations`,
|
||||
),
|
||||
createdObjectMetadata.workspaceId,
|
||||
buildMigrationsForCustomObjectRelations(
|
||||
createdObjectMetadata,
|
||||
relatedObjectMetadataCollection,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public async createRenameTableMigration(
|
||||
existingObjectMetadata: ObjectMetadataEntity,
|
||||
objectMetadataForUpdate: ObjectMetadataEntity,
|
||||
) {
|
||||
const newTargetTableName = computeObjectTargetTable(
|
||||
objectMetadataForUpdate,
|
||||
);
|
||||
const existingTargetTableName = computeObjectTargetTable(
|
||||
existingObjectMetadata,
|
||||
);
|
||||
|
||||
this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(`rename-${existingObjectMetadata.nameSingular}`),
|
||||
objectMetadataForUpdate.workspaceId,
|
||||
[
|
||||
{
|
||||
name: existingTargetTableName,
|
||||
newName: newTargetTableName,
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public async createRelationsUpdatesMigrations(
|
||||
existingObjectMetadata: ObjectMetadataEntity,
|
||||
updatedObjectMetadata: ObjectMetadataEntity,
|
||||
) {
|
||||
const existingTableName = computeObjectTargetTable(existingObjectMetadata);
|
||||
const newTableName = computeObjectTargetTable(updatedObjectMetadata);
|
||||
|
||||
if (existingTableName !== newTableName) {
|
||||
const searchCriteria = {
|
||||
isCustom: false,
|
||||
settings: {
|
||||
isForeignKey: true,
|
||||
},
|
||||
name: `${existingObjectMetadata.nameSingular}Id`,
|
||||
};
|
||||
|
||||
const fieldsWihStandardRelation = await this.fieldMetadataRepository.find(
|
||||
{
|
||||
where: {
|
||||
isCustom: false,
|
||||
settings: {
|
||||
isForeignKey: true,
|
||||
},
|
||||
name: `${existingObjectMetadata.nameSingular}Id`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
await this.fieldMetadataRepository.update(searchCriteria, {
|
||||
name: `${updatedObjectMetadata.nameSingular}Id`,
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
fieldsWihStandardRelation.map(async (fieldWihStandardRelation) => {
|
||||
const relatedObject = await this.objectMetadataRepository.findOneBy({
|
||||
id: fieldWihStandardRelation.objectMetadataId,
|
||||
workspaceId: updatedObjectMetadata.workspaceId,
|
||||
});
|
||||
|
||||
if (relatedObject) {
|
||||
await this.fieldMetadataRepository.update(
|
||||
{
|
||||
name: existingObjectMetadata.nameSingular,
|
||||
label: existingObjectMetadata.labelSingular,
|
||||
},
|
||||
{
|
||||
name: updatedObjectMetadata.nameSingular,
|
||||
label: updatedObjectMetadata.labelSingular,
|
||||
},
|
||||
);
|
||||
|
||||
const relationTableName = computeObjectTargetTable(relatedObject);
|
||||
const columnName = `${existingObjectMetadata.nameSingular}Id`;
|
||||
const columnType = fieldMetadataTypeToColumnType(
|
||||
fieldWihStandardRelation.type,
|
||||
);
|
||||
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(
|
||||
`rename-${existingObjectMetadata.nameSingular}-to-${updatedObjectMetadata.nameSingular}-in-${relatedObject.nameSingular}`,
|
||||
),
|
||||
updatedObjectMetadata.workspaceId,
|
||||
[
|
||||
{
|
||||
name: relationTableName,
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||
currentColumnDefinition: {
|
||||
columnName,
|
||||
columnType,
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
},
|
||||
alteredColumnDefinition: {
|
||||
columnName: `${updatedObjectMetadata.nameSingular}Id`,
|
||||
columnType,
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async deleteAllRelationsAndDropTable(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const relationsToDelete: RelationToDelete[] = [];
|
||||
|
||||
// TODO: Most of this logic should be moved to relation-metadata.service.ts
|
||||
for (const relation of [
|
||||
...objectMetadata.fromRelations,
|
||||
...objectMetadata.toRelations,
|
||||
]) {
|
||||
relationsToDelete.push({
|
||||
id: relation.id,
|
||||
fromFieldMetadataId: relation.fromFieldMetadata.id,
|
||||
toFieldMetadataId: relation.toFieldMetadata.id,
|
||||
fromFieldMetadataName: relation.fromFieldMetadata.name,
|
||||
toFieldMetadataName: relation.toFieldMetadata.name,
|
||||
fromObjectMetadataId: relation.fromObjectMetadata.id,
|
||||
toObjectMetadataId: relation.toObjectMetadata.id,
|
||||
fromObjectName: relation.fromObjectMetadata.nameSingular,
|
||||
toObjectName: relation.toObjectMetadata.nameSingular,
|
||||
toFieldMetadataIsCustom: relation.toFieldMetadata.isCustom,
|
||||
toObjectMetadataIsCustom: relation.toObjectMetadata.isCustom,
|
||||
direction:
|
||||
relation.fromObjectMetadata.nameSingular ===
|
||||
objectMetadata.nameSingular
|
||||
? 'from'
|
||||
: 'to',
|
||||
});
|
||||
}
|
||||
|
||||
if (relationsToDelete.length > 0) {
|
||||
await this.relationMetadataRepository.delete(
|
||||
relationsToDelete.map((relation) => relation.id),
|
||||
);
|
||||
}
|
||||
|
||||
for (const relationToDelete of relationsToDelete) {
|
||||
const foreignKeyFieldsToDelete = await this.fieldMetadataRepository.find({
|
||||
where: {
|
||||
name: `${relationToDelete.toFieldMetadataName}Id`,
|
||||
objectMetadataId: relationToDelete.toObjectMetadataId,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
const foreignKeyFieldsToDeleteIds = foreignKeyFieldsToDelete.map(
|
||||
(field) => field.id,
|
||||
);
|
||||
|
||||
await this.fieldMetadataRepository.delete([
|
||||
...foreignKeyFieldsToDeleteIds,
|
||||
relationToDelete.fromFieldMetadataId,
|
||||
relationToDelete.toFieldMetadataId,
|
||||
]);
|
||||
|
||||
if (relationToDelete.direction === 'from') {
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(
|
||||
`delete-${relationToDelete.fromObjectName}-${relationToDelete.toObjectName}`,
|
||||
),
|
||||
workspaceId,
|
||||
[
|
||||
{
|
||||
name: computeTableName(
|
||||
relationToDelete.toObjectName,
|
||||
relationToDelete.toObjectMetadataIsCustom,
|
||||
),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.DROP,
|
||||
columnName: computeColumnName(
|
||||
relationToDelete.toFieldMetadataName,
|
||||
{ isForeignKey: true },
|
||||
),
|
||||
} satisfies WorkspaceMigrationColumnDrop,
|
||||
],
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// DROP TABLE
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(`delete-${objectMetadata.nameSingular}`),
|
||||
workspaceId,
|
||||
[
|
||||
{
|
||||
name: computeObjectTargetTable(objectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.DROP,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
|
||||
import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity';
|
||||
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class ObjectMetadataRelatedRecordsService {
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
) {}
|
||||
|
||||
public async createObjectRelatedRecords(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
) {
|
||||
const view = await this.createView(objectMetadata);
|
||||
|
||||
await this.createViewFields(objectMetadata, view.id);
|
||||
await this.createViewWorkspaceFavorite(objectMetadata.workspaceId, view.id);
|
||||
}
|
||||
|
||||
private async createView(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
): Promise<ViewWorkspaceEntity> {
|
||||
const viewRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ViewWorkspaceEntity>(
|
||||
objectMetadata.workspaceId,
|
||||
'view',
|
||||
);
|
||||
|
||||
return await viewRepository.save({
|
||||
objectMetadataId: objectMetadata.id,
|
||||
type: 'table',
|
||||
name: `All ${objectMetadata.labelPlural}`,
|
||||
key: 'INDEX',
|
||||
icon: objectMetadata.icon,
|
||||
});
|
||||
}
|
||||
|
||||
private async createViewFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
viewId: string,
|
||||
): Promise<void> {
|
||||
const viewFieldRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ViewFieldWorkspaceEntity>(
|
||||
objectMetadata.workspaceId,
|
||||
'viewField',
|
||||
);
|
||||
|
||||
const viewFields = objectMetadata.fields
|
||||
.filter((field) => field.name !== 'id' && field.name !== 'deletedAt')
|
||||
.map((field, index) => ({
|
||||
fieldMetadataId: field.id,
|
||||
position: index,
|
||||
isVisible: true,
|
||||
size: 180,
|
||||
viewId: viewId,
|
||||
}));
|
||||
|
||||
await viewFieldRepository.insert(viewFields);
|
||||
}
|
||||
|
||||
private async createViewWorkspaceFavorite(
|
||||
workspaceId: string,
|
||||
viewId: string,
|
||||
): Promise<void> {
|
||||
const favoriteRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<FavoriteWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'favorite',
|
||||
);
|
||||
|
||||
const favoriteCount = await favoriteRepository.count();
|
||||
|
||||
await favoriteRepository.insert(
|
||||
favoriteRepository.create({
|
||||
viewId: viewId,
|
||||
position: favoriteCount,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public async updateObjectViews(
|
||||
updatedObjectMetadata: ObjectMetadataEntity,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const viewRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ViewWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'view',
|
||||
);
|
||||
|
||||
await viewRepository.update(
|
||||
{ objectMetadataId: updatedObjectMetadata.id, key: 'INDEX' },
|
||||
{
|
||||
name: `All ${updatedObjectMetadata.labelPlural}`,
|
||||
icon: updatedObjectMetadata.icon,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
public async deleteObjectViews(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const viewRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ViewWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'view',
|
||||
);
|
||||
|
||||
await viewRepository.delete({
|
||||
objectMetadataId: objectMetadata.id,
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,253 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
|
||||
import {
|
||||
FieldMetadataEntity,
|
||||
FieldMetadataType,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import {
|
||||
RelationMetadataEntity,
|
||||
RelationMetadataType,
|
||||
RelationOnDeleteAction,
|
||||
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import {
|
||||
CUSTOM_OBJECT_STANDARD_FIELD_IDS,
|
||||
STANDARD_OBJECT_FIELD_IDS,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import {
|
||||
createForeignKeyDeterministicUuid,
|
||||
createRelationDeterministicUuid,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util';
|
||||
import { capitalize } from 'src/utils/capitalize';
|
||||
|
||||
@Injectable()
|
||||
export class ObjectMetadataRelationService {
|
||||
constructor(
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
@InjectRepository(FieldMetadataEntity, 'metadata')
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
@InjectRepository(RelationMetadataEntity, 'metadata')
|
||||
private readonly relationMetadataRepository: Repository<RelationMetadataEntity>,
|
||||
) {}
|
||||
|
||||
public async createMetadata(
|
||||
workspaceId: string,
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
objectPrimaryKeyType: FieldMetadataType,
|
||||
objectPrimaryKeyFieldSettings:
|
||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
||||
| undefined,
|
||||
relatedObjectMetadataName: string,
|
||||
) {
|
||||
const relatedObjectMetadata =
|
||||
await this.objectMetadataRepository.findOneByOrFail({
|
||||
nameSingular: relatedObjectMetadataName,
|
||||
workspaceId: workspaceId,
|
||||
});
|
||||
|
||||
await this.createForeignKeyFieldMetadata(
|
||||
workspaceId,
|
||||
createdObjectMetadata,
|
||||
relatedObjectMetadata,
|
||||
objectPrimaryKeyType,
|
||||
objectPrimaryKeyFieldSettings,
|
||||
);
|
||||
|
||||
const relationFieldMetadata = await this.createRelationFields(
|
||||
workspaceId,
|
||||
createdObjectMetadata,
|
||||
relatedObjectMetadata,
|
||||
);
|
||||
|
||||
await this.createRelationMetadata(
|
||||
workspaceId,
|
||||
createdObjectMetadata,
|
||||
relatedObjectMetadata,
|
||||
relationFieldMetadata,
|
||||
);
|
||||
|
||||
return relatedObjectMetadata;
|
||||
}
|
||||
|
||||
private async createForeignKeyFieldMetadata(
|
||||
workspaceId: string,
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
relatedObjectMetadata: ObjectMetadataEntity,
|
||||
objectPrimaryKeyType: FieldMetadataType,
|
||||
objectPrimaryKeyFieldSettings:
|
||||
| FieldMetadataSettings<FieldMetadataType | 'default'>
|
||||
| undefined,
|
||||
) {
|
||||
const customStandardFieldId =
|
||||
STANDARD_OBJECT_FIELD_IDS[relatedObjectMetadata.nameSingular].custom;
|
||||
|
||||
if (!customStandardFieldId) {
|
||||
throw new Error(
|
||||
`Custom standard field ID not found for ${relatedObjectMetadata.nameSingular}`,
|
||||
);
|
||||
}
|
||||
|
||||
await this.fieldMetadataRepository.save({
|
||||
standardId: createForeignKeyDeterministicUuid({
|
||||
objectId: createdObjectMetadata.id,
|
||||
standardId: customStandardFieldId,
|
||||
}),
|
||||
objectMetadataId: relatedObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: objectPrimaryKeyType,
|
||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
||||
description: `${relatedObjectMetadata.labelSingular} ${createdObjectMetadata.labelSingular} id foreign key`,
|
||||
icon: undefined,
|
||||
isNullable: true,
|
||||
isSystem: true,
|
||||
defaultValue: undefined,
|
||||
settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true },
|
||||
});
|
||||
}
|
||||
|
||||
private async createRelationFields(
|
||||
workspaceId: string,
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
relatedObjectMetadata: ObjectMetadataEntity,
|
||||
) {
|
||||
return await this.fieldMetadataRepository.save([
|
||||
this.createFromField(
|
||||
workspaceId,
|
||||
createdObjectMetadata,
|
||||
relatedObjectMetadata,
|
||||
),
|
||||
this.createToField(
|
||||
workspaceId,
|
||||
createdObjectMetadata,
|
||||
relatedObjectMetadata,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
private createFromField(
|
||||
workspaceId: string,
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
relatedObjectMetadata: ObjectMetadataEntity,
|
||||
) {
|
||||
const relationObjectMetadataNamePlural =
|
||||
relatedObjectMetadata.nameSingular + 's';
|
||||
|
||||
return {
|
||||
standardId:
|
||||
CUSTOM_OBJECT_STANDARD_FIELD_IDS[relationObjectMetadataNamePlural],
|
||||
objectMetadataId: createdObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: relationObjectMetadataNamePlural,
|
||||
label: capitalize(relationObjectMetadataNamePlural),
|
||||
description: `${capitalize(relationObjectMetadataNamePlural)} tied to the ${createdObjectMetadata.labelSingular}`,
|
||||
icon:
|
||||
STANDARD_OBJECT_ICONS[relatedObjectMetadata.nameSingular] ||
|
||||
'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
};
|
||||
}
|
||||
|
||||
private createToField(
|
||||
workspaceId: string,
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
relatedObjectMetadata: ObjectMetadataEntity,
|
||||
) {
|
||||
const customStandardFieldId =
|
||||
STANDARD_OBJECT_FIELD_IDS[relatedObjectMetadata.nameSingular].custom;
|
||||
|
||||
if (!customStandardFieldId) {
|
||||
throw new Error(
|
||||
`Custom standard field ID not found for ${relatedObjectMetadata.nameSingular}`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
standardId: createRelationDeterministicUuid({
|
||||
objectId: createdObjectMetadata.id,
|
||||
standardId: customStandardFieldId,
|
||||
}),
|
||||
objectMetadataId: relatedObjectMetadata.id,
|
||||
workspaceId: workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: createdObjectMetadata.nameSingular,
|
||||
label: createdObjectMetadata.labelSingular,
|
||||
description: `${capitalize(relatedObjectMetadata.nameSingular)} ${createdObjectMetadata.labelSingular}`,
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: true,
|
||||
};
|
||||
}
|
||||
|
||||
private async createRelationMetadata(
|
||||
workspaceId: string,
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
relatedObjectMetadata: ObjectMetadataEntity,
|
||||
relationFieldMetadata: FieldMetadataEntity[],
|
||||
) {
|
||||
const relationFieldMetadataMap = relationFieldMetadata.reduce(
|
||||
(acc, fieldMetadata: FieldMetadataEntity) => {
|
||||
if (fieldMetadata.type === FieldMetadataType.RELATION) {
|
||||
acc[fieldMetadata.objectMetadataId] = fieldMetadata;
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
await this.relationMetadataRepository.save([
|
||||
{
|
||||
workspaceId: workspaceId,
|
||||
relationType: RelationMetadataType.ONE_TO_MANY,
|
||||
fromObjectMetadataId: createdObjectMetadata.id,
|
||||
toObjectMetadataId: relatedObjectMetadata.id,
|
||||
fromFieldMetadataId:
|
||||
relationFieldMetadataMap[createdObjectMetadata.id].id,
|
||||
toFieldMetadataId:
|
||||
relationFieldMetadataMap[relatedObjectMetadata.id].id,
|
||||
onDeleteAction: RelationOnDeleteAction.CASCADE,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
async updateObjectRelationships(objectMetadataId: string, isActive: boolean) {
|
||||
const affectedRelations = await this.relationMetadataRepository.find({
|
||||
where: [
|
||||
{ fromObjectMetadataId: objectMetadataId },
|
||||
{ toObjectMetadataId: objectMetadataId },
|
||||
],
|
||||
});
|
||||
|
||||
const affectedFieldIds = affectedRelations.reduce(
|
||||
(acc, { fromFieldMetadataId, toFieldMetadataId }) => {
|
||||
acc.push(fromFieldMetadataId, toFieldMetadataId);
|
||||
|
||||
return acc;
|
||||
},
|
||||
[] as string[],
|
||||
);
|
||||
|
||||
if (affectedFieldIds.length > 0) {
|
||||
await this.fieldMetadataRepository.update(
|
||||
{ id: In(affectedFieldIds) },
|
||||
{ isActive: isActive },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
import {
|
||||
FieldMetadataEntity,
|
||||
FieldMetadataType,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
BASE_OBJECT_STANDARD_FIELD_IDS,
|
||||
CUSTOM_OBJECT_STANDARD_FIELD_IDS,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
|
||||
export const buildDefaultFieldsForCustomObject = (
|
||||
workspaceId: string,
|
||||
): Partial<FieldMetadataEntity>[] => [
|
||||
{
|
||||
standardId: BASE_OBJECT_STANDARD_FIELD_IDS.id,
|
||||
type: FieldMetadataType.UUID,
|
||||
name: 'id',
|
||||
label: 'Id',
|
||||
icon: 'Icon123',
|
||||
description: 'Id',
|
||||
isNullable: false,
|
||||
isActive: true,
|
||||
isCustom: false,
|
||||
isSystem: true,
|
||||
workspaceId,
|
||||
defaultValue: 'uuid',
|
||||
},
|
||||
{
|
||||
standardId: CUSTOM_OBJECT_STANDARD_FIELD_IDS.name,
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
icon: 'IconAbc',
|
||||
description: 'Name',
|
||||
isNullable: false,
|
||||
isActive: true,
|
||||
isCustom: false,
|
||||
workspaceId,
|
||||
defaultValue: "'Untitled'",
|
||||
},
|
||||
{
|
||||
standardId: BASE_OBJECT_STANDARD_FIELD_IDS.createdAt,
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
name: 'createdAt',
|
||||
label: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
description: 'Creation date',
|
||||
isNullable: false,
|
||||
isActive: true,
|
||||
isCustom: false,
|
||||
workspaceId,
|
||||
defaultValue: 'now',
|
||||
},
|
||||
{
|
||||
standardId: BASE_OBJECT_STANDARD_FIELD_IDS.updatedAt,
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
name: 'updatedAt',
|
||||
label: 'Last update',
|
||||
icon: 'IconCalendarClock',
|
||||
description: 'Last time the record was changed',
|
||||
isNullable: false,
|
||||
isActive: true,
|
||||
isCustom: false,
|
||||
isSystem: false,
|
||||
workspaceId,
|
||||
defaultValue: 'now',
|
||||
},
|
||||
{
|
||||
standardId: BASE_OBJECT_STANDARD_FIELD_IDS.deletedAt,
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
name: 'deletedAt',
|
||||
label: 'Deleted at',
|
||||
icon: 'IconCalendarClock',
|
||||
description: 'Deletion date',
|
||||
isNullable: true,
|
||||
isActive: true,
|
||||
isCustom: false,
|
||||
isSystem: false,
|
||||
workspaceId,
|
||||
defaultValue: null,
|
||||
},
|
||||
{
|
||||
standardId: CUSTOM_OBJECT_STANDARD_FIELD_IDS.createdBy,
|
||||
type: FieldMetadataType.ACTOR,
|
||||
name: 'createdBy',
|
||||
label: 'Created by',
|
||||
icon: 'IconCreativeCommonsSa',
|
||||
description: 'The creator of the record',
|
||||
isNullable: false,
|
||||
isActive: true,
|
||||
isCustom: false,
|
||||
isSystem: false,
|
||||
workspaceId,
|
||||
defaultValue: { name: "''", source: "'MANUAL'" },
|
||||
},
|
||||
{
|
||||
standardId: CUSTOM_OBJECT_STANDARD_FIELD_IDS.position,
|
||||
type: FieldMetadataType.POSITION,
|
||||
name: 'position',
|
||||
label: 'Position',
|
||||
icon: 'IconHierarchy2',
|
||||
description: 'Position',
|
||||
isNullable: true,
|
||||
isActive: true,
|
||||
isCustom: false,
|
||||
isSystem: true,
|
||||
workspaceId,
|
||||
defaultValue: null,
|
||||
},
|
||||
];
|
@ -11,197 +11,46 @@ import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target
|
||||
|
||||
export const buildMigrationsForCustomObjectRelations = (
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
activityTargetObjectMetadata: ObjectMetadataEntity,
|
||||
attachmentObjectMetadata: ObjectMetadataEntity,
|
||||
timelineActivityObjectMetadata: ObjectMetadataEntity,
|
||||
favoriteObjectMetadata: ObjectMetadataEntity,
|
||||
noteTargetObjectMetadata: ObjectMetadataEntity,
|
||||
taskTargetObjectMetadata: ObjectMetadataEntity,
|
||||
): WorkspaceMigrationTableAction[] => [
|
||||
// Add activity target relation
|
||||
{
|
||||
name: computeObjectTargetTable(activityTargetObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
relatedObjectMetadataCollection: ObjectMetadataEntity[],
|
||||
): WorkspaceMigrationTableAction[] => {
|
||||
const migrations: WorkspaceMigrationTableAction[] = [];
|
||||
|
||||
for (const relatedObjectMetadata of relatedObjectMetadataCollection) {
|
||||
migrations.push(
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: 'uuid',
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(activityTargetObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
referencedTableName: computeObjectTargetTable(createdObjectMetadata),
|
||||
referencedTableColumnName: 'id',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
name: computeObjectTargetTable(relatedObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: 'uuid',
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// Add note target relation
|
||||
{
|
||||
name: computeObjectTargetTable(noteTargetObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: 'uuid',
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(noteTargetObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
referencedTableName: computeObjectTargetTable(createdObjectMetadata),
|
||||
referencedTableColumnName: 'id',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
name: computeObjectTargetTable(relatedObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
referencedTableName: computeObjectTargetTable(
|
||||
createdObjectMetadata,
|
||||
),
|
||||
referencedTableColumnName: 'id',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// Add task target relation
|
||||
{
|
||||
name: computeObjectTargetTable(taskTargetObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: 'uuid',
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(taskTargetObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
referencedTableName: computeObjectTargetTable(createdObjectMetadata),
|
||||
referencedTableColumnName: 'id',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
},
|
||||
],
|
||||
},
|
||||
// Add attachment relation
|
||||
{
|
||||
name: computeObjectTargetTable(attachmentObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: 'uuid',
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(attachmentObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
referencedTableName: computeObjectTargetTable(createdObjectMetadata),
|
||||
referencedTableColumnName: 'id',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
},
|
||||
],
|
||||
},
|
||||
// Add timeline activity relation
|
||||
{
|
||||
name: computeObjectTargetTable(timelineActivityObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: 'uuid',
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(timelineActivityObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
referencedTableName: computeObjectTargetTable(createdObjectMetadata),
|
||||
referencedTableColumnName: 'id',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
},
|
||||
],
|
||||
},
|
||||
// Add favorite relation
|
||||
{
|
||||
name: computeObjectTargetTable(favoriteObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: 'uuid',
|
||||
isNullable: true,
|
||||
defaultValue: null,
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(favoriteObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
referencedTableName: computeObjectTargetTable(createdObjectMetadata),
|
||||
referencedTableColumnName: 'id',
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
return migrations;
|
||||
};
|
||||
|
@ -99,24 +99,20 @@ const validateNameIsNotReservedKeywordOrThrow = (name?: string) => {
|
||||
};
|
||||
|
||||
const validateNameCamelCasedOrThrow = (name?: string) => {
|
||||
if (name) {
|
||||
if (name !== camelCase(name)) {
|
||||
throw new ObjectMetadataException(
|
||||
`Name should be in camelCase: ${name}`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
if (name && name !== camelCase(name)) {
|
||||
throw new ObjectMetadataException(
|
||||
`Name should be in camelCase: ${name}`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const validateNameIsNotTooLongThrow = (name?: string) => {
|
||||
if (name) {
|
||||
if (exceedsDatabaseIdentifierMaximumLength(name)) {
|
||||
throw new ObjectMetadataException(
|
||||
`Name exceeds 63 characters: ${name}`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
if (name && exceedsDatabaseIdentifierMaximumLength(name)) {
|
||||
throw new ObjectMetadataException(
|
||||
`Name exceeds 63 characters: ${name}`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -142,3 +138,29 @@ export const computeMetadataNameFromLabelOrThrow = (label: string): string => {
|
||||
|
||||
return formattedString;
|
||||
};
|
||||
|
||||
export const validateNameAndLabelAreSyncOrThrow = (
|
||||
label: string,
|
||||
name: string,
|
||||
) => {
|
||||
const computedName = computeMetadataNameFromLabelOrThrow(label);
|
||||
|
||||
if (name !== computedName) {
|
||||
throw new ObjectMetadataException(
|
||||
`Name is not synced with label. Expected name: "${computedName}", got ${name}`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const validateNameSingularAndNamePluralAreDifferentOrThrow = (
|
||||
nameSingular: string,
|
||||
namePlural: string,
|
||||
) => {
|
||||
if (nameSingular === namePlural) {
|
||||
throw new ObjectMetadataException(
|
||||
'The singular and plural name cannot be the same for an object',
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -1,19 +0,0 @@
|
||||
import {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import { computeMetadataNameFromLabelOrThrow } from 'src/engine/metadata-modules/object-metadata/utils/validate-object-metadata-input.util';
|
||||
|
||||
export const validateNameAndLabelAreSyncOrThrow = (
|
||||
label: string,
|
||||
name: string,
|
||||
) => {
|
||||
const computedName = computeMetadataNameFromLabelOrThrow(label);
|
||||
|
||||
if (name !== computedName) {
|
||||
throw new ObjectMetadataException(
|
||||
`Name is not synced with label. Expected name: "${computedName}", got ${name}`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
};
|
@ -9,13 +9,13 @@ import {
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { EmailService } from 'src/engine/core-modules/email/email.service';
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
|
@ -506,3 +506,47 @@ export const CUSTOM_OBJECT_STANDARD_FIELD_IDS = {
|
||||
timelineActivities: '20202020-f1ef-4ba4-8f33-1a4577afa477',
|
||||
searchVector: '70e56537-18ef-4811-b1c7-0a444006b815',
|
||||
};
|
||||
|
||||
export const STANDARD_OBJECT_FIELD_IDS = {
|
||||
activityTarget: ACTIVITY_TARGET_STANDARD_FIELD_IDS,
|
||||
activity: ACTIVITY_STANDARD_FIELD_IDS,
|
||||
apiKey: API_KEY_STANDARD_FIELD_IDS,
|
||||
attachment: ATTACHMENT_STANDARD_FIELD_IDS,
|
||||
blocklist: BLOCKLIST_STANDARD_FIELD_IDS,
|
||||
behavioralEvent: BEHAVIORAL_EVENT_STANDARD_FIELD_IDS,
|
||||
calendarChannelEventAssociation:
|
||||
CALENDAR_CHANNEL_EVENT_ASSOCIATION_STANDARD_FIELD_IDS,
|
||||
calendarChannel: CALENDAR_CHANNEL_STANDARD_FIELD_IDS,
|
||||
calendarEventParticipant: CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS,
|
||||
calendarEvent: CALENDAR_EVENT_STANDARD_FIELD_IDS,
|
||||
comment: COMMENT_STANDARD_FIELD_IDS,
|
||||
company: COMPANY_STANDARD_FIELD_IDS,
|
||||
connectedAccount: CONNECTED_ACCOUNT_STANDARD_FIELD_IDS,
|
||||
favorite: FAVORITE_STANDARD_FIELD_IDS,
|
||||
auditLog: AUDIT_LOGS_STANDARD_FIELD_IDS,
|
||||
messageChannelMessageAssociation:
|
||||
MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_STANDARD_FIELD_IDS,
|
||||
messageChannel: MESSAGE_CHANNEL_STANDARD_FIELD_IDS,
|
||||
messageParticipant: MESSAGE_PARTICIPANT_STANDARD_FIELD_IDS,
|
||||
messageThread: MESSAGE_THREAD_STANDARD_FIELD_IDS,
|
||||
messageThreadSubscriber: MESSAGE_THREAD_SUBSCRIBER_STANDARD_FIELD_IDS,
|
||||
message: MESSAGE_STANDARD_FIELD_IDS,
|
||||
note: NOTE_STANDARD_FIELD_IDS,
|
||||
noteTarget: NOTE_TARGET_STANDARD_FIELD_IDS,
|
||||
opportunity: OPPORTUNITY_STANDARD_FIELD_IDS,
|
||||
person: PERSON_STANDARD_FIELD_IDS,
|
||||
task: TASK_STANDARD_FIELD_IDS,
|
||||
taskTarget: TASK_TARGET_STANDARD_FIELD_IDS,
|
||||
timelineActivity: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS,
|
||||
viewField: VIEW_FIELD_STANDARD_FIELD_IDS,
|
||||
viewGroup: VIEW_GROUP_STANDARD_FIELD_IDS,
|
||||
viewFilter: VIEW_FILTER_STANDARD_FIELD_IDS,
|
||||
viewSort: VIEW_SORT_STANDARD_FIELD_IDS,
|
||||
view: VIEW_STANDARD_FIELD_IDS,
|
||||
webhook: WEBHOOK_STANDARD_FIELD_IDS,
|
||||
workflow: WORKFLOW_STANDARD_FIELD_IDS,
|
||||
workflowEventListener: WORKFLOW_EVENT_LISTENER_STANDARD_FIELD_IDS,
|
||||
workflowRun: WORKFLOW_RUN_STANDARD_FIELD_IDS,
|
||||
workflowVersion: WORKFLOW_VERSION_STANDARD_FIELD_IDS,
|
||||
workspaceMember: WORKSPACE_MEMBER_STANDARD_FIELD_IDS,
|
||||
};
|
||||
|
@ -0,0 +1,41 @@
|
||||
export const STANDARD_OBJECT_ICONS = {
|
||||
activityTarget: 'IconCheckbox',
|
||||
activity: 'IconCheckbox',
|
||||
apiKey: 'IconRobot',
|
||||
attachment: 'IconFileImport',
|
||||
blocklist: 'IconForbid2',
|
||||
behavioralEvent: 'IconTimelineEvent',
|
||||
calendarChannelEventAssociation: 'IconCalendar',
|
||||
calendarChannel: 'IconCalendar',
|
||||
calendarEventParticipant: 'IconCalendar',
|
||||
calendarEvent: 'IconCalendar',
|
||||
comment: 'IconMessageCircle',
|
||||
company: 'IconBuildingSkyscraper',
|
||||
connectedAccount: 'IconAt',
|
||||
favorite: 'IconHeart',
|
||||
auditLog: 'IconTimelineEvent',
|
||||
messageChannelMessageAssociation: 'IconMessage',
|
||||
messageChannel: 'IconMessage',
|
||||
messageParticipant: 'IconUserCircle',
|
||||
messageThread: 'IconMessage',
|
||||
messageThreadSubscriber: 'IconPerson',
|
||||
message: 'IconMessage',
|
||||
note: 'IconNotes',
|
||||
noteTarget: 'IconCheckbox',
|
||||
opportunity: 'IconTargetArrow',
|
||||
person: 'IconUser',
|
||||
task: 'IconCheckbox',
|
||||
taskTarget: 'IconCheckbox',
|
||||
timelineActivity: 'IconTimelineEvent',
|
||||
viewField: 'IconTag',
|
||||
viewGroup: 'IconTag',
|
||||
viewFilter: 'IconFilterBolt',
|
||||
viewSort: 'IconArrowsSort',
|
||||
view: 'IconLayoutCollage',
|
||||
webhook: 'IconRobot',
|
||||
workflow: 'IconSettingsAutomation',
|
||||
workflowEventListener: 'IconSettingsAutomation',
|
||||
workflowRun: 'IconSettingsAutomation',
|
||||
workflowVersion: 'IconSettingsAutomation',
|
||||
workspaceMember: 'IconUserCircle',
|
||||
};
|
@ -10,6 +10,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { ACTIVITY_TARGET_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { ActivityWorkspaceEntity } from 'src/modules/activity/standard-objects/activity.workspace-entity';
|
||||
import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/company.workspace-entity';
|
||||
@ -22,7 +23,7 @@ import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/perso
|
||||
labelSingular: 'Activity Target',
|
||||
labelPlural: 'Activity Targets',
|
||||
description: 'An activity target',
|
||||
icon: 'IconCheckbox',
|
||||
icon: STANDARD_OBJECT_ICONS.activityTarget,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
export class ActivityTargetWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
|
@ -13,6 +13,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { ACTIVITY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { ActivityTargetWorkspaceEntity } from 'src/modules/activity/standard-objects/activity-target.workspace-entity';
|
||||
import { CommentWorkspaceEntity } from 'src/modules/activity/standard-objects/comment.workspace-entity';
|
||||
@ -25,7 +26,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
labelSingular: 'Activity',
|
||||
labelPlural: 'Activities',
|
||||
description: 'An activity',
|
||||
icon: 'IconCheckbox',
|
||||
icon: STANDARD_OBJECT_ICONS.activity,
|
||||
labelIdentifierStandardId: ACTIVITY_STANDARD_FIELD_IDS.title,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -1,17 +1,18 @@
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { COMMENT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { ActivityWorkspaceEntity } from 'src/modules/activity/standard-objects/activity.workspace-entity';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
|
||||
@WorkspaceEntity({
|
||||
standardId: STANDARD_OBJECT_IDS.comment,
|
||||
@ -19,7 +20,7 @@ import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-
|
||||
labelSingular: 'Comment',
|
||||
labelPlural: 'Comments',
|
||||
description: 'A comment',
|
||||
icon: 'IconMessageCircle',
|
||||
icon: STANDARD_OBJECT_ICONS.comment,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
export class CommentWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
|
@ -6,6 +6,7 @@ import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/work
|
||||
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { API_KEY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
|
||||
@WorkspaceEntity({
|
||||
@ -14,7 +15,7 @@ import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync
|
||||
labelSingular: 'API Key',
|
||||
labelPlural: 'API Keys',
|
||||
description: 'An API key',
|
||||
icon: 'IconRobot',
|
||||
icon: STANDARD_OBJECT_ICONS.apiKey,
|
||||
labelIdentifierStandardId: API_KEY_STANDARD_FIELD_IDS.name,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -13,6 +13,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { ATTACHMENT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { ActivityWorkspaceEntity } from 'src/modules/activity/standard-objects/activity.workspace-entity';
|
||||
import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/company.workspace-entity';
|
||||
@ -28,7 +29,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
labelSingular: 'Attachment',
|
||||
labelPlural: 'Attachments',
|
||||
description: 'An attachment',
|
||||
icon: 'IconFileImport',
|
||||
icon: STANDARD_OBJECT_ICONS.attachment,
|
||||
labelIdentifierStandardId: ATTACHMENT_STANDARD_FIELD_IDS.name,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -10,6 +10,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { BLOCKLIST_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
@ -19,7 +20,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
labelSingular: 'Blocklist',
|
||||
labelPlural: 'Blocklists',
|
||||
description: 'Blocklist',
|
||||
icon: 'IconForbid2',
|
||||
icon: STANDARD_OBJECT_ICONS.blocklist,
|
||||
labelIdentifierStandardId: BLOCKLIST_STANDARD_FIELD_IDS.handle,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -10,6 +10,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { CALENDAR_CHANNEL_EVENT_ASSOCIATION_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
@ -20,7 +21,7 @@ import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standa
|
||||
labelSingular: 'Calendar Channel Event Association',
|
||||
labelPlural: 'Calendar Channel Event Associations',
|
||||
description: 'Calendar Channel Event Associations',
|
||||
icon: 'IconCalendar',
|
||||
icon: STANDARD_OBJECT_ICONS.calendarChannelEventAssociation,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
|
@ -16,6 +16,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { CALENDAR_CHANNEL_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
@ -71,7 +72,7 @@ registerEnumType(CalendarChannelContactAutoCreationPolicy, {
|
||||
labelSingular: 'Calendar Channel',
|
||||
labelPlural: 'Calendar Channels',
|
||||
description: 'Calendar Channels',
|
||||
icon: 'IconCalendar',
|
||||
icon: STANDARD_OBJECT_ICONS.calendarChannel,
|
||||
labelIdentifierStandardId: CALENDAR_CHANNEL_STANDARD_FIELD_IDS.handle,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -11,6 +11,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
@ -29,7 +30,7 @@ export enum CalendarEventParticipantResponseStatus {
|
||||
labelSingular: 'Calendar event participant',
|
||||
labelPlural: 'Calendar event participants',
|
||||
description: 'Calendar event participants',
|
||||
icon: 'IconCalendar',
|
||||
icon: STANDARD_OBJECT_ICONS.calendarEventParticipant,
|
||||
labelIdentifierStandardId:
|
||||
CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS.handle,
|
||||
})
|
||||
|
@ -14,6 +14,7 @@ import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { CALENDAR_EVENT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
|
||||
@ -24,7 +25,7 @@ import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/co
|
||||
labelSingular: 'Calendar event',
|
||||
labelPlural: 'Calendar events',
|
||||
description: 'Calendar events',
|
||||
icon: 'IconCalendar',
|
||||
icon: STANDARD_OBJECT_ICONS.calendarEvent,
|
||||
labelIdentifierStandardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.title,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -24,6 +24,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { COMPANY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import {
|
||||
FieldTypeAndNameMetadata,
|
||||
@ -53,7 +54,7 @@ export const SEARCH_FIELDS_FOR_COMPANY: FieldTypeAndNameMetadata[] = [
|
||||
labelSingular: 'Company',
|
||||
labelPlural: 'Companies',
|
||||
description: 'A company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
icon: STANDARD_OBJECT_ICONS.company,
|
||||
shortcut: 'C',
|
||||
labelIdentifierStandardId: COMPANY_STANDARD_FIELD_IDS.name,
|
||||
})
|
||||
|
@ -14,6 +14,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { CONNECTED_ACCOUNT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
@ -29,7 +30,7 @@ export enum ConnectedAccountProvider {
|
||||
labelSingular: 'Connected Account',
|
||||
labelPlural: 'Connected Accounts',
|
||||
description: 'A connected account',
|
||||
icon: 'IconAt',
|
||||
icon: STANDARD_OBJECT_ICONS.connectedAccount,
|
||||
labelIdentifierStandardId: CONNECTED_ACCOUNT_STANDARD_FIELD_IDS.handle,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -15,6 +15,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { FAVORITE_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/company.workspace-entity';
|
||||
import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.workspace-entity';
|
||||
@ -33,7 +34,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
labelSingular: 'Favorite',
|
||||
labelPlural: 'Favorites',
|
||||
description: 'A favorite',
|
||||
icon: 'IconHeart',
|
||||
icon: STANDARD_OBJECT_ICONS.favorite,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -19,7 +19,10 @@ import {
|
||||
WorkflowStepExecutorExceptionCode,
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/types/workflow-action-result.type';
|
||||
import { WorkflowSendEmailStepInput } from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
|
||||
import {
|
||||
WorkflowSendEmailStepInput,
|
||||
WorkflowSendEmailStepOutputSchema,
|
||||
} from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
|
||||
@Injectable()
|
||||
@ -112,7 +115,9 @@ export class SendEmailWorkflowAction implements WorkflowAction {
|
||||
|
||||
this.logger.log(`Email sent successfully`);
|
||||
|
||||
return { result: { success: true } };
|
||||
return {
|
||||
result: { success: true } satisfies WorkflowSendEmailStepOutputSchema,
|
||||
};
|
||||
} catch (error) {
|
||||
return { error };
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_STANDARD_FIELD_IDS,
|
||||
MESSAGE_STANDARD_FIELD_IDS,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { MessageDirection } from 'src/modules/messaging/common/enums/message-direction.enum';
|
||||
import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
@ -25,7 +26,7 @@ import { MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-ob
|
||||
labelSingular: 'Message Channel Message Association',
|
||||
labelPlural: 'Message Channel Message Associations',
|
||||
description: 'Message Synced with a Message Channel',
|
||||
icon: 'IconMessage',
|
||||
icon: STANDARD_OBJECT_ICONS.messageChannelMessageAssociation,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -16,6 +16,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { MESSAGE_CHANNEL_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity';
|
||||
@ -80,7 +81,7 @@ registerEnumType(MessageChannelContactAutoCreationPolicy, {
|
||||
labelSingular: 'Message Channel',
|
||||
labelPlural: 'Message Channels',
|
||||
description: 'Message Channels',
|
||||
icon: 'IconMessage',
|
||||
icon: STANDARD_OBJECT_ICONS.messageChannel,
|
||||
labelIdentifierStandardId: MESSAGE_CHANNEL_STANDARD_FIELD_IDS.handle,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
|
@ -11,6 +11,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { MESSAGE_PARTICIPANT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message.workspace-entity';
|
||||
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
@ -22,7 +23,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
labelSingular: 'Message Participant',
|
||||
labelPlural: 'Message Participants',
|
||||
description: 'Message Participants',
|
||||
icon: 'IconUserCircle',
|
||||
icon: STANDARD_OBJECT_ICONS.messageParticipant,
|
||||
labelIdentifierStandardId: MESSAGE_PARTICIPANT_STANDARD_FIELD_IDS.handle,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
|
@ -10,6 +10,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { MESSAGE_THREAD_SUBSCRIBER_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { MessageThreadWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-thread.workspace-entity';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
@ -20,7 +21,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
labelSingular: 'Message Thread Subscriber',
|
||||
labelPlural: 'Message Threads Subscribers',
|
||||
description: 'Message Thread Subscribers',
|
||||
icon: 'IconPerson',
|
||||
icon: STANDARD_OBJECT_ICONS.messageThreadSubscriber,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -13,6 +13,7 @@ import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { MESSAGE_THREAD_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { MessageThreadSubscriberWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-thread-subscriber.workspace-entity';
|
||||
import { MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message.workspace-entity';
|
||||
@ -23,7 +24,7 @@ import { MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-ob
|
||||
labelSingular: 'Message Thread',
|
||||
labelPlural: 'Message Threads',
|
||||
description: 'Message Thread',
|
||||
icon: 'IconMessage',
|
||||
icon: STANDARD_OBJECT_ICONS.messageThread,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -14,6 +14,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { MESSAGE_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity';
|
||||
import { MessageParticipantWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-participant.workspace-entity';
|
||||
@ -25,7 +26,7 @@ import { MessageThreadWorkspaceEntity } from 'src/modules/messaging/common/stand
|
||||
labelSingular: 'Message',
|
||||
labelPlural: 'Messages',
|
||||
description: 'Message',
|
||||
icon: 'IconMessage',
|
||||
icon: STANDARD_OBJECT_ICONS.message,
|
||||
labelIdentifierStandardId: MESSAGE_STANDARD_FIELD_IDS.subject,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
|
@ -10,6 +10,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { NOTE_TARGET_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/company.workspace-entity';
|
||||
import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.workspace-entity';
|
||||
@ -22,7 +23,7 @@ import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/perso
|
||||
labelSingular: 'Note Target',
|
||||
labelPlural: 'Note Targets',
|
||||
description: 'A note target',
|
||||
icon: 'IconCheckbox',
|
||||
icon: STANDARD_OBJECT_ICONS.noteTarget,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
export class NoteTargetWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
|
@ -19,6 +19,7 @@ import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { NOTE_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import {
|
||||
FieldTypeAndNameMetadata,
|
||||
@ -43,7 +44,7 @@ export const SEARCH_FIELDS_FOR_NOTES: FieldTypeAndNameMetadata[] = [
|
||||
labelSingular: 'Note',
|
||||
labelPlural: 'Notes',
|
||||
description: 'A note',
|
||||
icon: 'IconNotes',
|
||||
icon: STANDARD_OBJECT_ICONS.note,
|
||||
shortcut: 'N',
|
||||
labelIdentifierStandardId: NOTE_STANDARD_FIELD_IDS.title,
|
||||
})
|
||||
|
@ -23,6 +23,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { OPPORTUNITY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import {
|
||||
FieldTypeAndNameMetadata,
|
||||
@ -49,7 +50,7 @@ export const SEARCH_FIELDS_FOR_OPPORTUNITY: FieldTypeAndNameMetadata[] = [
|
||||
labelSingular: 'Opportunity',
|
||||
labelPlural: 'Opportunities',
|
||||
description: 'An opportunity',
|
||||
icon: 'IconTargetArrow',
|
||||
icon: STANDARD_OBJECT_ICONS.opportunity,
|
||||
shortcut: 'O',
|
||||
labelIdentifierStandardId: OPPORTUNITY_STANDARD_FIELD_IDS.name,
|
||||
})
|
||||
|
@ -26,6 +26,7 @@ import { WorkspaceIsUnique } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { PERSON_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import {
|
||||
FieldTypeAndNameMetadata,
|
||||
@ -58,7 +59,7 @@ export const SEARCH_FIELDS_FOR_PERSON: FieldTypeAndNameMetadata[] = [
|
||||
labelSingular: 'Person',
|
||||
labelPlural: 'People',
|
||||
description: 'A person',
|
||||
icon: 'IconUser',
|
||||
icon: STANDARD_OBJECT_ICONS.person,
|
||||
shortcut: 'P',
|
||||
labelIdentifierStandardId: PERSON_STANDARD_FIELD_IDS.name,
|
||||
imageIdentifierStandardId: PERSON_STANDARD_FIELD_IDS.avatarUrl,
|
||||
|
@ -10,6 +10,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { TASK_TARGET_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/company.workspace-entity';
|
||||
import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
|
||||
@ -22,7 +23,7 @@ import { TaskWorkspaceEntity } from 'src/modules/task/standard-objects/task.work
|
||||
labelSingular: 'Task Target',
|
||||
labelPlural: 'Task Targets',
|
||||
description: 'An task target',
|
||||
icon: 'IconCheckbox',
|
||||
icon: STANDARD_OBJECT_ICONS.taskTarget,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
export class TaskTargetWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
|
@ -20,6 +20,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { TASK_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import {
|
||||
FieldTypeAndNameMetadata,
|
||||
@ -45,7 +46,7 @@ export const SEARCH_FIELDS_FOR_TASK: FieldTypeAndNameMetadata[] = [
|
||||
labelSingular: 'Task',
|
||||
labelPlural: 'Tasks',
|
||||
description: 'A task',
|
||||
icon: 'IconCheckbox',
|
||||
icon: STANDARD_OBJECT_ICONS.task,
|
||||
shortcut: 'T',
|
||||
labelIdentifierStandardId: TASK_STANDARD_FIELD_IDS.title,
|
||||
})
|
||||
|
@ -10,6 +10,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { AUDIT_LOGS_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
@ -19,7 +20,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
labelSingular: 'Audit Log',
|
||||
labelPlural: 'Audit Logs',
|
||||
description: 'An audit log of actions performed in the system',
|
||||
icon: 'IconIconTimelineEvent',
|
||||
icon: STANDARD_OBJECT_ICONS.auditLog,
|
||||
labelIdentifierStandardId: AUDIT_LOGS_STANDARD_FIELD_IDS.name,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -7,6 +7,7 @@ import { WorkspaceGate } from 'src/engine/twenty-orm/decorators/workspace-gate.d
|
||||
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { BEHAVIORAL_EVENT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
|
||||
@WorkspaceEntity({
|
||||
@ -15,7 +16,7 @@ import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync
|
||||
labelSingular: 'Behavioral Event',
|
||||
labelPlural: 'Behavioral Events',
|
||||
description: 'An event related to user behavior',
|
||||
icon: 'IconIconTimelineEvent',
|
||||
icon: STANDARD_OBJECT_ICONS.behavioralEvent,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
@WorkspaceGate({
|
||||
|
@ -15,6 +15,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { TIMELINE_ACTIVITY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { CompanyWorkspaceEntity } from 'src/modules/company/standard-objects/company.workspace-entity';
|
||||
import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.workspace-entity';
|
||||
@ -32,7 +33,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
labelSingular: 'Timeline Activity',
|
||||
labelPlural: 'Timeline Activities',
|
||||
description: 'Aggregated / filtered event to be displayed on the timeline',
|
||||
icon: 'IconIconTimelineEvent',
|
||||
icon: STANDARD_OBJECT_ICONS.timelineActivity,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
|
@ -9,6 +9,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { VIEW_FIELD_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||
|
||||
@ -18,7 +19,7 @@ import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.work
|
||||
labelSingular: 'View Field',
|
||||
labelPlural: 'View Fields',
|
||||
description: '(System) View Fields',
|
||||
icon: 'IconTag',
|
||||
icon: STANDARD_OBJECT_ICONS.viewField,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -11,6 +11,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { VIEW_FILTER_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||
|
||||
@ -20,7 +21,7 @@ import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.work
|
||||
labelSingular: 'View Filter',
|
||||
labelPlural: 'View Filters',
|
||||
description: '(System) View Filters',
|
||||
icon: 'IconFilterBolt',
|
||||
icon: STANDARD_OBJECT_ICONS.viewFilter,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -1,16 +1,17 @@
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
|
||||
import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator';
|
||||
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { VIEW_GROUP_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
|
||||
@WorkspaceEntity({
|
||||
standardId: STANDARD_OBJECT_IDS.viewGroup,
|
||||
@ -18,7 +19,7 @@ import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-
|
||||
labelSingular: 'View Group',
|
||||
labelPlural: 'View Groups',
|
||||
description: '(System) View Groups',
|
||||
icon: 'IconTag',
|
||||
icon: STANDARD_OBJECT_ICONS.viewGroup,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -11,6 +11,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { VIEW_SORT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity';
|
||||
|
||||
@ -20,7 +21,7 @@ import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.work
|
||||
labelSingular: 'View Sort',
|
||||
labelPlural: 'View Sorts',
|
||||
description: '(System) View Sorts',
|
||||
icon: 'IconArrowsSort',
|
||||
icon: STANDARD_OBJECT_ICONS.viewSort,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -13,13 +13,14 @@ import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { VIEW_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
|
||||
import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity';
|
||||
import { ViewFilterGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-filter-group.workspace-entity';
|
||||
import { ViewFilterWorkspaceEntity } from 'src/modules/view/standard-objects/view-filter.workspace-entity';
|
||||
import { ViewSortWorkspaceEntity } from 'src/modules/view/standard-objects/view-sort.workspace-entity';
|
||||
import { ViewGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-group.workspace-entity';
|
||||
import { ViewSortWorkspaceEntity } from 'src/modules/view/standard-objects/view-sort.workspace-entity';
|
||||
|
||||
@WorkspaceEntity({
|
||||
standardId: STANDARD_OBJECT_IDS.view,
|
||||
@ -27,7 +28,7 @@ import { ViewGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view
|
||||
labelSingular: 'View',
|
||||
labelPlural: 'Views',
|
||||
description: '(System) Views',
|
||||
icon: 'IconLayoutCollage',
|
||||
icon: STANDARD_OBJECT_ICONS.view,
|
||||
labelIdentifierStandardId: VIEW_STANDARD_FIELD_IDS.name,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
|
@ -2,12 +2,13 @@ import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/fi
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { WorkspaceEntity } from 'src/engine/twenty-orm/decorators/workspace-entity.decorator';
|
||||
import { WorkspaceField } from 'src/engine/twenty-orm/decorators/workspace-field.decorator';
|
||||
import { WorkspaceIsDeprecated } from 'src/engine/twenty-orm/decorators/workspace-is-deprecated.decorator';
|
||||
import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator';
|
||||
import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-is-nullable.decorator';
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WEBHOOK_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { WorkspaceIsDeprecated } from 'src/engine/twenty-orm/decorators/workspace-is-deprecated.decorator';
|
||||
|
||||
@WorkspaceEntity({
|
||||
standardId: STANDARD_OBJECT_IDS.webhook,
|
||||
@ -15,7 +16,7 @@ import { WorkspaceIsDeprecated } from 'src/engine/twenty-orm/decorators/workspac
|
||||
labelSingular: 'Webhook',
|
||||
labelPlural: 'Webhooks',
|
||||
description: 'A webhook',
|
||||
icon: 'IconRobot',
|
||||
icon: STANDARD_OBJECT_ICONS.webhook,
|
||||
labelIdentifierStandardId: WEBHOOK_STANDARD_FIELD_IDS.targetUrl,
|
||||
})
|
||||
@WorkspaceIsNotAuditLogged()
|
||||
|
@ -12,6 +12,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { WORKFLOW_EVENT_LISTENER_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
|
||||
|
||||
@ -21,6 +22,7 @@ import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-ob
|
||||
labelSingular: 'WorkflowEventListener',
|
||||
labelPlural: 'WorkflowEventListeners',
|
||||
description: 'A workflow event listener',
|
||||
icon: STANDARD_OBJECT_ICONS.workflowEventListener,
|
||||
labelIdentifierStandardId:
|
||||
WORKFLOW_EVENT_LISTENER_STANDARD_FIELD_IDS.eventName,
|
||||
})
|
||||
|
@ -19,6 +19,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { WORKFLOW_RUN_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
|
||||
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
|
||||
@ -54,7 +55,7 @@ export type WorkflowRunOutput = {
|
||||
labelPlural: 'Workflow Runs',
|
||||
description: 'A workflow run',
|
||||
labelIdentifierStandardId: WORKFLOW_RUN_STANDARD_FIELD_IDS.name,
|
||||
icon: 'IconSettingsAutomation',
|
||||
icon: STANDARD_OBJECT_ICONS.workflowRun,
|
||||
})
|
||||
@WorkspaceGate({
|
||||
featureFlag: FeatureFlagKey.IsWorkflowEnabled,
|
||||
|
@ -15,6 +15,7 @@ import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is
|
||||
import { WorkspaceJoinColumn } from 'src/engine/twenty-orm/decorators/workspace-join-column.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { WORKFLOW_VERSION_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
|
||||
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
|
||||
@ -63,7 +64,7 @@ const WorkflowVersionStatusOptions = [
|
||||
labelSingular: 'Workflow Version',
|
||||
labelPlural: 'Workflow Versions',
|
||||
description: 'A workflow version',
|
||||
icon: 'IconSettingsAutomation',
|
||||
icon: STANDARD_OBJECT_ICONS.workflowVersion,
|
||||
labelIdentifierStandardId: WORKFLOW_VERSION_STANDARD_FIELD_IDS.name,
|
||||
})
|
||||
@WorkspaceGate({
|
||||
|
@ -14,6 +14,7 @@ import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { WORKFLOW_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
|
||||
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
|
||||
@ -54,7 +55,7 @@ const WorkflowStatusOptions = [
|
||||
labelSingular: 'Workflow',
|
||||
labelPlural: 'Workflows',
|
||||
description: 'A workflow',
|
||||
icon: 'IconSettingsAutomation',
|
||||
icon: STANDARD_OBJECT_ICONS.workflow,
|
||||
shortcut: 'W',
|
||||
labelIdentifierStandardId: WORKFLOW_STANDARD_FIELD_IDS.name,
|
||||
})
|
||||
|
@ -0,0 +1,77 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
|
||||
import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event';
|
||||
import { ObjectRecordDestroyEvent } from 'src/engine/core-modules/event-emitter/types/object-record-destroy.event';
|
||||
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record';
|
||||
|
||||
export const generateFakeObjectRecordEvent = <Entity>(
|
||||
objectMetadataEntity: ObjectMetadataEntity,
|
||||
action: 'created' | 'updated' | 'deleted' | 'destroyed',
|
||||
):
|
||||
| ObjectRecordCreateEvent<Entity>
|
||||
| ObjectRecordUpdateEvent<Entity>
|
||||
| ObjectRecordDeleteEvent<Entity>
|
||||
| ObjectRecordDestroyEvent<Entity> => {
|
||||
const recordId = v4();
|
||||
const userId = v4();
|
||||
const workspaceMemberId = v4();
|
||||
|
||||
const after = generateFakeObjectRecord<Entity>(objectMetadataEntity);
|
||||
|
||||
if (action === 'created') {
|
||||
return {
|
||||
recordId,
|
||||
userId,
|
||||
workspaceMemberId,
|
||||
objectMetadata: objectMetadataEntity,
|
||||
properties: {
|
||||
after,
|
||||
},
|
||||
} satisfies ObjectRecordCreateEvent<Entity>;
|
||||
}
|
||||
|
||||
const before = generateFakeObjectRecord<Entity>(objectMetadataEntity);
|
||||
|
||||
if (action === 'updated') {
|
||||
return {
|
||||
recordId,
|
||||
userId,
|
||||
workspaceMemberId,
|
||||
objectMetadata: objectMetadataEntity,
|
||||
properties: {
|
||||
before,
|
||||
after,
|
||||
diff: after,
|
||||
},
|
||||
} satisfies ObjectRecordUpdateEvent<Entity>;
|
||||
}
|
||||
|
||||
if (action === 'deleted') {
|
||||
return {
|
||||
recordId,
|
||||
userId,
|
||||
workspaceMemberId,
|
||||
objectMetadata: objectMetadataEntity,
|
||||
properties: {
|
||||
before,
|
||||
},
|
||||
} satisfies ObjectRecordDeleteEvent<Entity>;
|
||||
}
|
||||
|
||||
if (action === 'destroyed') {
|
||||
return {
|
||||
recordId,
|
||||
userId,
|
||||
workspaceMemberId,
|
||||
objectMetadata: objectMetadataEntity,
|
||||
properties: {
|
||||
before,
|
||||
},
|
||||
} satisfies ObjectRecordDestroyEvent<Entity>;
|
||||
}
|
||||
|
||||
throw new Error(`Unknown action '${action}'`);
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { generateFakeValue } from 'src/engine/utils/generate-fake-value';
|
||||
|
||||
export const generateFakeObjectRecord = <Entity>(
|
||||
objectMetadataEntity: ObjectMetadataEntity,
|
||||
): Entity =>
|
||||
objectMetadataEntity.fields.reduce((acc, field) => {
|
||||
acc[field.name] = generateFakeValue(field.type);
|
||||
|
||||
return acc;
|
||||
}, {} as Entity);
|
@ -1,24 +1,26 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { join } from 'path';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { generateFakeObjectRecordEvent } from 'src/engine/core-modules/event-emitter/utils/generate-fake-object-record-event';
|
||||
import { INDEX_FILE_NAME } from 'src/engine/core-modules/serverless/drivers/constants/index-file-name';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||
import { CodeIntrospectionService } from 'src/modules/code-introspection/code-introspection.service';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import {
|
||||
WorkflowTrigger,
|
||||
WorkflowTriggerType,
|
||||
} from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
|
||||
import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record';
|
||||
import {
|
||||
WorkflowActionType,
|
||||
WorkflowStep,
|
||||
} from 'src/modules/workflow/workflow-executor/types/workflow-action.type';
|
||||
import { WorkflowSendEmailStepOutputSchema } from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
|
||||
import {
|
||||
WorkflowTrigger,
|
||||
WorkflowTriggerType,
|
||||
} from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
import { generateFakeObjectRecordEvent } from 'src/engine/core-modules/event-emitter/utils/generate-fake-object-record-event';
|
||||
import { INDEX_FILE_NAME } from 'src/engine/core-modules/serverless/drivers/constants/index-file-name';
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowBuilderService {
|
||||
@ -35,77 +37,155 @@ export class WorkflowBuilderService {
|
||||
}: {
|
||||
step: WorkflowTrigger | WorkflowStep;
|
||||
workspaceId: string;
|
||||
}) {
|
||||
}): Promise<object> {
|
||||
const stepType = step.type;
|
||||
|
||||
switch (stepType) {
|
||||
case WorkflowTriggerType.DATABASE_EVENT: {
|
||||
const [nameSingular, action] = step.settings.eventName.split('.');
|
||||
return await this.computeDatabaseEventTriggerOutputSchema({
|
||||
eventName: step.settings.eventName,
|
||||
workspaceId,
|
||||
objectMetadataRepository: this.objectMetadataRepository,
|
||||
});
|
||||
}
|
||||
case WorkflowTriggerType.MANUAL: {
|
||||
const { objectType } = step.settings;
|
||||
|
||||
if (!['created', 'updated', 'deleted', 'destroyed'].includes(action)) {
|
||||
if (!objectType) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const objectMetadata =
|
||||
await this.objectMetadataRepository.findOneOrFail({
|
||||
where: {
|
||||
nameSingular,
|
||||
workspaceId,
|
||||
},
|
||||
relations: ['fields'],
|
||||
});
|
||||
|
||||
if (!isDefined(objectMetadata)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return generateFakeObjectRecordEvent(
|
||||
objectMetadata,
|
||||
action as 'created' | 'updated' | 'deleted' | 'destroyed',
|
||||
);
|
||||
return await this.computeManualTriggerOutputSchema({
|
||||
objectType,
|
||||
workspaceId,
|
||||
objectMetadataRepository: this.objectMetadataRepository,
|
||||
});
|
||||
}
|
||||
case WorkflowActionType.SEND_EMAIL: {
|
||||
return { success: true };
|
||||
return this.computeSendEmailActionOutputSchema();
|
||||
}
|
||||
case WorkflowActionType.CODE: {
|
||||
const { serverlessFunctionId, serverlessFunctionVersion } =
|
||||
step.settings.input;
|
||||
|
||||
if (serverlessFunctionId === '') {
|
||||
return {};
|
||||
}
|
||||
|
||||
const sourceCode = (
|
||||
await this.serverlessFunctionService.getServerlessFunctionSourceCode(
|
||||
workspaceId,
|
||||
serverlessFunctionId,
|
||||
serverlessFunctionVersion,
|
||||
)
|
||||
)?.[join('src', INDEX_FILE_NAME)];
|
||||
|
||||
if (!isDefined(sourceCode)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const fakeFunctionInput =
|
||||
this.codeIntrospectionService.generateInputData(sourceCode);
|
||||
|
||||
// handle the case when event parameter is destructured:
|
||||
// (event: {param1: string; param2: number}) VS ({param1, param2}: {param1: string; param2: number})
|
||||
const formattedInput = Object.values(fakeFunctionInput)[0];
|
||||
|
||||
const resultFromFakeInput =
|
||||
await this.serverlessFunctionService.executeOneServerlessFunction(
|
||||
serverlessFunctionId,
|
||||
workspaceId,
|
||||
formattedInput,
|
||||
serverlessFunctionVersion,
|
||||
);
|
||||
|
||||
return resultFromFakeInput.data ?? {};
|
||||
return await this.computeCodeActionOutputSchema({
|
||||
serverlessFunctionId,
|
||||
serverlessFunctionVersion,
|
||||
workspaceId,
|
||||
serverlessFunctionService: this.serverlessFunctionService,
|
||||
codeIntrospectionService: this.codeIntrospectionService,
|
||||
});
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unknown type ${stepType}`);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
private async computeDatabaseEventTriggerOutputSchema({
|
||||
eventName,
|
||||
workspaceId,
|
||||
objectMetadataRepository,
|
||||
}: {
|
||||
eventName: string;
|
||||
workspaceId: string;
|
||||
objectMetadataRepository: Repository<ObjectMetadataEntity>;
|
||||
}) {
|
||||
const [nameSingular, action] = eventName.split('.');
|
||||
|
||||
if (!['created', 'updated', 'deleted', 'destroyed'].includes(action)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const objectMetadata = await objectMetadataRepository.findOneOrFail({
|
||||
where: {
|
||||
nameSingular,
|
||||
workspaceId,
|
||||
},
|
||||
relations: ['fields'],
|
||||
});
|
||||
|
||||
if (!isDefined(objectMetadata)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return generateFakeObjectRecordEvent(
|
||||
objectMetadata,
|
||||
action as 'created' | 'updated' | 'deleted' | 'destroyed',
|
||||
);
|
||||
}
|
||||
|
||||
private async computeManualTriggerOutputSchema<Entity>({
|
||||
objectType,
|
||||
workspaceId,
|
||||
objectMetadataRepository,
|
||||
}: {
|
||||
objectType: string;
|
||||
workspaceId: string;
|
||||
objectMetadataRepository: Repository<ObjectMetadataEntity>;
|
||||
}) {
|
||||
const objectMetadata = await objectMetadataRepository.findOneOrFail({
|
||||
where: {
|
||||
nameSingular: objectType,
|
||||
workspaceId,
|
||||
},
|
||||
relations: ['fields'],
|
||||
});
|
||||
|
||||
if (!isDefined(objectMetadata)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return generateFakeObjectRecord<Entity>(objectMetadata);
|
||||
}
|
||||
|
||||
private computeSendEmailActionOutputSchema(): WorkflowSendEmailStepOutputSchema {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
private async computeCodeActionOutputSchema({
|
||||
serverlessFunctionId,
|
||||
serverlessFunctionVersion,
|
||||
workspaceId,
|
||||
serverlessFunctionService,
|
||||
codeIntrospectionService,
|
||||
}: {
|
||||
serverlessFunctionId: string;
|
||||
serverlessFunctionVersion: string;
|
||||
workspaceId: string;
|
||||
serverlessFunctionService: ServerlessFunctionService;
|
||||
codeIntrospectionService: CodeIntrospectionService;
|
||||
}) {
|
||||
if (serverlessFunctionId === '') {
|
||||
return {};
|
||||
}
|
||||
|
||||
const sourceCode = (
|
||||
await serverlessFunctionService.getServerlessFunctionSourceCode(
|
||||
workspaceId,
|
||||
serverlessFunctionId,
|
||||
serverlessFunctionVersion,
|
||||
)
|
||||
)?.[join('src', INDEX_FILE_NAME)];
|
||||
|
||||
if (!isDefined(sourceCode)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const fakeFunctionInput =
|
||||
codeIntrospectionService.generateInputData(sourceCode);
|
||||
|
||||
// handle the case when event parameter is destructured:
|
||||
// (event: {param1: string; param2: number}) VS ({param1, param2}: {param1: string; param2: number})
|
||||
const formattedInput = Object.values(fakeFunctionInput)[0];
|
||||
|
||||
const resultFromFakeInput =
|
||||
await serverlessFunctionService.executeOneServerlessFunction(
|
||||
serverlessFunctionId,
|
||||
workspaceId,
|
||||
formattedInput,
|
||||
serverlessFunctionVersion,
|
||||
);
|
||||
|
||||
return resultFromFakeInput.data ?? {};
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,10 @@ export type WorkflowSendEmailStepInput = {
|
||||
body?: string;
|
||||
};
|
||||
|
||||
export type WorkflowSendEmailStepOutputSchema = {
|
||||
success: boolean;
|
||||
};
|
||||
|
||||
export type WorkflowSendEmailStepSettings = BaseWorkflowStepSettings & {
|
||||
input: WorkflowSendEmailStepInput;
|
||||
};
|
||||
|
@ -21,6 +21,7 @@ import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace-
|
||||
import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
|
||||
import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-relation.decorator';
|
||||
import { WORKSPACE_MEMBER_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
import {
|
||||
FieldTypeAndNameMetadata,
|
||||
@ -78,7 +79,7 @@ export const SEARCH_FIELDS_FOR_WORKSPACE_MEMBER: FieldTypeAndNameMetadata[] = [
|
||||
labelSingular: 'Workspace Member',
|
||||
labelPlural: 'Workspace Members',
|
||||
description: 'A workspace member',
|
||||
icon: 'IconUserCircle',
|
||||
icon: STANDARD_OBJECT_ICONS.workspaceMember,
|
||||
labelIdentifierStandardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.name,
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
|
@ -1,406 +0,0 @@
|
||||
import { TIM_ACCOUNT_ID } from 'test/integration/graphql/integration.constants';
|
||||
import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util';
|
||||
import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util';
|
||||
import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util';
|
||||
import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util';
|
||||
import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util';
|
||||
import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util';
|
||||
import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util';
|
||||
import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util';
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util';
|
||||
import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util';
|
||||
|
||||
const BLOCKLIST_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987';
|
||||
const BLOCKLIST_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988';
|
||||
const BLOCKLIST_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989';
|
||||
const BLOCKLIST_HANDLE_1 = 'email@email.com';
|
||||
const BLOCKLIST_HANDLE_2 = '@domain.com';
|
||||
const BLOCKLIST_HANDLE_3 = '@domain.org';
|
||||
const UPDATED_BLOCKLIST_HANDLE_1 = 'updated@email.com';
|
||||
const UPDATED_BLOCKLIST_HANDLE_2 = '@updated-domain.com';
|
||||
|
||||
const BLOCKLIST_GQL_FIELDS = `
|
||||
id
|
||||
handle
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
workspaceMemberId
|
||||
`;
|
||||
|
||||
describe('blocklists resolvers (integration)', () => {
|
||||
it('1. should create and return blocklists', async () => {
|
||||
const graphqlOperation = createManyOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
objectMetadataPluralName: 'blocklists',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
data: [
|
||||
{
|
||||
id: BLOCKLIST_1_ID,
|
||||
handle: BLOCKLIST_HANDLE_1,
|
||||
workspaceMemberId: TIM_ACCOUNT_ID,
|
||||
},
|
||||
{
|
||||
id: BLOCKLIST_2_ID,
|
||||
handle: BLOCKLIST_HANDLE_2,
|
||||
workspaceMemberId: TIM_ACCOUNT_ID,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.createBlocklists).toHaveLength(2);
|
||||
|
||||
response.body.data.createBlocklists.forEach((blocklist) => {
|
||||
expect(blocklist).toHaveProperty('handle');
|
||||
expect([BLOCKLIST_HANDLE_1, BLOCKLIST_HANDLE_2]).toContain(
|
||||
blocklist.handle,
|
||||
);
|
||||
expect(blocklist).toHaveProperty('id');
|
||||
expect(blocklist).toHaveProperty('createdAt');
|
||||
expect(blocklist).toHaveProperty('updatedAt');
|
||||
expect(blocklist).toHaveProperty('deletedAt');
|
||||
expect(blocklist).toHaveProperty('workspaceMemberId');
|
||||
});
|
||||
});
|
||||
|
||||
it('1b. should create and return one blocklist', async () => {
|
||||
const graphqlOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
data: {
|
||||
id: BLOCKLIST_3_ID,
|
||||
handle: BLOCKLIST_HANDLE_3,
|
||||
workspaceMemberId: TIM_ACCOUNT_ID,
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const createdBlocklist = response.body.data.createBlocklist;
|
||||
|
||||
expect(createdBlocklist).toHaveProperty('handle');
|
||||
expect(createdBlocklist.handle).toEqual(BLOCKLIST_HANDLE_3);
|
||||
expect(createdBlocklist).toHaveProperty('id');
|
||||
expect(createdBlocklist).toHaveProperty('createdAt');
|
||||
expect(createdBlocklist).toHaveProperty('updatedAt');
|
||||
expect(createdBlocklist).toHaveProperty('deletedAt');
|
||||
expect(createdBlocklist).toHaveProperty('workspaceMemberId');
|
||||
});
|
||||
|
||||
it('2. should find many blocklists', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
objectMetadataPluralName: 'blocklists',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const data = response.body.data.blocklists;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
if (data.edges.length > 0) {
|
||||
const blocklists = data.edges[0].node;
|
||||
|
||||
expect(blocklists).toHaveProperty('handle');
|
||||
expect(blocklists).toHaveProperty('id');
|
||||
expect(blocklists).toHaveProperty('createdAt');
|
||||
expect(blocklists).toHaveProperty('updatedAt');
|
||||
expect(blocklists).toHaveProperty('deletedAt');
|
||||
expect(blocklists).toHaveProperty('workspaceMemberId');
|
||||
}
|
||||
});
|
||||
|
||||
it('2b. should find one blocklist', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: BLOCKLIST_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const blocklist = response.body.data.blocklist;
|
||||
|
||||
expect(blocklist).toHaveProperty('handle');
|
||||
expect(blocklist).toHaveProperty('id');
|
||||
expect(blocklist).toHaveProperty('createdAt');
|
||||
expect(blocklist).toHaveProperty('updatedAt');
|
||||
expect(blocklist).toHaveProperty('deletedAt');
|
||||
expect(blocklist).toHaveProperty('workspaceMemberId');
|
||||
});
|
||||
|
||||
it('3. should not update many blocklists', async () => {
|
||||
const graphqlOperation = updateManyOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
objectMetadataPluralName: 'blocklists',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
data: {
|
||||
handle: UPDATED_BLOCKLIST_HANDLE_1,
|
||||
workspaceMemberId: TIM_ACCOUNT_ID,
|
||||
},
|
||||
filter: {
|
||||
id: {
|
||||
in: [BLOCKLIST_1_ID, BLOCKLIST_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.updateBlocklists).toBeNull();
|
||||
expect(response.body.errors).toStrictEqual([
|
||||
{
|
||||
extensions: { code: 'INTERNAL_SERVER_ERROR' },
|
||||
message: 'Method not allowed.',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('3b. should update one blocklist', async () => {
|
||||
const graphqlOperation = updateOneOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
data: {
|
||||
handle: UPDATED_BLOCKLIST_HANDLE_2,
|
||||
workspaceMemberId: TIM_ACCOUNT_ID,
|
||||
},
|
||||
recordId: BLOCKLIST_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedBlocklist = response.body.data.updateBlocklist;
|
||||
|
||||
expect(updatedBlocklist.handle).toEqual(UPDATED_BLOCKLIST_HANDLE_2);
|
||||
});
|
||||
|
||||
it('4. should not find many blocklists with updated name', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
objectMetadataPluralName: 'blocklists',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
filter: {
|
||||
handle: {
|
||||
eq: UPDATED_BLOCKLIST_HANDLE_1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.blocklists.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('4b. should find one blocklist with updated name', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
filter: {
|
||||
handle: {
|
||||
eq: UPDATED_BLOCKLIST_HANDLE_2,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.blocklist.handle).toEqual(
|
||||
UPDATED_BLOCKLIST_HANDLE_2,
|
||||
);
|
||||
});
|
||||
|
||||
it('5. should delete many blocklists', async () => {
|
||||
const graphqlOperation = deleteManyOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
objectMetadataPluralName: 'blocklists',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [BLOCKLIST_1_ID, BLOCKLIST_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const deletedBlocklists = response.body.data.deleteBlocklists;
|
||||
|
||||
expect(deletedBlocklists).toHaveLength(2);
|
||||
|
||||
deletedBlocklists.forEach((blocklist) => {
|
||||
expect(blocklist.deletedAt).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('5b. should delete one blocklist', async () => {
|
||||
const graphqlOperation = deleteOneOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
recordId: BLOCKLIST_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.deleteBlocklist.deletedAt).toBeTruthy();
|
||||
});
|
||||
|
||||
it('6. should not find many blocklists anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
objectMetadataPluralName: 'blocklists',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [BLOCKLIST_1_ID, BLOCKLIST_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const findBlocklistsResponse =
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(findBlocklistsResponse.body.data.blocklists.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('6b. should not find one blocklist anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: BLOCKLIST_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.blocklist).toBeNull();
|
||||
});
|
||||
|
||||
it('7. should find many deleted blocklists with deletedAt filter', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
objectMetadataPluralName: 'blocklists',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [BLOCKLIST_1_ID, BLOCKLIST_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.blocklists.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('7b. should find one deleted blocklist with deletedAt filter', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: BLOCKLIST_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.blocklist.id).toEqual(BLOCKLIST_3_ID);
|
||||
});
|
||||
|
||||
it('8. should destroy many blocklists', async () => {
|
||||
const graphqlOperation = destroyManyOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
objectMetadataPluralName: 'blocklists',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [BLOCKLIST_1_ID, BLOCKLIST_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.destroyBlocklists).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('8b. should destroy one blocklist', async () => {
|
||||
const graphqlOperation = destroyOneOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
recordId: BLOCKLIST_3_ID,
|
||||
});
|
||||
|
||||
const destroyBlocklistResponse =
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(destroyBlocklistResponse.body.data.destroyBlocklist).toBeTruthy();
|
||||
});
|
||||
|
||||
it('9. should not find many blocklists anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
objectMetadataPluralName: 'blocklists',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [BLOCKLIST_1_ID, BLOCKLIST_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.blocklists.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('9b. should not find one blocklist anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'blocklist',
|
||||
gqlFields: BLOCKLIST_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: BLOCKLIST_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.blocklist).toBeNull();
|
||||
});
|
||||
});
|
@ -1,482 +0,0 @@
|
||||
import { TIM_ACCOUNT_ID } from 'test/integration/graphql/integration.constants';
|
||||
import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util';
|
||||
import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util';
|
||||
import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util';
|
||||
import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util';
|
||||
import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util';
|
||||
import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util';
|
||||
import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util';
|
||||
import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util';
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util';
|
||||
import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util';
|
||||
import { generateRecordName } from 'test/integration/utils/generate-record-name';
|
||||
|
||||
const CALENDAR_CHANNEL_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987';
|
||||
const CALENDAR_CHANNEL_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988';
|
||||
const CALENDAR_CHANNEL_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989';
|
||||
const CONNECTED_ACCOUNT_ID = '777a8457-eb2d-40ac-a707-441b615b6989';
|
||||
|
||||
const CALENDAR_CHANNEL_GQL_FIELDS = `
|
||||
id
|
||||
handle
|
||||
syncStatus
|
||||
syncStage
|
||||
visibility
|
||||
isContactAutoCreationEnabled
|
||||
contactAutoCreationPolicy
|
||||
isSyncEnabled
|
||||
syncCursor
|
||||
syncStageStartedAt
|
||||
throttleFailureCount
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
connectedAccountId
|
||||
`;
|
||||
|
||||
describe('calendarChannels resolvers (integration)', () => {
|
||||
beforeAll(async () => {
|
||||
const connectedAccountHandle = generateRecordName(CONNECTED_ACCOUNT_ID);
|
||||
const createConnectedAccountgraphqlOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'connectedAccount',
|
||||
gqlFields: `id`,
|
||||
data: {
|
||||
id: CONNECTED_ACCOUNT_ID,
|
||||
accountOwnerId: TIM_ACCOUNT_ID,
|
||||
handle: connectedAccountHandle,
|
||||
},
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(createConnectedAccountgraphqlOperation);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
const destroyConnectedAccountGraphqlOperation = destroyOneOperationFactory({
|
||||
objectMetadataSingularName: 'connectedAccount',
|
||||
gqlFields: `id`,
|
||||
recordId: CONNECTED_ACCOUNT_ID,
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(destroyConnectedAccountGraphqlOperation);
|
||||
});
|
||||
|
||||
it('1. should create and return calendarChannels', async () => {
|
||||
const calendarChannelHandle1 = generateRecordName(CALENDAR_CHANNEL_1_ID);
|
||||
const calendarChannelHandle2 = generateRecordName(CALENDAR_CHANNEL_2_ID);
|
||||
|
||||
const graphqlOperation = createManyOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
objectMetadataPluralName: 'calendarChannels',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
data: [
|
||||
{
|
||||
id: CALENDAR_CHANNEL_1_ID,
|
||||
handle: calendarChannelHandle1,
|
||||
connectedAccountId: CONNECTED_ACCOUNT_ID,
|
||||
},
|
||||
{
|
||||
id: CALENDAR_CHANNEL_2_ID,
|
||||
handle: calendarChannelHandle2,
|
||||
connectedAccountId: CONNECTED_ACCOUNT_ID,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.createCalendarChannels).toHaveLength(2);
|
||||
|
||||
response.body.data.createCalendarChannels.forEach((calendarChannel) => {
|
||||
expect(calendarChannel).toHaveProperty('handle');
|
||||
expect([calendarChannelHandle1, calendarChannelHandle2]).toContain(
|
||||
calendarChannel.handle,
|
||||
);
|
||||
expect(calendarChannel).toHaveProperty('id');
|
||||
expect(calendarChannel).toHaveProperty('syncStatus');
|
||||
expect(calendarChannel).toHaveProperty('syncStage');
|
||||
expect(calendarChannel).toHaveProperty('visibility');
|
||||
expect(calendarChannel).toHaveProperty('isContactAutoCreationEnabled');
|
||||
expect(calendarChannel).toHaveProperty('contactAutoCreationPolicy');
|
||||
expect(calendarChannel).toHaveProperty('isSyncEnabled');
|
||||
expect(calendarChannel).toHaveProperty('syncCursor');
|
||||
expect(calendarChannel).toHaveProperty('syncStageStartedAt');
|
||||
expect(calendarChannel).toHaveProperty('throttleFailureCount');
|
||||
expect(calendarChannel).toHaveProperty('createdAt');
|
||||
expect(calendarChannel).toHaveProperty('updatedAt');
|
||||
expect(calendarChannel).toHaveProperty('deletedAt');
|
||||
expect(calendarChannel).toHaveProperty('connectedAccountId');
|
||||
});
|
||||
});
|
||||
|
||||
it('1b. should create and return one calendarChannel', async () => {
|
||||
const calendarChannelHandle = generateRecordName(CALENDAR_CHANNEL_3_ID);
|
||||
|
||||
const graphqlOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
data: {
|
||||
id: CALENDAR_CHANNEL_3_ID,
|
||||
handle: calendarChannelHandle,
|
||||
connectedAccountId: CONNECTED_ACCOUNT_ID,
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const createdCalendarChannel = response.body.data.createCalendarChannel;
|
||||
|
||||
expect(createdCalendarChannel).toHaveProperty('handle');
|
||||
expect(createdCalendarChannel.handle).toEqual(calendarChannelHandle);
|
||||
expect(createdCalendarChannel).toHaveProperty('id');
|
||||
expect(createdCalendarChannel).toHaveProperty('syncStatus');
|
||||
expect(createdCalendarChannel).toHaveProperty('syncStage');
|
||||
expect(createdCalendarChannel).toHaveProperty('visibility');
|
||||
expect(createdCalendarChannel).toHaveProperty(
|
||||
'isContactAutoCreationEnabled',
|
||||
);
|
||||
expect(createdCalendarChannel).toHaveProperty('contactAutoCreationPolicy');
|
||||
expect(createdCalendarChannel).toHaveProperty('isSyncEnabled');
|
||||
expect(createdCalendarChannel).toHaveProperty('syncCursor');
|
||||
expect(createdCalendarChannel).toHaveProperty('syncStageStartedAt');
|
||||
expect(createdCalendarChannel).toHaveProperty('throttleFailureCount');
|
||||
expect(createdCalendarChannel).toHaveProperty('createdAt');
|
||||
expect(createdCalendarChannel).toHaveProperty('updatedAt');
|
||||
expect(createdCalendarChannel).toHaveProperty('deletedAt');
|
||||
expect(createdCalendarChannel).toHaveProperty('connectedAccountId');
|
||||
});
|
||||
|
||||
it('2. should find many calendarChannels', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
objectMetadataPluralName: 'calendarChannels',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const data = response.body.data.calendarChannels;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
if (data.edges.length > 0) {
|
||||
const calendarChannels = data.edges[0].node;
|
||||
|
||||
expect(calendarChannels).toHaveProperty('handle');
|
||||
expect(calendarChannels).toHaveProperty('syncStatus');
|
||||
expect(calendarChannels).toHaveProperty('syncStage');
|
||||
expect(calendarChannels).toHaveProperty('visibility');
|
||||
expect(calendarChannels).toHaveProperty('isContactAutoCreationEnabled');
|
||||
expect(calendarChannels).toHaveProperty('contactAutoCreationPolicy');
|
||||
expect(calendarChannels).toHaveProperty('isSyncEnabled');
|
||||
expect(calendarChannels).toHaveProperty('syncCursor');
|
||||
expect(calendarChannels).toHaveProperty('syncStageStartedAt');
|
||||
expect(calendarChannels).toHaveProperty('throttleFailureCount');
|
||||
expect(calendarChannels).toHaveProperty('id');
|
||||
expect(calendarChannels).toHaveProperty('createdAt');
|
||||
expect(calendarChannels).toHaveProperty('updatedAt');
|
||||
expect(calendarChannels).toHaveProperty('deletedAt');
|
||||
expect(calendarChannels).toHaveProperty('connectedAccountId');
|
||||
}
|
||||
});
|
||||
|
||||
it('2b. should find one calendarChannel', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: CALENDAR_CHANNEL_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const calendarChannel = response.body.data.calendarChannel;
|
||||
|
||||
expect(calendarChannel).toHaveProperty('handle');
|
||||
expect(calendarChannel).toHaveProperty('syncStatus');
|
||||
expect(calendarChannel).toHaveProperty('syncStage');
|
||||
expect(calendarChannel).toHaveProperty('visibility');
|
||||
expect(calendarChannel).toHaveProperty('isContactAutoCreationEnabled');
|
||||
expect(calendarChannel).toHaveProperty('contactAutoCreationPolicy');
|
||||
expect(calendarChannel).toHaveProperty('isSyncEnabled');
|
||||
expect(calendarChannel).toHaveProperty('syncCursor');
|
||||
expect(calendarChannel).toHaveProperty('syncStageStartedAt');
|
||||
expect(calendarChannel).toHaveProperty('throttleFailureCount');
|
||||
expect(calendarChannel).toHaveProperty('id');
|
||||
expect(calendarChannel).toHaveProperty('createdAt');
|
||||
expect(calendarChannel).toHaveProperty('updatedAt');
|
||||
expect(calendarChannel).toHaveProperty('deletedAt');
|
||||
expect(calendarChannel).toHaveProperty('connectedAccountId');
|
||||
});
|
||||
|
||||
it('3. should update many calendarChannels', async () => {
|
||||
const graphqlOperation = updateManyOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
objectMetadataPluralName: 'calendarChannels',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
data: {
|
||||
handle: 'Updated Handle',
|
||||
},
|
||||
filter: {
|
||||
id: {
|
||||
in: [CALENDAR_CHANNEL_1_ID, CALENDAR_CHANNEL_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedCalendarChannels = response.body.data.updateCalendarChannels;
|
||||
|
||||
expect(updatedCalendarChannels).toHaveLength(2);
|
||||
|
||||
updatedCalendarChannels.forEach((calendarChannel) => {
|
||||
expect(calendarChannel.handle).toEqual('Updated Handle');
|
||||
});
|
||||
});
|
||||
|
||||
it('3b. should update one calendarChannel', async () => {
|
||||
const graphqlOperation = updateOneOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
data: {
|
||||
handle: 'New Handle',
|
||||
},
|
||||
recordId: CALENDAR_CHANNEL_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedCalendarChannel = response.body.data.updateCalendarChannel;
|
||||
|
||||
expect(updatedCalendarChannel.handle).toEqual('New Handle');
|
||||
});
|
||||
|
||||
it('4. should find many calendarChannels with updated handle', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
objectMetadataPluralName: 'calendarChannels',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
handle: {
|
||||
eq: 'Updated Handle',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.calendarChannels.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('4b. should find one calendarChannel with updated handle', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
handle: {
|
||||
eq: 'New Handle',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.calendarChannel.handle).toEqual('New Handle');
|
||||
});
|
||||
|
||||
it('5. should delete many calendarChannels', async () => {
|
||||
const graphqlOperation = deleteManyOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
objectMetadataPluralName: 'calendarChannels',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [CALENDAR_CHANNEL_1_ID, CALENDAR_CHANNEL_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const deletedCalendarChannels = response.body.data.deleteCalendarChannels;
|
||||
|
||||
expect(deletedCalendarChannels).toHaveLength(2);
|
||||
|
||||
deletedCalendarChannels.forEach((calendarChannel) => {
|
||||
expect(calendarChannel.deletedAt).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('5b. should delete one calendarChannel', async () => {
|
||||
const graphqlOperation = deleteOneOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
recordId: CALENDAR_CHANNEL_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.deleteCalendarChannel.deletedAt).toBeTruthy();
|
||||
});
|
||||
|
||||
it('6. should not find many calendarChannels anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
objectMetadataPluralName: 'calendarChannels',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [CALENDAR_CHANNEL_1_ID, CALENDAR_CHANNEL_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const findCalendarChannelsResponse =
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(
|
||||
findCalendarChannelsResponse.body.data.calendarChannels.edges,
|
||||
).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('6b. should not find one calendarChannel anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: CALENDAR_CHANNEL_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.calendarChannel).toBeNull();
|
||||
});
|
||||
|
||||
it('7. should find many deleted calendarChannels with deletedAt filter', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
objectMetadataPluralName: 'calendarChannels',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [CALENDAR_CHANNEL_1_ID, CALENDAR_CHANNEL_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.calendarChannels.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('7b. should find one deleted calendarChannel with deletedAt filter', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: CALENDAR_CHANNEL_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.calendarChannel.id).toEqual(
|
||||
CALENDAR_CHANNEL_3_ID,
|
||||
);
|
||||
});
|
||||
|
||||
it('8. should destroy many calendarChannels', async () => {
|
||||
const graphqlOperation = destroyManyOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
objectMetadataPluralName: 'calendarChannels',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [CALENDAR_CHANNEL_1_ID, CALENDAR_CHANNEL_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.destroyCalendarChannels).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('8b. should destroy one calendarChannel', async () => {
|
||||
const graphqlOperation = destroyOneOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
recordId: CALENDAR_CHANNEL_3_ID,
|
||||
});
|
||||
|
||||
const destroyCalendarChannelResponse =
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(
|
||||
destroyCalendarChannelResponse.body.data.destroyCalendarChannel,
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('9. should not find many calendarChannels anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
objectMetadataPluralName: 'calendarChannels',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [CALENDAR_CHANNEL_1_ID, CALENDAR_CHANNEL_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.calendarChannels.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('9b. should not find one calendarChannel anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'calendarChannel',
|
||||
gqlFields: CALENDAR_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: CALENDAR_CHANNEL_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.calendarChannel).toBeNull();
|
||||
});
|
||||
});
|
@ -1,455 +0,0 @@
|
||||
import { TIM_ACCOUNT_ID } from 'test/integration/graphql/integration.constants';
|
||||
import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util';
|
||||
import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util';
|
||||
import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util';
|
||||
import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util';
|
||||
import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util';
|
||||
import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util';
|
||||
import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util';
|
||||
import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util';
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util';
|
||||
import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util';
|
||||
import { generateRecordName } from 'test/integration/utils/generate-record-name';
|
||||
|
||||
const MESSAGE_CHANNEL_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987';
|
||||
const MESSAGE_CHANNEL_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988';
|
||||
const MESSAGE_CHANNEL_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989';
|
||||
const CONNECTED_ACCOUNT_ID = '777a8457-eb2d-40ac-a707-441b615b6989';
|
||||
|
||||
const MESSAGE_CHANNEL_GQL_FIELDS = `
|
||||
id
|
||||
handle
|
||||
deletedAt
|
||||
createdAt
|
||||
contactAutoCreationPolicy
|
||||
isContactAutoCreationEnabled
|
||||
isSyncEnabled
|
||||
syncCursor
|
||||
type
|
||||
`;
|
||||
|
||||
describe('messageChannels resolvers (integration)', () => {
|
||||
beforeAll(async () => {
|
||||
const connectedAccountHandle = generateRecordName(CONNECTED_ACCOUNT_ID);
|
||||
const graphqlOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'connectedAccount',
|
||||
gqlFields: `id`,
|
||||
data: {
|
||||
id: CONNECTED_ACCOUNT_ID,
|
||||
accountOwnerId: TIM_ACCOUNT_ID,
|
||||
handle: connectedAccountHandle,
|
||||
},
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
const graphqlOperation = destroyOneOperationFactory({
|
||||
objectMetadataSingularName: 'connectedAccount',
|
||||
gqlFields: `id`,
|
||||
recordId: CONNECTED_ACCOUNT_ID,
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
});
|
||||
|
||||
it('1. should create and return messageChannels', async () => {
|
||||
const messageChannelHandle1 = generateRecordName(MESSAGE_CHANNEL_1_ID);
|
||||
const messageChannelHandle2 = generateRecordName(MESSAGE_CHANNEL_2_ID);
|
||||
|
||||
const graphqlOperation = createManyOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
objectMetadataPluralName: 'messageChannels',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
data: [
|
||||
{
|
||||
id: MESSAGE_CHANNEL_1_ID,
|
||||
handle: messageChannelHandle1,
|
||||
connectedAccountId: CONNECTED_ACCOUNT_ID,
|
||||
},
|
||||
{
|
||||
id: MESSAGE_CHANNEL_2_ID,
|
||||
handle: messageChannelHandle2,
|
||||
connectedAccountId: CONNECTED_ACCOUNT_ID,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.createMessageChannels).toHaveLength(2);
|
||||
|
||||
response.body.data.createMessageChannels.forEach((messageChannel) => {
|
||||
expect(messageChannel).toHaveProperty('handle');
|
||||
expect([messageChannelHandle1, messageChannelHandle2]).toContain(
|
||||
messageChannel.handle,
|
||||
);
|
||||
|
||||
expect(messageChannel).toHaveProperty('id');
|
||||
expect(messageChannel).toHaveProperty('deletedAt');
|
||||
expect(messageChannel).toHaveProperty('createdAt');
|
||||
expect(messageChannel).toHaveProperty('contactAutoCreationPolicy');
|
||||
expect(messageChannel).toHaveProperty('isContactAutoCreationEnabled');
|
||||
expect(messageChannel).toHaveProperty('isSyncEnabled');
|
||||
expect(messageChannel).toHaveProperty('syncCursor');
|
||||
expect(messageChannel).toHaveProperty('type');
|
||||
});
|
||||
});
|
||||
|
||||
it('1b. should create and return one messageChannel', async () => {
|
||||
const messageChannelHandle = generateRecordName(MESSAGE_CHANNEL_3_ID);
|
||||
|
||||
const graphqlOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
data: {
|
||||
id: MESSAGE_CHANNEL_3_ID,
|
||||
handle: messageChannelHandle,
|
||||
connectedAccountId: CONNECTED_ACCOUNT_ID,
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const createdMessageChannel = response.body.data.createMessageChannel;
|
||||
|
||||
expect(createdMessageChannel).toHaveProperty('handle');
|
||||
expect(createdMessageChannel.handle).toEqual(messageChannelHandle);
|
||||
|
||||
expect(createdMessageChannel).toHaveProperty('id');
|
||||
expect(createdMessageChannel).toHaveProperty('deletedAt');
|
||||
expect(createdMessageChannel).toHaveProperty('createdAt');
|
||||
expect(createdMessageChannel).toHaveProperty('contactAutoCreationPolicy');
|
||||
expect(createdMessageChannel).toHaveProperty(
|
||||
'isContactAutoCreationEnabled',
|
||||
);
|
||||
expect(createdMessageChannel).toHaveProperty('isSyncEnabled');
|
||||
expect(createdMessageChannel).toHaveProperty('syncCursor');
|
||||
expect(createdMessageChannel).toHaveProperty('type');
|
||||
});
|
||||
|
||||
it('2. should find many messageChannels', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
objectMetadataPluralName: 'messageChannels',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const data = response.body.data.messageChannels;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const messageChannel = edges[0].node;
|
||||
|
||||
expect(messageChannel).toHaveProperty('handle');
|
||||
expect(messageChannel).toHaveProperty('id');
|
||||
expect(messageChannel).toHaveProperty('deletedAt');
|
||||
expect(messageChannel).toHaveProperty('createdAt');
|
||||
expect(messageChannel).toHaveProperty('contactAutoCreationPolicy');
|
||||
expect(messageChannel).toHaveProperty('isContactAutoCreationEnabled');
|
||||
expect(messageChannel).toHaveProperty('isSyncEnabled');
|
||||
expect(messageChannel).toHaveProperty('syncCursor');
|
||||
expect(messageChannel).toHaveProperty('type');
|
||||
}
|
||||
});
|
||||
|
||||
it('2b. should find one messageChannel', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: MESSAGE_CHANNEL_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const messageChannel = response.body.data.messageChannel;
|
||||
|
||||
expect(messageChannel).toHaveProperty('handle');
|
||||
|
||||
expect(messageChannel).toHaveProperty('id');
|
||||
expect(messageChannel).toHaveProperty('deletedAt');
|
||||
expect(messageChannel).toHaveProperty('createdAt');
|
||||
expect(messageChannel).toHaveProperty('contactAutoCreationPolicy');
|
||||
expect(messageChannel).toHaveProperty('isContactAutoCreationEnabled');
|
||||
expect(messageChannel).toHaveProperty('isSyncEnabled');
|
||||
expect(messageChannel).toHaveProperty('syncCursor');
|
||||
expect(messageChannel).toHaveProperty('type');
|
||||
});
|
||||
|
||||
it('3. should update many messageChannels', async () => {
|
||||
const graphqlOperation = updateManyOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
objectMetadataPluralName: 'messageChannels',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
data: {
|
||||
handle: 'New Handle',
|
||||
},
|
||||
filter: {
|
||||
id: {
|
||||
in: [MESSAGE_CHANNEL_1_ID, MESSAGE_CHANNEL_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedMessageChannels = response.body.data.updateMessageChannels;
|
||||
|
||||
expect(updatedMessageChannels).toHaveLength(2);
|
||||
|
||||
updatedMessageChannels.forEach((messageChannel) => {
|
||||
expect(messageChannel.handle).toEqual('New Handle');
|
||||
});
|
||||
});
|
||||
|
||||
it('3b. should update one messageChannel', async () => {
|
||||
const graphqlOperation = updateOneOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
data: {
|
||||
handle: 'Updated Handle',
|
||||
},
|
||||
recordId: MESSAGE_CHANNEL_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedMessageChannel = response.body.data.updateMessageChannel;
|
||||
|
||||
expect(updatedMessageChannel.handle).toEqual('Updated Handle');
|
||||
});
|
||||
|
||||
it('4. should find many messageChannels with updated handle', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
objectMetadataPluralName: 'messageChannels',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
handle: {
|
||||
eq: 'New Handle',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.messageChannels.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('4b. should find one messageChannel with updated handle', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
handle: {
|
||||
eq: 'Updated Handle',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.messageChannel.handle).toEqual('Updated Handle');
|
||||
});
|
||||
|
||||
it('5. should delete many messageChannels', async () => {
|
||||
const graphqlOperation = deleteManyOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
objectMetadataPluralName: 'messageChannels',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [MESSAGE_CHANNEL_1_ID, MESSAGE_CHANNEL_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const deleteMessageChannels = response.body.data.deleteMessageChannels;
|
||||
|
||||
expect(deleteMessageChannels).toHaveLength(2);
|
||||
|
||||
deleteMessageChannels.forEach((messageChannel) => {
|
||||
expect(messageChannel.deletedAt).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('5b. should delete one messageChannel', async () => {
|
||||
const graphqlOperation = deleteOneOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
recordId: MESSAGE_CHANNEL_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.deleteMessageChannel.deletedAt).toBeTruthy();
|
||||
});
|
||||
|
||||
it('6. should not find many messageChannels anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
objectMetadataPluralName: 'messageChannels',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [MESSAGE_CHANNEL_1_ID, MESSAGE_CHANNEL_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const findMessageChannelsResponse =
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(
|
||||
findMessageChannelsResponse.body.data.messageChannels.edges,
|
||||
).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('6b. should not find one messageChannel anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: MESSAGE_CHANNEL_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.messageChannel).toBeNull();
|
||||
});
|
||||
|
||||
it('7. should find many deleted messageChannels with deletedAt filter', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
objectMetadataPluralName: 'messageChannels',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [MESSAGE_CHANNEL_1_ID, MESSAGE_CHANNEL_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.messageChannels.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('7b. should find one deleted messageChannel with deletedAt filter', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: MESSAGE_CHANNEL_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.messageChannel.id).toEqual(MESSAGE_CHANNEL_3_ID);
|
||||
});
|
||||
|
||||
it('8. should destroy many messageChannels', async () => {
|
||||
const graphqlOperation = destroyManyOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
objectMetadataPluralName: 'messageChannels',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [MESSAGE_CHANNEL_1_ID, MESSAGE_CHANNEL_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.destroyMessageChannels).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('8b. should destroy one messageChannel', async () => {
|
||||
const graphqlOperation = destroyOneOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
recordId: MESSAGE_CHANNEL_3_ID,
|
||||
});
|
||||
|
||||
const destroyMessageChannelResponse =
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(
|
||||
destroyMessageChannelResponse.body.data.destroyMessageChannel,
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('9. should not find many messageChannels anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
objectMetadataPluralName: 'messageChannels',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [MESSAGE_CHANNEL_1_ID, MESSAGE_CHANNEL_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.messageChannels.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('9b. should not find one messageChannel anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'messageChannel',
|
||||
gqlFields: MESSAGE_CHANNEL_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: MESSAGE_CHANNEL_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.messageChannel).toBeNull();
|
||||
});
|
||||
});
|
@ -0,0 +1,416 @@
|
||||
import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util';
|
||||
import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util';
|
||||
import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util';
|
||||
import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util';
|
||||
import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util';
|
||||
import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util';
|
||||
import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util';
|
||||
import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util';
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util';
|
||||
import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util';
|
||||
import { generateRecordName } from 'test/integration/utils/generate-record-name';
|
||||
|
||||
const PERSON_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987';
|
||||
const PERSON_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988';
|
||||
const PERSON_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989';
|
||||
|
||||
const PERSON_GQL_FIELDS = `
|
||||
id
|
||||
city
|
||||
jobTitle
|
||||
avatarUrl
|
||||
intro
|
||||
searchVector
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
createdAt
|
||||
deletedAt
|
||||
`;
|
||||
|
||||
describe('people resolvers (integration)', () => {
|
||||
it('1. should create and return people', async () => {
|
||||
const personCity1 = generateRecordName(PERSON_1_ID);
|
||||
const personCity2 = generateRecordName(PERSON_2_ID);
|
||||
const graphqlOperation = createManyOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
objectMetadataPluralName: 'people',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
data: [
|
||||
{
|
||||
id: PERSON_1_ID,
|
||||
city: personCity1,
|
||||
},
|
||||
{
|
||||
id: PERSON_2_ID,
|
||||
city: personCity2,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.createPeople).toHaveLength(2);
|
||||
|
||||
response.body.data.createPeople.forEach((person) => {
|
||||
expect(person).toHaveProperty('city');
|
||||
expect([personCity1, personCity2]).toContain(person.city);
|
||||
|
||||
expect(person).toHaveProperty('id');
|
||||
expect(person).toHaveProperty('jobTitle');
|
||||
expect(person).toHaveProperty('avatarUrl');
|
||||
expect(person).toHaveProperty('intro');
|
||||
expect(person).toHaveProperty('searchVector');
|
||||
expect(person).toHaveProperty('name');
|
||||
expect(person).toHaveProperty('createdAt');
|
||||
expect(person).toHaveProperty('deletedAt');
|
||||
});
|
||||
});
|
||||
|
||||
it('1b. should create and return one person', async () => {
|
||||
const personCity3 = generateRecordName(PERSON_3_ID);
|
||||
|
||||
const graphqlOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
data: {
|
||||
id: PERSON_3_ID,
|
||||
city: personCity3,
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const createdPerson = response.body.data.createPerson;
|
||||
|
||||
expect(createdPerson).toHaveProperty('city');
|
||||
expect(createdPerson.city).toEqual(personCity3);
|
||||
|
||||
expect(createdPerson).toHaveProperty('id');
|
||||
expect(createdPerson).toHaveProperty('jobTitle');
|
||||
expect(createdPerson).toHaveProperty('avatarUrl');
|
||||
expect(createdPerson).toHaveProperty('intro');
|
||||
expect(createdPerson).toHaveProperty('searchVector');
|
||||
expect(createdPerson).toHaveProperty('name');
|
||||
expect(createdPerson).toHaveProperty('createdAt');
|
||||
expect(createdPerson).toHaveProperty('deletedAt');
|
||||
});
|
||||
|
||||
it('2. should find many people', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
objectMetadataPluralName: 'people',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const data = response.body.data.people;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const person = edges[0].node;
|
||||
|
||||
expect(person).toHaveProperty('id');
|
||||
expect(person).toHaveProperty('jobTitle');
|
||||
expect(person).toHaveProperty('avatarUrl');
|
||||
expect(person).toHaveProperty('intro');
|
||||
expect(person).toHaveProperty('searchVector');
|
||||
expect(person).toHaveProperty('name');
|
||||
expect(person).toHaveProperty('createdAt');
|
||||
expect(person).toHaveProperty('deletedAt');
|
||||
}
|
||||
});
|
||||
|
||||
it('2b. should find one person', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: PERSON_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const person = response.body.data.person;
|
||||
|
||||
expect(person).toHaveProperty('city');
|
||||
|
||||
expect(person).toHaveProperty('id');
|
||||
expect(person).toHaveProperty('jobTitle');
|
||||
expect(person).toHaveProperty('avatarUrl');
|
||||
expect(person).toHaveProperty('intro');
|
||||
expect(person).toHaveProperty('searchVector');
|
||||
expect(person).toHaveProperty('name');
|
||||
expect(person).toHaveProperty('createdAt');
|
||||
expect(person).toHaveProperty('deletedAt');
|
||||
});
|
||||
|
||||
it('3. should update many people', async () => {
|
||||
const graphqlOperation = updateManyOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
objectMetadataPluralName: 'people',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
data: {
|
||||
city: 'Updated City',
|
||||
},
|
||||
filter: {
|
||||
id: {
|
||||
in: [PERSON_1_ID, PERSON_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedPeople = response.body.data.updatePeople;
|
||||
|
||||
expect(updatedPeople).toHaveLength(2);
|
||||
|
||||
updatedPeople.forEach((person) => {
|
||||
expect(person.city).toEqual('Updated City');
|
||||
});
|
||||
});
|
||||
|
||||
it('3b. should update one person', async () => {
|
||||
const graphqlOperation = updateOneOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
data: {
|
||||
city: 'New City',
|
||||
},
|
||||
recordId: PERSON_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedPerson = response.body.data.updatePerson;
|
||||
|
||||
expect(updatedPerson.city).toEqual('New City');
|
||||
});
|
||||
|
||||
it('4. should find many people with updated city', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
objectMetadataPluralName: 'people',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
filter: {
|
||||
city: {
|
||||
eq: 'Updated City',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.people.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('4b. should find one person with updated city', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
filter: {
|
||||
city: {
|
||||
eq: 'New City',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.person.city).toEqual('New City');
|
||||
});
|
||||
|
||||
it('5. should delete many people', async () => {
|
||||
const graphqlOperation = deleteManyOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
objectMetadataPluralName: 'people',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [PERSON_1_ID, PERSON_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const deletePeople = response.body.data.deletePeople;
|
||||
|
||||
expect(deletePeople).toHaveLength(2);
|
||||
|
||||
deletePeople.forEach((person) => {
|
||||
expect(person.deletedAt).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('5b. should delete one person', async () => {
|
||||
const graphqlOperation = deleteOneOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
recordId: PERSON_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.deletePerson.deletedAt).toBeTruthy();
|
||||
});
|
||||
|
||||
it('6. should not find many people anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
objectMetadataPluralName: 'people',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [PERSON_1_ID, PERSON_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const findPeopleResponse = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(findPeopleResponse.body.data.people.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('6b. should not find one person anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: PERSON_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.person).toBeNull();
|
||||
});
|
||||
|
||||
it('7. should find many deleted people with deletedAt filter', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
objectMetadataPluralName: 'people',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [PERSON_1_ID, PERSON_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.people.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('7b. should find one deleted person with deletedAt filter', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: PERSON_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.person.id).toEqual(PERSON_3_ID);
|
||||
});
|
||||
|
||||
it('8. should destroy many people', async () => {
|
||||
const graphqlOperation = destroyManyOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
objectMetadataPluralName: 'people',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [PERSON_1_ID, PERSON_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.destroyPeople).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('8b. should destroy one person', async () => {
|
||||
const graphqlOperation = destroyOneOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
recordId: PERSON_3_ID,
|
||||
});
|
||||
|
||||
const destroyPeopleResponse = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(destroyPeopleResponse.body.data.destroyPerson).toBeTruthy();
|
||||
});
|
||||
|
||||
it('9. should not find many people anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
objectMetadataPluralName: 'people',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [PERSON_1_ID, PERSON_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.people.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('9b. should not find one person anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: PERSON_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.person).toBeNull();
|
||||
});
|
||||
});
|
@ -0,0 +1,444 @@
|
||||
import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util';
|
||||
import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util';
|
||||
import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util';
|
||||
import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util';
|
||||
import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util';
|
||||
import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util';
|
||||
import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util';
|
||||
import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util';
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util';
|
||||
import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util';
|
||||
import { generateRecordName } from 'test/integration/utils/generate-record-name';
|
||||
|
||||
const TASK_TARGET_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987';
|
||||
const TASK_TARGET_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988';
|
||||
const TASK_TARGET_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989';
|
||||
const PERSON_1_ID = '777a8457-eb2d-40ac-a707-441b615b6989';
|
||||
const PERSON_2_ID = '777a8457-eb2d-40ac-a707-331b615b6989';
|
||||
const TASK_TARGET_GQL_FIELDS = `
|
||||
id
|
||||
createdAt
|
||||
deletedAt
|
||||
rocketId
|
||||
personId
|
||||
companyId
|
||||
opportunityId
|
||||
person{
|
||||
id
|
||||
}
|
||||
`;
|
||||
|
||||
describe('taskTargets resolvers (integration)', () => {
|
||||
beforeAll(async () => {
|
||||
const personName1 = generateRecordName(PERSON_1_ID);
|
||||
const personName2 = generateRecordName(PERSON_2_ID);
|
||||
const graphqlOperation = createManyOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
objectMetadataPluralName: 'people',
|
||||
gqlFields: `id`,
|
||||
data: [
|
||||
{
|
||||
id: PERSON_1_ID,
|
||||
name: {
|
||||
firstName: personName1,
|
||||
lastName: personName1,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: PERSON_2_ID,
|
||||
name: {
|
||||
firstName: personName2,
|
||||
lastName: personName2,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
const graphqlOperation = destroyManyOperationFactory({
|
||||
objectMetadataSingularName: 'person',
|
||||
objectMetadataPluralName: 'people',
|
||||
gqlFields: `id`,
|
||||
filter: {
|
||||
id: {
|
||||
in: [PERSON_1_ID, PERSON_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
});
|
||||
it('1. should create and return taskTargets', async () => {
|
||||
const graphqlOperation = createManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
data: [
|
||||
{
|
||||
id: TASK_TARGET_1_ID,
|
||||
},
|
||||
{
|
||||
id: TASK_TARGET_2_ID,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.createTaskTargets).toHaveLength(2);
|
||||
|
||||
response.body.data.createTaskTargets.forEach((taskTarget) => {
|
||||
expect(taskTarget).toHaveProperty('id');
|
||||
expect(taskTarget).toHaveProperty('createdAt');
|
||||
expect(taskTarget).toHaveProperty('deletedAt');
|
||||
expect(taskTarget).toHaveProperty('rocketId');
|
||||
expect(taskTarget).toHaveProperty('personId');
|
||||
expect(taskTarget).toHaveProperty('companyId');
|
||||
expect(taskTarget).toHaveProperty('opportunityId');
|
||||
expect(taskTarget).toHaveProperty('person');
|
||||
});
|
||||
});
|
||||
|
||||
it('1b. should create and return one taskTarget', async () => {
|
||||
const graphqlOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
data: {
|
||||
id: TASK_TARGET_3_ID,
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const createdTaskTarget = response.body.data.createTaskTarget;
|
||||
|
||||
expect(createdTaskTarget).toHaveProperty('id');
|
||||
expect(createdTaskTarget).toHaveProperty('createdAt');
|
||||
expect(createdTaskTarget).toHaveProperty('deletedAt');
|
||||
expect(createdTaskTarget).toHaveProperty('rocketId');
|
||||
expect(createdTaskTarget).toHaveProperty('personId');
|
||||
expect(createdTaskTarget).toHaveProperty('companyId');
|
||||
expect(createdTaskTarget).toHaveProperty('opportunityId');
|
||||
expect(createdTaskTarget).toHaveProperty('person');
|
||||
});
|
||||
|
||||
it('2. should find many taskTargets', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const data = response.body.data.taskTargets;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const taskTarget = edges[0].node;
|
||||
|
||||
expect(taskTarget).toHaveProperty('id');
|
||||
expect(taskTarget).toHaveProperty('createdAt');
|
||||
expect(taskTarget).toHaveProperty('deletedAt');
|
||||
expect(taskTarget).toHaveProperty('rocketId');
|
||||
expect(taskTarget).toHaveProperty('personId');
|
||||
expect(taskTarget).toHaveProperty('companyId');
|
||||
expect(taskTarget).toHaveProperty('opportunityId');
|
||||
expect(taskTarget).toHaveProperty('person');
|
||||
}
|
||||
});
|
||||
|
||||
it('2b. should find one taskTarget', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: TASK_TARGET_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const taskTarget = response.body.data.taskTarget;
|
||||
|
||||
expect(taskTarget).toHaveProperty('id');
|
||||
expect(taskTarget).toHaveProperty('createdAt');
|
||||
expect(taskTarget).toHaveProperty('deletedAt');
|
||||
expect(taskTarget).toHaveProperty('rocketId');
|
||||
expect(taskTarget).toHaveProperty('personId');
|
||||
expect(taskTarget).toHaveProperty('companyId');
|
||||
expect(taskTarget).toHaveProperty('opportunityId');
|
||||
expect(taskTarget).toHaveProperty('person');
|
||||
});
|
||||
|
||||
it('3. should update many taskTargets', async () => {
|
||||
const graphqlOperation = updateManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
data: {
|
||||
personId: PERSON_1_ID,
|
||||
},
|
||||
filter: {
|
||||
id: {
|
||||
in: [TASK_TARGET_1_ID, TASK_TARGET_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedTaskTargets = response.body.data.updateTaskTargets;
|
||||
|
||||
expect(updatedTaskTargets).toHaveLength(2);
|
||||
|
||||
updatedTaskTargets.forEach((taskTarget) => {
|
||||
expect(taskTarget.person.id).toEqual(PERSON_1_ID);
|
||||
});
|
||||
});
|
||||
|
||||
it('3b. should update one taskTarget', async () => {
|
||||
const graphqlOperation = updateOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
data: {
|
||||
personId: PERSON_2_ID,
|
||||
},
|
||||
recordId: TASK_TARGET_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedTaskTarget = response.body.data.updateTaskTarget;
|
||||
|
||||
expect(updatedTaskTarget.person.id).toEqual(PERSON_2_ID);
|
||||
});
|
||||
|
||||
it('4. should find many taskTargets with updated personId', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
personId: {
|
||||
eq: PERSON_1_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.taskTargets.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('4b. should find one taskTarget with updated personId', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
personId: {
|
||||
eq: PERSON_2_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.taskTarget.person.id).toEqual(PERSON_2_ID);
|
||||
});
|
||||
|
||||
it('5. should delete many taskTargets', async () => {
|
||||
const graphqlOperation = deleteManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TASK_TARGET_1_ID, TASK_TARGET_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const deleteTaskTargets = response.body.data.deleteTaskTargets;
|
||||
|
||||
expect(deleteTaskTargets).toHaveLength(2);
|
||||
|
||||
deleteTaskTargets.forEach((taskTarget) => {
|
||||
expect(taskTarget.deletedAt).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('5b. should delete one taskTarget', async () => {
|
||||
const graphqlOperation = deleteOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
recordId: TASK_TARGET_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.deleteTaskTarget.deletedAt).toBeTruthy();
|
||||
});
|
||||
|
||||
it('6. should not find many taskTargets anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TASK_TARGET_1_ID, TASK_TARGET_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const findTaskTargetsResponse =
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(findTaskTargetsResponse.body.data.taskTargets.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('6b. should not find one taskTarget anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: TASK_TARGET_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.taskTarget).toBeNull();
|
||||
});
|
||||
|
||||
it('7. should find many deleted taskTargets with deletedAt filter', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TASK_TARGET_1_ID, TASK_TARGET_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.taskTargets.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('7b. should find one deleted taskTarget with deletedAt filter', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: TASK_TARGET_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.taskTarget.id).toEqual(TASK_TARGET_3_ID);
|
||||
});
|
||||
|
||||
it('8. should destroy many taskTargets', async () => {
|
||||
const graphqlOperation = destroyManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TASK_TARGET_1_ID, TASK_TARGET_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.destroyTaskTargets).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('8b. should destroy one taskTarget', async () => {
|
||||
const graphqlOperation = destroyOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
recordId: TASK_TARGET_3_ID,
|
||||
});
|
||||
|
||||
const destroyTaskTargetsResponse =
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(destroyTaskTargetsResponse.body.data.destroyTaskTarget).toBeTruthy();
|
||||
});
|
||||
|
||||
it('9. should not find many taskTargets anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
objectMetadataPluralName: 'taskTargets',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TASK_TARGET_1_ID, TASK_TARGET_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.taskTargets.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('9b. should not find one taskTarget anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'taskTarget',
|
||||
gqlFields: TASK_TARGET_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: TASK_TARGET_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.taskTarget).toBeNull();
|
||||
});
|
||||
});
|
@ -0,0 +1,403 @@
|
||||
import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util';
|
||||
import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util';
|
||||
import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util';
|
||||
import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util';
|
||||
import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util';
|
||||
import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util';
|
||||
import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util';
|
||||
import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util';
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util';
|
||||
import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util';
|
||||
import { generateRecordName } from 'test/integration/utils/generate-record-name';
|
||||
|
||||
const TASK_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987';
|
||||
const TASK_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988';
|
||||
const TASK_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989';
|
||||
|
||||
const TASK_GQL_FIELDS = `
|
||||
id
|
||||
title
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
body
|
||||
position
|
||||
`;
|
||||
|
||||
describe('tasks resolvers (integration)', () => {
|
||||
it('1. should create and return tasks', async () => {
|
||||
const taskTitle1 = generateRecordName(TASK_1_ID);
|
||||
const taskTitle2 = generateRecordName(TASK_2_ID);
|
||||
const graphqlOperation = createManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
data: [
|
||||
{
|
||||
id: TASK_1_ID,
|
||||
title: taskTitle1,
|
||||
},
|
||||
{
|
||||
id: TASK_2_ID,
|
||||
title: taskTitle2,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.createTasks).toHaveLength(2);
|
||||
|
||||
response.body.data.createTasks.forEach((task) => {
|
||||
expect(task).toHaveProperty('title');
|
||||
expect([taskTitle1, taskTitle2]).toContain(task.title);
|
||||
|
||||
expect(task).toHaveProperty('id');
|
||||
expect(task).toHaveProperty('createdAt');
|
||||
expect(task).toHaveProperty('updatedAt');
|
||||
expect(task).toHaveProperty('deletedAt');
|
||||
expect(task).toHaveProperty('body');
|
||||
expect(task).toHaveProperty('position');
|
||||
});
|
||||
});
|
||||
|
||||
it('1b. should create and return one task', async () => {
|
||||
const taskTitle3 = generateRecordName(TASK_3_ID);
|
||||
|
||||
const graphqlOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
data: {
|
||||
id: TASK_3_ID,
|
||||
title: taskTitle3,
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const createdTask = response.body.data.createTask;
|
||||
|
||||
expect(createdTask).toHaveProperty('title');
|
||||
expect(createdTask.title).toEqual(taskTitle3);
|
||||
|
||||
expect(createdTask).toHaveProperty('id');
|
||||
expect(createdTask).toHaveProperty('createdAt');
|
||||
expect(createdTask).toHaveProperty('updatedAt');
|
||||
expect(createdTask).toHaveProperty('deletedAt');
|
||||
expect(createdTask).toHaveProperty('body');
|
||||
expect(createdTask).toHaveProperty('position');
|
||||
});
|
||||
|
||||
it('2. should find many tasks', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const data = response.body.data.tasks;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const task = edges[0].node;
|
||||
|
||||
expect(task).toHaveProperty('id');
|
||||
expect(task).toHaveProperty('createdAt');
|
||||
expect(task).toHaveProperty('updatedAt');
|
||||
expect(task).toHaveProperty('deletedAt');
|
||||
expect(task).toHaveProperty('body');
|
||||
expect(task).toHaveProperty('position');
|
||||
}
|
||||
});
|
||||
|
||||
it('2b. should find one task', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: TASK_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const task = response.body.data.task;
|
||||
|
||||
expect(task).toHaveProperty('title');
|
||||
|
||||
expect(task).toHaveProperty('id');
|
||||
expect(task).toHaveProperty('createdAt');
|
||||
expect(task).toHaveProperty('updatedAt');
|
||||
expect(task).toHaveProperty('deletedAt');
|
||||
expect(task).toHaveProperty('body');
|
||||
expect(task).toHaveProperty('position');
|
||||
});
|
||||
|
||||
it('3. should update many tasks', async () => {
|
||||
const graphqlOperation = updateManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
data: {
|
||||
title: 'Updated Title',
|
||||
},
|
||||
filter: {
|
||||
id: {
|
||||
in: [TASK_1_ID, TASK_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedTasks = response.body.data.updateTasks;
|
||||
|
||||
expect(updatedTasks).toHaveLength(2);
|
||||
|
||||
updatedTasks.forEach((task) => {
|
||||
expect(task.title).toEqual('Updated Title');
|
||||
});
|
||||
});
|
||||
|
||||
it('3b. should update one task', async () => {
|
||||
const graphqlOperation = updateOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
data: {
|
||||
title: 'New Title',
|
||||
},
|
||||
recordId: TASK_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedTask = response.body.data.updateTask;
|
||||
|
||||
expect(updatedTask.title).toEqual('New Title');
|
||||
});
|
||||
|
||||
it('4. should find many tasks with updated title', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: {
|
||||
title: {
|
||||
eq: 'Updated Title',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.tasks.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('4b. should find one task with updated title', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: {
|
||||
title: {
|
||||
eq: 'New Title',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.task.title).toEqual('New Title');
|
||||
});
|
||||
|
||||
it('5. should delete many tasks', async () => {
|
||||
const graphqlOperation = deleteManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TASK_1_ID, TASK_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const deleteTasks = response.body.data.deleteTasks;
|
||||
|
||||
expect(deleteTasks).toHaveLength(2);
|
||||
|
||||
deleteTasks.forEach((task) => {
|
||||
expect(task.deletedAt).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('5b. should delete one task', async () => {
|
||||
const graphqlOperation = deleteOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
recordId: TASK_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.deleteTask.deletedAt).toBeTruthy();
|
||||
});
|
||||
|
||||
it('6. should not find many tasks anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TASK_1_ID, TASK_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const findTasksResponse = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(findTasksResponse.body.data.tasks.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('6b. should not find one task anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: TASK_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.task).toBeNull();
|
||||
});
|
||||
|
||||
it('7. should find many deleted tasks with deletedAt filter', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TASK_1_ID, TASK_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.tasks.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('7b. should find one deleted task with deletedAt filter', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: TASK_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.task.id).toEqual(TASK_3_ID);
|
||||
});
|
||||
|
||||
it('8. should destroy many tasks', async () => {
|
||||
const graphqlOperation = destroyManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TASK_1_ID, TASK_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.destroyTasks).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('8b. should destroy one task', async () => {
|
||||
const graphqlOperation = destroyOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
recordId: TASK_3_ID,
|
||||
});
|
||||
|
||||
const destroyTasksResponse = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(destroyTasksResponse.body.data.destroyTask).toBeTruthy();
|
||||
});
|
||||
|
||||
it('9. should not find many tasks anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
objectMetadataPluralName: 'tasks',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TASK_1_ID, TASK_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.tasks.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('9b. should not find one task anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'task',
|
||||
gqlFields: TASK_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: TASK_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.task).toBeNull();
|
||||
});
|
||||
});
|
@ -0,0 +1,477 @@
|
||||
import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util';
|
||||
import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util';
|
||||
import { deleteManyOperationFactory } from 'test/integration/graphql/utils/delete-many-operation-factory.util';
|
||||
import { deleteOneOperationFactory } from 'test/integration/graphql/utils/delete-one-operation-factory.util';
|
||||
import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util';
|
||||
import { destroyOneOperationFactory } from 'test/integration/graphql/utils/destroy-one-operation-factory.util';
|
||||
import { findManyOperationFactory } from 'test/integration/graphql/utils/find-many-operation-factory.util';
|
||||
import { findOneOperationFactory } from 'test/integration/graphql/utils/find-one-operation-factory.util';
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util';
|
||||
import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util';
|
||||
import { generateRecordName } from 'test/integration/utils/generate-record-name';
|
||||
|
||||
const TIMELINE_ACTIVITY_1_ID = '777a8457-eb2d-40ac-a707-551b615b6987';
|
||||
const TIMELINE_ACTIVITY_2_ID = '777a8457-eb2d-40ac-a707-551b615b6988';
|
||||
const TIMELINE_ACTIVITY_3_ID = '777a8457-eb2d-40ac-a707-551b615b6989';
|
||||
|
||||
const TIMELINE_ACTIVITY_GQL_FIELDS = `
|
||||
id
|
||||
happensAt
|
||||
name
|
||||
properties
|
||||
linkedRecordCachedName
|
||||
linkedRecordId
|
||||
linkedObjectMetadataId
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
workspaceMemberId
|
||||
personId
|
||||
companyId
|
||||
opportunityId
|
||||
noteId
|
||||
taskId
|
||||
workflowId
|
||||
workflowVersionId
|
||||
workflowRunId
|
||||
rocketId
|
||||
`;
|
||||
|
||||
describe('timelineActivities resolvers (integration)', () => {
|
||||
it('1. should create and return timelineActivities', async () => {
|
||||
const timelineActivityName1 = generateRecordName(TIMELINE_ACTIVITY_1_ID);
|
||||
const timelineActivityName2 = generateRecordName(TIMELINE_ACTIVITY_2_ID);
|
||||
|
||||
const graphqlOperation = createManyOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
objectMetadataPluralName: 'timelineActivities',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
data: [
|
||||
{
|
||||
id: TIMELINE_ACTIVITY_1_ID,
|
||||
name: timelineActivityName1,
|
||||
},
|
||||
{
|
||||
id: TIMELINE_ACTIVITY_2_ID,
|
||||
name: timelineActivityName2,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.createTimelineActivities).toHaveLength(2);
|
||||
|
||||
response.body.data.createTimelineActivities.forEach((timelineActivity) => {
|
||||
expect(timelineActivity).toHaveProperty('name');
|
||||
expect([timelineActivityName1, timelineActivityName2]).toContain(
|
||||
timelineActivity.name,
|
||||
);
|
||||
expect(timelineActivity).toHaveProperty('id');
|
||||
expect(timelineActivity).toHaveProperty('happensAt');
|
||||
expect(timelineActivity).toHaveProperty('properties');
|
||||
expect(timelineActivity).toHaveProperty('linkedRecordCachedName');
|
||||
expect(timelineActivity).toHaveProperty('linkedRecordId');
|
||||
expect(timelineActivity).toHaveProperty('linkedObjectMetadataId');
|
||||
expect(timelineActivity).toHaveProperty('createdAt');
|
||||
expect(timelineActivity).toHaveProperty('updatedAt');
|
||||
expect(timelineActivity).toHaveProperty('deletedAt');
|
||||
expect(timelineActivity).toHaveProperty('workspaceMemberId');
|
||||
expect(timelineActivity).toHaveProperty('personId');
|
||||
expect(timelineActivity).toHaveProperty('companyId');
|
||||
expect(timelineActivity).toHaveProperty('opportunityId');
|
||||
expect(timelineActivity).toHaveProperty('noteId');
|
||||
expect(timelineActivity).toHaveProperty('taskId');
|
||||
expect(timelineActivity).toHaveProperty('workflowId');
|
||||
expect(timelineActivity).toHaveProperty('workflowVersionId');
|
||||
expect(timelineActivity).toHaveProperty('workflowRunId');
|
||||
expect(timelineActivity).toHaveProperty('rocketId');
|
||||
});
|
||||
});
|
||||
|
||||
it('1b. should create and return one timelineActivity', async () => {
|
||||
const timelineActivityName = generateRecordName(TIMELINE_ACTIVITY_3_ID);
|
||||
|
||||
const graphqlOperation = createOneOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
data: {
|
||||
id: TIMELINE_ACTIVITY_3_ID,
|
||||
name: timelineActivityName,
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const createdTimelineActivity = response.body.data.createTimelineActivity;
|
||||
|
||||
expect(createdTimelineActivity).toHaveProperty('name');
|
||||
expect(createdTimelineActivity.name).toEqual(timelineActivityName);
|
||||
expect(createdTimelineActivity).toHaveProperty('id');
|
||||
expect(createdTimelineActivity).toHaveProperty('happensAt');
|
||||
expect(createdTimelineActivity).toHaveProperty('properties');
|
||||
expect(createdTimelineActivity).toHaveProperty('linkedRecordCachedName');
|
||||
expect(createdTimelineActivity).toHaveProperty('linkedRecordId');
|
||||
expect(createdTimelineActivity).toHaveProperty('linkedObjectMetadataId');
|
||||
expect(createdTimelineActivity).toHaveProperty('createdAt');
|
||||
expect(createdTimelineActivity).toHaveProperty('updatedAt');
|
||||
expect(createdTimelineActivity).toHaveProperty('deletedAt');
|
||||
expect(createdTimelineActivity).toHaveProperty('workspaceMemberId');
|
||||
expect(createdTimelineActivity).toHaveProperty('personId');
|
||||
expect(createdTimelineActivity).toHaveProperty('companyId');
|
||||
expect(createdTimelineActivity).toHaveProperty('opportunityId');
|
||||
expect(createdTimelineActivity).toHaveProperty('noteId');
|
||||
expect(createdTimelineActivity).toHaveProperty('taskId');
|
||||
expect(createdTimelineActivity).toHaveProperty('workflowId');
|
||||
expect(createdTimelineActivity).toHaveProperty('workflowVersionId');
|
||||
expect(createdTimelineActivity).toHaveProperty('workflowRunId');
|
||||
expect(createdTimelineActivity).toHaveProperty('rocketId');
|
||||
});
|
||||
|
||||
it('2. should find many timelineActivities', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
objectMetadataPluralName: 'timelineActivities',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const data = response.body.data.timelineActivities;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
if (data.edges.length > 0) {
|
||||
const timelineActivities = data.edges[0].node;
|
||||
|
||||
expect(timelineActivities).toHaveProperty('happensAt');
|
||||
expect(timelineActivities).toHaveProperty('name');
|
||||
expect(timelineActivities).toHaveProperty('properties');
|
||||
expect(timelineActivities).toHaveProperty('linkedRecordCachedName');
|
||||
expect(timelineActivities).toHaveProperty('linkedRecordId');
|
||||
expect(timelineActivities).toHaveProperty('linkedObjectMetadataId');
|
||||
expect(timelineActivities).toHaveProperty('id');
|
||||
expect(timelineActivities).toHaveProperty('createdAt');
|
||||
expect(timelineActivities).toHaveProperty('updatedAt');
|
||||
expect(timelineActivities).toHaveProperty('deletedAt');
|
||||
expect(timelineActivities).toHaveProperty('workspaceMemberId');
|
||||
expect(timelineActivities).toHaveProperty('personId');
|
||||
expect(timelineActivities).toHaveProperty('companyId');
|
||||
expect(timelineActivities).toHaveProperty('opportunityId');
|
||||
expect(timelineActivities).toHaveProperty('noteId');
|
||||
expect(timelineActivities).toHaveProperty('taskId');
|
||||
expect(timelineActivities).toHaveProperty('workflowId');
|
||||
expect(timelineActivities).toHaveProperty('workflowVersionId');
|
||||
expect(timelineActivities).toHaveProperty('workflowRunId');
|
||||
expect(timelineActivities).toHaveProperty('rocketId');
|
||||
}
|
||||
});
|
||||
|
||||
it('2b. should find one timelineActivity', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: TIMELINE_ACTIVITY_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const timelineActivity = response.body.data.timelineActivity;
|
||||
|
||||
expect(timelineActivity).toHaveProperty('happensAt');
|
||||
expect(timelineActivity).toHaveProperty('name');
|
||||
expect(timelineActivity).toHaveProperty('properties');
|
||||
expect(timelineActivity).toHaveProperty('linkedRecordCachedName');
|
||||
expect(timelineActivity).toHaveProperty('linkedRecordId');
|
||||
expect(timelineActivity).toHaveProperty('linkedObjectMetadataId');
|
||||
expect(timelineActivity).toHaveProperty('id');
|
||||
expect(timelineActivity).toHaveProperty('createdAt');
|
||||
expect(timelineActivity).toHaveProperty('updatedAt');
|
||||
expect(timelineActivity).toHaveProperty('deletedAt');
|
||||
expect(timelineActivity).toHaveProperty('workspaceMemberId');
|
||||
expect(timelineActivity).toHaveProperty('personId');
|
||||
expect(timelineActivity).toHaveProperty('companyId');
|
||||
expect(timelineActivity).toHaveProperty('opportunityId');
|
||||
expect(timelineActivity).toHaveProperty('noteId');
|
||||
expect(timelineActivity).toHaveProperty('taskId');
|
||||
expect(timelineActivity).toHaveProperty('workflowId');
|
||||
expect(timelineActivity).toHaveProperty('workflowVersionId');
|
||||
expect(timelineActivity).toHaveProperty('workflowRunId');
|
||||
expect(timelineActivity).toHaveProperty('rocketId');
|
||||
});
|
||||
|
||||
it('3. should update many timelineActivities', async () => {
|
||||
const graphqlOperation = updateManyOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
objectMetadataPluralName: 'timelineActivities',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
data: {
|
||||
name: 'Updated Name',
|
||||
},
|
||||
filter: {
|
||||
id: {
|
||||
in: [TIMELINE_ACTIVITY_1_ID, TIMELINE_ACTIVITY_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedTimelineActivities =
|
||||
response.body.data.updateTimelineActivities;
|
||||
|
||||
expect(updatedTimelineActivities).toHaveLength(2);
|
||||
|
||||
updatedTimelineActivities.forEach((timelineActivity) => {
|
||||
expect(timelineActivity.name).toEqual('Updated Name');
|
||||
});
|
||||
});
|
||||
|
||||
it('3b. should update one timelineActivity', async () => {
|
||||
const graphqlOperation = updateOneOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
data: {
|
||||
name: 'New Name',
|
||||
},
|
||||
recordId: TIMELINE_ACTIVITY_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const updatedTimelineActivity = response.body.data.updateTimelineActivity;
|
||||
|
||||
expect(updatedTimelineActivity.name).toEqual('New Name');
|
||||
});
|
||||
|
||||
it('4. should find many timelineActivities with updated name', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
objectMetadataPluralName: 'timelineActivities',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
filter: {
|
||||
name: {
|
||||
eq: 'Updated Name',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.timelineActivities.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('4b. should find one timelineActivity with updated name', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
filter: {
|
||||
name: {
|
||||
eq: 'New Name',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.timelineActivity.name).toEqual('New Name');
|
||||
});
|
||||
|
||||
it('5. should delete many timelineActivities', async () => {
|
||||
const graphqlOperation = deleteManyOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
objectMetadataPluralName: 'timelineActivities',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TIMELINE_ACTIVITY_1_ID, TIMELINE_ACTIVITY_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
const deletedTimelineActivities =
|
||||
response.body.data.deleteTimelineActivities;
|
||||
|
||||
expect(deletedTimelineActivities).toHaveLength(2);
|
||||
|
||||
deletedTimelineActivities.forEach((timelineActivity) => {
|
||||
expect(timelineActivity.deletedAt).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('5b. should delete one timelineActivity', async () => {
|
||||
const graphqlOperation = deleteOneOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
recordId: TIMELINE_ACTIVITY_3_ID,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.deleteTimelineActivity.deletedAt).toBeTruthy();
|
||||
});
|
||||
|
||||
it('6. should not find many timelineActivities anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
objectMetadataPluralName: 'timelineActivities',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TIMELINE_ACTIVITY_1_ID, TIMELINE_ACTIVITY_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const findTimelineActivitiesResponse =
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(
|
||||
findTimelineActivitiesResponse.body.data.timelineActivities.edges,
|
||||
).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('6b. should not find one timelineActivity anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: TIMELINE_ACTIVITY_3_ID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.timelineActivity).toBeNull();
|
||||
});
|
||||
|
||||
it('7. should find many deleted timelineActivities with deletedAt filter', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
objectMetadataPluralName: 'timelineActivities',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TIMELINE_ACTIVITY_1_ID, TIMELINE_ACTIVITY_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.timelineActivities.edges).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('7b. should find one deleted timelineActivity with deletedAt filter', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: TIMELINE_ACTIVITY_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.timelineActivity.id).toEqual(
|
||||
TIMELINE_ACTIVITY_3_ID,
|
||||
);
|
||||
});
|
||||
|
||||
it('8. should destroy many timelineActivities', async () => {
|
||||
const graphqlOperation = destroyManyOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
objectMetadataPluralName: 'timelineActivities',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TIMELINE_ACTIVITY_1_ID, TIMELINE_ACTIVITY_2_ID],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.destroyTimelineActivities).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('8b. should destroy one timelineActivity', async () => {
|
||||
const graphqlOperation = destroyOneOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
recordId: TIMELINE_ACTIVITY_3_ID,
|
||||
});
|
||||
|
||||
const destroyTimelineActivityResponse =
|
||||
await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(
|
||||
destroyTimelineActivityResponse.body.data.destroyTimelineActivity,
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('9. should not find many timelineActivities anymore', async () => {
|
||||
const graphqlOperation = findManyOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
objectMetadataPluralName: 'timelineActivities',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
in: [TIMELINE_ACTIVITY_1_ID, TIMELINE_ACTIVITY_2_ID],
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.timelineActivities.edges).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('9b. should not find one timelineActivity anymore', async () => {
|
||||
const graphqlOperation = findOneOperationFactory({
|
||||
objectMetadataSingularName: 'timelineActivity',
|
||||
gqlFields: TIMELINE_ACTIVITY_GQL_FIELDS,
|
||||
filter: {
|
||||
id: {
|
||||
eq: TIMELINE_ACTIVITY_3_ID,
|
||||
},
|
||||
not: {
|
||||
deletedAt: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation);
|
||||
|
||||
expect(response.body.data.timelineActivity).toBeNull();
|
||||
});
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user