Merge changes from topic "gr-repo-detail-list-to-ts"

* changes:
  Convert gr-repo-detail-list to typescript
  Rename files to preserve history
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js
deleted file mode 100644
index 4989365..0000000
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js
+++ /dev/null
@@ -1,309 +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-input/iron-input.js';
-import '../../../styles/gr-form-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-button/gr-button.js';
-import '../../shared/gr-date-formatter/gr-date-formatter.js';
-import '../../shared/gr-dialog/gr-dialog.js';
-import '../../shared/gr-list-view/gr-list-view.js';
-import '../../shared/gr-overlay/gr-overlay.js';
-import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
-import '../gr-create-pointer-dialog/gr-create-pointer-dialog.js';
-import '../gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js';
-import {flush} from '@polymer/polymer/lib/legacy/polymer.dom.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-repo-detail-list_html.js';
-import {ListViewMixin} from '../../../mixins/gr-list-view-mixin/gr-list-view-mixin.js';
-import {encodeURL} from '../../../utils/url-util.js';
-
-const DETAIL_TYPES = {
-  BRANCHES: 'branches',
-  TAGS: 'tags',
-};
-
-const PGP_START = '-----BEGIN PGP SIGNATURE-----';
-
-/**
- * @appliesMixin ListViewMixin
- * @extends PolymerElement
- */
-class GrRepoDetailList extends ListViewMixin(GestureEventListeners(
-    LegacyElementMixin(
-        PolymerElement))) {
-  static get template() { return htmlTemplate; }
-
-  static get is() { return 'gr-repo-detail-list'; }
-
-  static get properties() {
-    return {
-    /**
-     * URL params passed from the router.
-     */
-      params: {
-        type: Object,
-        observer: '_paramsChanged',
-      },
-      /**
-       * The kind of detail we are displaying, possibilities are determined by
-       * the const DETAIL_TYPES.
-       */
-      detailType: String,
-
-      _editing: {
-        type: Boolean,
-        value: false,
-      },
-      _isOwner: {
-        type: Boolean,
-        value: false,
-      },
-      _loggedIn: {
-        type: Boolean,
-        value: false,
-      },
-      /**
-       * Offset of currently visible query results.
-       */
-      _offset: Number,
-      _repo: Object,
-      _items: Array,
-      /**
-       * Because  we request one more than the projectsPerPage, _shownProjects
-       * maybe one less than _projects.
-       */
-      _shownItems: {
-        type: Array,
-        computed: 'computeShownItems(_items)',
-      },
-      _itemsPerPage: {
-        type: Number,
-        value: 25,
-      },
-      _loading: {
-        type: Boolean,
-        value: true,
-      },
-      _filter: String,
-      _refName: String,
-      _hasNewItemName: Boolean,
-      _isEditing: Boolean,
-      _revisedRef: String,
-    };
-  }
-
-  _determineIfOwner(repo) {
-    return this.$.restAPI.getRepoAccess(repo)
-        .then(access =>
-          this._isOwner = access && !!access[repo].is_owner);
-  }
-
-  _paramsChanged(params) {
-    if (!params || !params.repo) { return; }
-
-    this._repo = params.repo;
-
-    this._getLoggedIn().then(loggedIn => {
-      this._loggedIn = loggedIn;
-      if (loggedIn) {
-        this._determineIfOwner(this._repo);
-      }
-    });
-
-    this.detailType = params.detail;
-
-    this._filter = this.getFilterValue(params);
-    this._offset = this.getOffsetValue(params);
-
-    return this._getItems(this._filter, this._repo,
-        this._itemsPerPage, this._offset, this.detailType);
-  }
-
-  _getItems(filter, repo, itemsPerPage, offset, detailType) {
-    this._loading = true;
-    this._items = [];
-    flush();
-    const errFn = response => {
-      this.dispatchEvent(new CustomEvent('page-error', {
-        detail: {response},
-        composed: true, bubbles: true,
-      }));
-    };
-    if (detailType === DETAIL_TYPES.BRANCHES) {
-      return this.$.restAPI.getRepoBranches(
-          filter, repo, itemsPerPage, offset, errFn).then(items => {
-        if (!items) { return; }
-        this._items = items;
-        this._loading = false;
-      });
-    } else if (detailType === DETAIL_TYPES.TAGS) {
-      return this.$.restAPI.getRepoTags(
-          filter, repo, itemsPerPage, offset, errFn).then(items => {
-        if (!items) { return; }
-        this._items = items;
-        this._loading = false;
-      });
-    }
-  }
-
-  _getPath(repo) {
-    return `/admin/repos/${encodeURL(repo, false)},` +
-        `${this.detailType}`;
-  }
-
-  _computeWeblink(repo) {
-    if (!repo.web_links) { return ''; }
-    const webLinks = repo.web_links;
-    return webLinks.length ? webLinks : null;
-  }
-
-  _computeMessage(message) {
-    if (!message) { return; }
-    // Strip PGP info.
-    return message.split(PGP_START)[0];
-  }
-
-  _stripRefs(item, detailType) {
-    if (detailType === DETAIL_TYPES.BRANCHES) {
-      return item.replace('refs/heads/', '');
-    } else if (detailType === DETAIL_TYPES.TAGS) {
-      return item.replace('refs/tags/', '');
-    }
-  }
-
-  _getLoggedIn() {
-    return this.$.restAPI.getLoggedIn();
-  }
-
-  _computeEditingClass(isEditing) {
-    return isEditing ? 'editing' : '';
-  }
-
-  _computeCanEditClass(ref, detailType, isOwner) {
-    return isOwner && this._stripRefs(ref, detailType) === 'HEAD' ?
-      'canEdit' : '';
-  }
-
-  _handleEditRevision(e) {
-    this._revisedRef = e.model.get('item.revision');
-    this._isEditing = true;
-  }
-
-  _handleCancelRevision() {
-    this._isEditing = false;
-  }
-
-  _handleSaveRevision(e) {
-    this._setRepoHead(this._repo, this._revisedRef, e);
-  }
-
-  _setRepoHead(repo, ref, e) {
-    return this.$.restAPI.setRepoHead(repo, ref).then(res => {
-      if (res.status < 400) {
-        this._isEditing = false;
-        e.model.set('item.revision', ref);
-        // This is needed to refresh _items property with fresh data,
-        // specifically can_delete from the json response.
-        this._getItems(
-            this._filter, this._repo, this._itemsPerPage,
-            this._offset, this.detailType);
-      }
-    });
-  }
-
-  _computeItemName(detailType) {
-    if (detailType === DETAIL_TYPES.BRANCHES) {
-      return 'Branch';
-    } else if (detailType === DETAIL_TYPES.TAGS) {
-      return 'Tag';
-    }
-  }
-
-  _handleDeleteItemConfirm() {
-    this.$.overlay.close();
-    if (this.detailType === DETAIL_TYPES.BRANCHES) {
-      return this.$.restAPI.deleteRepoBranches(this._repo, this._refName)
-          .then(itemDeleted => {
-            if (itemDeleted.status === 204) {
-              this._getItems(
-                  this._filter, this._repo, this._itemsPerPage,
-                  this._offset, this.detailType);
-            }
-          });
-    } else if (this.detailType === DETAIL_TYPES.TAGS) {
-      return this.$.restAPI.deleteRepoTags(this._repo, this._refName)
-          .then(itemDeleted => {
-            if (itemDeleted.status === 204) {
-              this._getItems(
-                  this._filter, this._repo, this._itemsPerPage,
-                  this._offset, this.detailType);
-            }
-          });
-    }
-  }
-
-  _handleConfirmDialogCancel() {
-    this.$.overlay.close();
-  }
-
-  _handleDeleteItem(e) {
-    const name = this._stripRefs(e.model.get('item.ref'), this.detailType);
-    if (!name) { return; }
-    this._refName = name;
-    this.$.overlay.open();
-  }
-
-  _computeHideDeleteClass(owner, canDelete) {
-    if (canDelete || owner) {
-      return 'show';
-    }
-
-    return '';
-  }
-
-  _handleCreateItem() {
-    this.$.createNewModal.handleCreateItem();
-    this._handleCloseCreate();
-  }
-
-  _handleCloseCreate() {
-    this.$.createOverlay.close();
-  }
-
-  _handleCreateClicked() {
-    this.$.createOverlay.open();
-  }
-
-  _hideIfBranch(type) {
-    if (type === DETAIL_TYPES.BRANCHES) {
-      return 'hideItem';
-    }
-
-    return '';
-  }
-
-  _computeHideTagger(tagger) {
-    return tagger ? '' : 'hide';
-  }
-}
-
-customElements.define(GrRepoDetailList.is, GrRepoDetailList);
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts
new file mode 100644
index 0000000..828d447
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.ts
@@ -0,0 +1,381 @@
+/**
+ * @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-input/iron-input';
+import '../../../styles/gr-form-styles';
+import '../../../styles/gr-table-styles';
+import '../../../styles/shared-styles';
+import '../../shared/gr-account-link/gr-account-link';
+import '../../shared/gr-button/gr-button';
+import '../../shared/gr-date-formatter/gr-date-formatter';
+import '../../shared/gr-dialog/gr-dialog';
+import '../../shared/gr-list-view/gr-list-view';
+import '../../shared/gr-overlay/gr-overlay';
+import '../../shared/gr-rest-api-interface/gr-rest-api-interface';
+import '../gr-create-pointer-dialog/gr-create-pointer-dialog';
+import '../gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog';
+import {flush} from '@polymer/polymer/lib/legacy/polymer.dom';
+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-repo-detail-list_html';
+import {ListViewMixin} from '../../../mixins/gr-list-view-mixin/gr-list-view-mixin';
+import {encodeURL} from '../../../utils/url-util';
+import {customElement, property} from '@polymer/decorators';
+import {
+  ErrorCallback,
+  RestApiService,
+} from '../../../services/services/gr-rest-api/gr-rest-api';
+import {GrOverlay} from '../../shared/gr-overlay/gr-overlay';
+import {GrCreatePointerDialog} from '../gr-create-pointer-dialog/gr-create-pointer-dialog';
+import {
+  RepoName,
+  ProjectInfo,
+  BranchInfo,
+  GitRef,
+  TagInfo,
+  GitPersonInfo,
+} from '../../../types/common';
+import {AppElementRepoParams} from '../../gr-app-types';
+import {PolymerDomRepeatEvent} from '../../../types/types';
+import {RepoDetailView} from '../../core/gr-navigation/gr-navigation';
+
+const PGP_START = '-----BEGIN PGP SIGNATURE-----';
+
+export interface GrRepoDetailList {
+  $: {
+    restAPI: RestApiService & Element;
+    overlay: GrOverlay;
+    createOverlay: GrOverlay;
+    createNewModal: GrCreatePointerDialog;
+  };
+}
+@customElement('gr-repo-detail-list')
+export class GrRepoDetailList extends ListViewMixin(
+  GestureEventListeners(LegacyElementMixin(PolymerElement))
+) {
+  static get template() {
+    return htmlTemplate;
+  }
+
+  @property({type: Object, observer: '_paramsChanged'})
+  params?: AppElementRepoParams;
+
+  @property({type: String})
+  detailType?: RepoDetailView;
+
+  @property({type: Boolean})
+  _editing = false;
+
+  @property({type: Boolean})
+  _isOwner = false;
+
+  @property({type: Boolean})
+  _loggedIn = false;
+
+  @property({type: Number})
+  _offset?: number;
+
+  @property({type: String})
+  _repo?: RepoName;
+
+  @property({type: Array})
+  _items?: BranchInfo[] | TagInfo[];
+
+  @property({type: Array, computed: 'computeShownItems(_items)'})
+  _shownItems?: BranchInfo[] | TagInfo[];
+
+  @property({type: Number})
+  _itemsPerPage = 25;
+
+  @property({type: Boolean})
+  _loading = true;
+
+  @property({type: String})
+  _filter?: string;
+
+  @property({type: String})
+  _refName?: GitRef;
+
+  @property({type: Boolean})
+  _hasNewItemName?: boolean;
+
+  @property({type: Boolean})
+  _isEditing?: boolean;
+
+  @property({type: String})
+  _revisedRef?: GitRef;
+
+  _determineIfOwner(repo: RepoName) {
+    return this.$.restAPI
+      .getRepoAccess(repo)
+      .then(access => (this._isOwner = !!access && !!access[repo].is_owner));
+  }
+
+  _paramsChanged(params?: AppElementRepoParams) {
+    if (!params?.repo) {
+      return Promise.reject(new Error('undefined repo'));
+    }
+
+    this._repo = params.repo;
+
+    this._getLoggedIn().then(loggedIn => {
+      this._loggedIn = loggedIn;
+      if (loggedIn && this._repo) {
+        this._determineIfOwner(this._repo);
+      }
+    });
+
+    this.detailType = params.detail;
+
+    this._filter = this.getFilterValue(params);
+    this._offset = this.getOffsetValue(params);
+    if (!this.detailType)
+      return Promise.reject(new Error('undefined detailType'));
+
+    return this._getItems(
+      this._filter,
+      this._repo,
+      this._itemsPerPage,
+      this._offset,
+      this.detailType
+    );
+  }
+
+  // TODO(TS) Move this to object for easier read, understand.
+  _getItems(
+    filter: string | undefined,
+    repo: RepoName | undefined,
+    itemsPerPage: number,
+    offset: number | undefined,
+    detailType: string
+  ) {
+    if (filter === undefined || !repo || offset === undefined) {
+      return Promise.reject(new Error('filter or repo or offset undefined'));
+    }
+    this._loading = true;
+    this._items = [];
+    flush();
+    const errFn: ErrorCallback = response => {
+      this.dispatchEvent(
+        new CustomEvent('page-error', {
+          detail: {response},
+          composed: true,
+          bubbles: true,
+        })
+      );
+    };
+    if (detailType === RepoDetailView.BRANCHES) {
+      return this.$.restAPI
+        .getRepoBranches(filter, repo, itemsPerPage, offset, errFn)
+        .then(items => {
+          if (!items) {
+            return;
+          }
+          this._items = items;
+          this._loading = false;
+        });
+    } else if (detailType === RepoDetailView.TAGS) {
+      return this.$.restAPI
+        .getRepoTags(filter, repo, itemsPerPage, offset, errFn)
+        .then(items => {
+          if (!items) {
+            return;
+          }
+          this._items = items;
+          this._loading = false;
+        });
+    }
+    return Promise.reject(new Error('unknown detail type'));
+  }
+
+  _getPath(repo: RepoName) {
+    return `/admin/repos/${encodeURL(repo, false)},${this.detailType}`;
+  }
+
+  _computeWeblink(repo: ProjectInfo) {
+    if (!repo.web_links) {
+      return '';
+    }
+    const webLinks = repo.web_links;
+    return webLinks.length ? webLinks : null;
+  }
+
+  _computeMessage(message?: string) {
+    if (!message) {
+      return;
+    }
+    // Strip PGP info.
+    return message.split(PGP_START)[0];
+  }
+
+  _stripRefs(item: GitRef, detailType?: string) {
+    if (detailType === RepoDetailView.BRANCHES) {
+      return item.replace('refs/heads/', '');
+    } else if (detailType === RepoDetailView.TAGS) {
+      return item.replace('refs/tags/', '');
+    }
+    throw new Error('unknown detailType');
+  }
+
+  _getLoggedIn() {
+    return this.$.restAPI.getLoggedIn();
+  }
+
+  _computeEditingClass(isEditing: boolean) {
+    return isEditing ? 'editing' : '';
+  }
+
+  _computeCanEditClass(ref: GitRef, detailType: string, isOwner: boolean) {
+    return isOwner && this._stripRefs(ref, detailType) === 'HEAD'
+      ? 'canEdit'
+      : '';
+  }
+
+  _handleEditRevision(e: PolymerDomRepeatEvent<BranchInfo | TagInfo>) {
+    this._revisedRef = (e.model.get('item.revision') as unknown) as GitRef;
+    this._isEditing = true;
+  }
+
+  _handleCancelRevision() {
+    this._isEditing = false;
+  }
+
+  _handleSaveRevision(e: PolymerDomRepeatEvent<GitRef>) {
+    if (this._revisedRef && this._repo)
+      this._setRepoHead(this._repo, this._revisedRef, e);
+  }
+
+  _setRepoHead(repo: RepoName, ref: GitRef, e: PolymerDomRepeatEvent<GitRef>) {
+    return this.$.restAPI.setRepoHead(repo, ref).then(res => {
+      if (res.status < 400) {
+        this._isEditing = false;
+        e.model.set('item.revision', ref);
+        // This is needed to refresh _items property with fresh data,
+        // specifically can_delete from the json response.
+        this._getItems(
+          this._filter,
+          this._repo,
+          this._itemsPerPage,
+          this._offset,
+          this.detailType!
+        );
+      }
+    });
+  }
+
+  _computeItemName(detailType: string) {
+    if (detailType === RepoDetailView.BRANCHES) {
+      return 'Branch';
+    } else if (detailType === RepoDetailView.TAGS) {
+      return 'Tag';
+    }
+    throw new Error('unknown detailType');
+  }
+
+  _handleDeleteItemConfirm() {
+    this.$.overlay.close();
+    if (!this._repo || !this._refName) {
+      return Promise.reject(new Error('undefined repo or refName'));
+    }
+    if (this.detailType === RepoDetailView.BRANCHES) {
+      return this.$.restAPI
+        .deleteRepoBranches(this._repo, this._refName)
+        .then(itemDeleted => {
+          if (itemDeleted.status === 204) {
+            this._getItems(
+              this._filter,
+              this._repo,
+              this._itemsPerPage,
+              this._offset,
+              this.detailType!
+            );
+          }
+        });
+    } else if (this.detailType === RepoDetailView.TAGS) {
+      return this.$.restAPI
+        .deleteRepoTags(this._repo, this._refName)
+        .then(itemDeleted => {
+          if (itemDeleted.status === 204) {
+            this._getItems(
+              this._filter,
+              this._repo,
+              this._itemsPerPage,
+              this._offset,
+              this.detailType!
+            );
+          }
+        });
+    }
+    return Promise.reject(new Error('unknown detail type'));
+  }
+
+  _handleConfirmDialogCancel() {
+    this.$.overlay.close();
+  }
+
+  _handleDeleteItem(e: PolymerDomRepeatEvent<GitRef>) {
+    const name = this._stripRefs(
+      e.model.get('item.ref'),
+      this.detailType
+    ) as GitRef;
+    if (!name) {
+      return;
+    }
+    this._refName = name;
+    this.$.overlay.open();
+  }
+
+  _computeHideDeleteClass(owner: boolean, canDelete: boolean) {
+    if (canDelete || owner) {
+      return 'show';
+    }
+
+    return '';
+  }
+
+  _handleCreateItem() {
+    this.$.createNewModal.handleCreateItem();
+    this._handleCloseCreate();
+  }
+
+  _handleCloseCreate() {
+    this.$.createOverlay.close();
+  }
+
+  _handleCreateClicked() {
+    this.$.createOverlay.open();
+  }
+
+  _hideIfBranch(type: string) {
+    if (type === RepoDetailView.BRANCHES) {
+      return 'hideItem';
+    }
+
+    return '';
+  }
+
+  _computeHideTagger(tagger: GitPersonInfo) {
+    return tagger ? '' : 'hide';
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'gr-repo-detail-list': GrRepoDetailList;
+  }
+}
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.js b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.js
index 7f6ba08..f86f613 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list_test.js
@@ -17,6 +17,7 @@
 
 import '../../../test/common-test-setup-karma.js';
 import './gr-repo-detail-list.js';
+import 'lodash/lodash.js';
 import {page} from '../../../utils/page-wrapper-utils.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
 
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts
index 3dfe105..8461027 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts
@@ -125,6 +125,7 @@
   ProjectAccessInfo,
   CapabilityInfoMap,
   ProjectInfoWithName,
+  TagInfo,
 } from '../../../types/common';
 import {
   CancelConditionCallback,
@@ -1795,11 +1796,11 @@
       `/projects/${encodedRepo}/tags` + `?n=${n}&S=${offset}` + encodedFilter;
     // TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
     // supports it.
-    return this._restApiHelper.fetchJSON({
+    return (this._restApiHelper.fetchJSON({
       url,
       errFn,
       anonymizedUrl: '/projects/*/tags',
