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

View File

@ -1,16 +1,20 @@
import { PaginationShape } from "./types/PaginationShape";
export type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
export interface BackendApiEndpoint<
M extends Method,
PP extends string[],
T,
R
R,
PG extends boolean
> {
method: M;
endpoint: string;
pathParams: PP;
request?: T;
response?: R;
paginated: PG;
}
function nestedUrlSearchParams(data: any, topLevel: boolean = true): string {
@ -52,6 +56,39 @@ export interface MagApiError {
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 {
private readonly baseUrl: string;
@ -64,18 +101,24 @@ export class MagApiClient {
T["method"] & Method,
T["pathParams"] & string[],
T["request"],
T["response"]
T["response"],
T["paginated"] & boolean
>
>(
{ endpoint, method }: T,
data: T["request"],
{ endpoint, method, paginated }: T,
data: T["request"] &
(T["paginated"] extends true ? Partial<PaginationShape> : any),
pathParams: {
[K in keyof T["pathParams"] as T["pathParams"][K] & string]:
| string
| number;
},
token?: string | null | undefined
): Promise<T["response"]> {
): Promise<
T["paginated"] extends true
? PaginatedResult<T["response"]>
: T["response"]
> {
type Response = T["response"];
const authorizationToken = token ?? undefined;
@ -97,7 +140,7 @@ export class MagApiClient {
}
}
return await fetch(url, {
return (await fetch(url, {
method,
body: method !== "GET" ? JSON.stringify(data) : undefined,
credentials: "omit",
@ -108,9 +151,25 @@ export class MagApiClient {
const body = res.status === 204 ? null : await res.json();
if (res.status === 200) {
return body as Response;
if (paginated) {
return {
prev: extractHeaderRel(res.headers, "prev"),
data: body as Response,
next: extractHeaderRel(res.headers, "next"),
};
} else {
return body as Response;
}
} else if (res.status === 204) {
return null as any as Response;
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;
}
} else {
throw body as MagApiError;
}
@ -121,6 +180,8 @@ export class MagApiClient {
code: "Client:GenericApiError",
message: e,
} 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 { TimelineType } from "./types/TimelineType";
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.
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.
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.
import type { Empty } from "../Empty";
import type { PackUserBase } from "../packed/PackUserBase";
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
export const GetFollowRequestsSelf = {
endpoint: "/users/@self/follow-requests",
pathParams: [] as [],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
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.
import type { Empty } from "../Empty";
import type { PackUserBase } from "../packed/PackUserBase";
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
export const GetFollowersById = {
endpoint: "/users/:id/followers",
pathParams: ["id"] as ["id"],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
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.
import type { Empty } from "../Empty";
import type { PackUserBase } from "../packed/PackUserBase";
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
export const GetFollowersSelf = {
endpoint: "/users/@self/followers",
pathParams: [] as [],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
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.
import type { Empty } from "../Empty";
import type { PackUserBase } from "../packed/PackUserBase";
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
export const GetFollowingById = {
endpoint: "/users/:id/following",
pathParams: ["id"] as ["id"],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
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.
import type { Empty } from "../Empty";
import type { PackUserBase } from "../packed/PackUserBase";
import type { PackUserMaybeAll } from "../packed/PackUserMaybeAll";
export const GetFollowingSelf = {
endpoint: "/users/@self/following",
pathParams: [] as [],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
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 [],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
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"],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
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"],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
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"],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
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"],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
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 [],
method: "GET" as "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
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 request = None;
let mut response = None;
let mut paginated = false;
let mut request_args = None;
let mut response_args = None;
@ -203,6 +204,17 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
name = new_name.value();
}
"paginated" => {
let Expr::Lit(ExprLit {
lit: Lit::Bool(val),
..
}) = value
else {
panic!("expected a boolean");
};
paginated = val.value();
}
"endpoint" => {
let Expr::Lit(ExprLit {
lit: Lit::Str(endpoint_val),
@ -376,6 +388,7 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
const NAME: &'static str = #name;
const ENDPOINT: &'static str = #endpoint;
const METHOD: Method = #method;
const PAGINATED: bool = #paginated;
type Request = #request;
type Response = #response;
@ -397,7 +410,8 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
pathParams: {} as {},\n \
method: \"{}\" as \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\",\n \
request: undefined as unknown as {},\n \
response: undefined as unknown as {}\n\
response: undefined as unknown as {},\n \
paginated: {} as {}\n\
}}
",
Self::name(),
@ -406,7 +420,9 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
#path_params,
Self::METHOD,
#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 ENDPOINT: &'static str;
const METHOD: Method;
const PAGINATED: bool;
type Request: Serialize + DeserializeOwned + Send + Sync + 'static;
type Response: Serialize + DeserializeOwned + Send + Sync + 'static;

View File

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

View File

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