Skip to content
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

Convenient Pagination API, Browser Support, Missing API fields #60

Open
wants to merge 38 commits into
base: master
Choose a base branch
from

Conversation

pyrogenic
Copy link

Several additions to Discojs that I've accumulated over the years building Elephant. It's a browser-based tool to manage a Discogs collection and store listings. (source)

  • Add an all API to Discojs that retrieves all pages of a paginated endpoint given the first page:
    function updateCollection() {
      return client.listItemsInFolder(0, undefined, {
        perPage: 100,
      }).then(((r) => client.all("releases", r, addToCollection)), setError);
    }
  • Add a model for the error format used by Discogs to allow better error reporting
  • Add a few missing fields to various responses (notes to FolderReleases, name to Artist, avatar_url to User)
  • Fill out the model for marketplace listings, which did not include any of the owner-only fields
  • Fix nesting of the model for per-track information
  • Add the option to provide a ResultCache which may be used to skip retrieving results multiple times. (My app stores GET responses using IndexedDB.)
  • Tweak the package scripts to work on Windows (I develop on several platforms)

Happy to make changes to allow the PR to be accepted.

@pyrogenic pyrogenic marked this pull request as ready for review February 11, 2024 02:31
@aknorw
Copy link
Owner

aknorw commented Aug 1, 2024

@pyrogenic Sorry for missing this one, and thanks for all the improvements!
Would it be possible to split this PR into multiple ones so it'd be easier to review?
Happy to help!

Copy link
Owner

@aknorw aknorw left a comment

Choose a reason for hiding this comment

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

@pyrogenic I spent some time yesterday introducing some changes initiated in this PR:

  • Add missing fields (see comments for ones I did not add)
  • Improve error reporting
  • Introduce result cache
  • Add allowUnsafeHeaders option
  • Introduce dedicated methods to iterate through pages (see comment in the review about the implemenation difference)

Let me know what you think about them!


export type ErrorResponse = t.TypeOf<typeof ErrorResponseIO>

export type Pagination = t.TypeOf<typeof PaginationIO>
Copy link
Owner

Choose a reason for hiding this comment

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

We already have a Pagination type, named this one PaginationResponse.

@@ -70,7 +70,7 @@ export const ListingIO = t.intersection([
ResourceURLIO,
t.type({
id: t.Integer,
status: makeEnumIOType(ListingStatusesEnum),
status: makeEnumIOType(InventoryStatusesEnum),
Copy link
Owner

Choose a reason for hiding this comment

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

I chose not to introduce this change yet, as this is only the case when users are authenticated. IMHO we should have 2 different codecs/types depending on whether the user is authenticated.

Copy link
Author

Choose a reason for hiding this comment

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

Ah, I didn't know that difference, I only saw it not match. Is it the user being authenticated at all, or the authenticated user being the owner of the listing?

I'll check out the code paths I encounter this and see what might make sense.

import { IPaginated } from '../models/api'
import type { Discojs } from './discojs'

export class AllNext {
Copy link
Owner

Choose a reason for hiding this comment

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

I felt this implementation was a bit hard to follow, as you first have to call a method, then call client.all.
Instead of doing that, I introduced dedicated all methods that returns async iterators:

  • getAllMasterVersions
  • getAllArtistReleases
  • getAllLabelReleases
  • getAllRecentExports
  • getAllInventoryForUser
  • getAllInventory
  • listAllOrders
  • listAllItemsByReleaseForUser
  • listAllItemsByRelease
  • listAllItemsInFolderForUser
  • listAllItemsInFolder
  • getAllSubmissionsForUser
  • getAllSubmissions
  • getAllContributionsForUser
  • getAllContributions
  • getAllListsForUser
  • getAllLists
  • getAllWantlistForUser
  • getAllWantlist

The example you have in the PR description would become:

async function updateCollection() {
  for await (const { releases } of client.listAllItemsInFolder()) {
    for (const release of releases) {
      await addToCollection(release)
    }
  }
}

Copy link
Author

Choose a reason for hiding this comment

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

That seems much more user-friendly, if less magical. 😉

I probably chose my method based on writing the least code and exercising some TypeScript-fu.

Comment on lines +30 to +35
let buffer: undefined | typeof Buffer
try {
buffer = Buffer
} catch {
buffer = undefined
}
Copy link
Owner

Choose a reason for hiding this comment

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

What is the purpose of this?

Copy link
Author

Choose a reason for hiding this comment

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

I'm guessing there was a situation in some browser where accessing Buffer to calculate content length through. I honestly don't remember the details. Getting Chrome to be happy with those headers and their capitalization was a PITA. I myself would reject this without an explanatory comment about the circumstances of the error and the expected alternate behavior...

@pyrogenic
Copy link
Author

pyrogenic commented Sep 17, 2024

@aknorw thanks so much for taking the time to review, and sorry for the delayed response. I'll take a closer look outside work hours!

... and apologies for throwing a couple years of tweaks up in a single PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants