Refactoring of the Checks JavaScript plugin code Remove `reboot`. Rename `gr-checks.js` to `plugin.js`. Move fetcher into `plugin.js`. Move utility functions into separate `util.js` file. Change-Id: I859baf98e7366e9b3297811142524e141d1bbaea
diff --git a/gr-checks/BUILD b/gr-checks/BUILD index 8eee11e..7a1143b 100644 --- a/gr-checks/BUILD +++ b/gr-checks/BUILD
@@ -10,5 +10,5 @@ gerrit_js_bundle( name = "checks", srcs = glob(["*.js"]), - entry_point = "gr-checks.js", + entry_point = "plugin.js", )
diff --git a/gr-checks/gr-checks-reboot.js b/gr-checks/gr-checks-reboot.js deleted file mode 100644 index 1e7bdc2..0000000 --- a/gr-checks/gr-checks-reboot.js +++ /dev/null
@@ -1,164 +0,0 @@ -/** - * @license - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Heads up! Everything in this file is still in flux. The new reboot checks API - * is still in development. So everything in this file can change. And it is - * expected that the amount of comments and tests is limited for the time being. - */ - -function pluralize(count, noun) { - if (count === 0) return ''; - return `${count} ${noun}` + (count > 1 ? 's' : ''); -} - -function generateDurationString(startTime, endTime) { - const secondsAgo = Math.round((endTime - startTime) / 1000); - - if (secondsAgo === 0) { - return ZERO_SECONDS; - } - - const durationSegments = []; - if (secondsAgo % 60 !== 0) { - durationSegments.push(`${secondsAgo % 60} sec`); - } - const minutesAgo = Math.floor(secondsAgo / 60); - if (minutesAgo % 60 !== 0) { - durationSegments.push(`${minutesAgo % 60} min`); - } - const hoursAgo = Math.floor(minutesAgo / 60); - if (hoursAgo % 24 !== 0) { - const hours = pluralize(hoursAgo % 24, 'hour', 'hours'); - durationSegments.push(`${hours}`); - } - const daysAgo = Math.floor(hoursAgo / 24); - if (daysAgo % 30 !== 0) { - const days = pluralize(daysAgo % 30, 'day', 'days'); - durationSegments.push(`${days}`); - } - const monthsAgo = Math.floor(daysAgo / 30); - if (monthsAgo > 0) { - const months = pluralize(monthsAgo, 'month', 'months'); - durationSegments.push(`${months}`); - } - return durationSegments.reverse().slice(0, 2).join(' '); -} - -function computeDuration(check) { - if (!check.started || !check.finished) { - return '-'; - } - const startTime = new Date(check.started); - const finishTime = check.finished ? new Date(check.finished) : new Date(); - return generateDurationString(startTime, finishTime); -} - -export class RebootFetcher { - constructor(restApi) { - this.restApi = restApi; - } - - async fetchCurrent() { - return this.fetch(this.changeNumber, this.patchsetNumber); - } - - async fetch(changeData) { - const {changeNumber, patchsetNumber} = changeData; - this.changeNumber = changeNumber; - this.patchsetNumber = patchsetNumber; - const checks = await this.apiGet('?o=CHECKER'); - return { - responseCode: 'OK', - runs: checks.map(check => this.convert(check)), - }; - } - - async apiGet(suffix) { - return this.restApi.get( - '/changes/' + this.changeNumber + '/revisions/' + this.patchsetNumber - + '/checks' + suffix); - } - - async apiPost(suffix) { - return this.restApi.post( - '/changes/' + this.changeNumber + '/revisions/' + this.patchsetNumber - + '/checks' + suffix); - } - - /** - * Converts a Checks Plugin CheckInfo object into a Reboot Checks API Run - * object. - * - * TODO(brohlfs): Refine this conversion and add tests. - */ - convert(check) { - let status = 'RUNNABLE'; - if (check.state === 'RUNNING' || check.state === 'SCHEDULED') { - status = 'RUNNING'; - } else if (check.state === 'FAILED' || check.state === 'SUCCESSFUL') { - status = 'COMPLETED'; - } - const run = { - checkName: check.checker_name, - checkDescription: check.checker_description, - externalId: check.checker_uuid, - status, - }; - if (check.started) run.startedTimestamp = new Date(check.started); - if (check.finished) run.finishedTimestamp = new Date(check.finished); - if (status === 'RUNNING') { - run.statusDescription = check.message; - if (check.url) { - run.statusLink = check.url; - } - } else if (check.state === 'SUCCESSFUL') { - run.statusDescription = - check.message || `Passed (${computeDuration(check)})`; - if (check.url) { - run.statusLink = check.url; - } - } else if (check.state === 'FAILED') { - run.results = [{ - category: 'ERROR', - summary: check.message || `Failed (${computeDuration(check)})`, - }]; - if (check.url) { - run.results[0].links = [{ - url: check.url, - primary: true, - icon: 'EXTERNAL', - }]; - } - } - if (status !== 'RUNNING') { - run.actions = [{ - name: 'Run', - primary: true, - callback: () => this.run(check.checker_uuid), - }]; - } - return run; - } - - run(uuid) { - return this.apiPost('/' + uuid + '/rerun') - .catch(e => { - return {errorMessage: `Triggering the run failed: ${e.message}`}; - }); - } -}
diff --git a/gr-checks/gr-checks.js b/gr-checks/gr-checks.js deleted file mode 100644 index d72e920..0000000 --- a/gr-checks/gr-checks.js +++ /dev/null
@@ -1,25 +0,0 @@ -/** - * @license - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import {RebootFetcher} from './gr-checks-reboot.js'; - -Gerrit.install(plugin => { - const checksApi = plugin.checks(); - const fetcher = new RebootFetcher(plugin.restApi()); - checksApi.register({ - fetch: data => fetcher.fetch(data), - }); -});
diff --git a/gr-checks/plugin.js b/gr-checks/plugin.js new file mode 100644 index 0000000..f8a0a35 --- /dev/null +++ b/gr-checks/plugin.js
@@ -0,0 +1,117 @@ +/** + * @license + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {computeDuration} from './util.js'; + +class ChecksFetcher { + constructor(restApi) { + this.restApi = restApi; + } + + async fetchCurrent() { + return this.fetch(this.changeNumber, this.patchsetNumber); + } + + async fetch(changeData) { + const {changeNumber, patchsetNumber} = changeData; + this.changeNumber = changeNumber; + this.patchsetNumber = patchsetNumber; + const checks = await this.apiGet('?o=CHECKER'); + return { + responseCode: 'OK', + runs: checks.map(check => this.convert(check)), + }; + } + + async apiGet(suffix) { + return this.restApi.get( + '/changes/' + this.changeNumber + '/revisions/' + this.patchsetNumber + + '/checks' + suffix); + } + + async apiPost(suffix) { + return this.restApi.post( + '/changes/' + this.changeNumber + '/revisions/' + this.patchsetNumber + + '/checks' + suffix); + } + + /** + * Converts a Checks Plugin CheckInfo object into a Checks API Run object. + */ + convert(check) { + let status = 'RUNNABLE'; + if (check.state === 'RUNNING' || check.state === 'SCHEDULED') { + status = 'RUNNING'; + } else if (check.state === 'FAILED' || check.state === 'SUCCESSFUL') { + status = 'COMPLETED'; + } + const run = { + checkName: check.checker_name, + checkDescription: check.checker_description, + externalId: check.checker_uuid, + status, + }; + if (check.started) run.startedTimestamp = new Date(check.started); + if (check.finished) run.finishedTimestamp = new Date(check.finished); + if (status === 'RUNNING') { + run.statusDescription = check.message; + if (check.url) { + run.statusLink = check.url; + } + } else if (check.state === 'SUCCESSFUL') { + run.statusDescription = + check.message || `Passed (${computeDuration(check)})`; + if (check.url) { + run.statusLink = check.url; + } + } else if (check.state === 'FAILED') { + run.results = [{ + category: 'ERROR', + summary: check.message || `Failed (${computeDuration(check)})`, + }]; + if (check.url) { + run.results[0].links = [{ + url: check.url, + primary: true, + icon: 'EXTERNAL', + }]; + } + } + if (status !== 'RUNNING') { + run.actions = [{ + name: 'Run', + primary: true, + callback: () => this.run(check.checker_uuid), + }]; + } + return run; + } + + run(uuid) { + return this.apiPost('/' + uuid + '/rerun') + .catch(e => { + return {errorMessage: `Triggering the run failed: ${e.message}`}; + }); + } +} + +Gerrit.install(plugin => { + const checksApi = plugin.checks(); + const fetcher = new ChecksFetcher(plugin.restApi()); + checksApi.register({ + fetch: data => fetcher.fetch(data), + }); +});
diff --git a/gr-checks/util.js b/gr-checks/util.js new file mode 100644 index 0000000..478124c --- /dev/null +++ b/gr-checks/util.js
@@ -0,0 +1,60 @@ +/** + * @license + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +function pluralize(count, noun) { + if (count === 0) return ''; + return `${count} ${noun}` + (count > 1 ? 's' : ''); +} + +function generateDurationString(startTime, endTime) { + const secondsAgo = Math.round((endTime - startTime) / 1000); + if (secondsAgo === 0) { + return '0 sec'; + } + const durationSegments = []; + if (secondsAgo % 60 !== 0) { + durationSegments.push(`${secondsAgo % 60} sec`); + } + const minutesAgo = Math.floor(secondsAgo / 60); + if (minutesAgo % 60 !== 0) { + durationSegments.push(`${minutesAgo % 60} min`); + } + const hoursAgo = Math.floor(minutesAgo / 60); + if (hoursAgo % 24 !== 0) { + const hours = pluralize(hoursAgo % 24, 'hour', 'hours'); + durationSegments.push(`${hours}`); + } + const daysAgo = Math.floor(hoursAgo / 24); + if (daysAgo % 30 !== 0) { + const days = pluralize(daysAgo % 30, 'day', 'days'); + durationSegments.push(`${days}`); + } + const monthsAgo = Math.floor(daysAgo / 30); + if (monthsAgo > 0) { + const months = pluralize(monthsAgo, 'month', 'months'); + durationSegments.push(`${months}`); + } + return durationSegments.reverse().slice(0, 2).join(' '); +} + +export function computeDuration(check) { + if (!check.started || !check.finished) { + return '-'; + } + const startTime = new Date(check.started); + const finishTime = check.finished ? new Date(check.finished) : new Date(); + return generateDurationString(startTime, finishTime); +}