-
Notifications
You must be signed in to change notification settings - Fork 172
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
Conversation
…tend into jeff/backend-v2-integration
…det-frontend into jeff/backend-v2-april
130cc46
to
94db8bd
Compare
Deploying with
|
Latest commit: |
94db8bd
|
Status: | ✅ Deploy successful! |
Preview URL: | https://db7968c8.cadet-frontend.pages.dev |
There was a problem hiding this 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.
export const customFetch = async ( | ||
url: string, | ||
opts: FullRequestParams | ||
): Promise<Response | null> => { |
There was a problem hiding this comment.
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
.
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; | ||
} |
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
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.
interface FullRequestParams { | ||
accessToken?: string; | ||
refreshToken?: string; | ||
errorMessage?: string; | ||
shouldAutoLogout?: boolean; | ||
shouldRefresh?: boolean; | ||
} |
There was a problem hiding this comment.
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 @@ | |||
{ |
There was a problem hiding this comment.
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> => { |
There was a problem hiding this comment.
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[], |
There was a problem hiding this comment.
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.
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 :) |
Sorry for the delays. I was planning to come back to it, and will take a look at it hopefully this weekend. |
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. |
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:
swagger.json
tosrc/commons/api/
yarn run update-cadet-api
fetch
to the new API globallyType of change
How to test
Run the test suite.
Checklist