Skip to content

Commit af06a9c

Browse files
authored
Merge pull request #11 from mapswipe/feat/topo-sort-generated-py
Sort generated pydantic classes topologically
2 parents e2435a8 + 1950585 commit af06a9c

File tree

4 files changed

+399
-10
lines changed

4 files changed

+399
-10
lines changed

functions/definition/models.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
# yaml-language-server: $schema=https://schema.typesync.org/v0.13.json
22

3-
# XXX(tnagorra): The pydantic classess are generated in alphabetical order.
4-
# So, we must be careful on how we name the models so that the dependencies
5-
# are always generated before hand.
6-
73
# ENUM
84

95
FbEnumValidateInputType:

functions/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"description": "Cloud Functions for Firebase",
44
"packageManager": "[email protected]",
55
"scripts": {
6+
"postinstall": "patch-package",
67
"lint": "eslint --ext .js,.ts .",
78
"build": "tsc",
89
"build:watch": "tsc --watch",
@@ -36,6 +37,7 @@
3637
"eslint-plugin-import": "^2.25.4",
3738
"eslint-plugin-promise": "^4.0.1",
3839
"firebase-functions-test": "^0.2.0",
40+
"patch-package": "^8.0.0",
3941
"typescript": "^4.5.4",
4042
"typesync-cli": "^0.13.0"
4143
},
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
diff --git a/node_modules/typesync-cli/dist/esm/index.js b/node_modules/typesync-cli/dist/esm/index.js
2+
index 4aa9f0b..3553b28 100755
3+
--- a/node_modules/typesync-cli/dist/esm/index.js
4+
+++ b/node_modules/typesync-cli/dist/esm/index.js
5+
@@ -5,6 +5,102 @@ var __export = (target, all) => {
6+
__defProp(target, name, { get: all[name], enumerable: true });
7+
};
8+
9+
+// sorting
10+
+
11+
+function createGraph(
12+
+ items,
13+
+ getKey,
14+
+ getIncomingNodesKey,
15+
+) {
16+
+ const graph = {};
17+
+ const outgoingNodesMap = {};
18+
+
19+
+ // Populate graph with incoming nodes
20+
+ for (const item of items) {
21+
+ const nodeKey = getKey(item);
22+
+ const incomingNodesKey = getIncomingNodesKey(item);
23+
+
24+
+ graph[nodeKey] = {
25+
+ key: nodeKey,
26+
+ item,
27+
+ incomingNodesKey,
28+
+ outgoingNodesKey: new Set(),
29+
+ };
30+
+
31+
+ // Create a outgoing nodes mapping
32+
+ for (const incomingNodeKey of incomingNodesKey ) {
33+
+ if (!outgoingNodesMap[incomingNodeKey]) {
34+
+ outgoingNodesMap[incomingNodeKey] = new Set();
35+
+ }
36+
+ outgoingNodesMap[incomingNodeKey].add(nodeKey);
37+
+ }
38+
+ }
39+
+
40+
+ // Set outgoing nodes on graph
41+
+ for (const nodeKey of Object.keys(graph)) {
42+
+ graph[nodeKey] = {
43+
+ ...graph[nodeKey],
44+
+ outgoingNodesKey: outgoingNodesMap[nodeKey] ?? new Set(),
45+
+ }
46+
+ }
47+
+
48+
+ return graph;
49+
+}
50+
+
51+
+function removeFromGraph(
52+
+ graph,
53+
+ removeKey,
54+
+) {
55+
+ const nodeToRemove = graph[removeKey];
56+
+ if (!nodeToRemove) {
57+
+ throw Error(`Error while removing node with key ${removeKey}`);
58+
+ }
59+
+
60+
+ // Remove from graph
61+
+ delete graph[removeKey];
62+
+
63+
+ // Update incomingNodesKey from outgoingNodes
64+
+ const affectedOutgoingNodes = [];
65+
+ for (const outgoingNodeKey of nodeToRemove.outgoingNodesKey) {
66+
+ const outgoingNode = graph[outgoingNodeKey];
67+
+ outgoingNode.incomingNodesKey.delete(removeKey);
68+
+ affectedOutgoingNodes.push(outgoingNode);
69+
+ }
70+
+
71+
+ return affectedOutgoingNodes;
72+
+}
73+
+
74+
+function topoSortGraph(
75+
+ graph,
76+
+) {
77+
+ const orderedNodeList = [];
78+
+
79+
+ const removableNodes = Object.values(graph).filter(
80+
+ (item) => item.incomingNodesKey.size === 0
81+
+ );
82+
+ if (removableNodes.length <= 0) {
83+
+ throw Error(`Expected removableNodes to greater than zero while sorting.`);
84+
+ }
85+
+
86+
+ while (removableNodes.length > 0) {
87+
+ const removedNode = removableNodes.shift();
88+
+ orderedNodeList.push(removedNode);
89+
+
90+
+ const affectedOutgoingNodes = removeFromGraph(graph, removedNode.key);
91+
+
92+
+ removableNodes.push(
93+
+ ...affectedOutgoingNodes.filter((node) => node.incomingNodesKey.size === 0),
94+
+ )
95+
+ }
96+
+
97+
+ const graphNodesCount = Object.keys(graph).length;
98+
+ if (graphNodesCount != 0) {
99+
+ throw Error(`Expected graph to have no nodes left while sorting. But got ${graphNodesCount} nodes`);
100+
+ }
101+
+
102+
+ return orderedNodeList;
103+
+}
104+
+
105+
// src/cli/index.tsx
106+
import { render } from "ink";
107+
import { resolve as resolve2 } from "path";
108+
@@ -562,10 +658,12 @@ var AbstractSchema = class {
109+
aliasModelsById;
110+
documentModelsById;
111+
get aliasModels() {
112+
- return Array.from(this.aliasModelsById.values()).sort((m1, m2) => m1.name.localeCompare(m2.name));
113+
+ // return Array.from(this.aliasModelsById.values()).sort((m1, m2) => m1.name.localeCompare(m2.name));
114+
+ return Array.from(this.aliasModelsById.values());
115+
}
116+
get documentModels() {
117+
- return Array.from(this.documentModelsById.values()).sort((m1, m2) => m1.name.localeCompare(m2.name));
118+
+ // return Array.from(this.documentModelsById.values()).sort((m1, m2) => m1.name.localeCompare(m2.name));
119+
+ return Array.from(this.documentModelsById.values());
120+
}
121+
constructor() {
122+
this.aliasModelsById = /* @__PURE__ */ new Map();
123+
@@ -1771,7 +1869,39 @@ var PythonGeneratorImpl = class {
124+
const d = this.createDeclarationForDocumentModel(model2);
125+
declarations.push(d);
126+
});
127+
- return { type: "python", declarations };
128+
+
129+
+ const graph = createGraph(
130+
+ declarations,
131+
+ (item) => item.modelName,
132+
+ (item) => {
133+
+ if (item.modelType.type !== 'object-class') {
134+
+ // NOTE: we can skip enum-class
135+
+ console.warn(`Skipping declaration "${item.modelName}" as modelType is ${item.modelType.type}`)
136+
+ return new Set();
137+
+ }
138+
+ const deps = new Set();
139+
+ for (const attribute of item.modelType.attributes) {
140+
+ if (attribute.type.type === "alias") {
141+
+ deps.add(attribute.type.name);
142+
+ } else if (attribute.type.type === "dict") {
143+
+ if (attribute.type.valueType.type === "alias") {
144+
+ deps.add(attribute.type.valueType.name);
145+
+ }
146+
+ } else if (attribute.type.type === "list") {
147+
+ if (attribute.type.elementType.type === "alias") {
148+
+ deps.add(attribute.type.elementType.name);
149+
+ }
150+
+ }
151+
+ // FIXME(tnagorra): Handle union, tuple
152+
+ }
153+
+ return deps;
154+
+ },
155+
+ );
156+
+ const sortedDeclarations = topoSortGraph(graph)
157+
+ return {
158+
+ type: "python",
159+
+ declarations: sortedDeclarations.map((node) => node.item),
160+
+ };
161+
}
162+
createDeclarationForAliasModel(model2) {
163+
switch (model2.type.type) {

0 commit comments

Comments
 (0)