Updated TS types and added support
ci/woodpecker/push/ociImagePush Pipeline is running Details

This commit is contained in:
Natty 2024-01-09 23:34:14 +01:00
parent 1bef42ead5
commit b2543b29d3
Signed by: natty
GPG Key ID: BF6CB659ADEE60EC
21 changed files with 139 additions and 48 deletions

View File

@ -18,9 +18,9 @@ import {
FrontendApiEndpoints, FrontendApiEndpoints,
MagApiClient, MagApiClient,
Method, Method,
types,
} from "magnetar-common"; } from "magnetar-common";
import { magReactionToLegacy } from "@/scripts-mag/mag-util"; import { magReactionToLegacy } from "@/scripts-mag/mag-util";
import { types } from "magnetar-common";
export const pendingApiRequestsCount = ref(0); export const pendingApiRequestsCount = ref(0);
@ -35,11 +35,13 @@ export async function magApi<
T["method"] & Method, T["method"] & Method,
T["pathParams"] & string[], T["pathParams"] & string[],
T["request"], T["request"],
T["response"] T["response"],
T["paginated"] & boolean
> >
>( >(
endpoint: T, endpoint: T,
data: T["request"], data: T["request"] &
(T["paginated"] extends true ? Partial<types.PaginationShape> : any),
pathParams: { pathParams: {
[K in keyof T["pathParams"] as T["pathParams"][K] & string]: [K in keyof T["pathParams"] as T["pathParams"][K] & string]:
| string | string

View File

@ -1,16 +1,20 @@
import { PaginationShape } from "./types/PaginationShape";
export type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; export type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
export interface BackendApiEndpoint< export interface BackendApiEndpoint<
M extends Method, M extends Method,
PP extends string[], PP extends string[],
T, T,
R R,
PG extends boolean
> { > {
method: M; method: M;
endpoint: string; endpoint: string;
pathParams: PP; pathParams: PP;
request?: T; request?: T;
response?: R; response?: R;
paginated: PG;
} }
function nestedUrlSearchParams(data: any, topLevel: boolean = true): string { function nestedUrlSearchParams(data: any, topLevel: boolean = true): string {
@ -52,6 +56,39 @@ export interface MagApiError {
message: string; message: string;
} }
export interface PaginatedResult<T> {
prev?: URL;
data: T;
next?: URL;
}
function extractHeaderRel(
headers: Headers,
rel: "prev" | "next"
): URL | undefined {
for (const [k, v] of headers) {
if (k.toLowerCase() !== "link") {
continue;
}
for (const links of v.split(",").map(String.prototype.trim)) {
const [url, relPar] = links.split(";").map(String.prototype.trim);
if (!url || !relPar) {
continue;
}
const urlMatch = url.match(/^<(.+?)>$/)?.[1];
const relMatch = relPar.match(/rel="(.+?)"/)?.[1];
if (relMatch == rel && urlMatch) {
return new URL(urlMatch);
}
}
}
return undefined;
}
export class MagApiClient { export class MagApiClient {
private readonly baseUrl: string; private readonly baseUrl: string;
@ -64,18 +101,24 @@ export class MagApiClient {
T["method"] & Method, T["method"] & Method,
T["pathParams"] & string[], T["pathParams"] & string[],
T["request"], T["request"],
T["response"] T["response"],
T["paginated"] & boolean
> >
>( >(
{ endpoint, method }: T, { endpoint, method, paginated }: T,
data: T["request"], data: T["request"] &
(T["paginated"] extends true ? Partial<PaginationShape> : any),
pathParams: { pathParams: {
[K in keyof T["pathParams"] as T["pathParams"][K] & string]: [K in keyof T["pathParams"] as T["pathParams"][K] & string]:
| string | string
| number; | number;
}, },
token?: string | null | undefined token?: string | null | undefined
): Promise<T["response"]> { ): Promise<
T["paginated"] extends true
? PaginatedResult<T["response"]>
: T["response"]
> {
type Response = T["response"]; type Response = T["response"];
const authorizationToken = token ?? undefined; const authorizationToken = token ?? undefined;
@ -97,7 +140,7 @@ export class MagApiClient {
} }
} }
return await fetch(url, { return (await fetch(url, {
method, method,
body: method !== "GET" ? JSON.stringify(data) : undefined, body: method !== "GET" ? JSON.stringify(data) : undefined,
credentials: "omit", credentials: "omit",
@ -108,9 +151,25 @@ export class MagApiClient {
const body = res.status === 204 ? null : await res.json(); const body = res.status === 204 ? null : await res.json();
if (res.status === 200) { if (res.status === 200) {
if (paginated) {
return {
prev: extractHeaderRel(res.headers, "prev"),
data: body as Response,
next: extractHeaderRel(res.headers, "next"),
};
} else {
return body as Response; return body as Response;
}
} else if (res.status === 204) { } else if (res.status === 204) {
if (paginated) {
return {
prev: extractHeaderRel(res.headers, "prev"),
data: null as any as Response,
next: extractHeaderRel(res.headers, "next"),
};
} else {
return null as any as Response; return null as any as Response;
}
} else { } else {
throw body as MagApiError; throw body as MagApiError;
} }
@ -121,6 +180,8 @@ export class MagApiClient {
code: "Client:GenericApiError", code: "Client:GenericApiError",
message: e, message: e,
} as MagApiError; } as MagApiError;
}); })) as T["paginated"] extends true
? PaginatedResult<Response>
: Response;
} }
} }

View File

@ -48,3 +48,4 @@ export { StartFilter } from "./types/StartFilter";
export { NoFilter } from "./types/NoFilter"; export { NoFilter } from "./types/NoFilter";
export { TimelineType } from "./types/TimelineType"; export { TimelineType } from "./types/TimelineType";
export { Empty } from "./types/Empty"; export { Empty } from "./types/Empty";
export { PaginationShape } from "./types/PaginationShape";

View File

@ -1,3 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type Empty = null; export type Empty = Record<string, never>;

View File

@ -1,5 +1,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { NoteListFilter } from "./NoteListFilter"; import type { NoteListFilter } from "./NoteListFilter";
import type { SpanFilter } from "./SpanFilter";
export interface GetTimelineReq { limit?: bigint, filter?: NoteListFilter, range?: SpanFilter, } export interface GetTimelineReq { filter?: NoteListFilter, }

View File

@ -0,0 +1,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SpanFilter } from "./SpanFilter";
export interface PaginationShape { pagination: SpanFilter, limit: bigint, }

View File

@ -1,12 +1,13 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Empty } from "../Empty"; import type { Empty } from "../Empty";
import type { PackUserBase } from "../packed/PackUserBase"; import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
export const GetFollowRequestsSelf = { export const GetFollowRequestsSelf = {
endpoint: "/users/@self/follow-requests", endpoint: "/users/@self/follow-requests",
pathParams: [] as [], pathParams: [] as [],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH", method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
request: undefined as unknown as Empty, request: undefined as unknown as Empty,
response: undefined as unknown as Array<PackUserBase> response: undefined as unknown as Array<PackUserMaybeAll>,
paginated: true as true
} }

View File

@ -1,12 +1,13 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Empty } from "../Empty"; import type { Empty } from "../Empty";
import type { PackUserBase } from "../packed/PackUserBase"; import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
export const GetFollowersById = { export const GetFollowersById = {
endpoint: "/users/:id/followers", endpoint: "/users/:id/followers",
pathParams: ["id"] as ["id"], pathParams: ["id"] as ["id"],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH", method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
request: undefined as unknown as Empty, request: undefined as unknown as Empty,
response: undefined as unknown as Array<PackUserBase> response: undefined as unknown as Array<PackUserMaybeAll>,
paginated: true as true
} }

