Merge "Upstream fixes from import"
diff --git a/web/code-owners-api.ts b/web/code-owners-api.ts
index c2a2374..70573c1 100644
--- a/web/code-owners-api.ts
+++ b/web/code-owners-api.ts
@@ -92,7 +92,7 @@
 }
 export interface PathCodeOwnerStatusInfo {
   path: string;
-  status?: OwnerStatus;
+  status: OwnerStatus;
   reasons?: Array<string>;
 }
 
@@ -145,7 +145,7 @@
    * Send a get request and provides custom response-code handling
    */
   private async get(url: string): Promise<unknown> {
-    const errFn = (response?: Response | null, error?: Error) => {
+    const errFn = (response?: Response|null, error?: Error) => {
       if (error) throw error;
       if (response) throw new ResponseError(response);
       throw new Error('Generic REST API error');
@@ -163,57 +163,57 @@
   }
 
   /**
-   * Returns a promise fetching the owner statuses for all files within the change.
+   * Returns a promise fetching the owner statuses for all files within the
+   * change.
    *
-   * @doc https://gerrit.googlesource.com/plugins/code-owners/+/HEAD/resources/Documentation/rest-api.md#change-endpoints
+   * @doc
+   * https://gerrit.googlesource.com/plugins/code-owners/+/HEAD/resources/Documentation/rest-api.md#change-endpoints
    */
   listOwnerStatus(changeId: NumericChangeId): Promise<CodeOwnerStatusInfo> {
-    return this.get(
-      `/changes/${changeId}/code_owners.status?limit=100000`
-    ) as Promise<CodeOwnerStatusInfo>;
+    return this.get(`/changes/${changeId}/code_owners.status?limit=100000`) as
+        Promise<CodeOwnerStatusInfo>;
   }
 
   /**
    * Returns a promise fetching the owners for a given path.
    *
-   * @doc https://gerrit.googlesource.com/plugins/code-owners/+/HEAD/resources/Documentation/rest-api.md#list-code-owners-for-path-in-branch
+   * @doc
+   * https://gerrit.googlesource.com/plugins/code-owners/+/HEAD/resources/Documentation/rest-api.md#list-code-owners-for-path-in-branch
    */
-  listOwnersForPath(
-    changeId: NumericChangeId,
-    path: string,
-    limit: number
-  ): Promise<CodeOwnersInfo> {
+  listOwnersForPath(changeId: NumericChangeId, path: string, limit: number):
+      Promise<CodeOwnersInfo> {
     return this.get(
-      `/changes/${changeId}/revisions/current/code_owners` +
-        `/${encodeURIComponent(path)}?limit=${limit}&o=DETAILS`
-    ) as Promise<CodeOwnersInfo>;
+               `/changes/${changeId}/revisions/current/code_owners` +
+               `/${encodeURIComponent(path)}?limit=${limit}&o=DETAILS`) as
+        Promise<CodeOwnersInfo>;
   }
 
   /**
    * Returns a promise fetching the owners config for a given path.
    *
-   * @doc https://gerrit.googlesource.com/plugins/code-owners/+/HEAD/resources/Documentation/rest-api.md#branch-endpoints
+   * @doc
+   * https://gerrit.googlesource.com/plugins/code-owners/+/HEAD/resources/Documentation/rest-api.md#branch-endpoints
    */
   getConfigForPath(project: string, branch: string, path: string) {
     return this.get(
-      `/projects/${encodeURIComponent(project)}/` +
+        `/projects/${encodeURIComponent(project)}/` +
         `branches/${encodeURIComponent(branch)}/` +
-        `code_owners.config/${encodeURIComponent(path)}`
-    );
+        `code_owners.config/${encodeURIComponent(path)}`);
   }
 
   /**
    * Returns a promise fetching the owners config for a given branch.
    *
-   * @doc https://gerrit.googlesource.com/plugins/code-owners/+/HEAD/resources/Documentation/rest-api.md#branch-endpoints
+   * @doc
+   * https://gerrit.googlesource.com/plugins/code-owners/+/HEAD/resources/Documentation/rest-api.md#branch-endpoints
    */
   async getBranchConfig(project: RepoName, branch: BranchName) {
     try {
-      const config = (await this.get(
-        `/projects/${encodeURIComponent(project)}/` +
-          `branches/${encodeURIComponent(branch)}/` +
-          `code_owners.branch_config`
-      )) as CodeOwnerBranchConfigInfo;
+      const config =
+          (await this.get(
+              `/projects/${encodeURIComponent(project)}/` +
+              `branches/${encodeURIComponent(branch)}/` +
+              `code_owners.branch_config`)) as CodeOwnerBranchConfigInfo;
       return config;
     } catch (err) {
       if (err instanceof ResponseError) {
@@ -249,14 +249,11 @@
   private promises = new Map<string, Promise<unknown>>();
 
   constructor(
-    private readonly codeOwnerApi: CodeOwnersApi,
-    private readonly change: ChangeInfo
-  ) {}
+      private readonly codeOwnerApi: CodeOwnersApi,
+      private readonly change: ChangeInfo) {}
 
-  private fetchOnce(
-    cacheKey: string,
-    asyncFn: () => Promise<unknown>
-  ): Promise<unknown> {
+  private fetchOnce(cacheKey: string, asyncFn: () => Promise<unknown>):
+      Promise<unknown> {
     let promise = this.promises.get(cacheKey);
     if (promise) return promise;
     promise = asyncFn();
@@ -264,10 +261,9 @@
     return promise;
   }
 
-  getAccount(): Promise<AccountDetailInfo | undefined> {
-    return this.fetchOnce('getAccount', () => this.getAccountImpl()) as Promise<
-      AccountDetailInfo | undefined
-    >;
+  getAccount(): Promise<AccountDetailInfo|undefined> {
+    return this.fetchOnce('getAccount', () => this.getAccountImpl()) as
+        Promise<AccountDetailInfo|undefined>;
   }
 
   private async getAccountImpl() {
@@ -277,14 +273,17 @@
   }
 
   listOwnerStatus(): Promise<CodeOwnerStatusInfo> {
-    return this.fetchOnce('listOwnerStatus', () =>
-      this.codeOwnerApi.listOwnerStatus(this.change._number)
-    ) as Promise<CodeOwnerStatusInfo>;
+    return this.fetchOnce(
+               'listOwnerStatus',
+               () => this.codeOwnerApi.listOwnerStatus(this.change._number)) as
+        Promise<CodeOwnerStatusInfo>;
   }
 
   getBranchConfig(): Promise<CodeOwnerBranchConfigInfo> {
-    return this.fetchOnce('getBranchConfig', () =>
-      this.codeOwnerApi.getBranchConfig(this.change.project, this.change.branch)
-    ) as Promise<CodeOwnerBranchConfigInfo>;
+    return this.fetchOnce(
+               'getBranchConfig',
+               () => this.codeOwnerApi.getBranchConfig(
+                   this.change.project, this.change.branch)) as
+        Promise<CodeOwnerBranchConfigInfo>;
   }
 }
diff --git a/web/code-owners-model.ts b/web/code-owners-model.ts
index 18ebf64..3e8d8ff 100644
--- a/web/code-owners-model.ts
+++ b/web/code-owners-model.ts
@@ -48,9 +48,8 @@
 
 export function isPluginErrorState(state: PluginState) {
   return (
-    state === PluginState.ServerConfigurationError ||
-    state === PluginState.Failed
-  );
+      state === PluginState.ServerConfigurationError ||
+      state === PluginState.Failed);
 }
 
 export enum SuggestionsType {
@@ -88,14 +87,14 @@
 export interface FileStatus {
   changeType: ChangeType;
   status: OwnerStatus;
-  newPath?: string | null;
-  oldPath?: string | null;
+  newPath?: string|null;
+  oldPath?: string|null;
 }
 
 export const BestSuggestionsLimit = 5;
 export const AllSuggestionsLimit = 1000;
 
-let codeOwnersModel: CodeOwnersModel | undefined;
+let codeOwnersModel: CodeOwnersModel|undefined;
 
 export interface CodeOwnersState {
   showSuggestions: boolean;
@@ -112,11 +111,11 @@
  * Maintain the state of code-owners.
  * Raises 'model-property-changed' event when a property is changed.
  * The plugin shares the same model between all UI elements (if it is not,
- * the plugin can't maintain showSuggestions state across different UI elements).
- * UI elements use values from this model to display information
- * and listens for the model-property-changed event. To do so, UI elements
- * add CodeOwnersModelMixin, which is doing the listening and the translation
- * from model-property-changed event to Polymer property-changed-event. The
+ * the plugin can't maintain showSuggestions state across different UI
+ * elements). UI elements use values from this model to display information and
+ * listens for the model-property-changed event. To do so, UI elements add
+ * CodeOwnersModelMixin, which is doing the listening and the translation from
+ * model-property-changed event to Polymer property-changed-event. The
  * translation allows to use model properties in observables, bindings,
  * computed properties, etc...
  * The CodeOwnersModelLoader updates the model.
@@ -136,26 +135,23 @@
 
   public showSuggestions$ = select(this.state$, state => state.showSuggestions);
 
-  public selectedSuggestionsType$ = select(
-    this.state$,
-    state => state.selectedSuggestionsType
-  );
+  public selectedSuggestionsType$ =
+      select(this.state$, state => state.selectedSuggestionsType);
 
   public selectedSuggestionsFiles$ = select(
-    this.state$,
-    state => state.suggestionsByTypes.get(state.selectedSuggestionsType)?.files
-  );
+      this.state$,
+      state =>
+          state.suggestionsByTypes.get(state.selectedSuggestionsType)?.files);
 
   public selectedSuggestionsState$ = select(
-    this.state$,
-    state => state.suggestionsByTypes.get(state.selectedSuggestionsType)!.state
-  );
+      this.state$,
+      state =>
+          state.suggestionsByTypes.get(state.selectedSuggestionsType)!.state);
 
   public selectedSuggestionsLoadProgress$ = select(
-    this.state$,
-    state =>
-      state.suggestionsByTypes.get(state.selectedSuggestionsType)!.loadProgress
-  );
+      this.state$,
+      state => state.suggestionsByTypes.get(state.selectedSuggestionsType)!
+                   .loadProgress);
 
   constructor(readonly change: ChangeInfo) {
     super();
@@ -233,9 +229,8 @@
   }
 
   private updateSuggestion(
-    suggestionsType: SuggestionsType,
-    suggestionsUpdate: Partial<Suggestion>
-  ) {
+      suggestionsType: SuggestionsType,
+      suggestionsUpdate: Partial<Suggestion>) {
     const current = this.subject$.getValue();
     const nextState = {
       ...current,
@@ -249,9 +244,7 @@
   }
 
   setSuggestionsFiles(
-    suggestionsType: SuggestionsType,
-    files: Array<FetchedFile>
-  ) {
+      suggestionsType: SuggestionsType, files: Array<FetchedFile>) {
     const current = this.subject$.getValue();
     const suggestions = current.suggestionsByTypes.get(suggestionsType);
     if (!suggestions) return;
@@ -261,9 +254,7 @@
   }
 
   setSuggestionsState(
-    suggestionsType: SuggestionsType,
-    state: SuggestionsState
-  ) {
+      suggestionsType: SuggestionsType, state: SuggestionsState) {
     const current = this.subject$.getValue();
     const suggestions = current.suggestionsByTypes.get(suggestionsType);
     if (!suggestions) return;
@@ -272,9 +263,7 @@
   }
 
   setSuggestionsLoadProgress(
-    suggestionsType: SuggestionsType,
-    loadProgress: string
-  ) {
+      suggestionsType: SuggestionsType, loadProgress: string) {
     const current = this.subject$.getValue();
     const suggestions = current.suggestionsByTypes.get(suggestionsType);
     if (!suggestions) return;
@@ -300,16 +289,14 @@
   }
 
   _arePluginStatusesEqual(
-    status1: PluginStatus | undefined,
-    status2: PluginStatus | undefined
-  ) {
+      status1: PluginStatus|undefined, status2: PluginStatus|undefined) {
     if (status1 === undefined || status2 === undefined) {
       return status1 === status2;
     }
     if (status1.state !== status2.state) return false;
-    return isPluginErrorState(status1.state)
-      ? status1.failedMessage === status2.failedMessage
-      : true;
+    return isPluginErrorState(status1.state) ?
+        status1.failedMessage === status2.failedMessage :
+        true;
   }
 
   static getModel(change: ChangeInfo) {
diff --git a/web/code-owners-service.ts b/web/code-owners-service.ts
index 2bf9cd1..a7206f0 100644
--- a/web/code-owners-service.ts
+++ b/web/code-owners-service.ts
@@ -56,6 +56,8 @@
   maxConcurrentRequests?: number;
 }
 
+function noAwait(_promise: Promise<unknown>) {}
+
 /**
  * Service for the data layer used in the plugin UI.
  */
@@ -169,7 +171,7 @@
       // new status - it is expected, that after several retry a status
       // for the newest patchset is returned
       this.reset();
-      this.prefetch();
+      noAwait(this.prefetch());
       return await this.getStatus();
     }
     return status;
@@ -181,7 +183,7 @@
       return {
         patchsetNumber: 0,
         enabled: false,
-        codeOwnerStatusMap: new Map(),
+        codeOwnerStatusMap: new Map<string, FileStatus>(),
         rawStatuses: [],
         newerPatchsetUploaded: false,
       };
@@ -281,7 +283,7 @@
         });
       }
       return prev;
