Merge "Allow assigning code ownership to all users"
diff --git a/ui/code-owners-service.js b/ui/code-owners-service.js
index ae7b2f4..15bc3e0 100644
--- a/ui/code-owners-service.js
+++ b/ui/code-owners-service.js
@@ -64,7 +64,8 @@
*/
listOwnersForPath(project, branch, path) {
return this.restApi.get(
- `/projects/${project}/branches/${branch}/code_owners/${encodeURIComponent(path)}?limit=5&o=DETAILS`
+ `/projects/${project}/branches/${branch}/` +
+ `code_owners/${encodeURIComponent(path)}?limit=5&o=DETAILS`
);
}
@@ -134,8 +135,9 @@
return this.getStatus()
.then(({codeOwnerStatusMap}) => {
// only fetch those not approved yet
- let filesToFetchOwners = [...codeOwnerStatusMap.keys()].filter(
- file => codeOwnerStatusMap.get(file).status !== OwnerStatus.APPROVED
+ const filesToFetchOwners = [...codeOwnerStatusMap.keys()].filter(
+ file => codeOwnerStatusMap
+ .get(file).status !== OwnerStatus.APPROVED
);
return this.batchFetchCodeOwners(filesToFetchOwners)
.then(ownersMap =>
@@ -188,7 +190,10 @@
const ownersFilesMap = new Map();
const failedToFetchFiles = new Set();
for (let i = 0; i < allFiles.length; i++) {
- const fileInfo = {path: allFiles[i], status: this._computeFileStatus(codeOwnerStatusMap, allFiles[i])};
+ const fileInfo = {
+ path: allFiles[i],
+ status: this._computeFileStatus(codeOwnerStatusMap, allFiles[i]),
+ };
// for files failed to fetch, add them to the special group
if (fileOwnersMap[fileInfo.path].error) {
failedToFetchFiles.add(fileInfo);
@@ -200,7 +205,10 @@
.map(account => account._account_id)
.sort()
.join(',');
- ownersFilesMap.set(ownersKey, ownersFilesMap.get(ownersKey) || {files: [], owners});
+ ownersFilesMap.set(
+ ownersKey,
+ ownersFilesMap.get(ownersKey) || {files: [], owners}
+ );
ownersFilesMap.get(ownersKey).files.push(fileInfo);
}
const groupedItems = [];
@@ -218,7 +226,7 @@
groupedItems.push({
groupName: this.getGroupName(failedFiles),
files: failedFiles,
- error: new Error("Failed to fetch owner info")
+ error: new Error('Failed to fetch owner info'),
});
}
@@ -227,9 +235,10 @@
getGroupName(files) {
const fileName = files[0].path.split('/').pop();
- return `${
- files.length > 1 ? `(${files.length} files) ${fileName}, ...` : fileName
- }`;
+ return {
+ name: fileName,
+ prefix: files.length > 1 ? `+ ${files.length - 1} more` : '',
+ };
}
/**
diff --git a/ui/owner-requirement.js b/ui/owner-requirement.js
index 111b9d9..140ccda 100644
--- a/ui/owner-requirement.js
+++ b/ui/owner-requirement.js
@@ -56,7 +56,9 @@
<template is="dom-if" if="[[!isLoading]]">
<span>[[statusText]]</span>
<template is="dom-if" if="[[!allApproved]]">
- <gr-button link on-click="_openReplyDialog">Suggest owners</gr-button>
+ <gr-button link on-click="_openReplyDialog">
+ Suggest owners
+ </gr-button>
</template>
</template>
`;
@@ -85,7 +87,8 @@
.then(({rawStatuses}) => {
const statusText = [];
const statusCount = this._computeStatusCount(rawStatuses);
- this.allApproved = statusCount.missing === 0 && statusCount.pending === 0;
+ this.allApproved = statusCount.missing === 0
+ && statusCount.pending === 0;
if (statusCount.missing) {
statusText.push(`${statusCount.missing} missing`);
}
diff --git a/ui/owner-status-column.js b/ui/owner-status-column.js
index d413272..1e5e9fe 100644
--- a/ui/owner-status-column.js
+++ b/ui/owner-status-column.js
@@ -206,7 +206,7 @@
this.status = newPathStatus;
} else {
this.status = newPathStatus === STATUS_CODE.APPROVED
- ? this._computeStatus(oldPathStatus, /* oldPath=*/ true)
+ ? this._computeStatus(oldPathStatus, /* oldPath= */ true)
: newPathStatus;
}
})
diff --git a/ui/plugin.js b/ui/plugin.js
index e77e406..4ac253a 100644
--- a/ui/plugin.js
+++ b/ui/plugin.js
@@ -15,9 +15,10 @@
* limitations under the License.
*/
-import {SuggestOwners, SuggestOwnersTrigger} from './suggest-owners.js';
+import {SuggestOwners} from './suggest-owners.js';
import {OwnerStatusColumnContent, OwnerStatusColumnHeader} from './owner-status-column.js';
import {OwnerRequirementValue} from './owner-requirement.js';
+import {SuggestOwnersTrigger} from './suggest-owners-trigger.js';
Gerrit.install(plugin => {
const ENABLED_EXPERIMENTS = window.ENABLED_EXPERIMENTS || [];
diff --git a/ui/suggest-owners-trigger.js b/ui/suggest-owners-trigger.js
new file mode 100644
index 0000000..5ef18d6
--- /dev/null
+++ b/ui/suggest-owners-trigger.js
@@ -0,0 +1,99 @@
+/**
+ * @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 {CodeOwnerService, OwnerStatus} from './code-owners-service.js';
+import {ownerState} from './owner-ui-state.js';
+
+export class SuggestOwnersTrigger extends Polymer.Element {
+ static get is() {
+ return 'suggest-owners-trigger';
+ }
+
+ static get properties() {
+ return {
+ change: Object,
+ expanded: {
+ type: Boolean,
+ value: false,
+ },
+ restApi: Object,
+ hidden: {
+ type: Boolean,
+ value: true,
+ reflectToAttribute: true,
+ },
+ };
+ }
+
+ static get observers() {
+ return ['onInputChanged(restApi, change)'];
+ }
+
+ static get template() {
+ return Polymer.html`
+ <style include="shared-styles">
+ iron-icon {
+ padding-left: var(--spacing-m);
+ }
+ </style>
+ <gr-button
+ on-click="toggleControlContent"
+ has-tooltip
+ title="Suggest owners for your change"
+ >
+ [[computeButtonText(expanded)]]
+ <iron-icon icon="gr-icons:info-outline"></iron-icon>
+ </gr-button>
+ `;
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ ownerState.onExpandSuggestionChange(expanded => {
+ this.expanded = expanded;
+ });
+ }
+
+ onInputChanged(restApi, change) {
+ if ([restApi, change].includes(undefined)) return;
+ this.ownerService = CodeOwnerService.getOwnerService(
+ this.restApi,
+ this.change
+ );
+ this.ownerService.getStatus().then(({rawStatuses}) => {
+ const notAllApproved = rawStatuses.some(status => {
+ const oldPathStatus = status.old_path_status;
+ const newPathStatus = status.new_path_status;
+ if (newPathStatus.status !== OwnerStatus.APPROVED) {
+ return true;
+ }
+ return oldPathStatus && oldPathStatus.status !== OwnerStatus.APPROVED;
+ });
+ this.hidden = !notAllApproved;
+ });
+ }
+
+ toggleControlContent() {
+ this.expanded = !this.expanded;
+ ownerState.expandSuggestion = this.expanded;
+ }
+
+ computeButtonText(expanded) {
+ return expanded ? 'Hide owners' : 'Suggest owners';
+ }
+}
+
+customElements.define(SuggestOwnersTrigger.is, SuggestOwnersTrigger);
\ No newline at end of file
diff --git a/ui/suggest-owners.js b/ui/suggest-owners.js
index 7d6ad6a..ca38cda 100644
--- a/ui/suggest-owners.js
+++ b/ui/suggest-owners.js
@@ -14,91 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {CodeOwnerService, OwnerStatus, RenamedFileChip} from './code-owners-service.js';
+import {CodeOwnerService, RenamedFileChip} from './code-owners-service.js';
import {ownerState} from './owner-ui-state.js';
-export class SuggestOwnersTrigger extends Polymer.Element {
- static get is() {
- return 'suggest-owners-trigger';
- }
-
- static get properties() {
- return {
- change: Object,
- expanded: {
- type: Boolean,
- value: false,
- },
- restApi: Object,
- hidden: {
- type: Boolean,
- value: true,
- reflectToAttribute: true,
- },
- };
- }
-
-
- static get observers() {
- return [
- 'onInputChanged(restApi, change)',
- ];
- }
-
- static get template() {
- return Polymer.html`
- <style include="shared-styles">
- iron-icon {
- padding-left: var(--spacing-m);
- }
- </style>
- <gr-button
- on-click="toggleControlContent"
- has-tooltip
- title="Suggest owners for your change"
- >
- [[computeButtonText(expanded)]]
- <iron-icon icon="gr-icons:info-outline"></iron-icon>
- </gr-button>
- `;
- }
-
- connectedCallback() {
- super.connectedCallback();
- ownerState.onExpandSuggestionChange(expanded => {
- this.expanded = expanded;
- });
- }
-
- onInputChanged(restApi, change) {
- if ([restApi, change].includes(undefined)) return;
- this.ownerService = CodeOwnerService
- .getOwnerService(this.restApi, this.change);
- this.ownerService.getStatus().then(({rawStatuses}) => {
- const notAllApproved = rawStatuses.some(status => {
- const oldPathStatus = status.old_path_status;
- const newPathStatus = status.new_path_status;
- if (newPathStatus.status !== OwnerStatus.APPROVED) {
- return true;
- }
- return oldPathStatus && oldPathStatus.status !== OwnerStatus.APPROVED;
- });
- this.hidden = !notAllApproved;
- });
- }
-
- toggleControlContent() {
- this.expanded = !this.expanded;
- ownerState.expandSuggestion = this.expanded;
- }
-
- computeButtonText(expanded) {
- return expanded ? 'Hide owners' : 'Suggest owners';
- }
-}
-
-customElements.define(SuggestOwnersTrigger.is, SuggestOwnersTrigger);
-
class OwnerGroupFileList extends Polymer.Element {
static get is() {
return 'owner-group-file-list';
@@ -121,10 +39,10 @@
color: var(--primary-text-color);
font-size: var(--font-size-small);
}
- span.renamed-old {
+ .renamed-old {
background-color: var(--dark-remove-highlight-color);
}
- span.renamed-new {
+ .renamed-new {
background-color: var(--dark-add-highlight-color);
}
</style>
@@ -138,7 +56,9 @@
[[computeFilePath(file)]]<!--
--><strong>[[computeFileName(file)]]</strong>
<template is="dom-if" if="[[file.status]]">
- <span class$="[[computeStatusClass(file)]]">[[computeFileStatus(file)]]</span>
+ <span class$="[[computeStatusClass(file)]]">
+ [[computeFileStatus(file)]]
+ </span>
</template>
</li>
</template>
@@ -147,12 +67,12 @@
}
computeFilePath(file) {
- const parts = file.path.split("/");
- return parts.slice(0, parts.length - 2).join("/") + "/";
+ const parts = file.path.split('/');
+ return parts.slice(0, parts.length - 2).join('/') + '/';
}
computeFileName(file) {
- const parts = file.path.split("/");
+ const parts = file.path.split('/');
return parts.pop();
}
@@ -164,7 +84,6 @@
if (!file.status) return '';
return file.status === RenamedFileChip.NEW ? 'renamed-new' : 'renamed-old';
}
-
}
customElements.define(OwnerGroupFileList.is, OwnerGroupFileList);
@@ -209,13 +128,17 @@
.suggestion-row:hover {
background: var(--hover-background-color);
}
- .suggestion-grou-name {
+ .suggestion-group-name {
width: 200px;
+ line-height: 26px;
text-overflow: ellipsis;
overflow: hidden;
padding-right: var(--spacing-l);
white-space: nowrap;
}
+ .group-name-prefix {
+ color: var(--deemphasized-text-color);
+ }
.suggested-owners {
flex: 1;
}
@@ -252,9 +175,16 @@
index-as="suggestionIndex"
>
<li class="suggestion-row">
- <div class="suggestion-grou-name">
+ <div class="suggestion-group-name">
<span>
- [[suggestion.groupName]]
+ <template is="dom-if" if="[[suggestion.groupName.prefix]]">
+ <span class="group-name-prefix">
+ ([[suggestion.groupName.prefix]])
+ </span>
+ </template>
+ <span>
+ [[suggestion.groupName.name]]
+ </span>
<gr-hovercard hidden="[[suggestion.expanded]]">
<owner-group-file-list
files="[[suggestion.files]]"
@@ -271,6 +201,9 @@
[[suggestion.error]]
</template>
<template is="dom-if" if="[[!suggestion.error]]">
+ <template is="dom-if" if="[[!suggestion.owners.length]]">
+ All owners are not visible to you or missing required permissions.
+ </template>
<ul class="suggested-owners">
<template
is="dom-repeat"
@@ -404,7 +337,8 @@
toggleAccount(e) {
const grAccountLabel = e.currentTarget;
- const owner = this.suggestedOwners[grAccountLabel.dataset.suggestionIndex].owners[grAccountLabel.dataset.ownerIndex];
+ const owner = this.suggestedOwners[grAccountLabel.dataset.suggestionIndex]
+ .owners[grAccountLabel.dataset.ownerIndex];
if (this.isSelected(owner)) {
this.removeAccount(owner);
} else {
@@ -418,7 +352,8 @@
this.suggestedOwners.forEach((suggestion, sId) => {
suggestion.owners.forEach((owner, oId) => {
if (
- accounts.some(account => account._account_id === owner.account._account_id)
+ accounts.some(account => account._account_id
+ === owner.account._account_id)
) {
this.set(
['suggestedOwners', sId, 'owners', oId],