Skip to content

Commit 0ea29e5

Browse files
committed
Add heuristic fallback filter for legacy proxy auth (v1.5.0)
When CSDL is unavailable (OrganizationServiceProxy auth), apply filterEntityFallback instead of dropping all entities: filters Virtual attributes, suppresses scalar-shadowed and null nav props, and deduplicates polymorphic ManyToOne relationships. Bump changelog to v1.5.0.
1 parent ed36716 commit 0ea29e5

3 files changed

Lines changed: 52 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
# Changelog
22

3-
## [Unreleased]
3+
## [1.5.0] - 2026-05-08
44
### Changed
55
- Attributes and navigation properties are now filtered against the OData CSDL `$metadata`, ensuring generated typings reflect only what is accessible on the wire
66
- Virtual attributes (shadow fields, `yomi*` names, metadata-only columns) are now excluded via CSDL rather than SDK-side heuristics
77
- File-type columns are now typed as GUIDs, reflecting their actual wire format
8+
- When CSDL is unavailable (legacy proxy auth), heuristic filters are applied instead
89
### Fixed
910
- Duplicate ManyToOne navigation properties (collapsed by the CSDL) no longer generate duplicate interface members
1011

11-
## [1.4.0] - 2026-04-28
12+
## [1.4.0] - 2026-04-28 (unreleased)
1213
### Changed
1314
- WebEntities internal interfaces reorganized into sub-namespaces under `_`: `Scalars`, `Read`, `Write`, `Binds`, and `Lookup`, replacing the previous flat layout
1415
- Lookup value properties (`_*_value`) now include the lookup field's logical name in their JSDoc comment
@@ -57,7 +58,7 @@
5758
### Added
5859
- Initial public release
5960

60-
[1.4.0]: https://github.com/Mosh-K/XrmTypeScript/releases/tag/v1.4.0
61+
[1.5.0]: https://github.com/Mosh-K/XrmTypeScript/releases/tag/v1.5.0
6162
[1.3.0]: https://github.com/Mosh-K/XrmTypeScript/releases/tag/v1.3.0
6263
[1.2.0]: https://github.com/Mosh-K/XrmTypeScript/releases/tag/v1.2.0
6364
[1.1.0]: https://github.com/Mosh-K/XrmTypeScript/releases/tag/v1.1.0

src/Generation/Setup.fs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,18 @@ let interpretCrmData (gSettings: XdtGenerationSettings) (rawState: RawState) =
7676

7777
let entityMetadata =
7878
let csdlMap = rawState.csdlData |> Array.map (fun e -> e.Name, e) |> Map.ofArray
79-
rawState.metadata
80-
|> Array.Parallel.map (interpretEntity infoMap gSettings.labelMapping)
81-
|> Array.Parallel.choose (fun e ->
82-
match Map.tryFind e.logicalName csdlMap with
83-
| Some c -> Some (filterEntity c e)
84-
| None -> None)
79+
let interpreted = rawState.metadata |> Array.Parallel.map (interpretEntity infoMap gSettings.labelMapping)
80+
printfn "Done!"
81+
if csdlMap.IsEmpty then
82+
printf "CSDL unavailable, applying heuristic filters..."
83+
interpreted |> Array.Parallel.map filterEntityFallback
84+
else
85+
printf "Filtering entities against OData CSDL metadata..."
86+
interpreted
87+
|> Array.Parallel.choose (fun e ->
88+
match Map.tryFind e.logicalName csdlMap with
89+
| Some c -> Some (filterEntity c e)
90+
| None -> None)
8591

8692
let bpfControls = interpretBpfs rawState.bpfData
8793

src/Interpretation/FilterByCsdl.fs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,39 @@ let filterEntity (entityInfo: CsdlEntityInfo) (entity: XrmEntity) : XrmEntity =
3636
then r.Entity2NavigationPropertyName
3737
else r.Entity1NavigationPropertyName
3838
Set.contains navProp csdlNavProps) }
39+
40+
/// True when an attribute on the entity claims the same OData property name as the given
41+
/// navigation property name. Lookup-style attributes (specialType = EntityReference) are
42+
/// renamed to `_<name>_value` in the OData CSDL so they don't shadow; everything else
43+
/// (Uniqueidentifier, File, Image, plain scalars) keeps its natural name and forces
44+
/// Dataverse to drop the navigation property from the wire.
45+
let private isShadowedByScalar (attrMap: Map<string, XrmAttribute>) navPropName =
46+
Map.tryFind navPropName attrMap
47+
|> Option.exists (fun a -> a.specialType <> SpecialType.EntityReference)
48+
49+
let filterEntityFallback (entity: XrmEntity) : XrmEntity =
50+
let filteredAttrs =
51+
entity.attributes |> List.filter (fun a -> a.colType <> XrmAttributeType.Virtual)
52+
let attrMap =
53+
filteredAttrs |> List.map (fun a -> a.logicalName, a) |> Map.ofList
54+
55+
{ entity with
56+
attributes = filteredAttrs
57+
58+
manyToOneRelationships =
59+
entity.manyToOneRelationships
60+
|> List.filter (fun rel ->
61+
not (isNull rel.ReferencingEntityNavigationPropertyName)
62+
&& not (isShadowedByScalar attrMap rel.ReferencingEntityNavigationPropertyName))
63+
|> List.distinctBy (fun r -> r.ReferencingEntityNavigationPropertyName)
64+
65+
oneToManyRelationships =
66+
entity.oneToManyRelationships
67+
|> List.filter (fun rel -> not (isNull rel.ReferencedEntityNavigationPropertyName))
68+
69+
manyToManyRelationships =
70+
entity.manyToManyRelationships
71+
|> List.filter (fun rel ->
72+
(entity.logicalName = rel.Entity1LogicalName || entity.logicalName = rel.Entity2LogicalName) // Filter intersect tables
73+
&& not (isNull rel.Entity1NavigationPropertyName)
74+
&& not (isNull rel.Entity2NavigationPropertyName)) }

0 commit comments

Comments
 (0)