123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- // Copyright 2022 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- 'use strict';
- const process = require('child_process');
- const https = require('https');
- function log(msg) {
- // console.log(msg);
- }
- class CrBugUser {
- constructor(json) {
- this.name_ = json.displayName;
- this.id_ = json.name;
- this.email_ = json.email;
- }
- get name() {
- return this.name_;
- }
- get id() {
- return this.id_;
- }
- get email() {
- return this.email_;
- }
- };
- class CrBugIssue {
- constructor(json) {
- this.number_ = json.name;
- this.reporter_id_ = json.reporter;
- this.owner_id_ = json.owner ? json.owner.user : undefined;
- this.last_update_ = json.modifyTime;
- this.close_ = json.closeTime ? new Date(json.closeTime) : undefined;
- this.url_ = undefined;
- const parts = this.number_.split('/');
- if (parts[0] === 'projects' && parts[2] === 'issues') {
- const project = parts[1];
- const num = parts[3];
- this.url_ =
- `https://bugs.chromium.org/p/${project}/issues/detail?id=${num}`;
- }
- }
- get number() {
- return this.number_;
- }
- get owner_id() {
- return this.owner_id_;
- }
- get reporter_id() {
- return this.reporter_id_;
- }
- get url() {
- return this.url_;
- }
- };
- class CrBugComment {
- constructor(json) {
- this.user_id_ = json.commenter;
- this.timestamp_ = new Date(json.createTime);
- this.timestamp_.setSeconds(0);
- this.content_ = json.content;
- this.fields_ = json.amendments ?
- json.amendments.map(m => m.fieldName.toLowerCase()) :
- undefined;
- this.json_ = JSON.stringify(json);
- }
- get user_id() {
- return this.user_id_;
- }
- get timestamp() {
- return this.timestamp_;
- }
- get content() {
- return this.content_;
- }
- get updatedFields() {
- return this.fields_;
- }
- isActivity() {
- if (this.content)
- return true;
- const fields = this.updatedFields;
- // If bug A gets merged into bug B, then ignore the update for bug A. There
- // will also be an update for bug B, and that will be counted instead.
- if (fields && fields.indexOf('mergedinto') >= 0) {
- return false;
- }
- // If bug A is marked as blocked on bug B, then that triggers updates for
- // both bugs. So only count 'blockedon', and ignore 'blocking'.
- const allowedFields = [
- 'blockedon', 'cc', 'components', 'label', 'owner', 'priority', 'status',
- 'summary'
- ];
- if (fields && fields.some(f => allowedFields.indexOf(f) >= 0)) {
- return true;
- }
- return false;
- }
- };
- class CrBug {
- constructor(project) {
- this.token_ = this.getAuthToken_();
- this.project_ = project;
- }
- getAuthToken_() {
- const scope = 'https://www.googleapis.com/auth/userinfo.email';
- const args = [
- 'luci-auth', 'token', '-use-id-token', '-audience',
- 'https://monorail-prod.appspot.com', '-scopes', scope, '-json-output', '-'
- ];
- const stdout = process.execSync(args.join(' ')).toString().trim();
- const json = JSON.parse(stdout);
- return json.token;
- }
- async fetchFromServer_(path, message) {
- const hostname = 'api-dot-monorail-prod.appspot.com';
- return new Promise((resolve, reject) => {
- const postData = JSON.stringify(message);
- const options = {
- hostname: hostname,
- method: 'POST',
- path: path,
- headers: {
- 'Content-Type': 'application/json',
- 'Accept': 'application/json',
- 'Authorization': `Bearer ${this.token_}`,
- }
- };
- let data = '';
- const req = https.request(options, (res) => {
- log(`STATUS: ${res.statusCode}`);
- log(`HEADERS: ${JSON.stringify(res.headers)}`);
- res.setEncoding('utf8');
- res.on('data', (chunk) => {
- log(`BODY: ${chunk}`);
- data += chunk;
- });
- res.on('end', () => {
- if (data.startsWith(')]}\'')) {
- resolve(JSON.parse(data.substr(4)));
- } else {
- resolve(data);
- }
- });
- });
- req.on('error', (e) => {
- console.error(`problem with request: ${e.message}`);
- reject(e.message);
- });
- // Write data to request body
- log(`Writing ${postData}`);
- req.write(postData);
- req.end();
- });
- }
- /**
- * Calls SearchIssues with the given parameters.
- *
- * @param {string} query The query to use to search.
- * @param {Number} pageSize The maximum issues to return.
- * @param {string} pageToken The page token from the previous call.
- *
- * @return {JSON}
- */
- async searchIssuesPagination_(query, pageSize, pageToken) {
- const message = {
- 'projects': [this.project_],
- 'query': query,
- 'pageToken': pageToken,
- };
- if (pageSize) {
- message['pageSize'] = pageSize;
- }
- const url = '/prpc/monorail.v3.Issues/SearchIssues';
- return this.fetchFromServer_(url, message);
- }
- /**
- * Searches Monorail for issues using the given query.
- * TODO(crbug.com/monorail/7143): SearchIssues only accepts one project.
- *
- * @param {string} query The query to use to search.
- *
- * @return {Array<CrBugIssue>}
- */
- async search(query) {
- const pageSize = 100;
- let pageToken;
- let issues = [];
- do {
- const resp =
- await this.searchIssuesPagination_(query, pageSize, pageToken);
- if (resp.issues) {
- issues = issues.concat(resp.issues.map(i => new CrBugIssue(i)));
- }
- pageToken = resp.nextPageToken;
- } while (pageToken);
- return issues;
- }
- /**
- * Calls ListComments with the given parameters.
- *
- * @param {string} issueName Resource name of the issue.
- * @param {string} filter The approval filter query.
- * @param {Number} pageSize The maximum number of comments to return.
- * @param {string} pageToken The page token from the previous request.
- *
- * @return {JSON}
- */
- async listCommentsPagination_(issueName, pageToken, pageSize) {
- const message = {
- 'parent': issueName,
- 'pageToken': pageToken,
- 'filter': '',
- };
- if (pageSize) {
- message['pageSize'] = pageSize;
- }
- const url = '/prpc/monorail.v3.Issues/ListComments';
- return this.fetchFromServer_(url, message);
- }
- /**
- * Returns all comments and previous/current descriptions of an issue.
- *
- * @param {CrBugIssue} issue The CrBugIssue instance.
- *
- * @return {Array<CrBugComment>}
- */
- async getComments(issue) {
- let pageToken;
- let comments = [];
- do {
- const resp = await this.listCommentsPagination_(issue.number, pageToken);
- if (resp.comments) {
- comments = comments.concat(resp.comments.map(c => new CrBugComment(c)));
- }
- pageToken = resp.nextPageToken;
- } while (pageToken);
- return comments;
- }
- /**
- * Returns the user associated with 'username'.
- *
- * @param {string} username The username (e.g. linus@chromium.org).
- *
- * @return {CrBugUser}
- */
- async getUser(username) {
- const url = '/prpc/monorail.v3.Users/GetUser';
- const message = {
- name: `users/${username}`,
- };
- return new CrBugUser(await this.fetchFromServer_(url, message));
- }
- };
- module.exports = {
- CrBug,
- };
|