Skip to content

Commit

Permalink
GET with includes can't match children to parent when parent has a co…
Browse files Browse the repository at this point in the history
…mpound key #684

porting 4.x unit test to confirm the correct behavior
  • Loading branch information
andrus committed Jul 9, 2024
1 parent 090866a commit 24df915
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 4 deletions.
1 change: 1 addition & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* #679 Upgrading Jackson to 2.15.4
* #680 Upgrading SLF4J to 2.0.7
* #683 Upgrade Cayenne to 4.2.1
* #684 GET with includes can't match children to parent when parent has a compound key

## Release 5.0.M19

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import io.agrest.DataResponse;
import io.agrest.cayenne.cayenne.main.E2;
import io.agrest.cayenne.cayenne.main.E3;
import io.agrest.cayenne.cayenne.main.E32;
import io.agrest.cayenne.cayenne.main.E33;
import io.agrest.cayenne.cayenne.main.E4;
import io.agrest.cayenne.cayenne.main.E5;
import io.agrest.cayenne.unit.main.MainDbTest;
Expand All @@ -21,7 +23,7 @@ public class IncludeObjectIT extends MainDbTest {

@BQTestTool
static final MainModelTester tester = tester(Resource.class)
.entitiesAndDependencies(E2.class, E3.class, E4.class, E5.class)
.entitiesAndDependencies(E2.class, E3.class, E4.class, E5.class, E32.class, E33.class)
.build();

@Test
Expand Down Expand Up @@ -205,9 +207,9 @@ public void toMany_Sort() {
.queryParam("include", "{\"path\":\"e3s\",\"sort\":\"name\"}")
.queryParam("include", "id")
.get().wasOk().bodyEquals(1, "{\"id\":1,\"e3s\":["
+ "{\"id\":7,\"name\":\"b\",\"phoneNumber\":null},"
+ "{\"id\":9,\"name\":\"s\",\"phoneNumber\":null},"
+ "{\"id\":8,\"name\":\"z\",\"phoneNumber\":null}]}");
+ "{\"id\":7,\"name\":\"b\",\"phoneNumber\":null},"
+ "{\"id\":9,\"name\":\"s\",\"phoneNumber\":null},"
+ "{\"id\":8,\"name\":\"z\",\"phoneNumber\":null}]}");
}

@Test
Expand Down Expand Up @@ -410,6 +412,25 @@ public void toMany_IncludeExtMapRelated() {
+ "{\"e5\":{\"name\":\"A\"},\"name\":\"m\"}]}");
}

@Test
public void testCompoundRootKey() {

tester.e33().insertColumns("p_id", "name")
.values(11, "n33_1")
.values(12, "n33_2").exec();

tester.e32().insertColumns("p_id", "s_id", "t_id", "name")
.values(11, 101, 1001, "n32_1")
.values(12, 102, 1002, "n32_2").exec();

tester.target("/e32")
.queryParam("include", "e33")
.get()
.wasOk()
.bodyEquals(2, "{\"id\":{\"db:p_id\":11,\"db:s_id\":101,\"db:t_id\":1001},\"e33\":{\"id\":11,\"name\":\"n33_1\"},\"name\":\"n32_1\"}," +
"{\"id\":{\"db:p_id\":12,\"db:s_id\":102,\"db:t_id\":1002},\"e33\":{\"id\":12,\"name\":\"n33_2\"},\"name\":\"n32_2\"}");
}

@Path("")
public static class Resource {

Expand All @@ -433,5 +454,11 @@ public DataResponse<E3> getE3(@Context UriInfo uriInfo) {
public DataResponse<E4> getE4(@Context UriInfo uriInfo) {
return AgJaxrs.select(E4.class, config).clientParams(uriInfo.getQueryParameters()).get();
}

@GET
@Path("e32")
public DataResponse<E32> getE32(@Context UriInfo uriInfo) {
return AgJaxrs.select(E32.class, config).clientParams(uriInfo.getQueryParameters()).get();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.agrest.cayenne.cayenne.main;

import io.agrest.cayenne.cayenne.main.auto._E32;

public class E32 extends _E32 {

private static final long serialVersionUID = 1L;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.agrest.cayenne.cayenne.main;

import io.agrest.cayenne.cayenne.main.auto._E33;

public class E33 extends _E33 {

private static final long serialVersionUID = 1L;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package io.agrest.cayenne.cayenne.main.auto;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import org.apache.cayenne.BaseDataObject;
import org.apache.cayenne.exp.property.EntityProperty;
import org.apache.cayenne.exp.property.PropertyFactory;
import org.apache.cayenne.exp.property.StringProperty;

import io.agrest.cayenne.cayenne.main.E33;

/**
* Class _E32 was generated by Cayenne.
* It is probably a good idea to avoid changing this class manually,
* since it may be overwritten next time code is regenerated.
* If you need to make any customizations, please use subclass.
*/
public abstract class _E32 extends BaseDataObject {

private static final long serialVersionUID = 1L;

public static final String S_ID_PK_COLUMN = "s_id";
public static final String P_ID_PK_COLUMN = "p_id";
public static final String T_ID_PK_COLUMN = "t_id";

public static final StringProperty<String> NAME = PropertyFactory.createString("name", String.class);
public static final EntityProperty<E33> E33 = PropertyFactory.createEntity("e33", E33.class);

protected String name;

protected Object e33;

public void setName(String name) {
beforePropertyWrite("name", this.name, name);
this.name = name;
}

public String getName() {
beforePropertyRead("name");
return this.name;
}

public void setE33(E33 e33) {
setToOneTarget("e33", e33, true);
}

public E33 getE33() {
return (E33)readProperty("e33");
}

@Override
public Object readPropertyDirectly(String propName) {
if(propName == null) {
throw new IllegalArgumentException();
}

switch(propName) {
case "name":
return this.name;
case "e33":
return this.e33;
default:
return super.readPropertyDirectly(propName);
}
}

@Override
public void writePropertyDirectly(String propName, Object val) {
if(propName == null) {
throw new IllegalArgumentException();
}

switch (propName) {
case "name":
this.name = (String)val;
break;
case "e33":
this.e33 = val;
break;
default:
super.writePropertyDirectly(propName, val);
}
}

private void writeObject(ObjectOutputStream out) throws IOException {
writeSerialized(out);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
readSerialized(in);
}

@Override
protected void writeState(ObjectOutputStream out) throws IOException {
super.writeState(out);
out.writeObject(this.name);
out.writeObject(this.e33);
}

@Override
protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
super.readState(in);
this.name = (String)in.readObject();
this.e33 = in.readObject();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package io.agrest.cayenne.cayenne.main.auto;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import org.apache.cayenne.BaseDataObject;
import org.apache.cayenne.exp.property.PropertyFactory;
import org.apache.cayenne.exp.property.StringProperty;

/**
* Class _E33 was generated by Cayenne.
* It is probably a good idea to avoid changing this class manually,
* since it may be overwritten next time code is regenerated.
* If you need to make any customizations, please use subclass.
*/
public abstract class _E33 extends BaseDataObject {

private static final long serialVersionUID = 1L;

public static final String P_ID_PK_COLUMN = "p_id";

public static final StringProperty<String> NAME = PropertyFactory.createString("name", String.class);

protected String name;


public void setName(String name) {
beforePropertyWrite("name", this.name, name);
this.name = name;
}

public String getName() {
beforePropertyRead("name");
return this.name;
}

@Override
public Object readPropertyDirectly(String propName) {
if(propName == null) {
throw new IllegalArgumentException();
}

switch(propName) {
case "name":
return this.name;
default:
return super.readPropertyDirectly(propName);
}
}

@Override
public void writePropertyDirectly(String propName, Object val) {
if(propName == null) {
throw new IllegalArgumentException();
}

switch (propName) {
case "name":
this.name = (String)val;
break;
default:
super.writePropertyDirectly(propName, val);
}
}

private void writeObject(ObjectOutputStream out) throws IOException {
writeSerialized(out);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
readSerialized(in);
}

@Override
protected void writeState(ObjectOutputStream out) throws IOException {
super.writeState(out);
out.writeObject(this.name);
}

@Override
protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
super.readState(in);
this.name = (String)in.readObject();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,12 @@ public Table e30() {
public Table e31() {
return db.getTable("e31");
}

public Table e32() {
return db.getTable("e32");
}

public Table e33() {
return db.getTable("e33");
}
}
23 changes: 23 additions & 0 deletions agrest-cayenne/src/test/resources/main/datamap.map.xml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,16 @@
<db-attribute name="id" type="BIGINT" isPrimaryKey="true" isMandatory="true"/>
<db-attribute name="name" type="VARCHAR" length="100"/>
</db-entity>
<db-entity name="e32">
<db-attribute name="name" type="VARCHAR" length="100"/>
<db-attribute name="p_id" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
<db-attribute name="s_id" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
<db-attribute name="t_id" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
</db-entity>
<db-entity name="e33">
<db-attribute name="name" type="VARCHAR" length="100"/>
<db-attribute name="p_id" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
</db-entity>
<db-entity name="e4">
<db-attribute name="c_boolean" type="BOOLEAN"/>
<db-attribute name="c_date" type="DATE"/>
Expand Down Expand Up @@ -305,6 +315,12 @@
<obj-attribute name="id" type="java.lang.Long" db-attribute-path="id"/>
<obj-attribute name="name" type="java.lang.String" db-attribute-path="name"/>
</obj-entity>
<obj-entity name="E32" className="io.agrest.cayenne.cayenne.main.E32" dbEntityName="e32">
<obj-attribute name="name" type="java.lang.String" db-attribute-path="name"/>
</obj-entity>
<obj-entity name="E33" className="io.agrest.cayenne.cayenne.main.E33" dbEntityName="e33">
<obj-attribute name="name" type="java.lang.String" db-attribute-path="name"/>
</obj-entity>
<obj-entity name="E4" className="io.agrest.cayenne.cayenne.main.E4" dbEntityName="e4">
<obj-attribute name="cBoolean" type="java.lang.Boolean" db-attribute-path="c_boolean"/>
<obj-attribute name="cDate" type="java.util.Date" db-attribute-path="c_date"/>
Expand Down Expand Up @@ -416,6 +432,12 @@
<db-attribute-pair source="e29_id1" target="id1"/>
<db-attribute-pair source="e29_id2" target="id2"/>
</db-relationship>
<db-relationship name="e33" source="e32" target="e33">
<db-attribute-pair source="p_id" target="p_id"/>
</db-relationship>
<db-relationship name="e32s" source="e33" target="e32" toDependentPK="true" toMany="true">
<db-attribute-pair source="p_id" target="p_id"/>
</db-relationship>
<db-relationship name="e15e5" source="e5" target="e15_e5" toDependentPK="true" toMany="true">
<db-attribute-pair source="id" target="e5_id"/>
</db-relationship>
Expand Down Expand Up @@ -458,6 +480,7 @@
<obj-relationship name="e2" source="E3" target="E2" deleteRule="Nullify" db-relationship-path="e2"/>
<obj-relationship name="e5" source="E3" target="E5" deleteRule="Nullify" db-relationship-path="e5"/>
<obj-relationship name="e29" source="E30" target="E29" deleteRule="Nullify" db-relationship-path="e29"/>
<obj-relationship name="e33" source="E32" target="E33" deleteRule="Nullify" db-relationship-path="e33"/>
<obj-relationship name="e15s" source="E5" target="E15" db-relationship-path="e15e5.e15"/>
<obj-relationship name="e3s" source="E5" target="E3" deleteRule="Deny" db-relationship-path="e2s"/>
<obj-relationship name="e8" source="E7" target="E8" deleteRule="Nullify" db-relationship-path="e8"/>
Expand Down
15 changes: 15 additions & 0 deletions agrest-cayenne/src/test/resources/main/schema-derby.sql
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ CREATE TABLE "e30" ("id" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY, "e2
CREATE TABLE "e31" ("id" INTEGER NOT NULL, "name" VARCHAR(100) , PRIMARY KEY ("id"))
;

CREATE TABLE "e33" ("name" VARCHAR (100), "p_id" INTEGER NOT NULL, PRIMARY KEY ("p_id"))
;

CREATE TABLE "e32" ("s_id" INTEGER NOT NULL, "p_id" INTEGER NOT NULL, "t_id" INTEGER NOT NULL, "name" VARCHAR (100), PRIMARY KEY ("s_id", "p_id", "t_id"))
;

ALTER TABLE "e14" ADD FOREIGN KEY ("e15_id") REFERENCES "e15" ("long_id")
;

Expand Down Expand Up @@ -153,6 +159,9 @@ ALTER TABLE "e3" ADD FOREIGN KEY ("e5_id") REFERENCES "e5" ("id")
ALTER TABLE "e30" ADD FOREIGN KEY ("e29_id1", "e29_id2") REFERENCES "e29" ("id1", "id2")
;

ALTER TABLE "e32" ADD FOREIGN KEY ("p_id") REFERENCES "e33" ("p_id")
;

CREATE SEQUENCE "PK_E1" AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE
;

Expand Down Expand Up @@ -237,6 +246,12 @@ CREATE SEQUENCE "PK_E29" AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO
CREATE SEQUENCE "PK_E30" AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE
;

CREATE SEQUENCE "PK_E33" AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE
;

CREATE SEQUENCE "PK_E32" AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE
;

-- iso
CREATE TABLE "SQL_DATE_TEST" ("Date" DATE , "ID" INTEGER NOT NULL, "Time" TIME , "Timestamp" TIMESTAMP , PRIMARY KEY ("ID"))
;
Expand Down

0 comments on commit 24df915

Please sign in to comment.