-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathFireDeIndex.ts
More file actions
108 lines (95 loc) · 3.09 KB
/
FireDeIndex.ts
File metadata and controls
108 lines (95 loc) · 3.09 KB
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
import fs from 'fs';
import { InterfaceDeclaration, TypescriptParser } from 'typescript-parser';
import config from './fire-deindex.json';
// RUN USING:
// npx ts-node --project ./node.tsconfig.json FireDeIndex.ts
/* interface FireDeIndexConfig {
defaultConfig: FirebaseIndexConfig;
deIndex: FireDeIndexEntry;
} */
interface FireDeIndexEntry {
collectionId: string;
sourceFile: string;
interfaceName: string;
whitelistedProperties: string[];
}
interface FirebaseIndexConfig {
indexes: FirebaseCompoundIndex[];
fieldOverrides?: FirebaseFieldOverride[];
}
interface FirebaseCompoundIndex {
collectionGroup: string;
queryScope: 'COLLECTION' | 'COLLECTION_GROUP';
fields: FirebaseField[];
}
interface FirebaseField {
fieldPath: string;
order?: 'ASCENDING' | 'DESCENDING';
arrayConfig?: 'CONTAINS';
}
interface FirebaseFieldOverride {
collectionGroup: string;
fieldPath: string;
indexes: FirebaseIndex[];
}
interface FirebaseIndex {
queryScope: 'COLLECTION' | 'COLLECTION_GROUP';
order?: string;
arrayConfig?: 'CONTAINS';
}
const firebaseConfigPath = 'firestore.indexes.json';
runMain();
/**
* The main function.
*/
async function runMain() {
console.log('Generating Firebase index config...');
const firebaseConfig = await generateFirebaseIndexConfig();
fs.writeFileSync(firebaseConfigPath, JSON.stringify(firebaseConfig, null, 2));
console.log(`Wrote Firebase index config to ${firebaseConfigPath}`);
}
/**
* Generates a Firebase index config from the de-index config.
* @returns The Firebase index config.
*/
async function generateFirebaseIndexConfig(): Promise<FirebaseIndexConfig> {
const generatedFieldOverrides: FirebaseFieldOverride[] = [];
for (const deIndex of config.deIndex) {
generatedFieldOverrides.push(...(await generateFieldOverridesForInterface(deIndex)));
}
return {
indexes: config.defaultConfig.indexes as FirebaseCompoundIndex[],
fieldOverrides: [...generatedFieldOverrides,
...(config.defaultConfig.fieldOverrides || [])] as FirebaseFieldOverride[],
};
}
/**
* Generates a list of field overrides for a given de-index entry.
* @param deIndex The de-index entry.
* @returns The list of field overrides.
*/
async function generateFieldOverridesForInterface(
deIndex: FireDeIndexEntry,
): Promise<FirebaseFieldOverride[]> {
const overrides: FirebaseFieldOverride[] = [];
const parser = new TypescriptParser();
const parsed = await parser.parseFile(deIndex.sourceFile, 'workspace root');
const interfaceNode = parsed.declarations.find((d) => d.name === deIndex.interfaceName);
if (!interfaceNode) {
throw new Error(`Could not find interface ${deIndex.interfaceName} in ${deIndex.sourceFile}`);
}
if (!(interfaceNode instanceof InterfaceDeclaration)) {
throw new Error(`Interface ${deIndex.interfaceName} in ${deIndex.sourceFile} is not an interface`);
}
for (const property of interfaceNode.properties) {
if (deIndex.whitelistedProperties.includes(property.name)) {
continue;
}
overrides.push({
collectionGroup: deIndex.collectionId,
fieldPath: property.name,
indexes: [], // Disables all indexing for this field
});
}
return overrides;
}