Skip to content

Auto-generate TypeScript client with types from backend's documented Swagger API #1699

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 20 commits into from

Conversation

bnjmnt4n
Copy link
Contributor

@bnjmnt4n bnjmnt4n commented Apr 15, 2021

Description

Using https://github.com/acacode/swagger-typescript-api, a TypeScript-based client with types is generated from the cadet backend's swagger.json.

TODO:

  • Add documentation
    • Basic instructions
      • Copy swagger.json to src/commons/api/
      • Run yarn run update-cadet-api
  • Avoid typecasting
  • Ensure generated types are accurate
  • Switch from fetch to the new API globally

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update
  • Code quality improvements

How to test

Run the test suite.

Checklist

  • I have tested this code
  • I have updated the documentation

@bnjmnt4n bnjmnt4n force-pushed the bnjmnt4n/sa2122-backend branch from 130cc46 to 94db8bd Compare April 15, 2021 09:23
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Apr 15, 2021

Deploying with  Cloudflare Pages  Cloudflare Pages

Latest commit: 94db8bd
Status: ✅  Deploy successful!
Preview URL: https://db7968c8.cadet-frontend.pages.dev

View logs

Copy link
Contributor Author

@bnjmnt4n bnjmnt4n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to get a cursory review of this PR, to see if we're going on the right track. This PR allows a TypeScript client with types defined as per the backend's Swagger API to be generated automatically using https://github.com/acacode/swagger-typescript-api. The API is properly typed to accept the correct input arguments (whether query, path, JSON or FormData), and should reflect the accurate types of the response as defined by the backend. (Note that there are still differences between the types which should be fixed before this can be merged.) The API client also parses the response into resp.data and resp.error respectively if an error occurs.

Comment on lines +26 to +29
export const customFetch = async (
url: string,
opts: FullRequestParams
): Promise<Response | null> => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This customFetch function was based on the original request from RequestsSaga.

Comment on lines +67 to +78
if (!resp.ok) {
throw new Error('API call failed or got non-OK response');
}

return resp;
} catch (e) {
// Logout on all errors.
store.dispatch(actions.logOut());
showWarningMessage(opts.errorMessage ? opts.errorMessage : 'Please login again.');

return resp;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made some minor modifications to the error handling logic from the original request. The original code allowed null to be returned when errors occur. This means we have to do a lot of manual checking for null. Was there a reason for this?

import { actions } from '../utils/ActionsHelper';
import Constants from '../utils/Constants';
import { showWarningMessage } from '../utils/NotificationsHelper';
import { Api, FullRequestParams } from './api';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The api modules is automatically generated from the swagger.json file.

Comment on lines +8 to +14
interface FullRequestParams {
accessToken?: string;
refreshToken?: string;
errorMessage?: string;
shouldAutoLogout?: boolean;
shouldRefresh?: boolean;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, I'm adding some custom options which can be passed into the generated API methods. Overriding the interface here was the simplest way to do it, without having to mess with the internals of the generated API.

@@ -0,0 +1,3361 @@
{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is copied from cadet. Do you think there should be a yarn command to copy the file over for convenience?

@@ -125,25 +127,22 @@ export const getUser = async (tokens: Tokens): Promise<User | null> => {
* Will be updated after a separate db for student progress is ready
*/
export const getAchievements = async (tokens: Tokens): Promise<AchievementItem[] | null> => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the semantics of redux-saga, but I'm sure there's a reason for each request function accepting tokens separately instead of just reading from the store. That being said, do you think a reasonable default for customFetch is to read from the store if no tokens are passed?


// TODO
const resp = await Cadet.adminAchievements.bulkUpdate(
(achievements as unknown) as Achievement[],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's still quite a bit of typecasting around the file, as there are still slight differences between the auto-generated types and the handwritten types. I'll look into all of these cases later.

@bnjmnt4n bnjmnt4n requested a review from angelsl April 15, 2021 16:13
@angelsl
Copy link
Contributor

angelsl commented Apr 15, 2021

Yes, yes, so much yes. This is probably the best thing that's happened in the frontend code-quality wise ever since it was written. Looking forward to seeing this merged :)

Base automatically changed from sa2122-backend to master May 2, 2021 19:06
@martin-henz
Copy link
Member

Hmm, anyone looking after this PR? @bnjmnt4n ? There is enthusiastic support from @angelsl . But still in draft and currently with merge conflict...

@bnjmnt4n
Copy link
Contributor Author

Sorry for the delays. I was planning to come back to it, and will take a look at it hopefully this weekend.

@martin-henz
Copy link
Member

Reviewing our open PRs and this one comes up again. @bnjmnt4n do you have time these days? There is/was enthusiastic support from @angelsl . But still in draft and currently with merge conflict...

@angelsl
Copy link
Contributor

angelsl commented Dec 16, 2021

This will need quite a bit of time to properly convert all the places in the frontend that make requests to the backend. And we need to make sure the backend Swagger definitions are accurate as well (unfortunately Elixir is dynamically typed so it's not possible to generate Swagger definitions from type information...)

It'll be tedious but worth it.

@martin-henz martin-henz added tedious-but-worth-it help wanted Extra attention is needed and removed postponed labels Dec 16, 2021
@martin-henz martin-henz changed the title Auto-generate TypeScript client with types from Cadet's documented Swagger API Auto-generate TypeScript client with types from backend's documented Swagger API Jan 30, 2022
@martin-henz martin-henz deleted the bnjmnt4n/sa2122-backend branch March 11, 2023 07:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed tedious-but-worth-it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants