Skip to content

Commit ae6edec

Browse files
akoclaude
andcommitted
fix: ConcurrencyMode=Fixed property marked Creatable in OData import (issue #525)
When importing entities from an OData service (e.g. TripPin Airline.Concurrency), properties with ConcurrencyMode="Fixed" (OData v3 optimistic concurrency token) were being marked Creatable=true in the generated external entity. Studio Pro then rejected the model with: 'Concurrency' is marked Creatable=True in the OData service, but False in the app. Root cause: xmlProperty did not capture the ConcurrencyMode XML attribute, so parseXmlEntityType never set Computed=true for those properties. The creatable flag check (p.Computed → creatable=false) was therefore never triggered. Fix: parse ConcurrencyMode from the <Property> element and treat "Fixed" as equivalent to Computed=true — the server owns the value, the client cannot set it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent faa9790 commit ae6edec

2 files changed

Lines changed: 57 additions & 6 deletions

File tree

mdl/types/edmx.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,11 @@ func parseXmlEntityType(et *xmlEntityType) *EdmEntityType {
317317
v := p.Nullable != "false"
318318
prop.Nullable = &v
319319
}
320+
// ConcurrencyMode="Fixed" (OData v3) marks a property as an optimistic
321+
// concurrency token — the server manages it, the client cannot set it.
322+
if p.ConcurrencyMode == "Fixed" {
323+
prop.Computed = true
324+
}
320325
for _, ann := range p.Annotations {
321326
switch ann.Term {
322327
case "Org.OData.Core.V1.Computed":
@@ -474,12 +479,13 @@ type xmlPropertyRef struct {
474479
}
475480

476481
type xmlProperty struct {
477-
Name string `xml:"Name,attr"`
478-
Type string `xml:"Type,attr"`
479-
Nullable string `xml:"Nullable,attr"`
480-
MaxLength string `xml:"MaxLength,attr"`
481-
Scale string `xml:"Scale,attr"`
482-
Annotations []xmlAnnotation `xml:"Annotation"`
482+
Name string `xml:"Name,attr"`
483+
Type string `xml:"Type,attr"`
484+
Nullable string `xml:"Nullable,attr"`
485+
MaxLength string `xml:"MaxLength,attr"`
486+
Scale string `xml:"Scale,attr"`
487+
ConcurrencyMode string `xml:"ConcurrencyMode,attr"` // OData v3: "Fixed" = optimistic concurrency token
488+
Annotations []xmlAnnotation `xml:"Annotation"`
483489
}
484490

485491
type xmlNavigationProperty struct {

mdl/types/edmx_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,48 @@ func TestParseEdmx_ExternalAnnotations_WithoutSlash(t *testing.T) {
353353
t.Error("expected Updatable=true from external annotation without slash prefix")
354354
}
355355
}
356+
357+
// TestParseEdmx_ConcurrencyModeFixed verifies that a property with
358+
// ConcurrencyMode="Fixed" (OData v3 ETag token) is treated as Computed=true
359+
// so that mxcli does not mark it as Creatable in the generated external entity.
360+
// Regression test for issue #525 (TripPin Airline.Concurrency).
361+
func TestParseEdmx_ConcurrencyModeFixed(t *testing.T) {
362+
xml := `<?xml version="1.0" encoding="utf-8"?>
363+
<edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
364+
<edmx:DataServices m:DataServiceVersion="3.0" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
365+
<Schema Namespace="TripPin" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
366+
<EntityType Name="Airline">
367+
<Key><PropertyRef Name="AirlineCode" /></Key>
368+
<Property Name="AirlineCode" Type="Edm.String" Nullable="false" />
369+
<Property Name="Name" Type="Edm.String" Nullable="false" />
370+
<Property Name="Concurrency" Type="Edm.Int64" ConcurrencyMode="Fixed" />
371+
</EntityType>
372+
<EntityContainer Name="TripPinServiceRW" m:IsDefaultEntityContainer="true">
373+
<EntitySet Name="Airlines" EntityType="TripPin.Airline" />
374+
</EntityContainer>
375+
</Schema>
376+
</edmx:DataServices>
377+
</edmx:Edmx>`
378+
379+
doc, err := ParseEdmx(xml)
380+
if err != nil {
381+
t.Fatalf("ParseEdmx error: %v", err)
382+
}
383+
if len(doc.Schemas) == 0 || len(doc.Schemas[0].EntityTypes) == 0 {
384+
t.Fatal("expected at least one entity type")
385+
}
386+
et := doc.Schemas[0].EntityTypes[0]
387+
var concProp *EdmProperty
388+
for _, p := range et.Properties {
389+
if p.Name == "Concurrency" {
390+
concProp = p
391+
break
392+
}
393+
}
394+
if concProp == nil {
395+
t.Fatal("expected Concurrency property in parsed entity type")
396+
}
397+
if !concProp.Computed {
398+
t.Error("ConcurrencyMode='Fixed' must set Computed=true so the attribute is not marked Creatable (issue #525)")
399+
}
400+
}

0 commit comments

Comments
 (0)