forked from Azure/azure-rest-api-specs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathliveValidation.js
157 lines (127 loc) · 6.08 KB
/
liveValidation.js
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License in the project root for license information.
'use strict';
const utils = require('../test/util/utils'),
request = require('request-promise-native'),
zlib = require('zlib');
const repoUrl = utils.getSourceRepoUrl(),
validationService = "https://app.azure-devex-tools.com/api/validations",
branch = utils.getSourceBranch(),
processingDelay = 20,
isRunningInTravisCI = process.env.MODE === 'liveValidation' && process.env.PR_ONLY === 'true',
specsPaths = utils.getFilesChangedInPR(),
regex = /resource-manager[\\|\/](.*?)[\\|\/].*?[\\|\/](.*?)[\\|\/]/,
successThreshold = 90,
validationModels = new Map();
let durationInSeconds = parseInt(process.env.LIVE_VALIDATION_DURATION_IN_MINUTES) * 60;
if (isNaN(durationInSeconds)) {
durationInSeconds = 180;
}
async function runScript() {
// See whether script is in Travis CI context
console.log(`isRunningInTraviSCI: ${isRunningInTravisCI}`);
for (const specPath of specsPaths) {
let matchResult = specPath.match(regex);
if (matchResult === null) {
continue;
}
let resourceProvider = matchResult[1];
let apiVersion = matchResult[2];
if (!validationModels.has(resourceProvider)) {
validationModels.set(resourceProvider, new Set());
}
validationModels.get(resourceProvider).add(apiVersion);
}
if (validationModels.size === 0) {
console.log("Change didn't affect any swagger specs. No validation to be done.");
return;
} else if (validationModels.size > 1) {
console.log("WARNING: Multiple resource provider have changes, only the first one will be validated.");
}
let resourceProvider = validationModels.keys().next().value;
if (validationModels.get(resourceProvider).size > 1) {
console.log("WARNING: Multiple api versions have changes, only the first one will be validated.");
}
let apiVersion = validationModels.get(resourceProvider).values().next().value;
console.log(`Changes detected in a swagger spec.`);
console.log(`RP is: ${resourceProvider}`);
console.log(`ApiVersion is: ${apiVersion}`);
console.log(`Source repo is: ${repoUrl}`);
console.log(`Branch is: ${branch}`);
console.log(`Making the request to the validation service...`);
let response = await request.post(validationService).form({
repoUrl: repoUrl,
branch: branch,
resourceProvider: resourceProvider,
apiVersion: apiVersion,
duration: durationInSeconds
});
let validationId = JSON.parse(response).validationId;
let validationResultUrl = `${validationService}/${validationId}`;
console.log(`Request done, results will be available in ${durationInSeconds} seconds...`);
await timeout((durationInSeconds + processingDelay) * 1000);
let validationResult = JSON.parse(await request(validationResultUrl));
console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
console.log(`Results of validation ${validationId}:`);
let analyticsUrl = await createAnalyticsLink(validationId);
let failingOperations = [];
let noTrafficOperations = [];
for (const [operationId, operationResult] of Object.entries(validationResult.operationResults)) {
if (operationResult.operationCount === 0) {
noTrafficOperations.push(operationResult.operationId)
} else if (operationResult.successRate < successThreshold) {
failingOperations.push(operationResult.operationId);
}
console.log(JSON.stringify(operationResult));
}
console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
if (validationResult.totalOperationCount === 0) {
console.log(`There was no traffic detected for the provided RP and API version:${resourceProvider}-${apiVersion}. Please make sure there is traffic so the changes can be validated.`);
process.exitCode = 1;
} else if (failingOperations.length > 0 || noTrafficOperations.length > 0) {
console.log(`The changes in the specs introduced by this PR potentially do not reflect the Service API.`);
console.log(`Active traffic and success rate > ${successThreshold}% FOR EACH OPERATION is required. Please review the following operations before moving forward.`);
console.log(`SUCCESS RATE < ${successThreshold}%:
${JSON.stringify(failingOperations)}`);
if (noTrafficOperations.length > 0) {
console.log(`NO TRAFFIC:
${JSON.stringify(noTrafficOperations)}
`);
}
console.log(`To inspect the individual failures go to the url (add '| where customDimensions.operationId == "<OPERATION_ID>"' to filter for individual operations.):
${analyticsUrl}
`);
process.exitCode = 1;
} else {
console.log(`SUCCESS RATE: ${validationResult.successRate} > ${successThreshold}. You can move forward.`);
}
}
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function createAnalyticsLink(validationId) {
return new Promise(resolve => {
const query = `
traces
| where customDimensions.validationId == "${validationId}"
| where customDimensions.logType == "data"
| where customDimensions.isSuccess == "false"
| project timestamp, message, customDimensions
`;
zlib.deflate(query, (err, buffer) => {
if (!err) {
let queryParams = buffer.toString('base64');
let analyticsLink = `https://analytics.applicationinsights.io/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourcegroups/openapi-platform-logs/components/openapiAI?q=${queryParams}&apptype=Node.JS×pan=P1D`;
resolve(analyticsLink);
}
});
});
}
runScript().then(success => {
console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
console.log(`Thanks for using live validation.`);
console.log(`If you encounter any issue(s), please open issue(s) at https://github.com/Azure/openapi-platform/issues .`);
}).catch(err => {
console.log(err);
process.exitCode = 1;
});