Create a service to fetch the match list.

I plan to reuse the functionality for creating a notes scouting
schedule.

Change-Id: Ia449a6567ec63f5faca1af6e4a154cb77c359c88
Signed-off-by: Alex Perry <alex.perry96@gmail.com>
diff --git a/scouting/www/rpc/BUILD b/scouting/www/rpc/BUILD
new file mode 100644
index 0000000..581a7b8
--- /dev/null
+++ b/scouting/www/rpc/BUILD
@@ -0,0 +1,19 @@
+load("@npm//@bazel/typescript:index.bzl", "ts_library")
+
+ts_library(
+    name = "rpc",
+    srcs = [
+        "match_list_requestor.ts",
+    ],
+    compiler = "//tools:tsc_wrapped_with_angular",
+    target_compatible_with = ["@platforms//cpu:x86_64"],
+    use_angular_plugin = True,
+    visibility = ["//visibility:public"],
+    deps = [
+        "//scouting/webserver/requests/messages:error_response_ts_fbs",
+        "//scouting/webserver/requests/messages:request_all_matches_response_ts_fbs",
+        "//scouting/webserver/requests/messages:request_all_matches_ts_fbs",
+        "@com_github_google_flatbuffers//ts:flatbuffers_ts",
+        "@npm//@angular/core",
+    ],
+)
diff --git a/scouting/www/rpc/match_list_requestor.ts b/scouting/www/rpc/match_list_requestor.ts
new file mode 100644
index 0000000..917a9b3
--- /dev/null
+++ b/scouting/www/rpc/match_list_requestor.ts
@@ -0,0 +1,68 @@
+import {Injectable} from '@angular/core';
+import {Builder, ByteBuffer} from 'flatbuffers';
+import {ErrorResponse} from 'org_frc971/scouting/webserver/requests/messages/error_response_generated';
+import {RequestAllMatches} from 'org_frc971/scouting/webserver/requests/messages/request_all_matches_generated';
+import {
+  Match,
+  RequestAllMatchesResponse,
+} from 'org_frc971/scouting/webserver/requests/messages/request_all_matches_response_generated';
+
+const MATCH_TYPE_ORDERING = ['qm', 'ef', 'qf', 'sf', 'f'];
+
+@Injectable({providedIn: 'root'})
+export class MatchListRequestor {
+  async fetchMatchList(): Promise<Match[]> {
+    const builder = new Builder();
+    RequestAllMatches.startRequestAllMatches(builder);
+    builder.finish(RequestAllMatches.endRequestAllMatches(builder));
+
+    const buffer = builder.asUint8Array();
+    const res = await fetch('/requests/request/all_matches', {
+      method: 'POST',
+      body: buffer,
+    });
+
+    if (res.ok) {
+      const resBuffer = await res.arrayBuffer();
+      const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
+      const parsedResponse =
+        RequestAllMatchesResponse.getRootAsRequestAllMatchesResponse(fbBuffer);
+
+      // Convert the flatbuffer list into an array. That's more useful.
+      const matchList = [];
+      for (let i = 0; i < parsedResponse.matchListLength(); i++) {
+        matchList.push(parsedResponse.matchList(i));
+      }
+
+      // Sort the list so it is in chronological order.
+      matchList.sort((a, b) => {
+        const aMatchTypeIndex = MATCH_TYPE_ORDERING.indexOf(a.compLevel());
+        const bMatchTypeIndex = MATCH_TYPE_ORDERING.indexOf(b.compLevel());
+        if (aMatchTypeIndex < bMatchTypeIndex) {
+          return -1;
+        }
+        if (aMatchTypeIndex > bMatchTypeIndex) {
+          return 1;
+        }
+        const aMatchNumber = a.matchNumber();
+        const bMatchNumber = b.matchNumber();
+        if (aMatchNumber < bMatchNumber) {
+          return -1;
+        }
+        if (aMatchNumber > bMatchNumber) {
+          return 1;
+        }
+        return 0;
+      });
+
+      return matchList;
+    } else {
+      const resBuffer = await res.arrayBuffer();
+      const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
+      const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
+
+      const errorMessage = parsedResponse.errorMessage();
+      throw `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
+    }
+  }
+}