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

Ensure consistent behaviour for path and fullPath #19

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

dodgex
Copy link
Contributor

@dodgex dodgex commented Jan 26, 2025

This PR makes the behaviour of path and fullPath consistent. This is done by removing the version check, in fullPath. Also to not have the same String.format duplicated, i use the path method inside fullPath.

As this broke the related test, I updated the test and renamed the full_path_exists_version_supplied to full_path_double_version_with_version_supplied and added path_double_version_with_version_supplied to have a similar test for path only.

One thing to note here, is that this can and probably should be considered a minor breaking change. As this breaks cases where a user accidentally added a version to the referenced webjar resouce in thier html. Becaus of this I think maybe this should rather become a 1.1.0 instead of a 1.0.x release. Not sure if this kind of breakage deserverse a major bump.

This also ensures that if Spring Boot upgrades to the version containing this does not silently land in 3.4.x but only in 3.5.0 and thus can be mentioned in thier release notes.

Fixes #18

@jamesward
Copy link
Member

Thanks for tackling this. I think it makes sense to have the only difference between path and fullPath be the standard webjar location prefix. I'm not totally sure why we ever deviated from that. I agree on this being 1.1.0 to indicate possible breakage. Before I do that I want to give @sdeleuze a chance to provide any feedback.

@sdeleuze
Copy link

Thanks for the ping @jamesward. I am all for consistency, but I am not sure to be in favor of merging this PR as it is.

The version check and removal in fullPath is also present in webjars-locator-core for a very long time, used in multiple tests there, and this change will likely break Spring applications.

Maybe I miss some context, but I would recommend double checking why this support was introduced initially in webjars-locator-core, and make sure that this is a behavior you are sure you want not to support anymore. For example, if it was only useful for Bower support maybe that's fine, but if there was other use cases, maybe not.

If you are sure you want to do it, I think it should be applied on both webjars-locator-core and webjars-locator-lite. Bumping the minor would be a minimum, I am even wondering if you should not bump the major.

cc @bclozel for advice.

@bclozel
Copy link

bclozel commented Jan 27, 2025

Hey @dodgex @jamesward

I had a look at this PR and here's what I think.

This change should not break Spring as we're first checking if the resource exists before delegating to webjars-locator. So we shouldn't be calling the library if the version is already present in the path.

Now I think the main problem here is the lack of contract. the path and fullpath javadoc document the parameters but don't explain the intent. I'd say the main Intent is "Produce a WebJar-compliant URL for the given webjar and resource path, adding the webjar version if missing". I think the current behavior for fullPath is the correct one. We don't expect webjars-locator to check for the presence of the actual resource on the class. But even if you don't expect to be called with a version already resolved in the path, you shouldn't return a path that's obviously false and doesn't respect the webjars URL spec.

In summary, I would opt for the opposite: align path with the fullPath behavior and don't try and generate a webjar URL that will obviously fail at runtime.

@dodgex
Copy link
Contributor Author

dodgex commented Jan 27, 2025

Thank you guys for your feedback.

I agree with @bclozel that there is a lack of a contract. Here @jamesward should decide on a contract.
After doing some testing I found that this change does not actually change any behaviour related to Spring Framework.

Unfortunately the commit messages related to the lines with the version check did not seem to be very helpful to determine the need for the check.
The check has been there from the first commit adding the getFullPathExact.
The else branch has been added in a big cleanup commit.

In summary, I would opt for the opposite: align path with the fullPath behavior and don't try and generate a webjar URL that will obviously fail at runtime.

The/My intent was to intentionally fail early when adding a WebJar with a hardcoded path to avoid the same problem later, when the WebJar dependency is upgraded. In my opinion, the chance to catch a 404 is higher when adding a new WebJar to the HTML as when later upgrading e.g. Bootstrap 5.3.2 to 5.3.3. but having the 5.3.2 in the HTML.

But as the LiteWebJarsResourceResolver checks the initial url for existence, the changes in this PR have no effect at all on Spring. And after a dependency upgrade, the WebJar URL with a version in HTML is broken no matter of this PR or not.

So unless the Spring Team decides to remove that check, this PR has no impact on Spring projects at all.

For now I agree, with changing the PR to keep the check but still have path and fullPath consistent. @jamesward what do you think?


Examples

  1. Thymeleaf with version: <link data-th-href="@{/webjars/bootstrap/5.3.3/css/bootstrap.min.css}" rel="stylesheet">
  2. Thymeleaf without version: <link data-th-href="@{/webjars/bootstrap/css/bootstrap.min.css}" rel="stylesheet">
  3. Thymeleaf invalid version: <link data-th-href="@{/webjars/bootstrap/5.0.0/css/bootstrap.min.css}" rel="stylesheet">
  4. Hardcoded with version: <link href="/webjars/bootstrap/5.3.3/css/bootstrap.min.css" rel="stylesheet">
  5. Hardcoded without version: <link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet">
  6. Hardcoded invalid version: <link href="/webjars/bootstrap/5.0.0/css/bootstrap.min.css" rel="stylesheet">

Cases

In the following cases, the examples above are tested with and without the locator dependencies.
The results are represented with the state, and the href rendered into the resulting HTML.

✅ = works (CSS loaded successfully)
❌ = does not work

  • Tested with Spring Boot 3.4.2 with web and thymeleaf starter.

No locator dependency

  1. ✅ /webjars/bootstrap/5.3.3/css/bootstrap.min.css
  2. ❌ /webjars/bootstrap/css/bootstrap.min.css
  3. ❌ /webjars/bootstrap/5.0.0/css/bootstrap.min.css
  4. ✅ as is
  5. ❌ as is
  6. ❌ as is

webjars-locator-core 0.59

  1. ✅ /webjars/bootstrap/5.3.3/css/bootstrap.min.css
  2. ✅ /webjars/bootstrap/5.3.3/css/bootstrap.min.css
  3. ❌ /webjars/bootstrap/5.0.0/css/bootstrap.min.css
  4. ✅ as is
  5. ✅ as is
  6. ❌ as is

webjars-locator-lite 1.0.1

  1. ✅ /webjars/bootstrap/5.3.3/css/bootstrap.min.css
  2. ✅ /webjars/bootstrap/5.3.3/css/bootstrap.min.css
  3. ❌ /webjars/bootstrap/5.0.0/css/bootstrap.min.css
  4. ✅ as is
  5. ✅ as is
  6. ❌ as is

webjars-locator-lite 1.0.2-SNAPSHOT (local build of this PR)

  1. ✅ /webjars/bootstrap/5.3.3/css/bootstrap.min.css (WebJars locator is not even called)
  2. ✅ /webjars/bootstrap/5.3.3/css/bootstrap.min.css (WebJars locator fullPath returns META-INF/resources/webjars/bootstrap/5.3.3/css/bootstrap.min.css)
  3. ❌ /webjars/bootstrap/5.0.0/css/bootstrap.min.css (WebJars locator fullPath returns META-INF/resources/webjars/bootstrap/5.3.3/5.0.0/css/bootstrap.min.css)
  4. ✅ as is (WebJars locator is not even called)
  5. ✅ as is (WebJars locator fullPath returns META-INF/resources/webjars/bootstrap/5.3.3/css/bootstrap.min.css)
  6. ❌ as is (WebJars locator fullPath returns META-INF/resources/webjars/bootstrap/5.3.3/5.0.0/css/bootstrap.min.css)

The LiteWebJarsResourceResolver checks for the existence of the given path using chain.resolveUrlPath(),
if the file does not exist it results in null and the calling code falls back to using the original path.

@dodgex
Copy link
Contributor Author

dodgex commented Jan 27, 2025

I updated the PR to re-add the version check to path. Due to fullPath invoking path from previous changes, both are consistent in behaviour.

Just to be sure, I also tested the six test cases with a new build of this PR. As expected, there are no changes at all.

webjars-locator-lite 1.0.2-SNAPSHOT (local build of updated PR)

  1. ✅ /webjars/bootstrap/5.3.3/css/bootstrap.min.css (WebJars locator is not even called)
  2. ✅ /webjars/bootstrap/5.3.3/css/bootstrap.min.css (WebJars locator fullPath returns META-INF/resources/webjars/bootstrap/5.3.3/css/bootstrap.min.css)
  3. ❌ /webjars/bootstrap/5.0.0/css/bootstrap.min.css (WebJars locator fullPath returns META-INF/resources/webjars/bootstrap/5.3.3/5.0.0/css/bootstrap.min.css)
  4. ✅ as is (WebJars locator is not even called)
  5. ✅ as is (WebJars locator fullPath returns META-INF/resources/webjars/bootstrap/5.3.3/css/bootstrap.min.css)
  6. ❌ as is (WebJars locator fullPath returns META-INF/resources/webjars/bootstrap/5.3.3/5.0.0/css/bootstrap.min.css)

@dodgex
Copy link
Contributor Author

dodgex commented Jan 27, 2025

And btw, in LiteWebJarsResourceResolver you invoke fullPath and then cut the "prefix" added by fullPath again . This should not be necessary by just calling path. And as Spring Framework is not calling the WebJars locator when a valid version is given, there is not even a need to wait for this PR/release.

@jamesward
Copy link
Member

Thank you all for the thoughtful and thorough responses! I like where this is at. I think it is important we guide users to a place where upgrading WebJar deps won't break things at runtime. I do like @bclozel's suggestion to add better JavaDocs stating the "contract" - @dodgex can you take a stab at that?

@dodgex
Copy link
Contributor Author

dodgex commented Jan 28, 2025

can you take a stab at that?

At this point I wish I had just answered No. :D
But now after two hours and a bit, I would say that I am done.

I probably overshoot the desired goal by a mile or two, but I hope this is ok. Beside trying to add more details and some kind of "contract" to path and fullPath, I also updated the JavaDoc of verison and the class itself by adding information about the new webjars-locator. properties support.

@jamesward please double and tripple proofread the updated JavaDoc to ensure no mistakes or bad formulations or whaterver. I am not a native english speaker and did a lot of iterations to the comments. I tried to proofread it as good as I can, but at some point you become blind to your own mistakes.

@jamesward
Copy link
Member

Thank you!! I made a few small tweaks but overall looks great. I'll let this sit for a little while to see if @bclozel or @sdeleuze has any further feedback.

*
* <p>Custom WebJars can be registered by using a {@code webjars-locator.properties} file. See {@link WebJarVersionLocator} for details.
*
* @param webJarName The name of the WebJar (the Maven artifact ID)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jamesward for official WebJars the webJarName (as in: the part in the path) is also the Maven artifact ID, but for custom webjars this is not necessarily true. In custom WebJars those names can differ. Our in-house WebJars have e.g. camera-module-webjar as Maven artifact but just camera-module in the resource path. There it would be the name give in the webjars-locator.properties

Not sure if this extra case should be mentioned/taken care of in the javadoc, or not mention it as it was before.

Also you added the (the Maven artifact ID) only to the version method, but not to the path and fullPath. I guess, that was not intentional?

*
* <p>Custom WebJars can be registered by using a {@code webjars-locator.properties} file. See {@link WebJarVersionLocator} for details.
*
* @param webJarName The name of the WebJar (the Maven artifact ID)
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for pointing that out. How about:

Suggested change
* @param webJarName The name of the WebJar (the Maven artifact ID)
* @param webJarName The directory name in the WebJar following `META-INF/resources/webjars` - Usually the same as the Maven artifact ID

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Inspried by this suggestion I reworded the description a bit and added it to the other two methods.

Hope it is ok the way it is now. :)

* locator.path("unknown", "some/file.css");
* }</pre>
*
* <p><b>Note:</b> When the {@code filePath} starts with a version, that is not the correct version of the WebJar, this method returns a path with both versions.
Copy link

Choose a reason for hiding this comment

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

I'm not sure we need to point out this implementation detail in the Javadoc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right. I was unsure on how much details we need, so I might have added a bit too much. Its gone now.

* locator.path("bootstrap", "css/bootstrap.css");
*
* // returns "bootstrap/3.1.1/js/bootstrap.js" as well
* locator.path("bootstrap", "3.1.1/css/bootstrap.css");
Copy link

Choose a reason for hiding this comment

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

Typo? Is it ".js" or ".css"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep typos. Will fix that

@jamesward
Copy link
Member

Thanks all! I think this is looking good and I'm ready to merge and release 1.1.0 - let me know if there are any final comments.

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.

Inconsistent behaviour in path and fullPath
4 participants