g1t/packages/contracts/src/clients.ts

78 lines3,581 bytesCodeBlame
1import type { IdentityApi } from "./identity";
2import type { ReposApi } from "./repos";
3
4/** A service binding, as far as these clients need it. */
5export type ServiceBinding = {
6 fetch(input: string, init?: RequestInit): Promise<Response>;
7};
8
9/**
10 * Calls a method on a service that speaks the JSON protocol used by the
11 * Rust services: `POST /rpc/<method>` with the arguments as the body.
12 */
13async function rpc<T>(
14 service: ServiceBinding,
15 method: string,
16 args: object,
17): Promise<T> {
18 // The hostname is ignored; a service binding always reaches its service.
19 const response = await service.fetch(`https://service/rpc/${method}`, {
20 method: "POST",
21 headers: { "content-type": "application/json" },
22 body: JSON.stringify(args),
23 });
24 if (!response.ok) {
25 throw new Error(`${method} failed with status ${response.status}`);
26 }
27 return response.json();
28}
29
30export function identityClient(service: ServiceBinding): IdentityApi {
31 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
32 return {
33 register: (username, email, password) =>
34 call("register", { username, email, password }),
35 signIn: (username, password) => call("sign_in", { username, password }),
36 signOut: (sessionToken) => call("sign_out", { sessionToken }),
37 resendVerification: (user) => call("resend_verification", { user }),
38 verifyEmail: (token) => call("verify_email", { token }),
39 requestPasswordReset: (email) => call("request_password_reset", { email }),
40 resetPassword: (token, password) =>
41 call("reset_password", { token, password }),
42 userForSession: (sessionToken) => call("user_for_session", { sessionToken }),
43 userForGitCredentials: (username, secret) =>
44 call("user_for_git_credentials", { username, secret }),
45 userForAccessToken: (token) => call("user_for_access_token", { token }),
46 userForSshKey: (fingerprint) => call("user_for_ssh_key", { fingerprint }),
47 userByUsername: (username) => call("user_by_username", { username }),
48 listSshKeys: (user) => call("list_ssh_keys", { user }),
49 addSshKey: (user, title, publicKey) =>
50 call("add_ssh_key", { user, title, publicKey }),
51 removeSshKey: (user, id) => call("remove_ssh_key", { user, id }),
52 listAccessTokens: (user) => call("list_access_tokens", { user }),
53 createAccessToken: (user, name, ttlSeconds) =>
54 call("create_access_token", { user, name, ttlSeconds }),
55 removeAccessToken: (user, id) => call("remove_access_token", { user, id }),
56 };
57}
58
59export function reposClient(service: ServiceBinding): ReposApi {
60 const call = <T>(method: string, args: object) => rpc<T>(service, method, args);
61 return {
62 get: (path, viewer) => call("get", { path, viewer }),
63 getById: (id, viewer) => call("get_by_id", { id, viewer }),
64 list: (viewer, options = {}) => call("list", { viewer, ...options }),
65 create: (owner, input) => call("create", { owner, ...input }),
66 tree: (path, viewer, ref, treePath) =>
67 call("tree", { path, viewer, ref, treePath }),
68 blob: (path, viewer, ref, filePath) =>
69 call("blob", { path, viewer, ref, filePath }),
70 log: (path, viewer, ref, limit) => call("log", { path, viewer, ref, limit }),
71 forkForAttempt: (sourceId, attemptId, actor) =>
72 call("fork_for_attempt", { sourceId, attemptId, actor }),
73 gitAccess: (path, viewer, service) =>
74 call("git_access", { path, viewer, service }),
75 land: (forkId, actor) => call("land", { forkId, actor }),
76 compare: (repoId, viewer, base) => call("compare", { repoId, viewer, base }),
77 };
78}