View File

@ -1,12 +1,13 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Empty } from "../Empty"; import type { Empty } from "../Empty";
import type { PackUserBase } from "../packed/PackUserBase"; import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
export const GetFollowersSelf = { export const GetFollowersSelf = {
endpoint: "/users/@self/followers", endpoint: "/users/@self/followers",
pathParams: [] as [], pathParams: [] as [],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH", method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
request: undefined as unknown as Empty, request: undefined as unknown as Empty,
response: undefined as unknown as Array<PackUserBase> response: undefined as unknown as Array<PackUserMaybeAll>,
paginated: true as true
} }

View File

@ -1,12 +1,13 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Empty } from "../Empty"; import type { Empty } from "../Empty";
import type { PackUserBase } from "../packed/PackUserBase"; import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
export const GetFollowingById = { export const GetFollowingById = {
endpoint: "/users/:id/following", endpoint: "/users/:id/following",
pathParams: ["id"] as ["id"], pathParams: ["id"] as ["id"],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH", method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
request: undefined as unknown as Empty, request: undefined as unknown as Empty,
response: undefined as unknown as Array<PackUserBase> response: undefined as unknown as Array<PackUserMaybeAll>,
paginated: true as true
} }

View File

@ -1,12 +1,13 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Empty } from "../Empty"; import type { Empty } from "../Empty";
import type { PackUserBase } from "../packed/PackUserBase"; import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
export const GetFollowingSelf = { export const GetFollowingSelf = {
endpoint: "/users/@self/following", endpoint: "/users/@self/following",
pathParams: [] as [], pathParams: [] as [],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH", method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
request: undefined as unknown as Empty, request: undefined as unknown as Empty,
response: undefined as unknown as Array<PackUserBase> response: undefined as unknown as Array<PackUserMaybeAll>,
paginated: true as true
} }

