File size: 3,938 Bytes
d9494a5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { isDefined } from 'class-validator';
import { RELATION_NESTED_QUERY_KEYWORDS } from 'twenty-shared/constants';

import {
  type ConnectObject,
  type DisconnectObject,
} from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
import {
  type RelationConnectQueryFieldsByEntityIndex,
  type RelationDisconnectQueryFieldsByEntityIndex,
} from 'src/engine/twenty-orm/entity-manager/types/relation-nested-query-fields-by-entity-index.type';
import {
  TwentyORMException,
  TwentyORMExceptionCode,
} from 'src/engine/twenty-orm/exceptions/twenty-orm.exception';

const hasRelationConnect = (value: unknown): value is ConnectObject => {
  if (!isDefined(value) || typeof value !== 'object') {
    return false;
  }

  const obj = value as Record<string, unknown>;

  if (
    !isDefined(obj[RELATION_NESTED_QUERY_KEYWORDS.CONNECT]) ||
    typeof obj[RELATION_NESTED_QUERY_KEYWORDS.CONNECT] !== 'object'
  ) {
    return false;
  }

  const connect = obj[RELATION_NESTED_QUERY_KEYWORDS.CONNECT] as Record<
    string,
    unknown
  >;

  if (
    !isDefined(connect[RELATION_NESTED_QUERY_KEYWORDS.CONNECT_WHERE]) ||
    typeof connect[RELATION_NESTED_QUERY_KEYWORDS.CONNECT_WHERE] !== 'object'
  ) {
    return false;
  }

  const where = connect[RELATION_NESTED_QUERY_KEYWORDS.CONNECT_WHERE] as Record<
    string,
    unknown
  >;

  const whereKeys = Object.keys(where);

  if (whereKeys.length === 0) {
    return false;
  }

  return whereKeys.every((key) => {
    const whereValue = where[key];

    if (typeof whereValue === 'string') {
      return true;
    }
    if (whereValue && typeof whereValue === 'object') {
      const subObj = whereValue as Record<string, unknown>;

      return Object.values(subObj).every(
        (subValue) => typeof subValue === 'string',
      );
    }

    return false;
  });
};

const hasRelationDisconnect = (value: unknown): value is DisconnectObject => {
  if (!isDefined(value) || typeof value !== 'object') return false;

  const obj = value as Record<string, unknown>;

  if (
    !isDefined(obj[RELATION_NESTED_QUERY_KEYWORDS.DISCONNECT]) ||
    typeof obj[RELATION_NESTED_QUERY_KEYWORDS.DISCONNECT] !== 'boolean'
  )
    return false;

  return true;
};

export const extractNestedRelationFieldsByEntityIndex = (
  entities: Record<string, unknown>[],
): {
  relationConnectQueryFieldsByEntityIndex: RelationConnectQueryFieldsByEntityIndex;
  relationDisconnectQueryFieldsByEntityIndex: RelationDisconnectQueryFieldsByEntityIndex;
} => {
  const relationConnectQueryFieldsByEntityIndex: RelationConnectQueryFieldsByEntityIndex =
    {};
  const relationDisconnectQueryFieldsByEntityIndex: RelationDisconnectQueryFieldsByEntityIndex =
    {};

  for (const [entityIndex, entity] of Object.entries(entities)) {
    for (const [key, value] of Object.entries(entity)) {
      const hasConnect = hasRelationConnect(value);
      const hasDisconnect = hasRelationDisconnect(value);

      if (hasConnect && hasDisconnect) {
        throw new TwentyORMException(
          `Cannot have both connect and disconnect for the same field on ${entity.key}.`,
          TwentyORMExceptionCode.CONNECT_NOT_ALLOWED,
        );
      }

      const relationConnectQueryFields =
        relationConnectQueryFieldsByEntityIndex?.[entityIndex] || {};

      if (hasConnect) {
        relationConnectQueryFieldsByEntityIndex[entityIndex] = {
          ...relationConnectQueryFields,
          [key]: value,
        };
      }

      const relationDisconnectQueryFields =
        relationDisconnectQueryFieldsByEntityIndex?.[entityIndex] || {};

      if (hasDisconnect) {
        relationDisconnectQueryFieldsByEntityIndex[entityIndex] = {
          ...relationDisconnectQueryFields,
          [key]: value,
        };
      }
    }
  }

  return {
    relationConnectQueryFieldsByEntityIndex,
    relationDisconnectQueryFieldsByEntityIndex,
  };
};