-    });
+    }) as unknown) as Promise<TagInfo[]>;
   }
 
   getPlugins(
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 05500ec..5565180 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
@@ -79,6 +79,8 @@
   EncodedGroupId,
   Base64FileContent,
   UrlEncodedCommentId,
+  TagInfo,
+  GitRef,
 } from '../../../types/common';
 import {ParsedChangeInfo} from '../../../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser';
 import {HttpMethod} from '../../../constants/constants';
@@ -675,4 +677,15 @@
     path: string,
     contents: string
   ): Promise<Response>;
+  getRepoTags(
+    filter: string,
+    repo: RepoName,
+    reposTagsPerPage: number,
+    offset?: number,
+    errFn?: ErrorCallback
+  ): Promise<TagInfo[]>;
+
+  setRepoHead(repo: RepoName, ref: GitRef): Promise<Response>;
+  deleteRepoTags(repo: RepoName, ref: GitRef): Promise<Response>;
+  deleteRepoBranches(repo: RepoName, ref: GitRef): Promise<Response>;
 }
diff --git a/polygerrit-ui/app/types/common.ts b/polygerrit-ui/app/types/common.ts
index eec0fc3..ea7fb75 100644
--- a/polygerrit-ui/app/types/common.ts
+++ b/polygerrit-ui/app/types/common.ts
@@ -2008,3 +2008,18 @@
   title: string;
   url: string;
 }
+
+/**
+ * The TagInfo entity contains information about a tag.
+ * https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#tag-info
+ **/
+export interface TagInfo {
+  ref: GitRef;
+  revision: string;
+  object?: string;
+  message?: string;
+  tagger?: GitPersonInfo;
+  created?: string;
+  can_delete: boolean;
+  web_links?: WebLinkInfo[];
+}