Skip to content

Commit 45141b6

Browse files
committed
docs(hub): one more refactor
1 parent 55d7c2d commit 45141b6

2 files changed

Lines changed: 153 additions & 115 deletions

File tree

api/filter-availability.js

Lines changed: 66 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -21,37 +21,42 @@ function hasData(pathData) {
2121
return Object.keys(pathData).length > 0;
2222
}
2323

24-
function filterPathDataForEnvironment(pathData, environment) {
25-
const methods = Object.entries(pathData).filter(isHTTPMethod);
26-
const methodsForEnvironment = methods.filter(([_, metadata]) =>
27-
isAvailableInEnvironment(metadata, environment)
24+
function filterMethodsForEnvironment(pathData, environment) {
25+
return Object.fromEntries(
26+
Object.entries(pathData)
27+
.filter(isHTTPMethod)
28+
.filter(([_, metadata]) =>
29+
isAvailableInEnvironment(metadata, environment)
30+
)
2831
);
29-
const methodsChanged = methods.length != methodsForEnvironment.length;
32+
}
3033

31-
if (methodsForEnvironment.length == 0) {
32-
// when there are no available methods, drop all other metadata except $ref,
33-
// which will be cleaned up later
34-
const otherMetadata = Object.entries(pathData).filter(
35-
(d) => !isHTTPMethod(d)
36-
);
37-
const refsOnly = otherMetadata.filter((m) => m[0] === "$ref");
38-
const otherMetadataChanged = otherMetadata.length != refsOnly.length;
34+
function otherMetadata(pathData) {
35+
return Object.fromEntries(
36+
Object.entries(pathData).filter((d) => !isHTTPMethod(d))
37+
);
38+
}
3939

40-
return [
41-
Object.fromEntries(refsOnly),
42-
methodsChanged || otherMetadataChanged,
43-
];
44-
}
40+
function refsOnly(metadata) {
41+
return Object.fromEntries(
42+
Object.entries(metadata).filter(([key]) => key === "$ref")
43+
);
44+
}
4545

46-
// when there are available methods, return all other metadata
47-
const otherMetadata = Object.fromEntries(
48-
Object.entries(pathData).filter((d) => !isHTTPMethod(d))
46+
function filterPathDataForEnvironment(pathData, environment) {
47+
const methodsForEnvironment = filterMethodsForEnvironment(
48+
pathData,
49+
environment
4950
);
5051

51-
return [
52-
{ ...Object.fromEntries(methodsForEnvironment), ...otherMetadata },
53-
methodsChanged,
54-
];
52+
if (Object.keys(methodsForEnvironment).length === 0) {
53+
// when there are no available methods, drop all other metadata except
54+
// $ref, which will be cleaned up later
55+
return refsOnly(otherMetadata(pathData));
56+
}
57+
58+
// when there are available methods, return all other metadata too
59+
return { ...methodsForEnvironment, ...otherMetadata(pathData) };
5560
}
5661

5762
// we need to escape routes to match spec refs
@@ -62,16 +67,14 @@ function escapeRoute(route) {
6267

6368
function filterPaths(paths, environment) {
6469
const filtered = {};
65-
let pathsChanged = false;
6670
const removedRoutes = [];
6771

6872
if (paths) {
69-
for (const [route, metadata] of Object.entries(paths)) {
70-
const [filteredPathData, changed] = filterPathDataForEnvironment(
71-
metadata,
73+
for (const [route, pathData] of Object.entries(paths)) {
74+
const filteredPathData = filterPathDataForEnvironment(
75+
pathData,
7276
environment
7377
);
74-
pathsChanged = pathsChanged || changed;
7578

7679
if (hasData(filteredPathData)) {
7780
filtered[route] = filteredPathData;
@@ -81,31 +84,21 @@ function filterPaths(paths, environment) {
8184
}
8285
}
8386

84-
return [filtered, pathsChanged, removedRoutes];
87+
return [filtered, removedRoutes];
8588
}
8689

87-
function pruneRefs(spec, allRemovedRoutes) {
88-
const pruned = {};
89-
let changed = false;
90-
91-
if (spec.paths) {
92-
for (const [route, pathData] of Object.entries(spec.paths)) {
93-
if (
94-
Object.hasOwn(pathData, "$ref") &&
95-
allRemovedRoutes.has(pathData["$ref"])
96-
) {
97-
// Drop refs to removed routes
98-
changed = true;
99-
continue;
100-
} else {
101-
pruned[route] = pathData;
102-
}
103-
}
104-
} else {
105-
return [{}, false];
106-
}
90+
function isDanglingRef(pathData, allRemovedRoutes) {
91+
return (
92+
Object.hasOwn(pathData, "$ref") && allRemovedRoutes.has(pathData["$ref"])
93+
);
94+
}
10795

108-
return [pruned, changed];
96+
function pruneRefs(paths, allRemovedRoutes) {
97+
return Object.fromEntries(
98+
Object.entries(paths).filter(
99+
([, pathData]) => !isDanglingRef(pathData, allRemovedRoutes)
100+
)
101+
);
109102
}
110103

111104
// js-yaml drops comments on load/dump, so we should capture the leading
@@ -140,9 +133,13 @@ function getYamlFiles(specDir) {
140133
}
141134

142135
function writeFile(filePath, fileData) {
143-
if (fileData.shouldWrite) {
144-
fs.writeFileSync(filePath, fileData.header + yaml.dump(fileData.spec));
145-
}
136+
fs.writeFileSync(filePath, fileData.header + yaml.dump(fileData.spec));
137+
}
138+
139+
// compare against the original paths to know whether a file needs rewriting,
140+
// instead of threading a "changed" flag through every filtering step
141+
function pathsChanged(before, after) {
142+
return JSON.stringify(before ?? {}) !== JSON.stringify(after);
146143
}
147144

148145
function filterByAvailability(specDir, environment) {
@@ -151,9 +148,11 @@ function filterByAvailability(specDir, environment) {
151148
const mapping = {};
152149
const allRemovedRoutes = new Set();
153150
for (const file of files) {
151+
const originalPaths = file.spec.paths;
152+
154153
// filter methods by x-availability
155-
const [filteredPaths, shouldWrite, removedRoutes] = filterPaths(
156-
file.spec.paths,
154+
const [filteredPaths, removedRoutes] = filterPaths(
155+
originalPaths,
157156
environment
158157
);
159158
file.spec.paths = filteredPaths;
@@ -168,24 +167,21 @@ function filterByAvailability(specDir, environment) {
168167
mapping[file.path] = {
169168
spec: file.spec,
170169
header: file.header,
171-
shouldWrite,
170+
originalPaths,
172171
};
173172
}
174173

175174
// prune dangling refs
176-
for (const [filePath, fileData] of Object.entries(mapping)) {
177-
const [prunedPaths, changed] = pruneRefs(fileData.spec, allRemovedRoutes);
178-
179-
if (changed) {
180-
mapping[filePath].spec.paths = prunedPaths;
181-
fileData.shouldWrite = true;
182-
}
175+
for (const fileData of Object.values(mapping)) {
176+
fileData.spec.paths = pruneRefs(fileData.spec.paths, allRemovedRoutes);
183177
}
184178

185-
// write files with updates
186-
Object.entries(mapping).forEach(([filePath, fileData]) =>
187-
writeFile(filePath, fileData)
188-
);
179+
// write files whose paths actually changed
180+
Object.entries(mapping).forEach(([filePath, fileData]) => {
181+
if (pathsChanged(fileData.originalPaths, fileData.spec.paths)) {
182+
writeFile(filePath, fileData);
183+
}
184+
});
189185
}
190186

191187
module.exports = {
@@ -196,4 +192,5 @@ module.exports = {
196192
extractHeaderComments,
197193
escapeRoute,
198194
pruneRefs,
195+
pathsChanged,
199196
};

0 commit comments

Comments
 (0)