Add PolyGerrit UI to list service users

The GWT-based UI was removed recently to allow compatibility with Gerrit
3.0+.

This change adds a screen listing all service users as a first step to
reimplement the UI with PolyGerrit. The new screen can be found under
'Browse' > 'Service Users'.

The list does currently not support pagination, since the REST API of
the plugin does not support it yet. Also, filtering is currently not
implemented, since the router does not support query operators for
plugin screens.

Change-Id: Ibcbfac311e9a279d14dc92299e6d963cef19a87b
diff --git a/BUILD b/BUILD
index 06e79d5..36d967f 100644
--- a/BUILD
+++ b/BUILD
@@ -2,11 +2,12 @@
 
 gerrit_plugin(
     name = "serviceuser",
-    srcs = glob(["src/main/java/**/*.java"]),
+    srcs = glob(["src/main/java/com/googlesource/gerrit/plugins/serviceuser/**/*.java"]),
     manifest_entries = [
         "Gerrit-PluginName: serviceuser",
         "Gerrit-Module: com.googlesource.gerrit.plugins.serviceuser.Module",
+        "Gerrit-HttpModule: com.googlesource.gerrit.plugins.serviceuser.HttpModule",
         "Gerrit-SshModule: com.googlesource.gerrit.plugins.serviceuser.SshModule",
     ],
-    resources = glob(["src/main/**/*"]),
+    resources = glob(["src/main/resources/**/*"]),
 )
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/HttpModule.java
new file mode 100644
index 0000000..a99987b
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/HttpModule.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2019 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.
+
+package com.googlesource.gerrit.plugins.serviceuser;
+
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.webui.JavaScriptPlugin;
+import com.google.gerrit.extensions.webui.WebUiPlugin;
+import com.google.inject.AbstractModule;
+
+public class HttpModule extends AbstractModule {
+  @Override
+  protected void configure() {
+    DynamicSet.bind(binder(), WebUiPlugin.class)
+        .toInstance(new JavaScriptPlugin("gr-serviceuser.html"));
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/Module.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/Module.java
index ca6f8f0..56d14e1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/Module.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.extensions.restapi.RestApiModule;
-import com.google.gerrit.extensions.webui.TopMenu;
 import com.google.gerrit.server.git.validators.CommitValidationListener;
 import com.google.inject.AbstractModule;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
@@ -71,5 +70,6 @@
             delete(SERVICE_USER_KIND, "owner").to(PutOwner.class);
           }
         });
+    install(new HttpModule());
   }
 }
