Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions mdl-examples/bug-tests/595-published-odata-entitytypepointer.mdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
-- Bug test: published OData service EntitySet must carry EntityTypePointer.
-- Issue #595: serializePublishedODataService keyed its entityTypeIDMap by
-- ExposedName but looked it up by qualified entity name, so the resolved
-- ID was always empty and ODataPublish$EntitySet.EntityTypePointer was
-- never written. Studio Pro's EntitySet.Check then dereferenced null and
-- aborted the entire project checker with a NullReferenceException.
--
-- The Go unit test in sdk/mpr/writer_odata_test.go (TestSerializePublishedODataService)
-- asserts EntityTypePointer matches the EntityType's $ID. This MDL script
-- is the end-to-end fixture: applying it to a project must yield a project
-- that opens in Studio Pro without the EntitySet.Check NRE.
--
-- Run with:
-- mxcli exec mdl-examples/bug-tests/595-published-odata-entitytypepointer.mdl -p <project>.mpr
-- then open <project>.mpr in Studio Pro and confirm no NRE in the project checker.
create module bug595;

create persistent entity bug595.Customer (
Name: string(200),
Email: string(200)
);

create odata service bug595.CustomerAPI (
path: '/odata/customers',
version: '1.0.0',
ODataVersion: OData4,
namespace: 'bug595.Customers'
)
authentication basic
{
publish entity bug595.Customer as 'Customers' (
ReadMode: source,
InsertMode: source,
UpdateMode: source,
DeleteMode: not_supported
)
expose (*);
};
11 changes: 8 additions & 3 deletions sdk/mpr/writer_odata.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,16 +209,21 @@ func (w *Writer) serializePublishedODataService(svc *model.PublishedODataService
authTypes = append(authTypes, at)
}

// Serialize entity types and build ID map for entity set pointers
entityTypeIDMap := make(map[string]string) // ExposedName -> entity type ID
// Serialize entity types and build ID map for entity set pointers.
// Issue #595: key by qualified entity name (et.Entity), not ExposedName.
// PublishedEntitySet.EntityTypeName holds the qualified name, so keying
// by ExposedName made the lookup return "" and EntityTypePointer was
// never written. Studio Pro's EntitySet.Check then NREs dereferencing
// the missing pointer and aborts the whole project checker.
entityTypeIDMap := make(map[string]string) // qualified entity name -> entity type ID
entityTypes := bson.A{}
for _, et := range svc.EntityTypes {
etID := string(et.ID)
if etID == "" {
etID = generateUUID()
et.ID = model.ID(etID)
}
entityTypeIDMap[et.ExposedName] = etID
entityTypeIDMap[et.Entity] = etID
entityTypes = append(entityTypes, serializePublishedEntityType(et))
}

Expand Down
17 changes: 16 additions & 1 deletion sdk/mpr/writer_odata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/mendixlabs/mxcli/model"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)

func TestSerializeConsumedODataService(t *testing.T) {
Expand Down Expand Up @@ -158,7 +159,7 @@ func TestSerializePublishedODataService(t *testing.T) {
AuthenticationTypes: []string{"Basic", "Session"},
EntityTypes: []*model.PublishedEntityType{
{
BaseElement: model.BaseElement{ID: "et-1"},
BaseElement: model.BaseElement{ID: "11111111-1111-1111-1111-111111111111"},
Entity: "MyModule.Customer",
ExposedName: "Customers",
Members: []*model.PublishedMember{
Expand Down Expand Up @@ -282,6 +283,20 @@ func TestSerializePublishedODataService(t *testing.T) {
assertField(t, esMap, "$Type", "ODataPublish$EntitySet")
assertField(t, esMap, "ExposedName", "Customers")

// Issue #595: EntityTypePointer must reference the owning EntityType.
// Without it, Studio Pro's EntitySet.Check NREs (it can't navigate from
// the set to its type). The map lookup in serializePublishedODataService
// was previously keyed by ExposedName instead of the qualified entity
// name, so the resolved ID was always empty and the pointer was omitted.
etID := etMap["$ID"].(primitive.Binary)
esPointer, ok := esMap["EntityTypePointer"].(primitive.Binary)
if !ok {
t.Fatalf("EntityTypePointer: expected primitive.Binary, got %T (%v)", esMap["EntityTypePointer"], esMap["EntityTypePointer"])
}
if string(esPointer.Data) != string(etID.Data) {
t.Errorf("EntityTypePointer = %x, want %x (entity type $ID)", esPointer.Data, etID.Data)
}

if v, ok := esMap["UsePaging"].(bool); !ok || !v {
t.Errorf("UsePaging: expected true, got %v", esMap["UsePaging"])
}
Expand Down
Loading