Skip to content

Commit

Permalink
Add file uploads and downloads (#804)
Browse files Browse the repository at this point in the history
Co-authored-by: Lowell Torola <lmtorola@mit.edu> :shipit:
  • Loading branch information
nicolexxuu authored Sep 26, 2024
1 parent 0c3b83c commit 5fbf752
Show file tree
Hide file tree
Showing 20 changed files with 247 additions and 176 deletions.
12 changes: 11 additions & 1 deletion backend/siarnaq/api/teams/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,17 @@ def join(self, request, pk=None, *, episode_id):
logger.debug("team_join", message="User has joined team.", team=team.pk)
return Response(None, status=status.HTTP_204_NO_CONTENT)

@extend_schema(responses={status.HTTP_204_NO_CONTENT: None})
@extend_schema(
responses={status.HTTP_204_NO_CONTENT: None},
request={
"multipart/form-data": {
"type": "object",
"properties": {
"avatar": {"type": "string", "format": "binary"},
},
}
},
)
@action(
detail=False,
methods=["post"],
Expand Down
24 changes: 6 additions & 18 deletions frontend2/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ paths:
type: array
items:
type: integer
description: A list of teams to filter for. Defaults to just your own team.
description: A list of teams to filter for. Defaults to your own team.
tags:
- compete
security:
Expand Down Expand Up @@ -1290,16 +1290,13 @@ paths:
- team
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/TeamAvatarRequest'
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/TeamAvatarRequest'
multipart/form-data:
schema:
$ref: '#/components/schemas/TeamAvatarRequest'
required: true
type: object
properties:
avatar:
type: string
format: binary
security:
- jwtAuth: []
responses:
Expand Down Expand Up @@ -2749,15 +2746,6 @@ components:
type: boolean
required:
- invocation
TeamAvatarRequest:
type: object
properties:
avatar:
type: string
format: binary
writeOnly: true
required:
- avatar
TeamCreate:
type: object
properties:
Expand Down
1 change: 0 additions & 1 deletion frontend2/src/api/_autogen/.openapi-generator/FILES
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ models/StyleEnum.ts
models/Submission.ts
models/SubmissionDownload.ts
models/SubmissionReportRequest.ts
models/TeamAvatarRequest.ts
models/TeamCreate.ts
models/TeamCreateRequest.ts
models/TeamJoinRequest.ts
Expand Down
33 changes: 22 additions & 11 deletions frontend2/src/api/_autogen/apis/TeamApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import type {
PaginatedClassRequirementList,
PaginatedTeamPublicList,
PatchedTeamPrivateRequest,
TeamAvatarRequest,
TeamCreate,
TeamCreateRequest,
TeamJoinRequest,
Expand All @@ -38,8 +37,6 @@ import {
PaginatedTeamPublicListToJSON,
PatchedTeamPrivateRequestFromJSON,
PatchedTeamPrivateRequestToJSON,
TeamAvatarRequestFromJSON,
TeamAvatarRequestToJSON,
TeamCreateFromJSON,
TeamCreateToJSON,
TeamCreateRequestFromJSON,
Expand Down Expand Up @@ -89,7 +86,7 @@ export interface TeamRequirementRetrieveRequest {

export interface TeamTAvatarCreateRequest {
episodeId: string;
teamAvatarRequest: TeamAvatarRequest;
avatar?: Blob;
}

export interface TeamTCreateRequest {
Expand Down Expand Up @@ -394,16 +391,10 @@ export class TeamApi extends runtime.BaseAPI {
throw new runtime.RequiredError('episodeId','Required parameter requestParameters.episodeId was null or undefined when calling teamTAvatarCreate.');
}

if (requestParameters.teamAvatarRequest === null || requestParameters.teamAvatarRequest === undefined) {
throw new runtime.RequiredError('teamAvatarRequest','Required parameter requestParameters.teamAvatarRequest was null or undefined when calling teamTAvatarCreate.');
}

const queryParameters: any = {};

const headerParameters: runtime.HTTPHeaders = {};

headerParameters['Content-Type'] = 'application/json';

if (this.configuration && this.configuration.accessToken) {
const token = this.configuration.accessToken;
const tokenString = await token("jwtAuth", []);
Expand All @@ -412,12 +403,32 @@ export class TeamApi extends runtime.BaseAPI {
headerParameters["Authorization"] = `Bearer ${tokenString}`;
}
}
const consumes: runtime.Consume[] = [
{ contentType: 'multipart/form-data' },
];
// @ts-ignore: canConsumeForm may be unused
const canConsumeForm = runtime.canConsumeForm(consumes);

let formParams: { append(param: string, value: any): any };
let useForm = false;
// use FormData to transmit files using content-type "multipart/form-data"
useForm = canConsumeForm;
if (useForm) {
formParams = new FormData();
} else {
formParams = new URLSearchParams();
}

if (requestParameters.avatar !== undefined) {
formParams.append('avatar', requestParameters.avatar as any);
}

const response = await this.request({
path: `/api/team/{episode_id}/t/avatar/`.replace(`{${"episode_id"}}`, encodeURIComponent(String(requestParameters.episodeId))),
method: 'POST',
headers: headerParameters,
query: queryParameters,
body: TeamAvatarRequestToJSON(requestParameters.teamAvatarRequest),
body: formParams,
}, initOverrides);

return new runtime.VoidApiResponse(response);
Expand Down
66 changes: 0 additions & 66 deletions frontend2/src/api/_autogen/models/TeamAvatarRequest.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function UserProfilePrivateRequestFromJSONTyped(json: any, ignoreDiscrimi
return json;
}
return {

'gender': GenderEnumFromJSON(json['gender']),
'gender_details': !exists(json, 'gender_details') ? undefined : json['gender_details'],
'school': !exists(json, 'school') ? undefined : json['school'],
Expand All @@ -108,7 +108,7 @@ export function UserProfilePrivateRequestToJSON(value?: UserProfilePrivateReques
return null;
}
return {

'gender': GenderEnumToJSON(value.gender),
'gender_details': value.gender_details,
'school': value.school,
Expand Down
1 change: 0 additions & 1 deletion frontend2/src/api/_autogen/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export * from './StyleEnum';
export * from './Submission';
export * from './SubmissionDownload';
export * from './SubmissionReportRequest';
export * from './TeamAvatarRequest';
export * from './TeamCreate';
export * from './TeamCreateRequest';
export * from './TeamJoinRequest';
Expand Down
3 changes: 3 additions & 0 deletions frontend2/src/api/compete/competeKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ export const competeMutationKeys = {
uploadSub: ({ episodeId }: { episodeId: string }) =>
["compete", episodeId, "submit"] as const,

downloadSub: ({ episodeId }: { episodeId: string }) =>
["compete", episodeId, "submit", "download"] as const,

// --- SCRIMMAGES --- //
requestScrim: ({ episodeId }: { episodeId: string }) =>
["compete", episodeId, "scrimmage", "request"] as const,
Expand Down
29 changes: 29 additions & 0 deletions frontend2/src/api/compete/useCompete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {
CompeteSubmissionCreateRequest,
CompeteSubmissionListRequest,
CompeteSubmissionTournamentListRequest,
CompeteSubmissionDownloadRetrieveRequest,
HistoricalRating,
PaginatedMatchList,
PaginatedScrimmageRequestList,
Expand All @@ -38,6 +39,7 @@ import {
rejectScrimmage,
requestScrimmage,
uploadSubmission,
downloadSubmission,
} from "./competeApi";
import toast from "react-hot-toast";
import { buildKey } from "../helpers";
Expand Down Expand Up @@ -535,3 +537,30 @@ export const useCancelScrimmage = (
});
},
});

/**
* For downloading a submission.
*/
export const useDownloadSubmission = ({
episodeId,
}: {
episodeId: string;
}): UseMutationResult<
void,
Error,
CompeteSubmissionDownloadRetrieveRequest,
unknown
> =>
useMutation({
mutationKey: competeMutationKeys.downloadSub({ episodeId }),
mutationFn: async ({
episodeId,
id,
}: CompeteSubmissionDownloadRetrieveRequest) => {
await toast.promise(downloadSubmission({ episodeId, id }), {
loading: "Downloading submission...",
success: "Downloaded submission!",
error: "Error downloading submission.",
});
},
});
6 changes: 1 addition & 5 deletions frontend2/src/api/loaders/teamProfileLoader.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import type { QueryClient } from "@tanstack/react-query";
import type { LoaderFunction } from "react-router-dom";
import { matchListFactory } from "../compete/competeFactories";
import { buildKey } from "../helpers";
import {
otherTeamInfoFactory,
searchTeamsFactory,
} from "../team/teamFactories";
import { otherTeamInfoFactory } from "../team/teamFactories";

// loader for other team's public profile pages
export const teamProfileLoader =
Expand Down
11 changes: 5 additions & 6 deletions frontend2/src/api/team/teamApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,12 @@ export const searchTeams = async ({
/**
* Upload a new avatar for the currently logged in user's team.
* @param episodeId The current episode's ID.
* @param avatar The avatar file.
* @param teamAvatarRequest The avatar file.
*/
export const teamAvatarUpload = async ({
episodeId,
teamAvatarRequest,
}: TeamTAvatarCreateRequest): Promise<void> => {
await API.teamTAvatarCreate({ episodeId, teamAvatarRequest });
export const teamAvatarUpload = async (
teamAvatarRequest: TeamTAvatarCreateRequest,
): Promise<void> => {
await API.teamTAvatarCreate(teamAvatarRequest);
};

/**
Expand Down
2 changes: 1 addition & 1 deletion frontend2/src/api/team/teamKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const teamMutationKeys = {
update: ({ episodeId }: { episodeId: string }) =>
["team", "update", episodeId] as const,

avatar: ({ episodeId }: { episodeId: string }) =>
avatarUpload: ({ episodeId }: { episodeId: string }) =>
["team", "avatar", episodeId] as const,

report: ({ episodeId }: { episodeId: string }) =>
Expand Down
10 changes: 5 additions & 5 deletions frontend2/src/api/team/useTeam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
import type {
PaginatedTeamPublicList,
PatchedTeamPrivateRequest,
TeamAvatarRequest,
TeamCreate,
TeamJoinRequest,
TeamPrivate,
Expand Down Expand Up @@ -198,11 +197,12 @@ export const useUpdateTeamAvatar = (
episodeId: string;
},
queryClient: QueryClient,
): UseMutationResult<void, Error, TeamAvatarRequest, unknown> =>
): UseMutationResult<void, Error, Blob, unknown> =>
useMutation({
mutationKey: teamMutationKeys.avatar({ episodeId }),
mutationFn: async (teamAvatarRequest: TeamAvatarRequest) => {
await toast.promise(teamAvatarUpload({ episodeId, teamAvatarRequest }), {
mutationKey: teamMutationKeys.avatarUpload({ episodeId }),
// We pass in a Blob because we already have the episodeId
mutationFn: async (avatar: Blob) => {
await toast.promise(teamAvatarUpload({ episodeId, avatar }), {
loading: "Uploading team avatar...",
success: "Uploaded team avatar!",
error: "Error uploading team avatar.",
Expand Down
Loading

0 comments on commit 5fbf752

Please sign in to comment.