Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: Implement unified pagination and add template versions support#1308

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged

Conversation

mafredri
Copy link
Member

@mafredrimafredri commentedMay 5, 2022
edited
Loading

This PR adds support for pagination to the/api/v2/templates/:id/versions endpoint whilst breaking out some of the common functionality that can be shared between endpoints that will support pagination. As such, the/api/v2/users endpoint also received a small refactor.

After a discussion with the team, I settled on using embedded structs for the pagination options. We may want to consider using pointers&codersdk.Pagination{} for being explicit when they're not set, but since the zero values work well it should compose better in more use cases (e.g.req := codersdk.MyRequest{}; req.Limit = 10). Among other API designs considered were functional options and and optional arguments inside optional structs.

I decided to omit support forsearch in the template versions endpoint since I was uncertain if this will be needed. If we want to support it, we may want to consider support for defining the column/field to be searched (e.g. should search be applied to name, description or both).

Questions:

@Emyrk Currently using bothoffset andafter_id are supported, as per your design. What are your thoughts about splitting these up? We could for instance create two separate types:type PaginateOffset struct andtype PaginateID struct, an endpoint can choose to support either or both by embedding (example):

type MyEndpointRequest struct {        MyParam int        PaginateOffset        PaginateID}

Benefits of splitting them up would be to allow an endpoint to use a lesser feature set, but maybe there's a use-case for always supporting both?

Misc thoughts:

  • Incodersdk it would make sense to me to rename all.*Request structs to.*Params since they define the parameters that can be used for the Client method(s). My reasoning is that we're doing arequest by invoking the client method, the params only define the arguments.
  • Should we start to consider adding support for ordering by a specified column as well? (This would be left up to future exploration)

Fixes#53

@mafredrimafredri requested a review froma team as acode ownerMay 5, 2022 12:18
@mafredrimafredri self-assigned thisMay 5, 2022
@codecov
Copy link

codecovbot commentedMay 5, 2022
edited
Loading

Codecov Report

Merging#1308 (aab400c) intomain (dc115b8) willincrease coverage by0.01%.
The diff coverage is59.48%.

@@            Coverage Diff             @@##             main    #1308      +/-   ##==========================================+ Coverage   66.81%   66.82%   +0.01%==========================================  Files         284      282       -2       Lines       18633    18493     -140       Branches      235      235              ==========================================- Hits        12449    12358      -91+ Misses       4903     4871      -32+ Partials     1281     1264      -17
FlagCoverage Δ
unittest-go-macos-latest54.07% <52.58%> (+0.13%)⬆️
unittest-go-postgres-65.59% <54.31%> (+0.07%)⬆️
unittest-go-ubuntu-latest56.36% <52.58%> (-0.03%)⬇️
unittest-go-windows-2022?
unittest-js73.67% <ø> (ø)
Impacted FilesCoverage Δ
scripts/apitypings/main.go0.00% <0.00%> (ø)
coderd/pagination.go51.35% <51.35%> (ø)
coderd/templates.go64.24% <76.92%> (+1.95%)⬆️
coderd/users.go61.44% <88.88%> (+0.81%)⬆️
coderd/database/queries.sql.go77.90% <100.00%> (+0.06%)⬆️
codersdk/client.go59.74% <100.00%> (ø)
codersdk/pagination.go100.00% <100.00%> (ø)
codersdk/templates.go60.37% <100.00%> (ø)
codersdk/users.go63.79% <100.00%> (-0.62%)⬇️
cli/configssh.go62.58% <0.00%> (-6.48%)⬇️
... and15 more

Continue to review full report at Codecov.

Legend -Click here to learn more
Δ = absolute <relative> (impact),ø = not affected,? = missing data
Powered byCodecov. Last updatedc115b8...aab400c. Read thecomment docs.

Copy link
Member

@kylecarbskylecarbs left a comment

Choose a reason for hiding this comment

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

Looks good to me! Just two minor comments 😎

readonly status: string
readonly pagination: Pagination
Copy link
Contributor

Choose a reason for hiding this comment

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

@BrunoQuaresma just want to make sure you see this and get a chance to discuss it if needed

export interface Pagination {
readonly after_id: string
readonly limit: number
readonly offset: number
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this mean we send eitherafter_id orlimit andoffset? I think typescript will complain if we leave anything out of this type, so can we send 0 or empty string for the ones we're not using?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

@presleyp The way this is (currently) implemented means you can set any combination of them (including setting all or none). I don't know if we want to keep it that way though.

@Emyrk do you know if there's a way we can make the generator express the above? (E.g. would setting? all of these, likereadonly after_id?: string, work?)

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah I'm concerned that if the backend expects "just omit the ones you don't want to use" but the frontend enforces "these are all required" then we might have trouble sending well formed requests.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Yes I agree, that would be super inconvenient! I see pointers and a few other null types are marked as optional by the TypeScript generator, but not others. I could take a look at improving that. Perhaps using the jsonomitempty option (Go specific) could also be marked optional in TypeScript.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think bothafter_id andoffset make sense together. And, I'll argue that we should dropoffset entirely in favor ofafter_id.

The problem withoffset is that it is not safe to race conditions that add or delete rows. Say we have {a0, a1, a2, b0, b1, b2} as our data. I paginate withlimit=3 offset=0 and get {a0, a1, a2}. Now someone else deletes a1. Then I call paginate withlimit=3 offset=3. I get {b1, b2}!

You might argue that in some use cases, it doesn't matter if we duplicate or omit data, so why not give both options? The problem is that it's really hard to be sure we've communicated the danger aboutoffset to potential users of this API. They might not read the docstrings. This API might end up being offered to end users outside of Coder. Thatoffset is so tempting because the code is easier to write thanafter_id, but we're setting users up to shoot themselves in the foot.

Copy link
MemberAuthor

@mafredrimafredriMay 5, 2022
edited
Loading

Choose a reason for hiding this comment

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

I agree with this. It's also why I suggested separating the options (offset and after_id) into two structs in my PR description. But since adding both was deliberate (#1057 (comment)) I don't think we should remove it in this PR (at least not for the users endpoint). Personally I don't see much of a usability difference between the two and would prefer only havingafter_id (or another solution entirely).

// Pagination sets pagination options for the endpoints that support it.
type Pagination struct {
// AfterID returns all or up to Limit results after the given
// UUID. This option can be used with or as an alternative to
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand the use case for using both AfterID and Offset. I would have thought they should be mutually exclusive.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I agree, I'd be in favor of droppingoffset if there's no use-case for it.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I found some context in an older PR:#1057 (comment)

Copy link
Member

Choose a reason for hiding this comment

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

@presleyp@BrunoQuaresma Can the frontend just use cursor based pagination? How much harder would it be to support over offset

Copy link
Contributor

Choose a reason for hiding this comment

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

I wouldn't argue for offset-based pagination over cursor-based pagination based on how hard it is to implement on the frontend, but...we don't need pagination on the frontend in CE so we are currently ignoring it, sending no limit or offset params, which I believe works because the offset-based pagination defaults are the same as if it wasn't paginated. Could cursor-based pagination be similarly ignored?

Copy link
Contributor

Choose a reason for hiding this comment

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

Cursor-based pagination is similarly ignored if not set in the implementation I'm reading.

presleyp and mafredri reacted with thumbs up emoji
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I opened up an issue/feature for this:#1358

presleyp reacted with thumbs up emoji
export interface Pagination {
readonly after_id: string
readonly limit: number
readonly offset: number
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think bothafter_id andoffset make sense together. And, I'll argue that we should dropoffset entirely in favor ofafter_id.

The problem withoffset is that it is not safe to race conditions that add or delete rows. Say we have {a0, a1, a2, b0, b1, b2} as our data. I paginate withlimit=3 offset=0 and get {a0, a1, a2}. Now someone else deletes a1. Then I call paginate withlimit=3 offset=3. I get {b1, b2}!

You might argue that in some use cases, it doesn't matter if we duplicate or omit data, so why not give both options? The problem is that it's really hard to be sure we've communicated the danger aboutoffset to potential users of this API. They might not read the docstrings. This API might end up being offered to end users outside of Coder. Thatoffset is so tempting because the code is easier to write thanafter_id, but we're setting users up to shoot themselves in the foot.

// Offset for better performance. To use it as an alternative,
// set AfterID to the last UUID returned by the previous
// request.
AfterID uuid.UUID `json:"after_id"`
Copy link
Contributor

Choose a reason for hiding this comment

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

This locks us into having a UUID for every database table that we want to paginate. Have we fully considered the consequences?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I had the same thought, but at least all existing tables seem to be using UUID forid.

@coder/backend Does anyone else have input on this? Steven seems to be on PTO so can't check in with him.

Copy link
Member

Choose a reason for hiding this comment

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

This can easily be changed to a string or something if we ever want to support different ID types in the future so I think it's fine for now

FROM
template_versions
WHERE
id = @after_id
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if the after_id doesn't exist? Either because user modified a URL, or because someone deleted the row?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

No results, which is acceptable in the former scenario. As for the latter, good catch. Not sure if this has been thought of.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I opened up an issue for this#1357 (also relevant#1358).

Copy link
Member

@deansheatherdeansheather left a comment

Choose a reason for hiding this comment

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

LGTM

@mafredrimafredriforce-pushed themafredri/feat-paginate-template-versions-endpoint branch from04b3b1f to13fc406CompareMay 10, 2022 07:30
…gs (#1318)* feat: Add support for json omitempty to apitypings* feat: Express embedded structs via extends in TypeScript* Handle unembedding via json field in apitypings* Add scripts/apitypings/main.go to Makefile
@mafredrimafredrienabled auto-merge (squash)May 10, 2022 07:42
@mafredrimafredri merged commit2d3dc43 intomainMay 10, 2022
@mafredrimafredri deleted the mafredri/feat-paginate-template-versions-endpoint branchMay 10, 2022 07:44
@mafredrimafredri changed the titlefeat: Implement pagination for template versionsfeat: Implement unified pagination and add template versions supportMay 10, 2022
@missknissmisskniss added this to theV2 Beta milestoneMay 15, 2022
kylecarbs pushed a commit that referenced this pull requestJun 10, 2022
…1308)* feat: Implement pagination for template versions* feat: Use unified pagination between users and template versions* Sync codepaths between users and template versions* Create requestOption type in codersdk and add test* Fix created_at edge case for pagination cursor in queries* feat: Add support for json omitempty and embedded structs in apitypings (#1318)* Add scripts/apitypings/main.go to Makefile
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@presleyppresleyppresleyp left review comments

@spikecurtisspikecurtisspikecurtis left review comments

@kylecarbskylecarbskylecarbs approved these changes

@deansheatherdeansheatherdeansheather approved these changes

@coadlercoadlercoadler approved these changes

@BrunoQuaresmaBrunoQuaresmaAwaiting requested review from BrunoQuaresma

@EmyrkEmyrkAwaiting requested review from Emyrk

Assignees

@mafredrimafredri

Labels
None yet
Projects
None yet
Milestone
V2 Beta
Development

Successfully merging this pull request may close these issues.

Paginate template history
7 participants
@mafredri@presleyp@spikecurtis@coadler@kylecarbs@deansheather@misskniss

[8]ページ先頭

©2009-2025 Movatter.jp