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

2 query parameters 'fields' on different resources but on the same set of fields cancel the last one #856

Open
lgringo opened this issue Sep 14, 2022 · 0 comments

Comments

@lgringo
Copy link

lgringo commented Sep 14, 2022

Issue

Given a resource named main having two related resources of type a and b.
When requesting main using fields[a]=label and fields[b]=label
Result all fields of resource b are returned.
Expected only label must be returned.

Example :

Resources

@JsonApiResource(type = "main")
public class MainResource {

	@JsonApiId
	private String id;

	@JsonApiRelation
	private RelatedResourceA relatedResourceA;

	@JsonApiRelation
	private RelatedResourceB relatedResourceB;
}

@JsonApiResource(type = "a")
public class RelatedResourceA {
	@JsonApiId
	private String id;

	private String label;

	private String otherField;
}

@JsonApiResource(type = "b")
public class RelatedResourceB {
	@JsonApiId
	private String id;

	private String label;

	private String somethingDifferent;
}

NB: Getters and setters are omitted for brevity.

Requests

NB: requests are written with httpie, for these examples, you just have to know that 'x==y' means add a request parameter x with value y.

No fields 👍

  • http :8080/main/mainId include==relatedResourceA,relatedResourceB
{
    "data": {
        "id": "mainId",
        "links": " [...] "
        "relationships": {
            "relatedResourceA": {
                "data": {
                    "id": "mainId.A",
                    "type": "a"
                },
                "links": " [...] "
            },
            "relatedResourceB": {
                "data": {
                    "id": "mainId.B",
                    "type": "b"
                },
                "links": " [...] "
            }
        },
        "type": "main"
    },
    "included": [
        {
            "attributes": {
                "label": "Label from resource A",
                "otherField": "Other"
            },
            "id": "mainId.A",
            "links": " [...] ",
            "type": "a"
        },
        {
            "attributes": {
                "label": "Label from resource B",
                "somethingDifferent": "Other"
            },
            "id": "mainId.B",
            "links": " [...] ",
            "type": "b"
        }
    ],
    "links": " [...] "
}

All fields from resource A and resource B are returned.

Only label for resource A (or resource B) 👍

  • http :8080/main/mainId include==relatedResourceA,relatedResourceB 'fields[a]==label'
{
...
    "included": [
        {
            "attributes": {
                "label": "Label from resource A"
            },
            "id": "mainId.A",
            "links": " [...] ",
            "type": "a"
        },
        {
            "attributes": {
                "label": "Label from resource B",
                "somethingDifferent": "Other"
            },
            "id": "mainId.B",
            "links": " [...] ",
            "type": "b"
        }
    ],
...
}

Field otherField has been filtered out.

Only label for resource A and resource B 👎

  • http :8080/main/mainId include==relatedResourceA,relatedResourceB 'fields[a]==label' 'fields[b]==label'
{
...
    "included": [
        {
            "attributes": {
                "label": "Label from resource A"
            },
            "id": "mainId.A",
            "links": " [...] ",
            "type": "a"
        },
        {
            "attributes": {
                "label": "Label from resource B",
                "somethingDifferent": "Other"
            },
            "id": "mainId.B",
            "links": " [...] ",
            "type": "b"
        }
    ],
...
}

The field 'somethingDifferent' is returned when it shouldn't

Here the full spring boot app to illustrate
You need maven and java (8+), unzip, go to directory, run mvn spring-boot:run

Causes :

QuerySpec. getNestedSpecs returns a Set of QuerySpec

    public Collection<QuerySpec> getNestedSpecs() {
    	// Using a set to remove duplicate querySpec between typeRelatedSpecs and classRelatedSpecs
    	Set<QuerySpec> allRelatedSpecs = new HashSet(typeRelatedSpecs.values());
    	allRelatedSpecs.addAll(classRelatedSpecs.values());
        return Collections.unmodifiableCollection(allRelatedSpecs);
    }

and

QuerySpec.equals does not use resourceType (neither resourceClass).

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        QuerySpec other = (QuerySpec) obj;
        return CompareUtils.isEquals(filters, other.filters) // NOSONAR
                && CompareUtils.isEquals(includedFields, other.includedFields)
                && CompareUtils.isEquals(includedRelations, other.includedRelations)
                && CompareUtils.isEquals(pagingSpec, other.pagingSpec)
                && CompareUtils.isEquals(typeRelatedSpecs, other.typeRelatedSpecs)
				&& CompareUtils.isEquals(classRelatedSpecs, other.classRelatedSpecs)
                && CompareUtils.isEquals(sort, other.sort);
    }

So when DocumentMapperUtil.getRequestedFields is called, QuerySpecAdapter.getIncludedFields is called, and then QuerySpec.getNestedSpecs is called ... and a spec is missing.

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

No branches or pull requests

1 participant