Commit 81807f2
authored
feat: add a Go SDK alongside the Python and Node.js ports (#119)
* feat: add a Go SDK alongside the Python and Node.js ports
The port carries the same features as the other two, with the surface
shaped for Go: a context on every request, errors instead of exceptions,
Options instead of keyword arguments, and a zero value that means the
default the other ports declare in their signatures.
Two pieces have no counterpart in the sibling ports, because their HTTP
clients provide them:
- a cookie jar that honours the scope FR24 sets, so a cookie stored by
www. is not replayed to cdn./api./data-live., and a single cookie can
be dropped to shed load-balancer stickiness without losing the login
- content decoding owned by the package (gzip, deflate, brotli), which
is what makes the size budget enforceable before a body expands
Parity is enforced rather than promised: ports_test.go reads the Python
source and fails when the country list, the zones, the tracker config,
the flight attributes or the client methods drift apart.
* ci: run, release and label the Go port
The offline suite gates pull requests and the live FR24 suite runs with
retries, mirroring the Python and Node.js workflows. gofmt, go vet and
staticcheck stand in for flake8/mypy and eslint/tsd, and a consumer
module is built against the package so a broken public surface fails
the build.
Releases need no registry upload: for Go the tag is the release. Since
the module lives in a subdirectory, the toolchain only sees tags
carrying that prefix, so publish.yml now pushes go/vX.Y.Z alongside the
release tag, and the version check covers all three ports.
The labeler tells the Go tests apart from the package itself, which
matters because Go keeps them in the same directory where python/ and
nodejs/ keep a sibling tests/ folder.
* docs: list Go across the project docs
The per-package READMEs now describe only their own port, and the
project-wide pages list all three. The issue templates asked for a
"Python Version", which was already wrong for Node.js.
* docs: trim the Go readme to the shape of its siblings
The package readme now carries what the Python and Node.js ones do —
install, basic usage, documentation link — and nothing else. The deeper
material it held (entity constructors, client options, error handling,
TLS impersonation, the differences table) moves to docs/go.md, where the
equivalent Python and Node.js pages already live.
* docs: carry the Go badges in every readme
Each readme leads with its own workflow badge and then repeats the
project-wide block, so the Go one was missing Pypi, Npm, Downloads and
Frequency, and the other three were missing the Go reference and version.
The project-wide pages listed only some of the workflow badges: the root
readme had no Node.js one and the documentation home had neither Node.js
nor Go.
* docs: add a Node version badge and group the license with the workflows
Each registry badge now sits next to the version it needs — Pypi with
Python, Npm with Node, the Go reference with Go — and the license moves
up beside the workflow badges.
The Node floor is the engines.node of nodejs/package.json.
* fix: harden the edges a public surface exposes
Eight findings from the review on the pull request, each reproduced
before it was changed:
- CheckInfo reported an unknown field only when Go's randomised map
iteration put it before a criterion that fails: 26 of 200 runs on the
same input. Every criterion is now validated before any is evaluated.
- GetFlightDetails and GetHistoryData take a pointer, so nil is valid at
compile time and panicked instead of returning an error.
- A body with data spliced after the JSON was accepted, where the Python
and Node.js parsers reject it: Decode reads the first value only.
- Network failures did not wrap ErrFlightRadar, contradicting the
taxonomy documented on that sentinel. They now wrap both it and the
transport's own cause, which the retry policy still reads.
- A negative Client.Timeout skipped the deadline entirely instead of
falling back to the default the field documents.
- New() panicked when the process had replaced http.DefaultTransport,
which mocking libraries legitimately do.
- SleepFor panicked on the largest Jitter a Duration can hold, and could
wrap round when adding it to the delay.
- Missing aircraft images stayed nil where the other ports default to an
empty list, which changes both a CheckInfo comparison and the JSON.
* fix: retry a body lost mid-download, and widen two value readers
A connection dropped partway through a response was classified as
permanent, because the body is read after Do returns and so carries no
*url.Error of its own: against a server that promises 5000 bytes and
hangs up after 6, a policy asking for three attempts made one. The
Python port retries the same failure, where curl_cffi reads the body
inside the call. The read failure is now wrapped the way the transport
wraps its own.
Two readers were narrower than they read: nativeNumber covered every
numeric kind except float64, so a defined type over float64 was refused
where one over int64 worked, and getString ignored json.Number, which
this package itself produces when it decodes with UseNumber.
* fix: send every in-scope cookie, and never sleep on a bad delay
The jar collapsed same-named cookies to one, so a request to /data/...
carried the root token where FR24 had scoped a different value to that
path. RFC 6265 5.4 asks for every match, longest path first and oldest
first among equal paths, which is also what the cookie jar behind the
Python port does. get() still answers with the newest re-issue: it says
which token is current, not what to put on the wire.
A negative BaseDelay reached the overflow guard added with the last
round of fixes and came back as the longest sleep a Duration can hold —
292 years before the first retry. The zero BaseDelay that turns into NaN
once the doubling overflows landed in the same place.
* test: fold two duplicated bounds tests into the parity one
GetBounds was asserted twice with the same call and different constants,
and the "box surrounds the point" test was subsumed by the one comparing
all four values against the numbers the Python suite pins.
Its two assertions moved into the survivor rather than being dropped:
they are what catches an expected-value fixture updated the wrong way.
Reordering the fields and "fixing" the fixture to match leaves the exact
comparison passing and fails on the shape.
* fix: keep the feed's order, and close four gaps the taxonomy left open
GetFlights walked a Go map, whose iteration order is randomised, so the
same feed answered in a different order on every call where the Python
and Node.js ports keep the order FR24 sent. The keys are now read from
the body itself.
Four smaller ones, each reproduced first:
- a body of "null" unmarshalled into a nil map with no error, so every
key read as missing instead of the caller seeing the failure
- Content-Type was matched case-sensitively, though a media type is not
- Content-Encoding split across header fields decoded only its first
layer and left the body compressed
- cancellation during the retry backoff returned the bare context error,
outside the taxonomy every other failure path follows
A negative flightLimit, page or limit is no longer rewritten as the
default: only zero selects it, and anything else goes to FR24 as given,
which is what the sibling ports do.
The workflow now also runs when python/FlightRadarAPI changes, since
ports_test.go reads those files to detect drift and could not see a
change that never triggered it.
* fix: make an unset field mean the default, not an empty request
SetFlightTrackerConfig copied the struct it was given without looking at
it. Go's zero value is the empty string where the Python dataclass
carries a default, so a literal {Limit: "10"} sent the feed
adsb=&air=&estimated=&faa=… — while the very same empty value was
rejected when it arrived through the values map. Unset fields now take
the default and every field is validated, so the two paths agree.
RetryPolicy read a zero MaxDelay as "no cap at all", which let a struct
literal climb to a four-minute sleep where the Python constructor caps
at thirty seconds. Zero now means the default the other ports declare,
for BaseDelay as well, so &RetryPolicy{MaxAttempts: 5} backs off exactly
like NewRetryPolicy(5). A zero Jitter still means none: a deterministic
test wants to be able to ask for that.
A cookie with Domain=localhost sent from localhost was discarded by the
guard against a bare TLD, though RFC 6265 5.3.5 allows a domain that is
the host itself.
* chore: derive the accept-encoding header and pin the CI tools
The header was written out while its comment claimed it came from the
decoder table, so dropping a decoder would have left the client asking
for an encoding it can no longer read — and the body would come back
compressed, parsed as garbage rather than failing. It is now built from
the table, with the order kept apart as the one part that is a choice.
staticcheck and govulncheck ran as @latest inside a required CI step,
the only tools in these workflows not pinned, so an upstream release
could turn the build red on a tree nobody touched. Both are pinned in
the Makefile, which the workflow now calls instead of repeating the
command.
Two comments had drifted from their code: the one on matching still
described the one-cookie-per-name behaviour removed in 56c4fdf, and
GetAirlineLogo carried an unreachable 5xx branch that read as though a
server error fell through to the alternative URL.1 parent 473aa17 commit 81807f2
43 files changed
Lines changed: 10698 additions & 34 deletions
File tree
- .github
- ISSUE_TEMPLATE
- workflows
- docs
- go
- flightradarapi
- testdata
- nodejs
- python
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
30 | 32 | | |
31 | 33 | | |
32 | 34 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | | - | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
33 | 35 | | |
34 | 36 | | |
35 | 37 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
26 | 35 | | |
27 | 36 | | |
28 | 37 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
37 | 46 | | |
38 | 47 | | |
39 | 48 | | |
40 | 49 | | |
41 | | - | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
42 | 56 | | |
43 | 57 | | |
44 | | - | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
45 | 61 | | |
46 | 62 | | |
47 | | - | |
| 63 | + | |
| 64 | + | |
48 | 65 | | |
49 | | - | |
| 66 | + | |
| 67 | + | |
50 | 68 | | |
51 | 69 | | |
52 | 70 | | |
53 | 71 | | |
54 | 72 | | |
55 | 73 | | |
56 | | - | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
57 | 77 | | |
58 | 78 | | |
59 | 79 | | |
| |||
69 | 89 | | |
70 | 90 | | |
71 | 91 | | |
| 92 | + | |
72 | 93 | | |
73 | 94 | | |
74 | 95 | | |
| |||
83 | 104 | | |
84 | 105 | | |
85 | 106 | | |
| 107 | + | |
86 | 108 | | |
87 | 109 | | |
88 | 110 | | |
| |||
119 | 141 | | |
120 | 142 | | |
121 | 143 | | |
122 | | - | |
| 144 | + | |
123 | 145 | | |
124 | 146 | | |
125 | 147 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
35 | 36 | | |
36 | 37 | | |
37 | | - | |
| 38 | + | |
| 39 | + | |
38 | 40 | | |
39 | 41 | | |
| 42 | + | |
| 43 | + | |
40 | 44 | | |
41 | | - | |
42 | | - | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
43 | 48 | | |
44 | 49 | | |
45 | 50 | | |
| |||
53 | 58 | | |
54 | 59 | | |
55 | 60 | | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
56 | 88 | | |
57 | 89 | | |
58 | 90 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
23 | 26 | | |
24 | 27 | | |
25 | 28 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
4 | | - | |
5 | | - | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
28 | | - | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
29 | 37 | | |
30 | | - | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
31 | 41 | | |
32 | 42 | | |
33 | 43 | | |
34 | 44 | | |
35 | 45 | | |
36 | 46 | | |
37 | | - | |
38 | | - | |
39 | | - | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
40 | 51 | | |
41 | 52 | | |
42 | 53 | | |
43 | 54 | | |
44 | 55 | | |
| 56 | + | |
45 | 57 | | |
46 | | - | |
| 58 | + | |
47 | 59 | | |
48 | 60 | | |
49 | 61 | | |
| |||
53 | 65 | | |
54 | 66 | | |
55 | 67 | | |
56 | | - | |
| 68 | + | |
57 | 69 | | |
58 | 70 | | |
59 | 71 | | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
60 | 79 | | |
61 | 80 | | |
62 | 81 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
| 10 | + | |
10 | 11 | | |
| 12 | + | |
11 | 13 | | |
12 | 14 | | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
13 | 18 | | |
14 | 19 | | |
15 | 20 | | |
| |||
24 | 29 | | |
25 | 30 | | |
26 | 31 | | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
27 | 37 | | |
28 | | - | |
| 38 | + | |
29 | 39 | | |
30 | 40 | | |
31 | 41 | | |
| |||
0 commit comments