@balena/pinejs 23.2.5-build-translations-avoid-clone-3ce69936bb09b946a645c6b4ac77f730aa969cf4-1 → 23.2.6-build-renovate-bcrypt-6-x-2f51e49bdf6824a39939b00fbdbf3c1e3aa774ff-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.pinejs-cache.json +1 -1
- package/.versionbot/CHANGELOG.yml +33 -4
- package/CHANGELOG.md +6 -1
- package/VERSION +1 -1
- package/out/sbvr-api/common-types.d.ts +0 -3
- package/out/sbvr-api/permissions.js +36 -19
- package/out/sbvr-api/permissions.js.map +1 -1
- package/out/sbvr-api/translations.d.ts +2 -6
- package/out/sbvr-api/translations.js +2 -2
- package/out/sbvr-api/translations.js.map +1 -1
- package/package.json +3 -3
- package/src/sbvr-api/common-types.ts +0 -8
- package/src/sbvr-api/permissions.ts +182 -167
- package/src/sbvr-api/translations.ts +6 -24
|
@@ -7,7 +7,6 @@ import type {
|
|
|
7
7
|
Relationship,
|
|
8
8
|
RelationshipInternalNode,
|
|
9
9
|
RelationshipLeafNode,
|
|
10
|
-
RelationshipMapping,
|
|
11
10
|
SelectNode,
|
|
12
11
|
SelectQueryNode,
|
|
13
12
|
} from '@balena/abstract-sql-compiler';
|
|
@@ -20,11 +19,7 @@ import type {
|
|
|
20
19
|
} from '@balena/odata-parser';
|
|
21
20
|
import type { Tx } from '../database-layer/db.js';
|
|
22
21
|
import type { ApiKey, User } from '../sbvr-api/sbvr-utils.js';
|
|
23
|
-
import type {
|
|
24
|
-
AnyObject,
|
|
25
|
-
Dictionary,
|
|
26
|
-
ShallowWritableOnly,
|
|
27
|
-
} from './common-types.js';
|
|
22
|
+
import type { AnyObject, Dictionary } from './common-types.js';
|
|
28
23
|
|
|
29
24
|
import {
|
|
30
25
|
isBindReference,
|
|
@@ -773,175 +768,183 @@ const getAlias = (name: string) => {
|
|
|
773
768
|
return `permissions${permissionsJSON}`;
|
|
774
769
|
};
|
|
775
770
|
|
|
776
|
-
const rewriteRelationship =
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
}
|
|
771
|
+
const rewriteRelationship = (
|
|
772
|
+
value: Relationship,
|
|
773
|
+
name: string,
|
|
774
|
+
abstractSqlModel: AbstractSqlModel,
|
|
775
|
+
permissionsLookup: PermissionLookup,
|
|
776
|
+
vocabulary: string,
|
|
777
|
+
odata2AbstractSQL: OData2AbstractSQL,
|
|
778
|
+
) => {
|
|
779
|
+
let escapedName = sqlNameToODataName(name);
|
|
780
|
+
if (abstractSqlModel.tables[name]) {
|
|
781
|
+
escapedName = sqlNameToODataName(abstractSqlModel.tables[name].name);
|
|
782
|
+
}
|
|
789
783
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
784
|
+
const rewrite = (object: Relationship) => {
|
|
785
|
+
let ret = object;
|
|
786
|
+
if ('$' in ret && Array.isArray(ret.$)) {
|
|
787
|
+
// object is in the form of
|
|
788
|
+
// { "$": ["actor", ["actor", "id"]] } or { "$": ["device type"] }
|
|
789
|
+
// we are only interested in the first case, since this is a relationship
|
|
790
|
+
// to a different resource
|
|
791
|
+
const mapping = ret.$;
|
|
792
|
+
if (
|
|
793
|
+
mapping.length === 2 &&
|
|
794
|
+
Array.isArray(mapping[1]) &&
|
|
795
|
+
mapping[1].length === 2 &&
|
|
796
|
+
typeof mapping[1][0] === 'string'
|
|
797
|
+
) {
|
|
798
|
+
// now have ensured that mapping looks like ["actor", ["actor", "id"]]
|
|
799
|
+
// this relations ship means that:
|
|
800
|
+
// mapping[0] is the local field
|
|
801
|
+
// mapping[1] is the reference to the other resource, that joins this resource
|
|
802
|
+
// mapping[1][0] is the name of the other resource (actor in the example)
|
|
803
|
+
// mapping[1][1] is the name of the field on the other resource
|
|
804
|
+
//
|
|
805
|
+
// this therefore defines that the local field `actor` needs
|
|
806
|
+
// to match the `id` of the `actor` resources for the join
|
|
807
|
+
|
|
808
|
+
const possibleTargetResourceName = mapping[1][0];
|
|
809
|
+
|
|
810
|
+
// Skip this if we already shortcut this connection
|
|
811
|
+
if (possibleTargetResourceName.endsWith('$bypass')) {
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
819
814
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
815
|
+
const targetResourceEscaped = sqlNameToODataName(
|
|
816
|
+
abstractSqlModel.tables[possibleTargetResourceName]?.name ??
|
|
817
|
+
possibleTargetResourceName,
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
// This is either a translated or bypassed resource we don't
|
|
821
|
+
// mess with these
|
|
822
|
+
if (targetResourceEscaped.includes('$')) {
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
let foundCanAccessLink = false;
|
|
827
|
+
|
|
828
|
+
try {
|
|
829
|
+
const odata = memoizedParseOdata(`/${targetResourceEscaped}`);
|
|
830
|
+
|
|
831
|
+
const collapsedPermissionFilters = buildODataPermission(
|
|
832
|
+
permissionsLookup,
|
|
833
|
+
methodPermissions.GET,
|
|
834
|
+
vocabulary,
|
|
835
|
+
targetResourceEscaped,
|
|
836
|
+
odata,
|
|
823
837
|
);
|
|
824
838
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
839
|
+
if (collapsedPermissionFilters == null) {
|
|
840
|
+
// If we have full access already then there's no need to
|
|
841
|
+
// check for/rewrite based on `canAccess`
|
|
828
842
|
return;
|
|
829
843
|
}
|
|
830
844
|
|
|
831
|
-
|
|
845
|
+
_.set(
|
|
846
|
+
odata,
|
|
847
|
+
['tree', 'options', '$filter'],
|
|
848
|
+
collapsedPermissionFilters,
|
|
849
|
+
);
|
|
832
850
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
methodPermissions.GET,
|
|
839
|
-
vocabulary,
|
|
840
|
-
targetResourceEscaped,
|
|
841
|
-
odata,
|
|
842
|
-
);
|
|
851
|
+
const canAccessFunction: ResourceFunction = function (
|
|
852
|
+
property: AnyObject,
|
|
853
|
+
) {
|
|
854
|
+
// remove method property so that we won't loop back here again at this point
|
|
855
|
+
delete property.method;
|
|
843
856
|
|
|
844
|
-
if (
|
|
845
|
-
|
|
846
|
-
// check for/rewrite based on `canAccess`
|
|
847
|
-
return;
|
|
857
|
+
if (!this.defaultResource) {
|
|
858
|
+
throw new Error(`No resource selected in AST.`);
|
|
848
859
|
}
|
|
849
860
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
collapsedPermissionFilters,
|
|
861
|
+
const targetResourceAST = this.NavigateResources(
|
|
862
|
+
this.defaultResource,
|
|
863
|
+
property.name,
|
|
854
864
|
);
|
|
855
865
|
|
|
856
|
-
const
|
|
857
|
-
|
|
858
|
-
)
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
if (!this.defaultResource) {
|
|
863
|
-
throw new Error(`No resource selected in AST.`);
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
const targetResourceAST = this.NavigateResources(
|
|
867
|
-
this.defaultResource,
|
|
868
|
-
property.name,
|
|
869
|
-
);
|
|
870
|
-
|
|
871
|
-
const targetResourceName = sqlNameToODataName(
|
|
872
|
-
targetResourceAST.resource.name,
|
|
873
|
-
);
|
|
874
|
-
const currentResourceName = sqlNameToODataName(
|
|
875
|
-
this.defaultResource.name,
|
|
876
|
-
);
|
|
866
|
+
const targetResourceName = sqlNameToODataName(
|
|
867
|
+
targetResourceAST.resource.name,
|
|
868
|
+
);
|
|
869
|
+
const currentResourceName = sqlNameToODataName(
|
|
870
|
+
this.defaultResource.name,
|
|
871
|
+
);
|
|
877
872
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
}
|
|
884
|
-
// return a true expression to not select the relationship, which might be virtual
|
|
885
|
-
return ['Boolean', true];
|
|
886
|
-
};
|
|
887
|
-
|
|
888
|
-
try {
|
|
889
|
-
// We need execute the abstract SQL compiler to traverse
|
|
890
|
-
// through the permissions for that resource, using a
|
|
891
|
-
// special canAccess callback.
|
|
892
|
-
odata2AbstractSQL.match(
|
|
893
|
-
odata.tree,
|
|
894
|
-
'GET',
|
|
895
|
-
[],
|
|
896
|
-
odata.binds.length,
|
|
897
|
-
{
|
|
898
|
-
canAccess: canAccessFunction,
|
|
899
|
-
},
|
|
900
|
-
);
|
|
901
|
-
} catch (e: any) {
|
|
902
|
-
throw new ODataParser.SyntaxError(e);
|
|
903
|
-
}
|
|
904
|
-
if (foundCanAccessLink) {
|
|
905
|
-
// store the resource name as it was with a $bypass
|
|
906
|
-
// suffix in this relationship, this means that the
|
|
907
|
-
// query generator will use the plain resource instead
|
|
908
|
-
// of the filtered resource.
|
|
909
|
-
mapping[1][0] = `${possibleTargetResourceName}$bypass`;
|
|
910
|
-
}
|
|
911
|
-
} catch (e) {
|
|
912
|
-
if (e === constrainedPermissionError) {
|
|
913
|
-
// ignore
|
|
914
|
-
return;
|
|
873
|
+
if (
|
|
874
|
+
currentResourceName === targetResourceEscaped &&
|
|
875
|
+
targetResourceName === escapedName
|
|
876
|
+
) {
|
|
877
|
+
foundCanAccessLink = true;
|
|
915
878
|
}
|
|
879
|
+
// return a true expression to not select the relationship, which might be virtual
|
|
880
|
+
return ['Boolean', true];
|
|
881
|
+
};
|
|
916
882
|
|
|
917
|
-
|
|
918
|
-
//
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
883
|
+
try {
|
|
884
|
+
// We need execute the abstract SQL compiler to traverse
|
|
885
|
+
// through the permissions for that resource, using a
|
|
886
|
+
// special canAccess callback.
|
|
887
|
+
odata2AbstractSQL.match(odata.tree, 'GET', [], odata.binds.length, {
|
|
888
|
+
canAccess: canAccessFunction,
|
|
889
|
+
});
|
|
890
|
+
} catch (e: any) {
|
|
891
|
+
throw new ODataParser.SyntaxError(e);
|
|
892
|
+
}
|
|
893
|
+
if (foundCanAccessLink) {
|
|
894
|
+
// store the resource name as it was with a $bypass
|
|
895
|
+
// suffix in this relationship, this means that the
|
|
896
|
+
// query generator will use the plain resource instead
|
|
897
|
+
// of the filtered resource.
|
|
898
|
+
ret = { ...ret };
|
|
899
|
+
const modifiedMapping = _.cloneDeep(mapping);
|
|
900
|
+
modifiedMapping[1]![0] = `${possibleTargetResourceName}$bypass`;
|
|
901
|
+
ret.$ = modifiedMapping;
|
|
902
|
+
}
|
|
903
|
+
} catch (e) {
|
|
904
|
+
if (e === constrainedPermissionError) {
|
|
905
|
+
// ignore
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
923
908
|
|
|
924
|
-
|
|
909
|
+
// TODO: We should investigate in detail why this error
|
|
910
|
+
// occurse. It might be able to get rid of this.
|
|
911
|
+
if (e instanceof ODataParser.SyntaxError) {
|
|
912
|
+
// ignore
|
|
913
|
+
return;
|
|
925
914
|
}
|
|
915
|
+
|
|
916
|
+
throw e;
|
|
926
917
|
}
|
|
927
918
|
}
|
|
919
|
+
}
|
|
928
920
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
921
|
+
if (typeof object === 'object') {
|
|
922
|
+
for (const key of Object.keys(object)) {
|
|
923
|
+
if (key === '$') {
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
const v = (object as RelationshipInternalNode)[key];
|
|
927
|
+
// we want to recurse into the relationship path, but
|
|
928
|
+
// in case we hit a plain string, we don't need to bother
|
|
929
|
+
// checking it. This can happen since plain terms also have
|
|
930
|
+
// relationships to sbvr-types.
|
|
931
|
+
if (typeof v !== 'string') {
|
|
932
|
+
const changedEntry = rewrite(v);
|
|
933
|
+
if (changedEntry != null) {
|
|
934
|
+
ret = { ...ret };
|
|
935
|
+
(ret as RelationshipInternalNode)[key] = changedEntry;
|
|
937
936
|
}
|
|
938
|
-
}
|
|
937
|
+
}
|
|
939
938
|
}
|
|
940
|
-
}
|
|
939
|
+
}
|
|
941
940
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
941
|
+
if (ret !== object) {
|
|
942
|
+
return ret;
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
return rewrite(value);
|
|
947
|
+
};
|
|
945
948
|
|
|
946
949
|
const rewriteRelationships = (
|
|
947
950
|
abstractSqlModel: AbstractSqlModel,
|
|
@@ -959,21 +962,31 @@ const rewriteRelationships = (
|
|
|
959
962
|
originalAbstractSQLModel,
|
|
960
963
|
);
|
|
961
964
|
|
|
962
|
-
const newRelationships =
|
|
963
|
-
|
|
964
|
-
rewriteRelationship(
|
|
965
|
-
|
|
965
|
+
const newRelationships = { ...relationships };
|
|
966
|
+
for (const name of Object.keys(newRelationships)) {
|
|
967
|
+
const changedEntry = rewriteRelationship(
|
|
968
|
+
newRelationships[name],
|
|
966
969
|
name,
|
|
967
970
|
abstractSqlModel,
|
|
968
971
|
permissionsLookup,
|
|
969
972
|
vocabulary,
|
|
970
973
|
odata2AbstractSQL,
|
|
971
974
|
);
|
|
972
|
-
|
|
975
|
+
if (changedEntry != null) {
|
|
976
|
+
newRelationships[name] = changedEntry as RelationshipInternalNode;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
973
979
|
|
|
974
980
|
return newRelationships;
|
|
975
981
|
};
|
|
976
982
|
|
|
983
|
+
/**
|
|
984
|
+
* Ideally this would be a deep readonly but that causes typescript to complain about excessively deep instantiation so a single level is the compromise,
|
|
985
|
+
* meaning that it's actually 1st and 3rd+ level writable
|
|
986
|
+
*/
|
|
987
|
+
type ShallowWritableOnly<T> = {
|
|
988
|
+
[P in keyof T]: Readonly<T[P]>;
|
|
989
|
+
};
|
|
977
990
|
const getBoundConstrainedMemoizer = memoizeWeak(
|
|
978
991
|
(abstractSqlModel: AbstractSqlModel) =>
|
|
979
992
|
memoizeWeak(
|
|
@@ -982,16 +995,18 @@ const getBoundConstrainedMemoizer = memoizeWeak(
|
|
|
982
995
|
/** This is the final translated vocabulary that permissions get written against as that's what we will have to resolve permissions against */
|
|
983
996
|
finalVocabulary: string,
|
|
984
997
|
): AbstractSqlModel => {
|
|
985
|
-
const constrainedAbstractSqlModel: Omit<
|
|
998
|
+
const constrainedAbstractSqlModel: Omit<
|
|
999
|
+
AbstractSqlModel,
|
|
1000
|
+
'tables' | 'relationships'
|
|
1001
|
+
> & {
|
|
986
1002
|
tables: {
|
|
987
|
-
[resourceName: string]: ShallowWritableOnly<
|
|
988
|
-
AbstractSqlModel['tables'][string]
|
|
989
|
-
>;
|
|
1003
|
+
[resourceName: string]: ShallowWritableOnly<AbstractSqlTable>;
|
|
990
1004
|
};
|
|
1005
|
+
relationships: ShallowWritableOnly<AbstractSqlModel['relationships']>;
|
|
991
1006
|
} = {
|
|
992
1007
|
...abstractSqlModel,
|
|
993
1008
|
synonyms: _.cloneDeep(abstractSqlModel.synonyms),
|
|
994
|
-
relationships:
|
|
1009
|
+
relationships: {},
|
|
995
1010
|
tables: {},
|
|
996
1011
|
};
|
|
997
1012
|
const baseTables = abstractSqlModel.tables;
|
|
@@ -1021,9 +1036,7 @@ const getBoundConstrainedMemoizer = memoizeWeak(
|
|
|
1021
1036
|
},
|
|
1022
1037
|
);
|
|
1023
1038
|
|
|
1024
|
-
const origRelationships = Object.keys(
|
|
1025
|
-
constrainedAbstractSqlModel.relationships,
|
|
1026
|
-
);
|
|
1039
|
+
const origRelationships = Object.keys(abstractSqlModel.relationships);
|
|
1027
1040
|
|
|
1028
1041
|
const alreadyConstrainedTables = new Map<
|
|
1029
1042
|
ShallowWritableOnly<AbstractSqlTable>,
|
|
@@ -1162,7 +1175,7 @@ const getBoundConstrainedMemoizer = memoizeWeak(
|
|
|
1162
1175
|
// expands and filters to unconstraint resources
|
|
1163
1176
|
constrainedAbstractSqlModel.relationships = rewriteRelationships(
|
|
1164
1177
|
constrainedAbstractSqlModel as AbstractSqlModel,
|
|
1165
|
-
|
|
1178
|
+
abstractSqlModel.relationships,
|
|
1166
1179
|
permissionsLookup,
|
|
1167
1180
|
finalVocabulary,
|
|
1168
1181
|
);
|
|
@@ -1186,8 +1199,10 @@ const getBoundConstrainedMemoizer = memoizeWeak(
|
|
|
1186
1199
|
return;
|
|
1187
1200
|
}
|
|
1188
1201
|
for (const relationship of origRelationships) {
|
|
1189
|
-
|
|
1190
|
-
|
|
1202
|
+
// TODO: Avoid this cloneDeep when not necessary
|
|
1203
|
+
relationships[`${relationship}$${alias}`] = relationships[
|
|
1204
|
+
relationship
|
|
1205
|
+
] = _.cloneDeep(relationships[relationship]);
|
|
1191
1206
|
namespaceRelationships(relationships[relationship], alias);
|
|
1192
1207
|
}
|
|
1193
1208
|
return relationships[permissionResourceName];
|
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
FieldNode,
|
|
16
16
|
ResourceNode,
|
|
17
17
|
} from '@balena/abstract-sql-compiler';
|
|
18
|
-
import type { Dictionary
|
|
18
|
+
import type { Dictionary } from './common-types.js';
|
|
19
19
|
|
|
20
20
|
export type AliasValidNodeType =
|
|
21
21
|
| SelectQueryNode
|
|
@@ -24,13 +24,7 @@ export type AliasValidNodeType =
|
|
|
24
24
|
| UnknownTypeNodes
|
|
25
25
|
| NullNode;
|
|
26
26
|
const aliasFields = (
|
|
27
|
-
translationAbstractSqlModel:
|
|
28
|
-
tables: {
|
|
29
|
-
[resourceName: string]: ShallowWritableOnly<
|
|
30
|
-
AbstractSqlModel['tables'][string]
|
|
31
|
-
>;
|
|
32
|
-
};
|
|
33
|
-
},
|
|
27
|
+
translationAbstractSqlModel: AbstractSqlModel,
|
|
34
28
|
fromResourceName: string,
|
|
35
29
|
toResource: string,
|
|
36
30
|
aliases: Dictionary<string | AliasValidNodeType>,
|
|
@@ -80,13 +74,7 @@ const aliasFields = (
|
|
|
80
74
|
};
|
|
81
75
|
|
|
82
76
|
const aliasResource = (
|
|
83
|
-
translationAbstractSqlModel:
|
|
84
|
-
tables: {
|
|
85
|
-
[resourceName: string]: ShallowWritableOnly<
|
|
86
|
-
AbstractSqlModel['tables'][string]
|
|
87
|
-
>;
|
|
88
|
-
};
|
|
89
|
-
},
|
|
77
|
+
translationAbstractSqlModel: AbstractSqlModel,
|
|
90
78
|
fromResourceName: string,
|
|
91
79
|
toResource: string,
|
|
92
80
|
aliases: Dictionary<string | AliasValidNodeType>,
|
|
@@ -124,7 +112,7 @@ const namespaceRelationships = (
|
|
|
124
112
|
}
|
|
125
113
|
|
|
126
114
|
const changedEntry = namespaceRelationships(relationship, alias);
|
|
127
|
-
if (changedEntry) {
|
|
115
|
+
if (changedEntry != null) {
|
|
128
116
|
ret = { ...ret };
|
|
129
117
|
(ret as RelationshipInternalNode)[key] = changedEntry;
|
|
130
118
|
}
|
|
@@ -149,13 +137,7 @@ const namespaceRelationships = (
|
|
|
149
137
|
};
|
|
150
138
|
|
|
151
139
|
export const translateAbstractSqlModel = (
|
|
152
|
-
fromAbstractSqlModel:
|
|
153
|
-
tables: {
|
|
154
|
-
[resourceName: string]: ShallowWritableOnly<
|
|
155
|
-
AbstractSqlModel['tables'][string]
|
|
156
|
-
>;
|
|
157
|
-
};
|
|
158
|
-
},
|
|
140
|
+
fromAbstractSqlModel: AbstractSqlModel,
|
|
159
141
|
toAbstractSqlModel: AbstractSqlModel,
|
|
160
142
|
fromVersion: string,
|
|
161
143
|
toVersion: string,
|
|
@@ -228,7 +210,7 @@ export const translateAbstractSqlModel = (
|
|
|
228
210
|
if (!key.includes('$')) {
|
|
229
211
|
key = `${key}${toVersionSuffix}`;
|
|
230
212
|
}
|
|
231
|
-
fromAbstractSqlModel.tables[key] =
|
|
213
|
+
fromAbstractSqlModel.tables[key] = _.cloneDeep(table);
|
|
232
214
|
}
|
|
233
215
|
|
|
234
216
|
for (const key of fromResourceKeys) {
|