-    }, new Map());
+    }, new Map<string, FileStatus>());
   }
 
   private computeFileStatus(
diff --git a/web/suggest-owners-util.ts b/web/suggest-owners-util.ts
index ca494d6..3a32d8e 100644
--- a/web/suggest-owners-util.ts
+++ b/web/suggest-owners-util.ts
@@ -20,10 +20,7 @@
 import {BestSuggestionsLimit} from './code-owners-model';
 
 export interface GroupedFiles {
-  groupName: {
-    name: string | undefined;
-    prefix: string;
-  };
+  groupName: {name: string|undefined; prefix: string;};
   files: Array<FetchedFile>;
   owners?: CodeOwnersInfo;
   error?: Error;
@@ -42,35 +39,28 @@
  * "fake" groups a user can see inconsistent state in dialog.
  */
 export function getDisplayOwnersGroups(
-  files: Array<FetchedFile>,
-  allOwnersByPathMap: Map<string, CodeOwnersInfo | undefined>,
-  reviewersIdSet: Set<AccountId | GroupId>,
-  allowAllOwnersSubstition: boolean
-): Array<GroupedFiles> {
-  const getDisplayOwnersFunc =
-    !allowAllOwnersSubstition ||
-    allOwnersByPathMap.size === 0 ||
-    reviewersIdSet.size === 0
-      ? (file: FetchedFile) => file.info.owners
-      : (file: FetchedFile) =>
+    files: Array<FetchedFile>,
+    allOwnersByPathMap: Map<string, CodeOwnersInfo|undefined>,
+    reviewersIdSet: Set<AccountId|GroupId>,
+    allowAllOwnersSubstition: boolean): Array<GroupedFiles> {
+  const getDisplayOwnersFunc = !allowAllOwnersSubstition ||
+          allOwnersByPathMap.size === 0 || reviewersIdSet.size === 0 ?
+      (file: FetchedFile) => file.info.owners :
+      (file: FetchedFile) =>
           getDisplayOwners(file, allOwnersByPathMap, reviewersIdSet);
   return groupFilesByOwners(files, getDisplayOwnersFunc);
 }
 
 function getDisplayOwners(
-  file: FetchedFile,
-  allOwnersByPathMap: Map<String, CodeOwnersInfo | undefined>,
-  reviewersIdSet: Set<AccountId | GroupId>
-) {
+    file: FetchedFile,
+    allOwnersByPathMap: Map<String, CodeOwnersInfo|undefined>,
+    reviewersIdSet: Set<AccountId|GroupId>) {
   const ownerSelected = (owner: CodeOwnerInfo) =>
-    owner?.account?._account_id !== undefined &&
-    reviewersIdSet.has(owner.account._account_id);
+      owner?.account?._account_id !== undefined &&
+      reviewersIdSet.has(owner.account._account_id);
   const defaultOwners = file.info.owners;
-  if (
-    !defaultOwners ||
-    defaultOwners.owned_by_all_users ||
-    defaultOwners.code_owners.some(ownerSelected)
-  ) {
+  if (!defaultOwners || defaultOwners.owned_by_all_users ||
+      defaultOwners.code_owners.some(ownerSelected)) {
     return defaultOwners;
   }
   const allOwners = allOwnersByPathMap.get(file.path);
@@ -84,13 +74,14 @@
 }
 
 function groupFilesByOwners(
-  files: Array<FetchedFile>,
-  getDisplayOwnersFunc: (file: FetchedFile) => CodeOwnersInfo | undefined
-): Array<GroupedFiles> {
+    files: Array<FetchedFile>,
+    getDisplayOwnersFunc: (file: FetchedFile) =>
+        CodeOwnersInfo | undefined): Array<GroupedFiles> {
   // Note: for renamed or moved files, they will have two entries in the map
   // we will treat them as two entries when group as well
-  const ownersFilesMap = new Map();
-  const failedToFetchFiles: Set<FetchedFile> = new Set();
+  const ownersFilesMap =
+      new Map<string, {files: Array<FetchedFile>, owners: CodeOwnersInfo}>();
+  const failedToFetchFiles = new Set<FetchedFile>();
   for (const file of files) {
     // for files failed to fetch, add them to the special group
     if (file.info.error) {
@@ -107,18 +98,17 @@
 
     const ownersKey = getOwnersGroupKey(displayOwners);
     ownersFilesMap.set(
-      ownersKey,
-      ownersFilesMap.get(ownersKey) || {files: [], owners: displayOwners}
-    );
-    ownersFilesMap.get(ownersKey).files.push(file);
+        ownersKey,
+        ownersFilesMap.get(ownersKey) ?? {files: [], owners: displayOwners});
+    ownersFilesMap.get(ownersKey)!.files.push(file);
   }
   const groupedItems = [];
   for (const ownersKey of ownersFilesMap.keys()) {
-    const groupName = getGroupName(ownersFilesMap.get(ownersKey).files);
+    const groupName = getGroupName(ownersFilesMap.get(ownersKey)!.files);
     groupedItems.push({
       groupName,
-      files: ownersFilesMap.get(ownersKey).files,
-      owners: ownersFilesMap.get(ownersKey).owners,
+      files: ownersFilesMap.get(ownersKey)!.files,
+      owners: ownersFilesMap.get(ownersKey)!.owners,
     });
   }
 
@@ -128,8 +118,7 @@
       groupName: getGroupName(failedFiles),
       files: failedFiles,
       error: new Error(
-        'Failed to fetch code owner info. Try to refresh the page.'
-      ),
+          'Failed to fetch code owner info. Try to refresh the page.'),
     });
   }
   return groupedItems;
@@ -140,10 +129,7 @@
     return '__owned_by_all_users__';
   }
   const code_owners = owners.code_owners;
-  return code_owners
-    .map(owner => owner.account?._account_id)
-    .sort()
-    .join(',');
+  return code_owners.map(owner => owner.account?._account_id).sort().join(',');
 }
 
 function getGroupName(files: Array<FetchedFile>) {