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

Support controller attribute in tags with resource attribute and resolve correct identity Fixes #13627 #13628

Merged
merged 9 commits into from
Sep 10, 2024

Conversation

codeconsole
Copy link
Contributor

@codeconsole codeconsole commented Sep 7, 2024

#13627

Fixes the following

<g:link method="GET" resource="user" controller="userAdmin"></g:link>

which should link to /userAdmin/show/1

but instead renders /user/show/1

Also fixes

class Sample {
    Long uniqueId
    static mapping {
        id: name: 'uniqueId'
    }
}

Currently (bug) grails creates a link to /sample/index for this class instead of /sample/${sample.indent()}/show because it does not have an id property and uses a static id mapping.

Design Assumptions

If a resource is a PersistentEntity or a Domain Artifact an attempt to call ident() is made, otherwise a MethodMissing exception occurs and a fallback to the id property is used.

Reason for Incorrect Behavior

The following code is where it is problematic
https://github.com/grails/grails-core/blob/da8668daa13c9c1b2157535e11986f49615e8a4b/grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/DefaultLinkGenerator.groovy#L174-L189

and is correctly handled here:
https://github.com/grails/grails-core/blob/da8668daa13c9c1b2157535e11986f49615e8a4b/grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/CachingLinkGenerator.java#L120-L139

https://github.com/grails/grails-core/blob/0daf7c69f98cd3f2f25ad990b52e005f7c976f5a/grails-core/src/main/groovy/grails/util/GrailsMetaClassUtils.java#L241-L248

Copy link
Contributor

@matrei matrei left a comment

Choose a reason for hiding this comment

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

Could you write a test that shows the bug and that this change fixes it?

@codeconsole
Copy link
Contributor Author

codeconsole commented Sep 8, 2024

Could you write a test that shows the bug and that this change fixes it?

Why can't I get the same treatment?

but I approve this so you can get on with more important tasks.

…rty that is not id. e.g. static mapping = { id: name: 'customId' }
@matrei
Copy link
Contributor

matrei commented Sep 9, 2024

Why can't I get the same treatment?

Haha, sorry 😄!

This changes the behavior of Grails and there should be a test showing and testing the correct behavior.
Also protects against regressions in future changes.

And it should be a simple test to write.

@codeconsole codeconsole changed the title Support controller attribute in tags with resource attribute. Fixes #13627 Support controller attribute in tags with resource attribute and resolve correct identity Fixes #13627 Sep 9, 2024
Copy link
Contributor

@matrei matrei left a comment

Choose a reason for hiding this comment

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

Update the year and copyright owner in the header of DefaultLinkGenerator.groovy: Copyright 2011-2024 the original author or authors.

if (ident) {
return ident.toString()
}
} catch (MissingMethodException | IllegalStateException e) {
Copy link
Contributor

Choose a reason for hiding this comment

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

  • You can use exception parameter name ignored to signal that this is expected.

  • I think it's fine to use try-catch here, until we find it's a performance problem or find a better way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -341,7 +365,8 @@ class LinkGeneratorSpec extends Specification {
def generator = cache ? new CachingLinkGenerator(baseUrl, context) : new DefaultLinkGenerator(baseUrl, context)
final callable = { String controller, String action, String namespace, String pluginName, String httpMethod, Map params ->
[createRelativeURL: { String c, String a, String n, String p, Map parameterValues, String encoding, String fragment ->
"${namespace ? '/' + namespace : ''}/$controller/$action".toString()

"${namespace ? '/' + namespace : ''}/$controller${parameterValues.id? '/'+parameterValues.id:''}/$action".toString()
Copy link
Contributor

Choose a reason for hiding this comment

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

To my knowledge, most common in Grails is using /controller/action/id. In the PR description you are using /controller/action/id and /controller/id/action. Should we be consistent, and use /controller/action/id here?

Copy link
Contributor

Choose a reason for hiding this comment

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

I do believe this needs to be namespace/controller/action/id

Copy link
Contributor

Choose a reason for hiding this comment

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

static mappings = {

        "/$namespace/$controller/$action?/$id?(.$format)?" {
            constraints {
            }
        }
  }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@matrei @jamesfredley I had made it a "restful mapping" for the test as it was specific to the test. To eliminate confusion, I just changed it to the format you guys are referring to.

@@ -289,6 +290,29 @@ class LinkGeneratorSpec extends Specification {
cacheKey == "somePrefix[resource:org.grails.web.mapping.Widget->2]"
}

//
def 'resource links should use ident and allow controller override'() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice!

  • You could add @Issue('https://github.com/grails/grails-core/issues/13627') on the test.

@@ -341,7 +365,8 @@ class LinkGeneratorSpec extends Specification {
def generator = cache ? new CachingLinkGenerator(baseUrl, context) : new DefaultLinkGenerator(baseUrl, context)
final callable = { String controller, String action, String namespace, String pluginName, String httpMethod, Map params ->
[createRelativeURL: { String c, String a, String n, String p, Map parameterValues, String encoding, String fragment ->
"${namespace ? '/' + namespace : ''}/$controller/$action".toString()

"${namespace ? '/' + namespace : ''}/$controller${parameterValues.id? '/'+parameterValues.id:''}/$action".toString()
Copy link
Contributor

Choose a reason for hiding this comment

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

I do believe this needs to be namespace/controller/action/id

@@ -341,7 +365,8 @@ class LinkGeneratorSpec extends Specification {
def generator = cache ? new CachingLinkGenerator(baseUrl, context) : new DefaultLinkGenerator(baseUrl, context)
final callable = { String controller, String action, String namespace, String pluginName, String httpMethod, Map params ->
[createRelativeURL: { String c, String a, String n, String p, Map parameterValues, String encoding, String fragment ->
"${namespace ? '/' + namespace : ''}/$controller/$action".toString()

"${namespace ? '/' + namespace : ''}/$controller${parameterValues.id? '/'+parameterValues.id:''}/$action".toString()
Copy link
Contributor

Choose a reason for hiding this comment

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

static mappings = {

        "/$namespace/$controller/$action?/$id?(.$format)?" {
            constraints {
            }
        }
  }

linkParams.resource = new Widget(id: 1, name: 'Some Widget')

then:
link == "/bar/widget/1/show"
Copy link
Contributor

Choose a reason for hiding this comment

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

linkParams.controller = 'widgetAdmin'

then:
link == "/bar/widgetAdmin/1/show"
Copy link
Contributor

Choose a reason for hiding this comment

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

@jamesfredley
Copy link
Contributor

the getResourceId(resourceAttribute) fix is big. I'm not sure how that had not come up before.

@codeconsole codeconsole merged commit 8871703 into apache:6.2.x Sep 10, 2024
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants