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

Fix non-canonical crate name pages #10369

Merged
merged 2 commits into from
Jan 13, 2025
Merged

Conversation

Turbo87
Copy link
Member

@Turbo87 Turbo87 commented Jan 10, 2025

Before the Ember Data 5 update it was possible to visit /crates/foo-bar when the crate was actually called foo_bar and vice-versa. The Ember Data 5 update broke this behavior, and this PR is restoring it, by using queryRecord() instead of findRecord(), which can no longer deal with requesting a certain id and getting a different one returned from the server.

Fixes #10368

@Turbo87 Turbo87 requested a review from eth3lbert January 10, 2025 15:45
@Turbo87 Turbo87 added C-bug 🐞 Category: unintended, undesired behavior A-frontend 🐹 labels Jan 10, 2025
return await this.store.findRecord('crate', crateName);
return await this.store.queryRecord('crate', { name: crateName });
Copy link
Member Author

Choose a reason for hiding this comment

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

as mentioned in the issue, ideally we would redirect the user to the same route, but with the crate name replaced. this appears to be non-trivial though, since the user could also have requested a child route and we would potentially redirect them to the wrong route. I think this fix matches the previous behavior though, so it's probably good enough for now :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Fair enough! I have no idea why this would be somewhat non-trivial in Ember, especially for nested routes 😅
Since queryRecord would make a request for every call, should we optimize this to something like peekRecord + queryRecord?

Copy link
Member Author

Choose a reason for hiding this comment

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

should we optimize this to something like peekRecord + queryRecord?

hmm, I guess that makes sense, yeah

Copy link
Contributor

@eth3lbert eth3lbert left a comment

Choose a reason for hiding this comment

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

Aside from the missing e2e tests, this looks good to me. The optimization part could also be implemented in a separate PR later.

Comment on lines 134 to 145
test('treats dash and underscore as equal', async function (assert) {
let crate = this.server.create('crate', { name: 'foo-bar' });
this.server.create('version', { crate });

await visit('/crates/foo_bar');

assert.strictEqual(currentURL(), '/crates/foo_bar');
assert.strictEqual(currentRouteName(), 'crate.index');
assert.strictEqual(getPageTitle(), 'foo-bar - crates.io: Rust Package Registry');

assert.dom('[data-test-heading] [data-test-crate-name]').hasText('foo-bar');
});
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should also check this in e2e.

@eth3lbert
Copy link
Contributor

I got an alternative workaround for this issue this morning. Since we know how to normalize crate.id, the response of the id is predictable. This means we could just make a unique lid for it. According to RFC1, we could leverage the setIdentifierGenerationMethod to tell the store how to generate a lid, which should be something like the following:

// crates.io/app/initializers/ember-data-identifier.js
import { setIdentifierGenerationMethod } from '@ember-data/store';

function toCanoncialName(name) {
  return name.toLowerCase().replace(/_/g, '-');
}

export function initialize(_app) {
  const generationMethod = (resource, _bucket) => {
    let { id, lid, type } = resource;
    if (lid) {
      return lid;
    }

    if (id) {
      if (resource.type === 'crate') {
        id = toCanoncialName(id);
      }

      return `@lid:${type}-${id}`;
    }

    return crypto.randomUUID();
  };

  setIdentifierGenerationMethod(generationMethod);
}

export default {
  name: 'app.configure-ember-data-identifier',
  initialize,
};

This way, IDs with dashes or underscores would be canonicalized to a single unique lid.

This approach has the benefit of not requiring modifications to other business logic. However, the downside is that if the response id ever becomes unpredictable, the issue would occur again.

Footnotes

  1. https://rfcs.emberjs.com/id/0403-ember-data-identifiers/

@Turbo87
Copy link
Member Author

Turbo87 commented Jan 13, 2025

we could leverage the setIdentifierGenerationMethod to tell the store how to generate a lid

I like the approach, but unfortunately this seems to break our API token creation flow, at least with the proposed implementation. it seems to be missing the NEW_IDENTIFIERS part of https://github.com/emberjs/data/blob/v5.3.9/packages/store/src/-private/caches/identifier-cache.ts, but I'm not sure if reimplementing that is worth the complexity.

@Turbo87 Turbo87 force-pushed the canonical-names branch 2 times, most recently from 85c637a to 4b40251 Compare January 13, 2025 12:34
@eth3lbert
Copy link
Contributor

I like the approach, but unfortunately this seems to break our API token creation flow, at least with the proposed implementation. it seems to be missing the NEW_IDENTIFIERS part of https://github.com/emberjs/data/blob/v5.3.9/packages/store/src/-private/caches/identifier-cache.ts,

Ah, that's indeed unfortunate. It would be nice if upstream exported the default generation method, then we could just use it as a fallback.

but I'm not sure if reimplementing that is worth the complexity.

Fair enough, unless the generation method approach could be easier to use without modifying other places, I think the adapter approach would make more sense.

Comment on lines 6 to 8
function toCanonicalName(name) {
return name.toLowerCase().replace(/_/g, '-');
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🙋 The canonical implementation in the backend(database) actually replaces - with _, but here we replace _ with -. Is this difference intentional?

Copy link
Member Author

Choose a reason for hiding this comment

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

Is this difference intentional?

no, it's not intentional. I guess ultimately it doesn't really matter which way around we do it, but I'll adjust it so that it matches.

Before the Ember Data 5 update it was possible to visit `/crates/foo-bar` when the crate was actually called `foo_bar` and vice-versa. The Ember Data 5 update broke this behavior, and this commit is restoring it, by using `queryRecord()` instead of `findRecord()`, which can no longer deal with requesting a certain `id` and getting a different one returned from the server.
@Turbo87 Turbo87 merged commit 6a6017e into rust-lang:main Jan 13, 2025
9 checks passed
@Turbo87 Turbo87 deleted the canonical-names branch January 13, 2025 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-frontend 🐹 C-bug 🐞 Category: unintended, undesired behavior
Projects
None yet
Development

Successfully merging this pull request may close these issues.

The crate view page is empty when the only difference to a published crate is - vs _
2 participants