Merge changes from topic "gr-group-*-to-ts"
* changes:
Convert gr-group-* to typescript
Rename files to preserve history
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
deleted file mode 100644
index 259a302..0000000
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
+++ /dev/null
@@ -1,138 +0,0 @@
-/**
- * @license
- * Copyright (C) 2017 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 '../../../styles/gr-table-styles.js';
-import '../../../styles/shared-styles.js';
-import '../../shared/gr-date-formatter/gr-date-formatter.js';
-import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
-import '../../shared/gr-account-link/gr-account-link.js';
-import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
-import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
-import {PolymerElement} from '@polymer/polymer/polymer-element.js';
-import {htmlTemplate} from './gr-group-audit-log_html.js';
-import {ListViewMixin} from '../../../mixins/gr-list-view-mixin/gr-list-view-mixin.js';
-import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
-
-const GROUP_EVENTS = ['ADD_GROUP', 'REMOVE_GROUP'];
-
-/**
- * @extends PolymerElement
- */
-class GrGroupAuditLog extends ListViewMixin(GestureEventListeners(
- LegacyElementMixin(
- PolymerElement))) {
- static get template() { return htmlTemplate; }
-
- static get is() { return 'gr-group-audit-log'; }
-
- static get properties() {
- return {
- groupId: String,
- _auditLog: Array,
- _loading: {
- type: Boolean,
- value: true,
- },
- };
- }
-
- /** @override */
- attached() {
- super.attached();
- this.dispatchEvent(new CustomEvent('title-change', {
- detail: {title: 'Audit Log'},
- composed: true, bubbles: true,
- }));
- }
-
- /** @override */
- ready() {
- super.ready();
- this._getAuditLogs();
- }
-
- _getAuditLogs() {
- if (!this.groupId) { return ''; }
-
- const errFn = response => {
- this.dispatchEvent(new CustomEvent('page-error', {
- detail: {response},
- composed: true, bubbles: true,
- }));
- };
-
- return this.$.restAPI.getGroupAuditLog(this.groupId, errFn)
- .then(auditLog => {
- if (!auditLog) {
- this._auditLog = [];
- return;
- }
- this._auditLog = auditLog;
- this._loading = false;
- });
- }
-
- _status(item) {
- return item.disabled ? 'Disabled' : 'Enabled';
- }
-
- itemType(type) {
- let item;
- switch (type) {
- case 'ADD_GROUP':
- case 'ADD_USER':
- item = 'Added';
- break;
- case 'REMOVE_GROUP':
- case 'REMOVE_USER':
- item = 'Removed';
- break;
- default:
- item = '';
- }
- return item;
- }
-
- _isGroupEvent(type) {
- return GROUP_EVENTS.indexOf(type) !== -1;
- }
-
- _computeGroupUrl(group) {
- if (group && group.url && group.id) {
- return GerritNav.getUrlForGroup(group.id);
- }
-
- return '';
- }
-
- _getIdForUser(account) {
- return account._account_id ? ' (' + account._account_id + ')' : '';
- }
-
- _getNameForGroup(group) {
- if (group && group.name) {
- return group.name;
- } else if (group && group.id) {
- // The URL encoded id of the member
- return decodeURIComponent(group.id);
- }
-
- return '';
- }
-}
-
-customElements.define(GrGroupAuditLog.is, GrGroupAuditLog);
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
new file mode 100644
index 0000000..f7cffac
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
@@ -0,0 +1,159 @@
+/**
+ * @license
+ * Copyright (C) 2017 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 '../../../styles/gr-table-styles';
+import '../../../styles/shared-styles';
+import '../../shared/gr-date-formatter/gr-date-formatter';
+import '../../shared/gr-rest-api-interface/gr-rest-api-interface';
+import '../../shared/gr-account-link/gr-account-link';
+import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
+import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
+import {PolymerElement} from '@polymer/polymer/polymer-element';
+import {htmlTemplate} from './gr-group-audit-log_html';
+import {ListViewMixin} from '../../../mixins/gr-list-view-mixin/gr-list-view-mixin';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation';
+import {customElement, property} from '@polymer/decorators';
+import {
+ ErrorCallback,
+ RestApiService,
+} from '../../../services/services/gr-rest-api/gr-rest-api';
+import {
+ GroupInfo,
+ AccountInfo,
+ EncodedGroupId,
+ GroupAuditEventInfo,
+} from '../../../types/common';
+
+const GROUP_EVENTS = ['ADD_GROUP', 'REMOVE_GROUP'];
+
+export interface GrGroupAuditLog {
+ $: {
+ restAPI: RestApiService & Element;
+ };
+}
+@customElement('gr-group-audit-log')
+export class GrGroupAuditLog extends ListViewMixin(
+ GestureEventListeners(LegacyElementMixin(PolymerElement))
+) {
+ static get template() {
+ return htmlTemplate;
+ }
+
+ @property({type: String})
+ groupId?: EncodedGroupId;
+
+ @property({type: Array})
+ _auditLog?: GroupAuditEventInfo[];
+
+ @property({type: Boolean})
+ _loading = true;
+
+ /** @override */
+ attached() {
+ super.attached();
+ this.dispatchEvent(
+ new CustomEvent('title-change', {
+ detail: {title: 'Audit Log'},
+ composed: true,
+ bubbles: true,
+ })
+ );
+ }
+
+ /** @override */
+ ready() {
+ super.ready();
+ this._getAuditLogs();
+ }
+
+ _getAuditLogs() {
+ if (!this.groupId) {
+ return '';
+ }
+
+ const errFn: ErrorCallback = response => {
+ this.dispatchEvent(
+ new CustomEvent('page-error', {
+ detail: {response},
+ composed: true,
+ bubbles: true,
+ })
+ );
+ };
+
+ return this.$.restAPI
+ .getGroupAuditLog(this.groupId, errFn)
+ .then(auditLog => {
+ if (!auditLog) {
+ this._auditLog = [];
+ return;
+ }
+ this._auditLog = auditLog;
+ this._loading = false;
+ });
+ }
+
+ itemType(type: string) {
+ let item;
+ switch (type) {
+ case 'ADD_GROUP':
+ case 'ADD_USER':
+ item = 'Added';
+ break;
+ case 'REMOVE_GROUP':
+ case 'REMOVE_USER':
+ item = 'Removed';
+ break;
+ default:
+ item = '';
+ }
+ return item;
+ }
+
+ _isGroupEvent(type: string) {
+ return GROUP_EVENTS.indexOf(type) !== -1;
+ }
+
+ _computeGroupUrl(group: GroupInfo) {
+ if (group && group.url && group.id) {
+ return GerritNav.getUrlForGroup(group.id);
+ }
+
+ return '';
+ }
+
+ _getIdForUser(account: AccountInfo) {
+ return account._account_id ? ` (${account._account_id})` : '';
+ }
+
+ _getNameForGroup(group: GroupInfo) {
+ if (group && group.name) {
+ return group.name;
+ } else if (group && group.id) {
+ // The URL encoded id of the member
+ return decodeURIComponent(group.id);
+ }
+
+ return '';
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'gr-group-audit-log': GrGroupAuditLog;
+ }
+}
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js
deleted file mode 100644
index 0fb57d3..0000000
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js
+++ /dev/null
@@ -1,310 +0,0 @@
-/**
- * @license
- * Copyright (C) 2017 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 '@polymer/iron-autogrow-textarea/iron-autogrow-textarea.js';
-import '../../../styles/gr-form-styles.js';
-import '../../../styles/gr-subpage-styles.js';
-import '../../../styles/gr-table-styles.js';
-import '../../../styles/shared-styles.js';
-import '../../shared/gr-account-link/gr-account-link.js';
-import '../../shared/gr-autocomplete/gr-autocomplete.js';
-import '../../shared/gr-button/gr-button.js';
-import '../../shared/gr-overlay/gr-overlay.js';
-import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
-import '../gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js';
-import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
-import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
-import {PolymerElement} from '@polymer/polymer/polymer-element.js';
-import {htmlTemplate} from './gr-group-members_html.js';
-import {getBaseUrl} from '../../../utils/url-util.js';
-
-const SUGGESTIONS_LIMIT = 15;
-const SAVING_ERROR_TEXT = 'Group may not exist, or you may not have '+
- 'permission to add it';
-
-const URL_REGEX = '^(?:[a-z]+:)?//';
-
-/**
- * @extends PolymerElement
- */
-class GrGroupMembers extends GestureEventListeners(
- LegacyElementMixin(
- PolymerElement)) {
- static get template() { return htmlTemplate; }
-
- static get is() { return 'gr-group-members'; }
-
- static get properties() {
- return {
- groupId: Number,
- _groupMemberSearchId: String,
- _groupMemberSearchName: String,
- _includedGroupSearchId: String,
- _includedGroupSearchName: String,
- _loading: {
- type: Boolean,
- value: true,
- },
- _groupName: String,
- _groupMembers: Object,
- _includedGroups: Object,
- _itemName: String,
- _itemType: String,
- _queryMembers: {
- type: Function,
- value() {
- return input => this._getAccountSuggestions(input);
- },
- },
- _queryIncludedGroup: {
- type: Function,
- value() {
- return input => this._getGroupSuggestions(input);
- },
- },
- _groupOwner: {
- type: Boolean,
- value: false,
- },
- _isAdmin: {
- type: Boolean,
- value: false,
- },
- };
- }
-
- /** @override */
- attached() {
- super.attached();
- this._loadGroupDetails();
-
- this.dispatchEvent(new CustomEvent('title-change', {
- detail: {title: 'Members'},
- composed: true, bubbles: true,
- }));
- }
-
- _loadGroupDetails() {
- if (!this.groupId) { return; }
-
- const promises = [];
-
- const errFn = response => {
- this.dispatchEvent(new CustomEvent('page-error', {
- detail: {response},
- composed: true, bubbles: true,
- }));
- };
-
- return this.$.restAPI.getGroupConfig(this.groupId, errFn)
- .then(config => {
- if (!config || !config.name) { return Promise.resolve(); }
-
- this._groupName = config.name;
-
- promises.push(this.$.restAPI.getIsAdmin().then(isAdmin => {
- this._isAdmin = !!isAdmin;
- }));
-
- promises.push(this.$.restAPI.getIsGroupOwner(config.name)
- .then(isOwner => {
- this._groupOwner = !!isOwner;
- }));
-
- promises.push(this.$.restAPI.getGroupMembers(config.name).then(
- members => {
- this._groupMembers = members;
- }));
-
- promises.push(this.$.restAPI.getIncludedGroup(config.name)
- .then(includedGroup => {
- this._includedGroups = includedGroup;
- }));
-
- return Promise.all(promises).then(() => {
- this._loading = false;
- });
- });
- }
-
- _computeLoadingClass(loading) {
- return loading ? 'loading' : '';
- }
-
- _isLoading() {
- return this._loading || this._loading === undefined;
- }
-
- _computeGroupUrl(url) {
- if (!url) { return; }
-
- const r = new RegExp(URL_REGEX, 'i');
- if (r.test(url)) {
- return url;
- }
-
- // For GWT compatibility
- if (url.startsWith('#')) {
- return getBaseUrl() + url.slice(1);
- }
- return getBaseUrl() + url;
- }
-
- _handleSavingGroupMember() {
- return this.$.restAPI.saveGroupMember(this._groupName,
- this._groupMemberSearchId).then(config => {
- if (!config) {
- return;
- }
- this.$.restAPI.getGroupMembers(this._groupName).then(members => {
- this._groupMembers = members;
- });
- this._groupMemberSearchName = '';
- this._groupMemberSearchId = '';
- });
- }
-
- _handleDeleteConfirm() {
- this.$.overlay.close();
- if (this._itemType === 'member') {
- return this.$.restAPI.deleteGroupMember(this._groupName,
- this._itemId)
- .then(itemDeleted => {
- if (itemDeleted.status === 204) {
- this.$.restAPI.getGroupMembers(this._groupName)
- .then(members => {
- this._groupMembers = members;
- });
- }
- });
- } else if (this._itemType === 'includedGroup') {
- return this.$.restAPI.deleteIncludedGroup(this._groupName,
- this._itemId)
- .then(itemDeleted => {
- if (itemDeleted.status === 204 || itemDeleted.status === 205) {
- this.$.restAPI.getIncludedGroup(this._groupName)
- .then(includedGroup => {
- this._includedGroups = includedGroup;
- });
- }
- });
- }
- }
-
- _handleConfirmDialogCancel() {
- this.$.overlay.close();
- }
-
- _handleDeleteMember(e) {
- const id = e.model.get('item._account_id');
- const name = e.model.get('item.name');
- const username = e.model.get('item.username');
- const email = e.model.get('item.email');
- const item = username || name || email || id;
- if (!item) {
- return '';
- }
- this._itemName = item;
- this._itemId = id;
- this._itemType = 'member';
- this.$.overlay.open();
- }
-
- _handleSavingIncludedGroups() {
- return this.$.restAPI.saveIncludedGroup(this._groupName,
- this._includedGroupSearchId.replace(/\+/g, ' '), (errResponse, err) => {
- if (errResponse) {
- if (errResponse.status === 404) {
- this.dispatchEvent(new CustomEvent('show-alert', {
- detail: {message: SAVING_ERROR_TEXT},
- bubbles: true,
- composed: true,
- }));
- return errResponse;
- }
- throw Error(err.statusText);
- }
- throw err;
- })
- .then(config => {
- if (!config) {
- return;
- }
- this.$.restAPI.getIncludedGroup(this._groupName)
- .then(includedGroup => {
- this._includedGroups = includedGroup;
- });
- this._includedGroupSearchName = '';
- this._includedGroupSearchId = '';
- });
- }
-
- _handleDeleteIncludedGroup(e) {
- const id = decodeURIComponent(e.model.get('item.id')).replace(/\+/g, ' ');
- const name = e.model.get('item.name');
- const item = name || id;
- if (!item) { return ''; }
- this._itemName = item;
- this._itemId = id;
- this._itemType = 'includedGroup';
- this.$.overlay.open();
- }
-
- _getAccountSuggestions(input) {
- if (input.length === 0) { return Promise.resolve([]); }
- return this.$.restAPI.getSuggestedAccounts(
- input, SUGGESTIONS_LIMIT).then(accounts => {
- const accountSuggestions = [];
- let nameAndEmail;
- if (!accounts) { return []; }
- for (const key in accounts) {
- if (!accounts.hasOwnProperty(key)) { continue; }
- if (accounts[key].email !== undefined) {
- nameAndEmail = accounts[key].name +
- ' <' + accounts[key].email + '>';
- } else {
- nameAndEmail = accounts[key].name;
- }
- accountSuggestions.push({
- name: nameAndEmail,
- value: accounts[key]._account_id,
- });
- }
- return accountSuggestions;
- });
- }
-
- _getGroupSuggestions(input) {
- return this.$.restAPI.getSuggestedGroups(input)
- .then(response => {
- const groups = [];
- for (const key in response) {
- if (!response.hasOwnProperty(key)) { continue; }
- groups.push({
- name: key,
- value: decodeURIComponent(response[key].id),
- });
- }
- return groups;
- });
- }
-
- _computeHideItemClass(owner, admin) {
- return admin || owner ? '' : 'canModify';
- }
-}
-
-customElements.define(GrGroupMembers.is, GrGroupMembers);
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
new file mode 100644
index 0000000..f8f1fee
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
@@ -0,0 +1,396 @@
+/**
+ * @license
+ * Copyright (C) 2017 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 '@polymer/iron-autogrow-textarea/iron-autogrow-textarea';
+import '../../../styles/gr-form-styles';
+import '../../../styles/gr-subpage-styles';
+import '../../../styles/gr-table-styles';
+import '../../../styles/shared-styles';
+import '../../shared/gr-account-link/gr-account-link';
+import '../../shared/gr-autocomplete/gr-autocomplete';
+import '../../shared/gr-button/gr-button';
+import '../../shared/gr-overlay/gr-overlay';
+import '../../shared/gr-rest-api-interface/gr-rest-api-interface';
+import '../gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog';
+import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
+import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
+import {PolymerElement} from '@polymer/polymer/polymer-element';
+import {htmlTemplate} from './gr-group-members_html';
+import {getBaseUrl} from '../../../utils/url-util';
+import {customElement, property} from '@polymer/decorators';
+import {
+ RestApiService,
+ ErrorCallback,
+} from '../../../services/services/gr-rest-api/gr-rest-api';
+import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
+import {
+ GroupId,
+ AccountId,
+ AccountInfo,
+ GroupInfo,
+} from '../../../types/common';
+import {AutocompleteQuery} from '../../shared/gr-autocomplete/gr-autocomplete';
+import {PolymerDomRepeatEvent} from '../../../types/types';
+import {hasOwnProperty} from '../../../utils/common-util';
+
+const SUGGESTIONS_LIMIT = 15;
+const SAVING_ERROR_TEXT =
+ 'Group may not exist, or you may not have ' + 'permission to add it';
+
+const URL_REGEX = '^(?:[a-z]+:)?//';
+
+export interface GrGroupMembers {
+ $: {
+ restAPI: RestApiService & Element;
+ overlay: GrOverlay;
+ };
+}
+@customElement('gr-group-members')
+export class GrGroupMembers extends GestureEventListeners(
+ LegacyElementMixin(PolymerElement)
+) {
+ static get template() {
+ return htmlTemplate;
+ }
+
+ @property({type: Number})
+ groupId?: GroupId;
+
+ @property({type: Number})
+ _groupMemberSearchId?: number;
+
+ @property({type: String})
+ _groupMemberSearchName?: string;
+
+ @property({type: String})
+ _includedGroupSearchId?: string;
+
+ @property({type: String})
+ _includedGroupSearchName?: string;
+
+ @property({type: Boolean})
+ _loading = true;
+
+ @property({type: String})
+ _groupName?: GroupId;
+
+ @property({type: Object})
+ _groupMembers?: AccountInfo[];
+
+ @property({type: Object})
+ _includedGroups?: GroupInfo[];
+
+ @property({type: String})
+ _itemName?: GroupInfo | AccountInfo;
+
+ @property({type: String})
+ _itemType?: string;
+
+ @property({type: Object})
+ _queryMembers: AutocompleteQuery;
+
+ @property({type: Object})
+ _queryIncludedGroup: AutocompleteQuery;
+
+ @property({type: Boolean})
+ _groupOwner = false;
+
+ @property({type: Boolean})
+ _isAdmin = false;
+
+ _itemId?: AccountId | GroupId;
+
+ constructor() {
+ super();
+ this._queryMembers = input => this._getAccountSuggestions(input);
+ this._queryIncludedGroup = input => this._getGroupSuggestions(input);
+ }
+
+ /** @override */
+ attached() {
+ super.attached();
+ this._loadGroupDetails();
+
+ this.dispatchEvent(
+ new CustomEvent('title-change', {
+ detail: {title: 'Members'},
+ composed: true,
+ bubbles: true,
+ })
+ );
+ }
+
+ _loadGroupDetails() {
+ if (!this.groupId) {
+ return;
+ }
+
+ const promises: Promise<void>[] = [];
+
+ const errFn: ErrorCallback = response => {
+ this.dispatchEvent(
+ new CustomEvent('page-error', {
+ detail: {response},
+ composed: true,
+ bubbles: true,
+ })
+ );
+ };
+
+ return this.$.restAPI.getGroupConfig(this.groupId, errFn).then(config => {
+ if (!config || !config.name) {
+ return Promise.resolve();
+ }
+
+ this._groupName = config.name as GroupId;
+
+ promises.push(
+ this.$.restAPI.getIsAdmin().then(isAdmin => {
+ this._isAdmin = !!isAdmin;
+ })
+ );
+
+ promises.push(
+ this.$.restAPI.getIsGroupOwner(this._groupName).then(isOwner => {
+ this._groupOwner = !!isOwner;
+ })
+ );
+
+ promises.push(
+ this.$.restAPI.getGroupMembers(this._groupName).then(members => {
+ this._groupMembers = members;
+ })
+ );
+
+ promises.push(
+ this.$.restAPI.getIncludedGroup(this._groupName).then(includedGroup => {
+ this._includedGroups = includedGroup;
+ })
+ );
+
+ return Promise.all(promises).then(() => {
+ this._loading = false;
+ });
+ });
+ }
+
+ _computeLoadingClass(loading: boolean) {
+ return loading ? 'loading' : '';
+ }
+
+ _isLoading() {
+ return this._loading || this._loading === undefined;
+ }
+
+ _computeGroupUrl(url: string) {
+ if (!url) {
+ return;
+ }
+
+ const r = new RegExp(URL_REGEX, 'i');
+ if (r.test(url)) {
+ return url;
+ }
+
+ // For GWT compatibility
+ if (url.startsWith('#')) {
+ return getBaseUrl() + url.slice(1);
+ }
+ return getBaseUrl() + url;
+ }
+
+ _handleSavingGroupMember() {
+ if (!this._groupName) {
+ return Promise.reject(new Error('group name undefined'));
+ }
+ return this.$.restAPI
+ .saveGroupMember(this._groupName, this._groupMemberSearchId as AccountId)
+ .then(config => {
+ if (!config || !this._groupName) {
+ return;
+ }
+ this.$.restAPI.getGroupMembers(this._groupName).then(members => {
+ this._groupMembers = members;
+ });
+ this._groupMemberSearchName = '';
+ this._groupMemberSearchId = undefined;
+ });
+ }
+
+ _handleDeleteConfirm() {
+ if (!this._groupName) {
+ return Promise.reject(new Error('group name undefined'));
+ }
+ this.$.overlay.close();
+ if (this._itemType === 'member') {
+ return this.$.restAPI
+ .deleteGroupMember(this._groupName, this._itemId! as AccountId)
+ .then(itemDeleted => {
+ if (itemDeleted.status === 204 && this._groupName) {
+ this.$.restAPI.getGroupMembers(this._groupName).then(members => {
+ this._groupMembers = members;
+ });
+ }
+ });
+ } else if (this._itemType === 'includedGroup') {
+ return this.$.restAPI
+ .deleteIncludedGroup(this._groupName, this._itemId! as GroupId)
+ .then(itemDeleted => {
+ if (
+ (itemDeleted.status === 204 || itemDeleted.status === 205) &&
+ this._groupName
+ ) {
+ this.$.restAPI
+ .getIncludedGroup(this._groupName)
+ .then(includedGroup => {
+ this._includedGroups = includedGroup;
+ });
+ }
+ });
+ }
+ return Promise.reject(new Error('Unrecognized item type'));
+ }
+
+ _handleConfirmDialogCancel() {
+ this.$.overlay.close();
+ }
+
+ _handleDeleteMember(e: PolymerDomRepeatEvent<AccountInfo>) {
+ const id = (e.model.get('item._account_id') as unknown) as AccountId;
+ const name = e.model.get('item.name');
+ const username = e.model.get('item.username');
+ const email = e.model.get('item.email');
+ const item = username || name || email || id;
+ if (!item) {
+ return;
+ }
+ this._itemName = item;
+ this._itemId = id;
+ this._itemType = 'member';
+ this.$.overlay.open();
+ }
+
+ _handleSavingIncludedGroups() {
+ if (!this._groupName || !this._includedGroupSearchId) {
+ return Promise.reject(
+ new Error('group name or includedGroupSearchId undefined')
+ );
+ }
+ return this.$.restAPI
+ .saveIncludedGroup(
+ this._groupName,
+ this._includedGroupSearchId.replace(/\+/g, ' ') as GroupId,
+ (errResponse, err) => {
+ if (errResponse) {
+ if (errResponse.status === 404) {
+ this.dispatchEvent(
+ new CustomEvent('show-alert', {
+ detail: {message: SAVING_ERROR_TEXT},
+ bubbles: true,
+ composed: true,
+ })
+ );
+ return errResponse;
+ }
+ throw Error(errResponse.statusText);
+ }
+ throw err;
+ }
+ )
+ .then(config => {
+ if (!config || !this._groupName) {
+ return;
+ }
+ this.$.restAPI.getIncludedGroup(this._groupName).then(includedGroup => {
+ this._includedGroups = includedGroup;
+ });
+ this._includedGroupSearchName = '';
+ this._includedGroupSearchId = '';
+ });
+ }
+
+ _handleDeleteIncludedGroup(e: PolymerDomRepeatEvent<GroupInfo>) {
+ const id = decodeURIComponent(`${e.model.get('item.id')}`).replace(
+ /\+/g,
+ ' '
+ ) as GroupId;
+ const name = e.model.get('item.name');
+ const item = name || id;
+ if (!item) {
+ return;
+ }
+ this._itemName = item;
+ this._itemId = id;
+ this._itemType = 'includedGroup';
+ this.$.overlay.open();
+ }
+
+ _getAccountSuggestions(input: string) {
+ if (input.length === 0) {
+ return Promise.resolve([]);
+ }
+ return this.$.restAPI
+ .getSuggestedAccounts(input, SUGGESTIONS_LIMIT)
+ .then(accounts => {
+ const accountSuggestions = [];
+ let nameAndEmail;
+ if (!accounts) {
+ return [];
+ }
+ for (const key in accounts) {
+ if (!hasOwnProperty(accounts, key)) {
+ continue;
+ }
+ if (accounts[key].email !== undefined) {
+ nameAndEmail = `${accounts[key].name} <${accounts[key].email}>`;
+ } else {
+ nameAndEmail = accounts[key].name;
+ }
+ accountSuggestions.push({
+ name: nameAndEmail,
+ value: accounts[key]._account_id,
+ });
+ }
+ return accountSuggestions;
+ });
+ }
+
+ _getGroupSuggestions(input: string) {
+ return this.$.restAPI.getSuggestedGroups(input).then(response => {
+ const groups = [];
+ for (const key in response) {
+ if (!hasOwnProperty(response, key)) {
+ continue;
+ }
+ groups.push({
+ name: key,
+ value: decodeURIComponent(response[key].id),
+ });
+ }
+ return groups;
+ });
+ }
+
+ _computeHideItemClass(owner: boolean, admin: boolean) {
+ return admin || owner ? '' : 'canModify';
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'gr-group-members': GrGroupMembers;
+ }
+}
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.js b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.js
index 03fccde..b5d2217 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members_test.js
@@ -17,7 +17,7 @@
import '../../../test/common-test-setup-karma.js';
import './gr-group-members.js';
-import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
+import {dom, flush} from '@polymer/polymer/lib/legacy/polymer.dom.js';
import {stubBaseUrl} from '../../../test/test-utils.js';
const basicFixture = fixtureFromElement('gr-group-members');
@@ -210,6 +210,7 @@
test('add included group 404 shows helpful error text', () => {
element._groupOwner = true;
+ element._groupName = 'test';
const memberName = 'bad-name';
const alertStub = sinon.stub();
@@ -224,9 +225,9 @@
element.$.groupMemberSearchInput.text = memberName;
element.$.groupMemberSearchInput.value = 1234;
- return element._handleSavingIncludedGroups().then(() => {
+ return flush(element._handleSavingIncludedGroups().then(() => {
assert.isTrue(alertStub.called);
- });
+ }));
});
test('add included group network-error throws an exception', async () => {
diff --git a/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts b/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts
index e3c2ce8..d172f2a 100644
--- a/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts
+++ b/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts
@@ -75,6 +75,8 @@
ProjectInput,
AccountId,
ChangeMessageId,
+ GroupAuditEventInfo,
+ EncodedGroupId,
} from '../../../types/common';
import {ParsedChangeInfo} from '../../../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser';
import {HttpMethod} from '../../../constants/constants';
@@ -605,8 +607,42 @@
changeNum: ChangeNum,
messageId: ChangeMessageId
): Promise<Response>;
+
removeChangeReviewer(
changeNum: ChangeNum,
reviewerID: AccountId | GroupId
): Promise<Response | undefined>;
+
+ getGroupAuditLog(
+ group: EncodedGroupId,
+ errFn?: ErrorCallback
+ ): Promise<GroupAuditEventInfo[] | undefined>;
+
+ getGroupMembers(
+ groupName: GroupId,
+ errFn?: ErrorCallback
+ ): Promise<AccountInfo[] | undefined>;
+
+ getIncludedGroup(groupName: GroupId): Promise<GroupInfo[] | undefined>;
+
+ saveGroupMember(
+ groupName: GroupId,
+ groupMember: AccountId
+ ): Promise<AccountInfo>;
+
+ saveIncludedGroup(
+ groupName: GroupId,
+ includedGroup: GroupId,
+ errFn?: ErrorCallback
+ ): Promise<GroupInfo | undefined>;
+
+ deleteGroupMember(
+ groupName: GroupId,
+ groupMember: AccountId
+ ): Promise<Response>;
+
+ deleteIncludedGroup(
+ groupName: GroupId,
+ includedGroup: GroupId
+ ): Promise<Response>;
}
diff --git a/polygerrit-ui/app/types/types.ts b/polygerrit-ui/app/types/types.ts
index 8dd9371..970fa21 100644
--- a/polygerrit-ui/app/types/types.ts
+++ b/polygerrit-ui/app/types/types.ts
@@ -126,6 +126,8 @@
* The index of the element in the dom-repeat.
*/
index: number;
+ get: (name: string) => T;
+ set: (name: string, val: T) => void;
}
/** https://highlightjs.readthedocs.io/en/latest/api.html */