@znemz/cft-utils 0.0.5-0
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/LICENSE +21 -0
- package/README.md +39 -0
- package/bin/taggableResourceMap.js +7 -0
- package/bin/taggableResources.js +7 -0
- package/debug.js +3 -0
- package/package.json +57 -0
- package/src/cache/cft_taggable_resource_map.json +755 -0
- package/src/cache/cft_taggable_resources.json +756 -0
- package/src/index.js +5 -0
- package/src/resources/index.js +3 -0
- package/src/resources/taggable.js +133 -0
- package/src/resources/taggable.test.js +18 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs/promises');
|
|
3
|
+
const globby = require('globby');
|
|
4
|
+
|
|
5
|
+
const cachePath = path.join(__dirname, '..', 'cache');
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* @returns {Promise<string[]>}
|
|
9
|
+
*
|
|
10
|
+
* Returns list of taggable resources from a cache file
|
|
11
|
+
* or from reading the aws-cdk-lib ITaggable Resources
|
|
12
|
+
*/
|
|
13
|
+
const getResources = async () => {
|
|
14
|
+
// use cached list if it exists in ${cachePath}/taggable.json
|
|
15
|
+
const cacheFile = path.join(cachePath, 'cft_taggable_resources.json');
|
|
16
|
+
let taggableResources;
|
|
17
|
+
taggableResources = await getCachedFile(cacheFile);
|
|
18
|
+
if (taggableResources) {
|
|
19
|
+
return taggableResources;
|
|
20
|
+
}
|
|
21
|
+
// note: should be called during prepublishing only
|
|
22
|
+
// cache miss, generate list of taggable resources
|
|
23
|
+
taggableResources = await getResourcesFromAwsCdk();
|
|
24
|
+
await mkCacheDir();
|
|
25
|
+
await fs.writeFile(cacheFile, JSON.stringify(taggableResources, null, 2));
|
|
26
|
+
return taggableResources;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
* Private as this should not be called from consuming code
|
|
31
|
+
* @returns {Promise<string[]>}
|
|
32
|
+
*
|
|
33
|
+
* Matches comments of an aws-* inside aws-cdk-lib
|
|
34
|
+
* resource file "*generated.d.ts" definition
|
|
35
|
+
* looking for @cloudformationResource and ITaggable
|
|
36
|
+
*/
|
|
37
|
+
const getResourcesFromAwsCdk = async () => {
|
|
38
|
+
// note: should be called during prepublishing only
|
|
39
|
+
const cdkDir = path.dirname(require.resolve('aws-cdk-lib'));
|
|
40
|
+
const globPaths = [
|
|
41
|
+
path.join(cdkDir, 'aws-*', 'lib', '*generated.d.ts'),
|
|
42
|
+
`!${path.join(cdkDir, 'aws-*', 'lib', 'index.d.ts')}`,
|
|
43
|
+
];
|
|
44
|
+
const paths = await globby(globPaths);
|
|
45
|
+
const nestedResourceNames = await Promise.all(
|
|
46
|
+
paths.map((file) => fileToTaggableResourceNames(file))
|
|
47
|
+
);
|
|
48
|
+
return nestedResourceNames.flat();
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const getResourceMap = async () => {
|
|
52
|
+
const cacheFile = path.join(cachePath, 'cft_taggable_resource_map.json');
|
|
53
|
+
let resTagMap;
|
|
54
|
+
resTagMap = await getCachedFile(cacheFile);
|
|
55
|
+
if (resTagMap) {
|
|
56
|
+
return resTagMap;
|
|
57
|
+
}
|
|
58
|
+
// note: should be called during prepublishing only
|
|
59
|
+
resTagMap = {};
|
|
60
|
+
const resources = await getResources();
|
|
61
|
+
// use cached map
|
|
62
|
+
if (Object.keys(resTagMap).length > 0) {
|
|
63
|
+
return resTagMap;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const resName of resources) {
|
|
67
|
+
resTagMap[resName] = true;
|
|
68
|
+
}
|
|
69
|
+
// cache it
|
|
70
|
+
await mkCacheDir();
|
|
71
|
+
await fs.writeFile(cacheFile, JSON.stringify(resTagMap, null, 2));
|
|
72
|
+
return resTagMap;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const isTaggableResource = async (resourceName) => {
|
|
76
|
+
const resMp = await getResourceMap();
|
|
77
|
+
return Boolean(resMp[resourceName]);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
* @param {string} file
|
|
82
|
+
* @returns {Promise<string[]>}
|
|
83
|
+
*
|
|
84
|
+
* Matches comments of an aws-* resource file d.ts file definition
|
|
85
|
+
* looking for @cloudformationResource and ITaggable
|
|
86
|
+
*/
|
|
87
|
+
async function fileToTaggableResourceNames(file) {
|
|
88
|
+
const taggableResources = [];
|
|
89
|
+
const data = (await fs.readFile(file)).toString();
|
|
90
|
+
// console.log(file);
|
|
91
|
+
const matches = [...data.matchAll(/.*@cloudformationResource\s(.*)(\s.*){4}(ITaggable)/gm)];
|
|
92
|
+
if (matches?.length >= 0) {
|
|
93
|
+
for (const match of matches) {
|
|
94
|
+
taggableResources.push(match[1]);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return taggableResources;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/*
|
|
101
|
+
* @param {string} cacheFile
|
|
102
|
+
* @returns {Promise<T>}
|
|
103
|
+
*
|
|
104
|
+
*/
|
|
105
|
+
async function getCachedFile(cacheFile) {
|
|
106
|
+
try {
|
|
107
|
+
const data = await fs.readFile(cacheFile);
|
|
108
|
+
const cached = JSON.parse(data.toString());
|
|
109
|
+
if (cached && cached.length > 0) {
|
|
110
|
+
return cached;
|
|
111
|
+
}
|
|
112
|
+
} catch (e) {
|
|
113
|
+
// do nothing
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function mkCacheDir() {
|
|
118
|
+
try {
|
|
119
|
+
await fs.access(cachePath);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
try {
|
|
122
|
+
await fs.mkdir(cachePath, { recursive: true });
|
|
123
|
+
} catch (e2) {
|
|
124
|
+
// --force
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = {
|
|
130
|
+
getResources,
|
|
131
|
+
getResourceMap,
|
|
132
|
+
isTaggableResource,
|
|
133
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const taggable = require('./taggable');
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
|
|
4
|
+
describe(taggable.isTaggableResource.name, () => {
|
|
5
|
+
['AWS::IAM::Role'].forEach((resourceName) => {
|
|
6
|
+
it(`${resourceName} is taggable`, async () => {
|
|
7
|
+
const isTag = await taggable.isTaggableResource(resourceName);
|
|
8
|
+
assert.ok(isTag);
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
['AWS::IAM::Policy', 'AWS::IAM::ManagedPolicy'].forEach((resourceName) => {
|
|
13
|
+
it(`${resourceName} is NOT taggable`, async () => {
|
|
14
|
+
const isTag = await taggable.isTaggableResource(resourceName);
|
|
15
|
+
assert.ok(!isTag);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
});
|