Add support for generating and uploading files via presigned URLs
Introduces schemas, types, and API endpoints for managing file uploads using presigned URLs. Includes request and response models, React Query hooks, and client SDK functionality for generating and consuming presigned URLs.
This commit is contained in:
@@ -22,6 +22,8 @@ import {
|
|||||||
getEvent,
|
getEvent,
|
||||||
updateEvent,
|
updateEvent,
|
||||||
getEventBySlug,
|
getEventBySlug,
|
||||||
|
generatePresignedUrlApiV1UploadsPresignedUrlPost,
|
||||||
|
uploadFileApiV1UploadsTokenPost,
|
||||||
} from "../sdk.gen";
|
} from "../sdk.gen";
|
||||||
import { queryOptions, type UseMutationOptions } from "@tanstack/react-query";
|
import { queryOptions, type UseMutationOptions } from "@tanstack/react-query";
|
||||||
import type {
|
import type {
|
||||||
@@ -65,6 +67,11 @@ import type {
|
|||||||
UpdateEventError,
|
UpdateEventError,
|
||||||
UpdateEventResponse,
|
UpdateEventResponse,
|
||||||
GetEventBySlugData,
|
GetEventBySlugData,
|
||||||
|
GeneratePresignedUrlApiV1UploadsPresignedUrlPostData,
|
||||||
|
GeneratePresignedUrlApiV1UploadsPresignedUrlPostError,
|
||||||
|
GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponse,
|
||||||
|
UploadFileApiV1UploadsTokenPostData,
|
||||||
|
UploadFileApiV1UploadsTokenPostError,
|
||||||
} from "../types.gen";
|
} from "../types.gen";
|
||||||
import type { AxiosError } from "axios";
|
import type { AxiosError } from "axios";
|
||||||
import { client as _heyApiClient } from "../client.gen";
|
import { client as _heyApiClient } from "../client.gen";
|
||||||
@@ -621,3 +628,88 @@ export const getEventBySlugOptions = (options: Options<GetEventBySlugData>) => {
|
|||||||
queryKey: getEventBySlugQueryKey(options),
|
queryKey: getEventBySlugQueryKey(options),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const generatePresignedUrlApiV1UploadsPresignedUrlPostQueryKey = (
|
||||||
|
options: Options<GeneratePresignedUrlApiV1UploadsPresignedUrlPostData>,
|
||||||
|
) =>
|
||||||
|
createQueryKey("generatePresignedUrlApiV1UploadsPresignedUrlPost", options);
|
||||||
|
|
||||||
|
export const generatePresignedUrlApiV1UploadsPresignedUrlPostOptions = (
|
||||||
|
options: Options<GeneratePresignedUrlApiV1UploadsPresignedUrlPostData>,
|
||||||
|
) => {
|
||||||
|
return queryOptions({
|
||||||
|
queryFn: async ({ queryKey, signal }) => {
|
||||||
|
const { data } = await generatePresignedUrlApiV1UploadsPresignedUrlPost({
|
||||||
|
...options,
|
||||||
|
...queryKey[0],
|
||||||
|
signal,
|
||||||
|
throwOnError: true,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
queryKey: generatePresignedUrlApiV1UploadsPresignedUrlPostQueryKey(options),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generatePresignedUrlApiV1UploadsPresignedUrlPostMutation = (
|
||||||
|
options?: Partial<
|
||||||
|
Options<GeneratePresignedUrlApiV1UploadsPresignedUrlPostData>
|
||||||
|
>,
|
||||||
|
) => {
|
||||||
|
const mutationOptions: UseMutationOptions<
|
||||||
|
GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponse,
|
||||||
|
AxiosError<GeneratePresignedUrlApiV1UploadsPresignedUrlPostError>,
|
||||||
|
Options<GeneratePresignedUrlApiV1UploadsPresignedUrlPostData>
|
||||||
|
> = {
|
||||||
|
mutationFn: async (localOptions) => {
|
||||||
|
const { data } = await generatePresignedUrlApiV1UploadsPresignedUrlPost({
|
||||||
|
...options,
|
||||||
|
...localOptions,
|
||||||
|
throwOnError: true,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return mutationOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const uploadFileApiV1UploadsTokenPostQueryKey = (
|
||||||
|
options: Options<UploadFileApiV1UploadsTokenPostData>,
|
||||||
|
) => createQueryKey("uploadFileApiV1UploadsTokenPost", options);
|
||||||
|
|
||||||
|
export const uploadFileApiV1UploadsTokenPostOptions = (
|
||||||
|
options: Options<UploadFileApiV1UploadsTokenPostData>,
|
||||||
|
) => {
|
||||||
|
return queryOptions({
|
||||||
|
queryFn: async ({ queryKey, signal }) => {
|
||||||
|
const { data } = await uploadFileApiV1UploadsTokenPost({
|
||||||
|
...options,
|
||||||
|
...queryKey[0],
|
||||||
|
signal,
|
||||||
|
throwOnError: true,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
queryKey: uploadFileApiV1UploadsTokenPostQueryKey(options),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const uploadFileApiV1UploadsTokenPostMutation = (
|
||||||
|
options?: Partial<Options<UploadFileApiV1UploadsTokenPostData>>,
|
||||||
|
) => {
|
||||||
|
const mutationOptions: UseMutationOptions<
|
||||||
|
unknown,
|
||||||
|
AxiosError<UploadFileApiV1UploadsTokenPostError>,
|
||||||
|
Options<UploadFileApiV1UploadsTokenPostData>
|
||||||
|
> = {
|
||||||
|
mutationFn: async (localOptions) => {
|
||||||
|
const { data } = await uploadFileApiV1UploadsTokenPost({
|
||||||
|
...options,
|
||||||
|
...localOptions,
|
||||||
|
throwOnError: true,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return mutationOptions;
|
||||||
|
};
|
||||||
|
|||||||
@@ -71,6 +71,19 @@ export const Body_login_oauthSchema = {
|
|||||||
title: "Body_login_oauth",
|
title: "Body_login_oauth",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const Body_upload_file_api_v1_uploads__token__postSchema = {
|
||||||
|
properties: {
|
||||||
|
file: {
|
||||||
|
type: "string",
|
||||||
|
format: "binary",
|
||||||
|
title: "File",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: "object",
|
||||||
|
required: ["file"],
|
||||||
|
title: "Body_upload_file_api_v1_uploads__token__post",
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const EventCreateSchema = {
|
export const EventCreateSchema = {
|
||||||
properties: {
|
properties: {
|
||||||
title: {
|
title: {
|
||||||
@@ -1118,6 +1131,55 @@ export const PaginatedResponse_EventResponse_Schema = {
|
|||||||
title: "PaginatedResponse[EventResponse]",
|
title: "PaginatedResponse[EventResponse]",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const PresignedUrlRequestSchema = {
|
||||||
|
properties: {
|
||||||
|
filename: {
|
||||||
|
type: "string",
|
||||||
|
title: "Filename",
|
||||||
|
description: "Original filename of the image",
|
||||||
|
},
|
||||||
|
content_type: {
|
||||||
|
type: "string",
|
||||||
|
title: "Content Type",
|
||||||
|
description: "Content type of the file (e.g., image/jpeg)",
|
||||||
|
},
|
||||||
|
folder: {
|
||||||
|
type: "string",
|
||||||
|
title: "Folder",
|
||||||
|
description: "Folder to store the file in",
|
||||||
|
default: "images",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: "object",
|
||||||
|
required: ["filename", "content_type"],
|
||||||
|
title: "PresignedUrlRequest",
|
||||||
|
description: "Request model for generating presigned URLs.",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const PresignedUrlResponseSchema = {
|
||||||
|
properties: {
|
||||||
|
upload_url: {
|
||||||
|
type: "string",
|
||||||
|
title: "Upload Url",
|
||||||
|
description: "URL to upload the file to",
|
||||||
|
},
|
||||||
|
file_url: {
|
||||||
|
type: "string",
|
||||||
|
title: "File Url",
|
||||||
|
description: "URL where the file will be accessible after upload",
|
||||||
|
},
|
||||||
|
expires_in: {
|
||||||
|
type: "integer",
|
||||||
|
title: "Expires In",
|
||||||
|
description: "Time in seconds until the upload URL expires",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: "object",
|
||||||
|
required: ["upload_url", "file_url", "expires_in"],
|
||||||
|
title: "PresignedUrlResponse",
|
||||||
|
description: "Response model for presigned URL generation.",
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const RefreshTokenRequestSchema = {
|
export const RefreshTokenRequestSchema = {
|
||||||
properties: {
|
properties: {
|
||||||
refresh_token: {
|
refresh_token: {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
type TDataShape,
|
type TDataShape,
|
||||||
type Client,
|
type Client,
|
||||||
urlSearchParamsBodySerializer,
|
urlSearchParamsBodySerializer,
|
||||||
|
formDataBodySerializer,
|
||||||
} from "@hey-api/client-axios";
|
} from "@hey-api/client-axios";
|
||||||
import type {
|
import type {
|
||||||
RootGetData,
|
RootGetData,
|
||||||
@@ -63,6 +64,11 @@ import type {
|
|||||||
GetEventBySlugData,
|
GetEventBySlugData,
|
||||||
GetEventBySlugResponse,
|
GetEventBySlugResponse,
|
||||||
GetEventBySlugError,
|
GetEventBySlugError,
|
||||||
|
GeneratePresignedUrlApiV1UploadsPresignedUrlPostData,
|
||||||
|
GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponse,
|
||||||
|
GeneratePresignedUrlApiV1UploadsPresignedUrlPostError,
|
||||||
|
UploadFileApiV1UploadsTokenPostData,
|
||||||
|
UploadFileApiV1UploadsTokenPostError,
|
||||||
} from "./types.gen";
|
} from "./types.gen";
|
||||||
import { client as _heyApiClient } from "./client.gen";
|
import { client as _heyApiClient } from "./client.gen";
|
||||||
|
|
||||||
@@ -270,7 +276,6 @@ export const listEventThemes = <ThrowOnError extends boolean = false>(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create Theme
|
* Create Theme
|
||||||
* Create new event theme.
|
|
||||||
*/
|
*/
|
||||||
export const createEventTheme = <ThrowOnError extends boolean = false>(
|
export const createEventTheme = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<CreateEventThemeData, ThrowOnError>,
|
options: Options<CreateEventThemeData, ThrowOnError>,
|
||||||
@@ -280,6 +285,12 @@ export const createEventTheme = <ThrowOnError extends boolean = false>(
|
|||||||
CreateEventThemeError,
|
CreateEventThemeError,
|
||||||
ThrowOnError
|
ThrowOnError
|
||||||
>({
|
>({
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
scheme: "bearer",
|
||||||
|
type: "http",
|
||||||
|
},
|
||||||
|
],
|
||||||
url: "/api/v1/event_themes/",
|
url: "/api/v1/event_themes/",
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
@@ -331,7 +342,6 @@ export const getEventTheme = <ThrowOnError extends boolean = false>(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Update Theme
|
* Update Theme
|
||||||
* Update specific theme by ID.
|
|
||||||
*/
|
*/
|
||||||
export const updateEventTheme = <ThrowOnError extends boolean = false>(
|
export const updateEventTheme = <ThrowOnError extends boolean = false>(
|
||||||
options: Options<UpdateEventThemeData, ThrowOnError>,
|
options: Options<UpdateEventThemeData, ThrowOnError>,
|
||||||
@@ -341,6 +351,12 @@ export const updateEventTheme = <ThrowOnError extends boolean = false>(
|
|||||||
UpdateEventThemeError,
|
UpdateEventThemeError,
|
||||||
ThrowOnError
|
ThrowOnError
|
||||||
>({
|
>({
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
scheme: "bearer",
|
||||||
|
type: "http",
|
||||||
|
},
|
||||||
|
],
|
||||||
url: "/api/v1/event_themes/{theme_id}",
|
url: "/api/v1/event_themes/{theme_id}",
|
||||||
...options,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
@@ -535,3 +551,65 @@ export const getEventBySlug = <ThrowOnError extends boolean = false>(
|
|||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate Presigned Url
|
||||||
|
* Generate a presigned URL for uploading a file.
|
||||||
|
*
|
||||||
|
* This endpoint creates a secure token that allows direct upload to the storage system.
|
||||||
|
* After successful upload, the file will be accessible at the returned file_url.
|
||||||
|
*/
|
||||||
|
export const generatePresignedUrlApiV1UploadsPresignedUrlPost = <
|
||||||
|
ThrowOnError extends boolean = false,
|
||||||
|
>(
|
||||||
|
options: Options<
|
||||||
|
GeneratePresignedUrlApiV1UploadsPresignedUrlPostData,
|
||||||
|
ThrowOnError
|
||||||
|
>,
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).post<
|
||||||
|
GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponse,
|
||||||
|
GeneratePresignedUrlApiV1UploadsPresignedUrlPostError,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
scheme: "bearer",
|
||||||
|
type: "http",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
url: "/api/v1/uploads/presigned-url",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options?.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload File
|
||||||
|
* Upload a file using a presigned URL token.
|
||||||
|
*
|
||||||
|
* This endpoint handles the actual file upload after a presigned URL is generated.
|
||||||
|
* The token validates the upload permissions and destination.
|
||||||
|
*/
|
||||||
|
export const uploadFileApiV1UploadsTokenPost = <
|
||||||
|
ThrowOnError extends boolean = false,
|
||||||
|
>(
|
||||||
|
options: Options<UploadFileApiV1UploadsTokenPostData, ThrowOnError>,
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).post<
|
||||||
|
unknown,
|
||||||
|
UploadFileApiV1UploadsTokenPostError,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
...formDataBodySerializer,
|
||||||
|
url: "/api/v1/uploads/{token}",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": null,
|
||||||
|
...options?.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ export type BodyLoginOauth = {
|
|||||||
client_secret?: string | null;
|
client_secret?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type BodyUploadFileApiV1UploadsTokenPost = {
|
||||||
|
file: Blob | File;
|
||||||
|
};
|
||||||
|
|
||||||
export type EventCreate = {
|
export type EventCreate = {
|
||||||
title: string;
|
title: string;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
@@ -179,6 +183,42 @@ export type PaginatedResponseEventResponse = {
|
|||||||
size: number;
|
size: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request model for generating presigned URLs.
|
||||||
|
*/
|
||||||
|
export type PresignedUrlRequest = {
|
||||||
|
/**
|
||||||
|
* Original filename of the image
|
||||||
|
*/
|
||||||
|
filename: string;
|
||||||
|
/**
|
||||||
|
* Content type of the file (e.g., image/jpeg)
|
||||||
|
*/
|
||||||
|
content_type: string;
|
||||||
|
/**
|
||||||
|
* Folder to store the file in
|
||||||
|
*/
|
||||||
|
folder?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response model for presigned URL generation.
|
||||||
|
*/
|
||||||
|
export type PresignedUrlResponse = {
|
||||||
|
/**
|
||||||
|
* URL to upload the file to
|
||||||
|
*/
|
||||||
|
upload_url: string;
|
||||||
|
/**
|
||||||
|
* URL where the file will be accessible after upload
|
||||||
|
*/
|
||||||
|
file_url: string;
|
||||||
|
/**
|
||||||
|
* Time in seconds until the upload URL expires
|
||||||
|
*/
|
||||||
|
expires_in: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type RefreshTokenRequest = {
|
export type RefreshTokenRequest = {
|
||||||
refresh_token: string;
|
refresh_token: string;
|
||||||
};
|
};
|
||||||
@@ -756,6 +796,59 @@ export type GetEventBySlugResponses = {
|
|||||||
export type GetEventBySlugResponse =
|
export type GetEventBySlugResponse =
|
||||||
GetEventBySlugResponses[keyof GetEventBySlugResponses];
|
GetEventBySlugResponses[keyof GetEventBySlugResponses];
|
||||||
|
|
||||||
|
export type GeneratePresignedUrlApiV1UploadsPresignedUrlPostData = {
|
||||||
|
body: PresignedUrlRequest;
|
||||||
|
path?: never;
|
||||||
|
query?: never;
|
||||||
|
url: "/api/v1/uploads/presigned-url";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GeneratePresignedUrlApiV1UploadsPresignedUrlPostErrors = {
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HttpValidationError;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GeneratePresignedUrlApiV1UploadsPresignedUrlPostError =
|
||||||
|
GeneratePresignedUrlApiV1UploadsPresignedUrlPostErrors[keyof GeneratePresignedUrlApiV1UploadsPresignedUrlPostErrors];
|
||||||
|
|
||||||
|
export type GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponses = {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: PresignedUrlResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponse =
|
||||||
|
GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponses[keyof GeneratePresignedUrlApiV1UploadsPresignedUrlPostResponses];
|
||||||
|
|
||||||
|
export type UploadFileApiV1UploadsTokenPostData = {
|
||||||
|
body: BodyUploadFileApiV1UploadsTokenPost;
|
||||||
|
path: {
|
||||||
|
token: string;
|
||||||
|
};
|
||||||
|
query?: never;
|
||||||
|
url: "/api/v1/uploads/{token}";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UploadFileApiV1UploadsTokenPostErrors = {
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HttpValidationError;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UploadFileApiV1UploadsTokenPostError =
|
||||||
|
UploadFileApiV1UploadsTokenPostErrors[keyof UploadFileApiV1UploadsTokenPostErrors];
|
||||||
|
|
||||||
|
export type UploadFileApiV1UploadsTokenPostResponses = {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
export type ClientOptions = {
|
export type ClientOptions = {
|
||||||
baseURL: "http://localhost:8000" | (string & {});
|
baseURL: "http://localhost:8000" | (string & {});
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user