View File

@ -7,6 +7,7 @@ export const GetManyUsersById = {
pathParams: [] as [], pathParams: [] as [],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH", method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
request: undefined as unknown as ManyUsersByIdReq, request: undefined as unknown as ManyUsersByIdReq,
response: undefined as unknown as Array<PackUserBase | null> response: undefined as unknown as Array<PackUserBase | null>,
paginated: false as false
} }

View File

@ -7,6 +7,7 @@ export const GetNoteById = {
pathParams: ["id"] as ["id"], pathParams: ["id"] as ["id"],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH", method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
request: undefined as unknown as NoteByIdReq, request: undefined as unknown as NoteByIdReq,
response: undefined as unknown as PackNoteMaybeFull response: undefined as unknown as PackNoteMaybeFull,
paginated: false as false
} }

View File

@ -7,6 +7,7 @@ export const GetTimeline = {
pathParams: ["timeline_type"] as ["timeline_type"], pathParams: ["timeline_type"] as ["timeline_type"],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH", method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
request: undefined as unknown as GetTimelineReq, request: undefined as unknown as GetTimelineReq,
response: undefined as unknown as Array<PackNoteMaybeFull> response: undefined as unknown as Array<PackNoteMaybeFull>,
paginated: true as true
} }

View File

@ -7,6 +7,7 @@ export const GetUserByAcct = {
pathParams: ["user_acct"] as ["user_acct"], pathParams: ["user_acct"] as ["user_acct"],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH", method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
request: undefined as unknown as UserByIdReq, request: undefined as unknown as UserByIdReq,
response: undefined as unknown as PackUserMaybeAll response: undefined as unknown as PackUserMaybeAll,
paginated: false as false
} }

View File

@ -7,6 +7,7 @@ export const GetUserById = {
pathParams: ["user_id"] as ["user_id"], pathParams: ["user_id"] as ["user_id"],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH", method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
request: undefined as unknown as UserByIdReq, request: undefined as unknown as UserByIdReq,
response: undefined as unknown as PackUserMaybeAll response: undefined as unknown as PackUserMaybeAll,
paginated: false as false
} }

View File

@ -7,6 +7,7 @@ export const GetUserSelf = {
pathParams: [] as [], pathParams: [] as [],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH", method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
request: undefined as unknown as UserSelfReq, request: undefined as unknown as UserSelfReq,
response: undefined as unknown as PackUserSelfMaybeAll response: undefined as unknown as PackUserSelfMaybeAll,
paginated: false as false
} }

View File

