Merge branch 'backstage:master' into plugin-azure-functions

This commit is contained in:
Wesley
2022-09-28 15:47:26 +02:00
committed by GitHub
11 changed files with 228 additions and 172 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/catalog-client': patch
---
Renamed argument in `validateEntity` from `location` to `locationRef`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-tech-insights-backend': patch
---
Modify router endpoint to handle singular and collections of request parameters similarly.
+2 -2
View File
@@ -68,7 +68,7 @@ export interface CatalogApi {
): Promise<void>;
validateEntity(
entity: Entity,
location: string,
locationRef: string,
options?: CatalogRequestOptions,
): Promise<ValidateEntityResponse>;
}
@@ -130,7 +130,7 @@ export class CatalogClient implements CatalogApi {
): Promise<void>;
validateEntity(
entity: Entity,
location: string,
locationRef: string,
options?: CatalogRequestOptions,
): Promise<ValidateEntityResponse>;
}
@@ -322,7 +322,7 @@ describe('CatalogClient', () => {
name: '',
},
},
'http://example.com',
'url:http://example.com',
),
).toMatchObject({
valid: false,
@@ -350,7 +350,7 @@ describe('CatalogClient', () => {
name: 'good',
},
},
'http://example.com',
'url:http://example.com',
),
).toMatchObject({
valid: true,
@@ -373,7 +373,7 @@ describe('CatalogClient', () => {
name: 'good',
},
},
'http://example.com',
'url:http://example.com',
),
).rejects.toThrow(/Request failed with 500 Error/);
});
+2 -2
View File
@@ -359,7 +359,7 @@ export class CatalogClient implements CatalogApi {
*/
async validateEntity(
entity: Entity,
location: string,
locationRef: string,
options?: CatalogRequestOptions,
): Promise<ValidateEntityResponse> {
const response = await this.fetchApi.fetch(
@@ -370,7 +370,7 @@ export class CatalogClient implements CatalogApi {
...(options?.token && { Authorization: `Bearer ${options?.token}` }),
},
method: 'POST',
body: JSON.stringify({ entity, location }),
body: JSON.stringify({ entity, location: locationRef }),
},
);
+2 -2
View File
@@ -405,11 +405,11 @@ export interface CatalogApi {
* Validate entity and its location.
*
* @param entity - Entity to validate
* @param location - URL location of the entity
* @param locationRef - Location ref in format `url:http://example.com/file`
*/
validateEntity(
entity: Entity,
location: string,
locationRef: string,
options?: CatalogRequestOptions,
): Promise<ValidateEntityResponse>;
}
@@ -87,59 +87,94 @@ describe('Tech Insights router tests', () => {
app = express().use(router);
});
it('should be able to retrieve latest schemas', async () => {
await request(app).get('/fact-schemas').expect(200);
expect(latestSchemasMock).toHaveBeenCalled();
describe('/fact-schemas', () => {
it('should be able to retrieve latest schemas', async () => {
await request(app).get('/fact-schemas').expect(200);
expect(latestSchemasMock).toHaveBeenCalled();
});
});
it('should not contain check endpoints when checker not present', async () => {
await request(app).get('/checks').expect(404);
await request(app).post('/checks/a/a/a').expect(404);
describe('/checks', () => {
it('should not contain check endpoints when checker not present', async () => {
await request(app).get('/checks').expect(404);
await request(app).post('/checks/a/a/a').expect(404);
});
});
it('should be able to parse id request params for fact retrieval', async () => {
await request(app)
.get('/facts/latest')
.query({
entity: 'a:a/a',
ids: ['firstId', 'secondId'],
})
.expect(200);
expect(latestFactsByIdsMock).toHaveBeenCalledWith(
['firstId', 'secondId'],
'a:a/a',
);
describe('/facts/latest', () => {
it('should be able to parse id request params for fact retrieval', async () => {
await request(app)
.get('/facts/latest')
.query({
entity: 'a:a/a',
ids: ['firstId', 'secondId'],
})
.expect(200);
expect(latestFactsByIdsMock).toHaveBeenCalledWith(
['firstId', 'secondId'],
'a:a/a',
);
});
it('should handle singular ids in query params correctly', async () => {
await request(app)
.get('/facts/latest')
.query({
entity: 'a:a/a',
ids: ['secondId'],
})
.expect(200);
expect(latestFactsByIdsMock).toHaveBeenCalledWith(['secondId'], 'a:a/a');
});
});
it('should be able to parse datetime request params for fact retrieval', async () => {
await request(app)
.get('/facts/range')
.query({
entity: 'a:a/a',
ids: ['firstId', 'secondId'],
startDatetime: '2021-12-12T12:12:12',
endDatetime: '2022-11-11T11:11:11',
})
.expect(200);
expect(factsBetweenTimestampsByIdsMock).toHaveBeenCalledWith(
['firstId', 'secondId'],
'a:a/a',
DateTime.fromISO('2021-12-12T12:12:12.000+00:00'),
DateTime.fromISO('2022-11-11T11:11:11.000+00:00'),
);
});
describe('/facts/range', () => {
it('should be able to parse datetime request params for fact retrieval', async () => {
await request(app)
.get('/facts/range')
.query({
entity: 'a:a/a',
ids: ['firstId', 'secondId'],
startDatetime: '2021-12-12T12:12:12',
endDatetime: '2022-11-11T11:11:11',
})
.expect(200);
expect(factsBetweenTimestampsByIdsMock).toHaveBeenCalledWith(
['firstId', 'secondId'],
'a:a/a',
DateTime.fromISO('2021-12-12T12:12:12.000+00:00'),
DateTime.fromISO('2022-11-11T11:11:11.000+00:00'),
);
});
it('should respond gracefully on parsing errors', async () => {
await request(app)
.get('/facts/range')
.query({
entity: 'a:a/a',
ids: ['firstId', 'secondId'],
startDatetime: '2021-12-1222T12:12:12',
endDatetime: '2022-1122-11T11:11:11',
})
.expect(422);
expect(latestFactsByIdsMock).toHaveBeenCalledTimes(0);
it('should respond gracefully on parsing errors', async () => {
await request(app)
.get('/facts/range')
.query({
entity: 'a:a/a',
ids: ['firstId', 'secondId'],
startDatetime: '2021-12-1222T12:12:12',
endDatetime: '2022-1122-11T11:11:11',
})
.expect(422);
expect(latestFactsByIdsMock).toHaveBeenCalledTimes(0);
});
it('should handle singular ids in query params correctly', async () => {
await request(app)
.get('/facts/range')
.query({
entity: 'a:a/a',
ids: ['firstId'],
startDatetime: '2021-12-12T12:12:12',
endDatetime: '2022-11-11T11:11:11',
})
.expect(200);
expect(factsBetweenTimestampsByIdsMock).toHaveBeenCalledWith(
['firstId'],
'a:a/a',
DateTime.fromISO('2021-12-12T12:12:12.000+00:00'),
DateTime.fromISO('2022-11-11T11:11:11.000+00:00'),
);
});
});
});
@@ -132,7 +132,13 @@ export async function createRouter<
router.get('/facts/latest', async (req, res) => {
const { entity } = req.query;
const { namespace, kind, name } = parseEntityRef(entity as string);
const ids = req.query.ids as string[];
if (!req.query.ids) {
return res
.status(422)
.send({ error: 'Failed to parse ids from request' });
}
const ids = [req.query.ids].flat() as string[];
return res.send(
await techInsightsStore.getLatestFactsByIds(
ids,
@@ -148,7 +154,12 @@ export async function createRouter<
const { entity } = req.query;
const { namespace, kind, name } = parseEntityRef(entity as string);
const ids = req.query.ids as string[];
if (!req.query.ids) {
return res
.status(422)
.send({ error: 'Failed to parse ids from request' });
}
const ids = [req.query.ids].flat() as string[];
const startDatetime = DateTime.fromISO(req.query.startDatetime as string);
const endDatetime = DateTime.fromISO(req.query.endDatetime as string);
if (!startDatetime.isValid || !endDatetime.isValid) {
+1 -1
View File
@@ -33,7 +33,7 @@ import { UserSettingsPage } from '@backstage/plugin-user-settings';
const AppRoutes = () => (
<Routes>
<Route path="/settings" element={<UserSettingsPage />}>
<Route path="/settings" element={<UserSettingsPage />} />
</Routes>
);
```
+55 -55
View File
@@ -2977,126 +2977,126 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-android-arm-eabi@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-android-arm-eabi@npm:1.3.2"
"@swc/core-android-arm-eabi@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-android-arm-eabi@npm:1.3.3"
dependencies:
"@swc/wasm": 1.2.122
conditions: os=android & cpu=arm
languageName: node
linkType: hard
"@swc/core-android-arm64@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-android-arm64@npm:1.3.2"
"@swc/core-android-arm64@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-android-arm64@npm:1.3.3"
dependencies:
"@swc/wasm": 1.2.130
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@swc/core-darwin-arm64@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-darwin-arm64@npm:1.3.2"
"@swc/core-darwin-arm64@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-darwin-arm64@npm:1.3.3"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@swc/core-darwin-x64@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-darwin-x64@npm:1.3.2"
"@swc/core-darwin-x64@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-darwin-x64@npm:1.3.3"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@swc/core-freebsd-x64@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-freebsd-x64@npm:1.3.2"
"@swc/core-freebsd-x64@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-freebsd-x64@npm:1.3.3"
dependencies:
"@swc/wasm": 1.2.130
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@swc/core-linux-arm-gnueabihf@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.2"
"@swc/core-linux-arm-gnueabihf@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.3"
dependencies:
"@swc/wasm": 1.2.130
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@swc/core-linux-arm64-gnu@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-linux-arm64-gnu@npm:1.3.2"
"@swc/core-linux-arm64-gnu@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-linux-arm64-gnu@npm:1.3.3"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
"@swc/core-linux-arm64-musl@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-linux-arm64-musl@npm:1.3.2"
"@swc/core-linux-arm64-musl@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-linux-arm64-musl@npm:1.3.3"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
"@swc/core-linux-x64-gnu@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-linux-x64-gnu@npm:1.3.2"
"@swc/core-linux-x64-gnu@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-linux-x64-gnu@npm:1.3.3"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
"@swc/core-linux-x64-musl@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-linux-x64-musl@npm:1.3.2"
"@swc/core-linux-x64-musl@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-linux-x64-musl@npm:1.3.3"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
"@swc/core-win32-arm64-msvc@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-win32-arm64-msvc@npm:1.3.2"
"@swc/core-win32-arm64-msvc@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-win32-arm64-msvc@npm:1.3.3"
dependencies:
"@swc/wasm": 1.2.130
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@swc/core-win32-ia32-msvc@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-win32-ia32-msvc@npm:1.3.2"
"@swc/core-win32-ia32-msvc@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-win32-ia32-msvc@npm:1.3.3"
dependencies:
"@swc/wasm": 1.2.130
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@swc/core-win32-x64-msvc@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-win32-x64-msvc@npm:1.3.2"
"@swc/core-win32-x64-msvc@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-win32-x64-msvc@npm:1.3.3"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@swc/core@npm:^1.2.239":
version: 1.3.2
resolution: "@swc/core@npm:1.3.2"
version: 1.3.3
resolution: "@swc/core@npm:1.3.3"
dependencies:
"@swc/core-android-arm-eabi": 1.3.2
"@swc/core-android-arm64": 1.3.2
"@swc/core-darwin-arm64": 1.3.2
"@swc/core-darwin-x64": 1.3.2
"@swc/core-freebsd-x64": 1.3.2
"@swc/core-linux-arm-gnueabihf": 1.3.2
"@swc/core-linux-arm64-gnu": 1.3.2
"@swc/core-linux-arm64-musl": 1.3.2
"@swc/core-linux-x64-gnu": 1.3.2
"@swc/core-linux-x64-musl": 1.3.2
"@swc/core-win32-arm64-msvc": 1.3.2
"@swc/core-win32-ia32-msvc": 1.3.2
"@swc/core-win32-x64-msvc": 1.3.2
"@swc/core-android-arm-eabi": 1.3.3
"@swc/core-android-arm64": 1.3.3
"@swc/core-darwin-arm64": 1.3.3
"@swc/core-darwin-x64": 1.3.3
"@swc/core-freebsd-x64": 1.3.3
"@swc/core-linux-arm-gnueabihf": 1.3.3
"@swc/core-linux-arm64-gnu": 1.3.3
"@swc/core-linux-arm64-musl": 1.3.3
"@swc/core-linux-x64-gnu": 1.3.3
"@swc/core-linux-x64-musl": 1.3.3
"@swc/core-win32-arm64-msvc": 1.3.3
"@swc/core-win32-ia32-msvc": 1.3.3
"@swc/core-win32-x64-msvc": 1.3.3
dependenciesMeta:
"@swc/core-android-arm-eabi":
optional: true
@@ -3126,7 +3126,7 @@ __metadata:
optional: true
bin:
swcx: run_swcx.js
checksum: c2c83d0e6b4c56d65fb3723aa0be5e777823a6efe5b12702d4d44827a6c6dd8fac3c5dec7ff45222c7b22b4084bffc846f8497d697e61a1706817977256a65dc
checksum: bead8463cd7c11e2cd87d3835045e4a245e755409e912a40c575026a0475cc0010fa6ef48936ea5f7e62c84cdc63c21a83d61755813f2ba9b565d397fad7c5f5
languageName: node
linkType: hard
+58 -58
View File
@@ -12578,126 +12578,126 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-android-arm-eabi@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-android-arm-eabi@npm:1.3.2"
"@swc/core-android-arm-eabi@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-android-arm-eabi@npm:1.3.3"
dependencies:
"@swc/wasm": 1.2.122
conditions: os=android & cpu=arm
languageName: node
linkType: hard
"@swc/core-android-arm64@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-android-arm64@npm:1.3.2"
"@swc/core-android-arm64@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-android-arm64@npm:1.3.3"
dependencies:
"@swc/wasm": 1.2.130
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@swc/core-darwin-arm64@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-darwin-arm64@npm:1.3.2"
"@swc/core-darwin-arm64@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-darwin-arm64@npm:1.3.3"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@swc/core-darwin-x64@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-darwin-x64@npm:1.3.2"
"@swc/core-darwin-x64@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-darwin-x64@npm:1.3.3"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@swc/core-freebsd-x64@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-freebsd-x64@npm:1.3.2"
"@swc/core-freebsd-x64@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-freebsd-x64@npm:1.3.3"
dependencies:
"@swc/wasm": 1.2.130
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@swc/core-linux-arm-gnueabihf@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.2"
"@swc/core-linux-arm-gnueabihf@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.3"
dependencies:
"@swc/wasm": 1.2.130
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@swc/core-linux-arm64-gnu@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-linux-arm64-gnu@npm:1.3.2"
"@swc/core-linux-arm64-gnu@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-linux-arm64-gnu@npm:1.3.3"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
"@swc/core-linux-arm64-musl@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-linux-arm64-musl@npm:1.3.2"
"@swc/core-linux-arm64-musl@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-linux-arm64-musl@npm:1.3.3"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
"@swc/core-linux-x64-gnu@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-linux-x64-gnu@npm:1.3.2"
"@swc/core-linux-x64-gnu@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-linux-x64-gnu@npm:1.3.3"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
"@swc/core-linux-x64-musl@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-linux-x64-musl@npm:1.3.2"
"@swc/core-linux-x64-musl@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-linux-x64-musl@npm:1.3.3"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
"@swc/core-win32-arm64-msvc@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-win32-arm64-msvc@npm:1.3.2"
"@swc/core-win32-arm64-msvc@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-win32-arm64-msvc@npm:1.3.3"
dependencies:
"@swc/wasm": 1.2.130
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@swc/core-win32-ia32-msvc@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-win32-ia32-msvc@npm:1.3.2"
"@swc/core-win32-ia32-msvc@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-win32-ia32-msvc@npm:1.3.3"
dependencies:
"@swc/wasm": 1.2.130
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@swc/core-win32-x64-msvc@npm:1.3.2":
version: 1.3.2
resolution: "@swc/core-win32-x64-msvc@npm:1.3.2"
"@swc/core-win32-x64-msvc@npm:1.3.3":
version: 1.3.3
resolution: "@swc/core-win32-x64-msvc@npm:1.3.3"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@swc/core@npm:^1.2.239":
version: 1.3.2
resolution: "@swc/core@npm:1.3.2"
version: 1.3.3
resolution: "@swc/core@npm:1.3.3"
dependencies:
"@swc/core-android-arm-eabi": 1.3.2
"@swc/core-android-arm64": 1.3.2
"@swc/core-darwin-arm64": 1.3.2
"@swc/core-darwin-x64": 1.3.2
"@swc/core-freebsd-x64": 1.3.2
"@swc/core-linux-arm-gnueabihf": 1.3.2
"@swc/core-linux-arm64-gnu": 1.3.2
"@swc/core-linux-arm64-musl": 1.3.2
"@swc/core-linux-x64-gnu": 1.3.2
"@swc/core-linux-x64-musl": 1.3.2
"@swc/core-win32-arm64-msvc": 1.3.2
"@swc/core-win32-ia32-msvc": 1.3.2
"@swc/core-win32-x64-msvc": 1.3.2
"@swc/core-android-arm-eabi": 1.3.3
"@swc/core-android-arm64": 1.3.3
"@swc/core-darwin-arm64": 1.3.3
"@swc/core-darwin-x64": 1.3.3
"@swc/core-freebsd-x64": 1.3.3
"@swc/core-linux-arm-gnueabihf": 1.3.3
"@swc/core-linux-arm64-gnu": 1.3.3
"@swc/core-linux-arm64-musl": 1.3.3
"@swc/core-linux-x64-gnu": 1.3.3
"@swc/core-linux-x64-musl": 1.3.3
"@swc/core-win32-arm64-msvc": 1.3.3
"@swc/core-win32-ia32-msvc": 1.3.3
"@swc/core-win32-x64-msvc": 1.3.3
dependenciesMeta:
"@swc/core-android-arm-eabi":
optional: true
@@ -12727,7 +12727,7 @@ __metadata:
optional: true
bin:
swcx: run_swcx.js
checksum: c2c83d0e6b4c56d65fb3723aa0be5e777823a6efe5b12702d4d44827a6c6dd8fac3c5dec7ff45222c7b22b4084bffc846f8497d697e61a1706817977256a65dc
checksum: bead8463cd7c11e2cd87d3835045e4a245e755409e912a40c575026a0475cc0010fa6ef48936ea5f7e62c84cdc63c21a83d61755813f2ba9b565d397fad7c5f5
languageName: node
linkType: hard
@@ -14708,12 +14708,12 @@ __metadata:
linkType: hard
"@types/tar@npm:^6.1.1":
version: 6.1.2
resolution: "@types/tar@npm:6.1.2"
version: 6.1.3
resolution: "@types/tar@npm:6.1.3"
dependencies:
"@types/node": "*"
minipass: ^3.3.5
checksum: 57e625e2db29e3b9c6d8d06774758cf6e4caa2096f4144f7c0fdb2760e1150146d2a86f0eb80b4d2e1ba0ecf07f06303775054c4f4d22bea5d51e8b54cdd0fd8
checksum: 3a221f74adfcef8555b9c4cc951907dfd567630744ffe5211da1b44caf2a4c3b02297b73c9fb02d171e93a7a7c74fb15c1826e3f0438f0e5e8f4c790db59ddcf
languageName: node
linkType: hard