diff --git a/src/main/resources/static/gr-serviceuser-list.html b/src/main/resources/static/gr-serviceuser-list.html
new file mode 100644
index 0000000..3ff745c
--- /dev/null
+++ b/src/main/resources/static/gr-serviceuser-list.html
@@ -0,0 +1,62 @@
+<!--
+@license
+Copyright (C) 2019 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.
+-->
+
+<dom-module id="gr-serviceuser-list">
+  <template>
+    <style include="shared-styles"></style>
+    <style include="gr-table-styles"></style>
+    <style>
+      .topHeader {
+        padding: 8px;
+      }
+    </style>
+    <div class="topHeader">
+      <h2>Service Users</h2>
+    </div>
+    <table id="list"
+           class="genericList">
+      <tr class="headerRow">
+        <th class="name topHeader">Username</th>
+        <th class="fullName topHeader">Full Name</th>
+        <th class="email topHeader">Email</th>
+        <th class="owner topHeader">Owner</th>
+        <th class="createdBy topHeader">Created By</th>
+        <th class="createdAt topHeader">Created At</th>
+        <th class="accountState topHeader">Account State</th>
+      </tr>
+      <tr id="loading"
+          class$="loadingMsg [[computeLoadingClass(_loading)]]">
+        <td>Loading...</td>
+      </tr>
+      <tbody class$="[[computeLoadingClass(_loading)]]">
+        <template is="dom-repeat"
+                  items="[[_serviceUsers]]">
+          <tr class="table">
+            <td class="name">[[item.username]]</td>
+            <td class="fullName">[[item.name]]</td>
+            <td class="email">[[item.email]]</td>
+            <td class="owner">[[_getOwnerGroup(item)]]</td>
+            <td class="createdBy">[[_getCreator(item)]]</td>
+            <td class="createdAt">[[item.created_at]]</td>
+            <td class="accountState">[[_active(item)]]</td>
+          </tr>
+        </template>
+      </tbody>
+    </table>
+  </template>
+  <script src="gr-serviceuser-list.js"></script>
+</dom-module>
diff --git a/src/main/resources/static/gr-serviceuser-list.js b/src/main/resources/static/gr-serviceuser-list.js
new file mode 100644
index 0000000..1a6272a
--- /dev/null
+++ b/src/main/resources/static/gr-serviceuser-list.js
@@ -0,0 +1,87 @@
+// Copyright (C) 2019 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.
+
+(function() {
+  'use strict';
+
+  const NOT_FOUND_MESSAGE = 'Not Found';
+
+  Polymer({
+    is: 'gr-serviceuser-list',
+    _legacyUndefinedCheck: true,
+
+    properties: {
+      _serviceUsers: Array,
+      _loading: {
+        type: Boolean,
+        value: true,
+      },
+    },
+
+    behaviors: [
+      Gerrit.ListViewBehavior,
+    ],
+
+    attached() {
+      this.fire('title-change', {title: 'Service Users'});
+      this._getServiceUsers();
+    },
+
+    _getServiceUsers() {
+      return this.plugin.restApi('/config/server/serviceuser~serviceusers/')
+          .get('')
+          .then(serviceUsers => {
+            if (!serviceUsers) {
+              this._serviceUsers = [];
+              return;
+            }
+            this._serviceUsers = Object.keys(serviceUsers)
+            .map(key => {
+              const serviceUser = serviceUsers[key];
+              serviceUser.username = key;
+              return serviceUser;
+            });
+            this._loading = false;
+          });
+    },
+
+    _active(item) {
+      if (!item) {
+        return NOT_FOUND_MESSAGE;
+      }
+
+      return item.inactive === true ? 'Inactive' : 'Active';
+    },
+
+    _getCreator(item) {
+      if (!item || !item.created_by) {
+        return NOT_FOUND_MESSAGE;
+      }
+
+      if (item.created_by.username != undefined) {
+        return item.created_by.username;
+      }
+
+      if (item.created_by._account_id != -1) {
+        return item.created_by._account_id;
+      }
+
+      return NOT_FOUND_MESSAGE;
+    },
+
+    _getOwnerGroup(item) {
+      return item && item.owner ? item.owner.name : NOT_FOUND_MESSAGE;
+    },
+  });
+})();
diff --git a/src/main/resources/static/gr-serviceuser.html b/src/main/resources/static/gr-serviceuser.html
new file mode 100644
index 0000000..982ba43
--- /dev/null
+++ b/src/main/resources/static/gr-serviceuser.html
@@ -0,0 +1,40 @@
+<!--
+@license
+Copyright (C) 2019 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.
+-->
+
+
+<link rel="import"
+      href="./gr-serviceuser-list.html">
+
+<dom-module id="gr-serviceuser">
+  <script>
+    Gerrit.install(plugin => {
+      plugin.restApi('/accounts/self/capabilities/').get('')
+          .then(capabilities => {
+            if (capabilities
+                && (capabilities.administrateServer
+                    || capabilities['serviceuser-createServiceUser'])) {
+              plugin.screen('list', 'gr-serviceuser-list');
+            }
+            plugin.admin()
+              .addMenuLink(
+                'Service Users',
+                '/x/serviceuser/list',
+                'serviceuser-createServiceUser');
+          });
+    });
+  </script>
+</dom-module>