-
Notifications
You must be signed in to change notification settings - Fork 7
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
feat: generic wrapper for JSON APIs #70
base: staging
Are you sure you want to change the base?
feat: generic wrapper for JSON APIs #70
Conversation
I like this in general, but needs a deeper review than I can do right now :) |
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.
LGTM, will wait on a second review from @pdellaert on this one
bca310f
to
01e424d
Compare
01e424d
to
dec8382
Compare
# Conflicts: # .github/CHANGELOG.md
dec8382
to
aa84fa9
Compare
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.
Only one tiny thing that is not a blocker to me. Otherwise looks really good
Sorry it took me so long to review
export const MetarTimeSchema = z.object({ dt: z.string().datetime() }); | ||
|
||
export const MetarWindDirectionSchema = z.object({ repr: z.string() }); | ||
|
||
export const MetarWindSpeedSchema = z.object({ repr: z.string() }); | ||
|
||
export const MetarVisibilitySchema = z.object({ repr: z.string() }); | ||
|
||
export const MetarTemperatureSchema = z.object({ repr: z.string() }); | ||
|
||
export const MetarDewpointSchema = z.object({ repr: z.string() }); | ||
|
||
export const MetarAltimeterSchema = z.object({ value: z.number() }); | ||
|
||
export const MetarUnitsSchema = z.object({ | ||
accumulation: z.string(), | ||
altimeter: z.string(), | ||
altitude: z.string(), | ||
temperature: z.string(), | ||
visibility: z.string(), | ||
wind_speed: z.string(), | ||
}); |
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.
export const MetarTimeSchema = z.object({ dt: z.string().datetime() }); | |
export const MetarWindDirectionSchema = z.object({ repr: z.string() }); | |
export const MetarWindSpeedSchema = z.object({ repr: z.string() }); | |
export const MetarVisibilitySchema = z.object({ repr: z.string() }); | |
export const MetarTemperatureSchema = z.object({ repr: z.string() }); | |
export const MetarDewpointSchema = z.object({ repr: z.string() }); | |
export const MetarAltimeterSchema = z.object({ value: z.number() }); | |
export const MetarUnitsSchema = z.object({ | |
accumulation: z.string(), | |
altimeter: z.string(), | |
altitude: z.string(), | |
temperature: z.string(), | |
visibility: z.string(), | |
wind_speed: z.string(), | |
}); | |
export const MetarTimeSchema = z.object({ dt: z.string().datetime() }); | |
export const MetarWindDirectionSchema = z.object({ repr: z.string() }); | |
export const MetarWindSpeedSchema = z.object({ repr: z.string() }); | |
export const MetarVisibilitySchema = z.object({ repr: z.string() }); | |
export const MetarTemperatureSchema = z.object({ repr: z.string() }); | |
export const MetarDewpointSchema = z.object({ repr: z.string() }); | |
export const MetarAltimeterSchema = z.object({ value: z.number() }); | |
export const MetarUnitsSchema = z.object({ | |
accumulation: z.string(), | |
altimeter: z.string(), | |
altitude: z.string(), | |
temperature: z.string(), | |
visibility: z.string(), | |
wind_speed: z.string(), | |
}); |
Any reason for the empty lines? Unless some linting requires this, I'd keep it compressed given they are one-liners? (I get it for others where they are more complex.
Overview
Using 3rd-party APIs in statically typed languages poses a challenge as you need some way to validate the type of the returned data to avoid type errors at runtime. So far, the codebase used TypeScript's
any
type on responses from 3rd-party APIs to access values on the returned data. Asany
basically means opting out of all type checking, it's best practice to avoid its usage wherever possible. Therefore, the upcoming lint overhaul will disallowany
completely.Refactoring all currently used API implementations to avoid using
any
is too complex to be included in the lint overhaul.This PR is a mix of
feat
andrefactor
: It introduces a new generic API wrapper and migrates all existing 3rd-party APIs to use it.Description
The API wrapper uses the 3rd-party library Zod to validate the returned data against statically declared schemas. It leverages TypeScript's ability to implicitly infer types thereby enabling type checking on the returned data. Code completion on the returned data is another QoL feature added by this PR/library.
Basic usage
Details
Using the wrapper is as simple as declaring a Zod schema for the returned data:
Then using the
fetchForeignAPI<ReturnType>(request, schema)
method:For simplicity you can also just pass a
URL
object orstring
tofetchForeignAPI()
.Testing
A list of all modified commands can be found below.
While testing make sure to use improbable inputs as well as more typical inputs. E.g. requesting station/taf/metar information from small airports, airports with almost no traffic etc.
Consider any case where the command doesn't succeed as 'failed'. The goal is to get correct typing, not limit the data we can show/use.
Affected Commands
/vatsim data <all_subcommands>
/vatsim events
/station
/taf
/metar
/live-flights
/simbrief-data retrieve
/wolframalpha
Additional Information
The
/taf
embed has been slightly modified to be more consistent. Instead of decoding the first part of the TAF only, it now only provides the raw string and links to our docs and https://e6bx.com/weather/{ICAO}/?showDecoded=1&focuspoint=tafdecoder which decodes the TAF for the queried airport.Discord Username
examplewastaken