@ -155,6 +155,7 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
let mut method = None; let mut method = None;
let mut request = None; let mut request = None;
let mut response = None; let mut response = None;
let mut paginated = false;
let mut request_args = None; let mut request_args = None;
let mut response_args = None; let mut response_args = None;
@ -203,6 +204,17 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
name = new_name.value(); name = new_name.value();
} }
"paginated" => {
let Expr::Lit(ExprLit {
lit: Lit::Bool(val),
..
}) = value
else {
panic!("expected a boolean");
};
paginated = val.value();
}
"endpoint" => { "endpoint" => {
let Expr::Lit(ExprLit { let Expr::Lit(ExprLit {
lit: Lit::Str(endpoint_val), lit: Lit::Str(endpoint_val),
@ -376,6 +388,7 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
const NAME: &'static str = #name; const NAME: &'static str = #name;
const ENDPOINT: &'static str = #endpoint; const ENDPOINT: &'static str = #endpoint;
const METHOD: Method = #method; const METHOD: Method = #method;
const PAGINATED: bool = #paginated;
type Request = #request; type Request = #request;
type Response = #response; type Response = #response;
@ -397,7 +410,8 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
pathParams: {} as {},\n \ pathParams: {} as {},\n \
method: \"{}\" as \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\",\n \ method: \"{}\" as \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\",\n \
request: undefined as unknown as {},\n \ request: undefined as unknown as {},\n \
response: undefined as unknown as {}\n\ response: undefined as unknown as {},\n \
paginated: {} as {}\n\
}} }}
", ",
Self::name(), Self::name(),
@ -406,7 +420,9 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
#path_params, #path_params,
Self::METHOD, Self::METHOD,
#call_name_req, #call_name_req,
#call_name_res #call_name_res,
#paginated,
#paginated
) )
} }

View File

@ -34,6 +34,7 @@ pub trait Endpoint {
const NAME: &'static str; const NAME: &'static str;
const ENDPOINT: &'static str; const ENDPOINT: &'static str;
const METHOD: Method; const METHOD: Method;
const PAGINATED: bool;
type Request: Serialize + DeserializeOwned + Send + Sync + 'static; type Request: Serialize + DeserializeOwned + Send + Sync + 'static;
type Response: Serialize + DeserializeOwned + Send + Sync + 'static; type Response: Serialize + DeserializeOwned + Send + Sync + 'static;

View File

@ -1,7 +1,5 @@
use crate::endpoints::Endpoint; use crate::endpoints::Endpoint;
use crate::types::note::{NoteListFilter, PackNoteMaybeFull}; use crate::types::note::{NoteListFilter, PackNoteMaybeFull};
use crate::types::SpanFilter;
use crate::util_types::U64Range;
use http::Method; use http::Method;
use magnetar_sdk_macros::Endpoint; use magnetar_sdk_macros::Endpoint;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -11,16 +9,8 @@ use ts_rs::TS;
#[derive(Serialize, Deserialize, TS)] #[derive(Serialize, Deserialize, TS)]
#[ts(export)] #[ts(export)]
pub struct GetTimelineReq { pub struct GetTimelineReq {
#[ts(optional)]
pub limit: Option<U64Range<1, 100>>,
#[ts(optional)] #[ts(optional)]
pub filter: Option<NoteListFilter>, pub filter: Option<NoteListFilter>,
#[ts(optional)]
pub range: Option<SpanFilter>,
}
pub fn default_timeline_limit<const MIN: u64, const MAX: u64>() -> U64Range<MIN, MAX> {
30.try_into().unwrap()
} }
#[derive(Endpoint)] #[derive(Endpoint)]
@ -29,5 +19,6 @@ pub fn default_timeline_limit<const MIN: u64, const MAX: u64>() -> U64Range<MIN,
method = Method::GET, method = Method::GET,
request = "GetTimelineReq", request = "GetTimelineReq",
response = "Vec<PackNoteMaybeFull>", response = "Vec<PackNoteMaybeFull>",
paginated = true
)] )]
pub struct GetTimeline; pub struct GetTimeline;

View File

@ -88,7 +88,8 @@ pub struct GetUserByAcct;
endpoint = "/users/:id/followers", endpoint = "/users/:id/followers",
method = Method::GET, method = Method::GET,
request = "Empty", request = "Empty",
response = "Vec<PackUserMaybeAll>" response = "Vec<PackUserMaybeAll>",
paginated = true
)] )]
pub struct GetFollowersById; pub struct GetFollowersById;
@ -97,7 +98,8 @@ pub struct GetFollowersById;
endpoint = "/users/:id/following", endpoint = "/users/:id/following",
method = Method::GET, method = Method::GET,
request = "Empty", request = "Empty",
response = "Vec<PackUserMaybeAll>" response = "Vec<PackUserMaybeAll>",
paginated = true
)] )]
pub struct GetFollowingById; pub struct GetFollowingById;
@ -106,7 +108,8 @@ pub struct GetFollowingById;
endpoint = "/users/@self/followers", endpoint = "/users/@self/followers",
method = Method::GET, method = Method::GET,
request = "Empty", request = "Empty",
response = "Vec<PackUserMaybeAll>" response = "Vec<PackUserMaybeAll>",
paginated = true
)] )]
pub struct GetFollowersSelf; pub struct GetFollowersSelf;
@ -115,7 +118,8 @@ pub struct GetFollowersSelf;
endpoint = "/users/@self/following", endpoint = "/users/@self/following",
method = Method::GET, method = Method::GET,
request = "Empty", request = "Empty",
response = "Vec<PackUserMaybeAll>" response = "Vec<PackUserMaybeAll>",
paginated = true
)] )]
pub struct GetFollowingSelf; pub struct GetFollowingSelf;
@ -124,6 +128,7 @@ pub struct GetFollowingSelf;
endpoint = "/users/@self/follow-requests", endpoint = "/users/@self/follow-requests",
method = Method::GET, method = Method::GET,
request = "Empty", request = "Empty",
response = "Vec<PackUserMaybeAll>" response = "Vec<PackUserMaybeAll>",
paginated = true
)] )]
pub struct GetFollowRequestsSelf; pub struct GetFollowRequestsSelf;