Skip to content

Commit b41dcf5

Browse files
calebblanchardarganoCaleb BlanchardCopilot
authored
Add BP suppression files and table key data (#39)
* Add BP suppression files * Add ARBTableKeyFieldMetadata table for primary key exposure Add new table, data entity, and supporting artifacts to expose primary index and replacement key field information for each table. New artifacts: - ARBTableKeyFieldMetadata table: stores one record per field per key with TableName, IndexName, FieldName, KeyType, and FieldPosition - ARBTableKeyFieldMetadataEntity: read-only OData entity - ARBTableKeyFieldMetadataEntityStaging: DMF staging table - ARBMetadataKeyType EDT (String, size 40) - ARBMetadataFieldPosition EDT (Int32) Modified artifacts: - ARBTableMetadataPopulateService: added processKeys and processKeyIndex methods to extract PrimaryIndex and ReplacementKey fields per table - ARBTableMetadata form: added Keys tab with grid for key field browsing - ARBTableMetadataEntityMaintain/View privileges: added entity permissions - Labels: added KeyType, Keys, IndexName, FieldPosition, TableKeyFieldMetadata, TableKeyFieldMetadataDevDoc - VS project and documentation updated Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Caleb Blanchard <caleb.blanchard@arbelatech.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 42c6dc9 commit b41dcf5

15 files changed

Lines changed: 729 additions & 6 deletions

.github/copilot-instructions.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,19 @@ powershell -ExecutionPolicy Bypass -File "Scripts\Mount.ps1"
5656
- `ARBAOTBrowser` expects caller context to be passed through `Args` (`parmEnumType`, `parmEnum`, `parm`, and sometimes `parmObject`). If you add a new launch point, keep that contract aligned with the browser form's `init` and `updateControls` logic.
5757
- The docs under `docs\` describe expected product behavior for search/filter, source viewing, table browser, inline extensions, open-from-form, open-from-entity, and jump-to-reference flows. Update those docs when a feature changes.
5858
- After install or metadata changes that affect discovery, the intended workflow is to run the "Populate AOT objects" action as a batch job and usually schedule it on a recurrence so the cache stays current.
59+
60+
## Table metadata tables
61+
62+
Four persisted tables store D365FO table/field/relation/enum metadata. The primary purpose is export via Synapse Link or Fabric Link to a data lake so that AI agents can use the metadata to understand the other D365FO tables that are also exported to the lake (field labels, data types, relationships, enum value meanings, etc.) without needing access to a development environment. They are populated by the `ARBTableMetadataPopulateService` batch job (controller: `ARBTableMetadataPopulateController`) and exposed via read-only OData data entities. All four tables have `SaveDataPerCompany = No` and `CacheLookup = Found`. See `docs/tablemetadata.md` for full documentation.
63+
64+
- **ARBTableMetadata** — One record per table. Key: `TableName`. Fields: `TableName`, `TableNum`, `TableLabel`, `TableGroup`, `Description`, `SqlTableName`, `SaveDataPerCompany` (NoYes), `TableType`.
65+
- **ARBTableFieldMetadata** — One record per field on each table. Key: `TableName` + `FieldName`. Fields: `TableName`, `FieldName`, `FieldId`, `FieldLabel`, `FieldDataType`, `ExtendedDataType`, `EnumTypeName`, `StringSize`, `IsMandatory` (NoYes), `HelpText`. Has a relation to `ARBTableMetadata`.
66+
- **ARBTableRelationMetadata** — One record per field constraint on each table relation. Key: `TableName` + `RelationName` + `FieldName`. Fields: `TableName`, `RelationName`, `RelatedTableName`, `RelationshipType`, `FieldName`, `RelatedFieldName`, `Cardinality`, `RelatedTableCardinality`. Has a relation to `ARBTableMetadata` and a non-unique index on `RelatedTableName`.
67+
- **ARBEnumMetadata** — One record per value on each base enumeration. Key: `EnumName` + `ValueName`. Fields: `EnumName`, `EnumLabel`, `ValueName`, `ValueLabel`, `IntegerValue`.
68+
- **ARBTableKeyFieldMetadata** — One record per field in each primary index and replacement key. Key: `TableName` + `IndexName` + `FieldName`. Fields: `TableName`, `IndexName`, `FieldName`, `KeyType` (PrimaryIndex or ReplacementKey), `FieldPosition`. Has a relation to `ARBTableMetadata`.
69+
70+
OData entities: `ARBTableMetadataEntity`, `ARBTableFieldMetadataEntity`, `ARBTableRelationMetadataEntity`, `ARBEnumMetadataEntity`, `ARBTableKeyFieldMetadataEntity`. Each has a corresponding staging table for data management.
71+
72+
Forms: **Table metadata** (`ARBTableMetadata` — Task Double pattern with fields/relations tabs) and **Enum metadata** (`ARBEnumMetadata` — SimpleList pattern). Both are under **Common > Common**.
73+
74+
Security: `ARBTableMetadataEntityMaintain` and `ARBTableMetadataEntityView` privileges control data entity access; populate actions are covered by `ARBAOTBrowserMaintain`.

Metadata/AOTBrowser/AOTBrowser/AxClass/ARBTableMetadataPopulateService.xml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ class ARBTableMetadataPopulateService extends SysOperationServiceBase
1515
ARBTableMetadata tableMetadata;
1616
ARBTableFieldMetadata fieldMetadata;
1717
ARBTableRelationMetadata relationMetadata;
18+
ARBTableKeyFieldMetadata keyFieldMetadata;
1819
ARBEnumMetadata enumMetadata;
1920
2021
RecordInsertList tableInsertList;
2122
RecordInsertList fieldInsertList;
2223
RecordInsertList relationInsertList;
24+
RecordInsertList keyFieldInsertList;
2325
RecordInsertList enumInsertList;
2426
2527
Set processedEnums;
@@ -37,6 +39,7 @@ class ARBTableMetadataPopulateService extends SysOperationServiceBase
3739
tableInsertList = new RecordInsertList(tableNum(ARBTableMetadata));
3840
fieldInsertList = new RecordInsertList(tableNum(ARBTableFieldMetadata));
3941
relationInsertList = new RecordInsertList(tableNum(ARBTableRelationMetadata));
42+
keyFieldInsertList = new RecordInsertList(tableNum(ARBTableKeyFieldMetadata));
4043
enumInsertList = new RecordInsertList(tableNum(ARBEnumMetadata));
4144
processedEnums = new Set(Types::String);
4245
@@ -45,6 +48,7 @@ class ARBTableMetadataPopulateService extends SysOperationServiceBase
4548
delete_from tableMetadata;
4649
delete_from fieldMetadata;
4750
delete_from relationMetadata;
51+
delete_from keyFieldMetadata;
4852
delete_from enumMetadata;
4953
5054
this.processTables();
@@ -53,6 +57,7 @@ class ARBTableMetadataPopulateService extends SysOperationServiceBase
5357
tableInsertList.insertDatabase();
5458
fieldInsertList.insertDatabase();
5559
relationInsertList.insertDatabase();
60+
keyFieldInsertList.insertDatabase();
5661
enumInsertList.insertDatabase();
5762
5863
ttscommit;
@@ -112,6 +117,7 @@ class ARBTableMetadataPopulateService extends SysOperationServiceBase
112117
// GetTable() returns merged metadata (base + extensions), so process once
113118
this.processFields(_tableName, tableName2Id(_tableName), _table.Fields);
114119
this.processRelations(_tableName, _table.Relations);
120+
this.processKeys(_tableName, _table);
115121
}
116122
117123
]]></Source>
@@ -246,6 +252,79 @@ class ARBTableMetadataPopulateService extends SysOperationServiceBase
246252
}
247253
}
248254
255+
]]></Source>
256+
</Method>
257+
<Method>
258+
<Name>processKeys</Name>
259+
<Source><![CDATA[
260+
/// <summary>
261+
/// Processes key fields (primary index and replacement key) for a table
262+
/// </summary>
263+
/// <param name = "_tableName">The owning table name</param>
264+
/// <param name = "_table">The AxTable metadata object</param>
265+
protected void processKeys(str _tableName, AxTable _table)
266+
{
267+
Set processedIndexNames = new Set(Types::String);
268+
269+
// Process primary index
270+
if (_table.PrimaryIndex != '')
271+
{
272+
this.processKeyIndex(_tableName, _table, _table.PrimaryIndex, 'PrimaryIndex');
273+
processedIndexNames.add(_table.PrimaryIndex);
274+
}
275+
276+
// Process replacement key (if different from primary index)
277+
if (_table.ReplacementKey != '' && !processedIndexNames.in(_table.ReplacementKey))
278+
{
279+
this.processKeyIndex(_tableName, _table, _table.ReplacementKey, 'ReplacementKey');
280+
}
281+
}
282+
283+
]]></Source>
284+
</Method>
285+
<Method>
286+
<Name>processKeyIndex</Name>
287+
<Source><![CDATA[
288+
/// <summary>
289+
/// Processes a single key index and inserts its fields into the key field metadata table
290+
/// </summary>
291+
/// <param name = "_tableName">The owning table name</param>
292+
/// <param name = "_table">The AxTable metadata object</param>
293+
/// <param name = "_indexName">The index name to process</param>
294+
/// <param name = "_keyType">The key type string (PrimaryIndex or ReplacementKey)</param>
295+
protected void processKeyIndex(str _tableName, AxTable _table, str _indexName, str _keyType)
296+
{
297+
var indexEnum = _table.Indexes.GetEnumerator();
298+
299+
while (indexEnum.MoveNext())
300+
{
301+
AxTableIndex tableIndex = indexEnum.Current;
302+
303+
if (tableIndex.Name == _indexName)
304+
{
305+
var fieldEnum = tableIndex.Fields.GetEnumerator();
306+
int position = 0;
307+
308+
while (fieldEnum.MoveNext())
309+
{
310+
AxTableIndexField indexField = fieldEnum.Current;
311+
position++;
312+
313+
keyFieldMetadata.clear();
314+
keyFieldMetadata.TableName = _tableName;
315+
keyFieldMetadata.IndexName = _indexName;
316+
keyFieldMetadata.FieldName = indexField.DataField;
317+
keyFieldMetadata.KeyType = _keyType;
318+
keyFieldMetadata.FieldPosition = position;
319+
320+
keyFieldInsertList.add(keyFieldMetadata);
321+
}
322+
323+
break;
324+
}
325+
}
326+
}
327+
249328
]]></Source>
250329
</Method>
251330
<Method>
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<AxDataEntityView xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
3+
<Name>ARBTableKeyFieldMetadataEntity</Name>
4+
<SourceCode>
5+
<Declaration><![CDATA[
6+
public class ARBTableKeyFieldMetadataEntity extends common
7+
{
8+
}
9+
]]></Declaration>
10+
<Methods />
11+
</SourceCode>
12+
<Label>@AOTBrowser:TableKeyFieldMetadata</Label>
13+
<DataManagementEnabled>Yes</DataManagementEnabled>
14+
<DataManagementStagingTable>ARBTableKeyFieldMetadataEntityStaging</DataManagementStagingTable>
15+
<EntityCategory>Reference</EntityCategory>
16+
<IsPublic>Yes</IsPublic>
17+
<IsReadOnly>Yes</IsReadOnly>
18+
<Modules>General</Modules>
19+
<PrimaryKey>EntityKey</PrimaryKey>
20+
<PublicCollectionName>ARBTableKeyFieldMetadata</PublicCollectionName>
21+
<PublicEntityName>ARBTableKeyFieldMetadata</PublicEntityName>
22+
<DeleteActions />
23+
<FieldGroups>
24+
<AxTableFieldGroup>
25+
<Name>AutoReport</Name>
26+
<Fields>
27+
<AxTableFieldGroupField>
28+
<DataField>TableName</DataField>
29+
</AxTableFieldGroupField>
30+
<AxTableFieldGroupField>
31+
<DataField>IndexName</DataField>
32+
</AxTableFieldGroupField>
33+
<AxTableFieldGroupField>
34+
<DataField>FieldName</DataField>
35+
</AxTableFieldGroupField>
36+
<AxTableFieldGroupField>
37+
<DataField>KeyType</DataField>
38+
</AxTableFieldGroupField>
39+
<AxTableFieldGroupField>
40+
<DataField>FieldPosition</DataField>
41+
</AxTableFieldGroupField>
42+
</Fields>
43+
</AxTableFieldGroup>
44+
<AxTableFieldGroup>
45+
<Name>AutoLookup</Name>
46+
<Fields />
47+
</AxTableFieldGroup>
48+
<AxTableFieldGroup>
49+
<Name>AutoIdentification</Name>
50+
<Fields />
51+
</AxTableFieldGroup>
52+
<AxTableFieldGroup>
53+
<Name>AutoSummary</Name>
54+
<Fields />
55+
</AxTableFieldGroup>
56+
<AxTableFieldGroup>
57+
<Name>AutoBrowse</Name>
58+
<Fields />
59+
</AxTableFieldGroup>
60+
</FieldGroups>
61+
<Fields>
62+
<AxDataEntityViewField xmlns=""
63+
i:type="AxDataEntityViewMappedField">
64+
<Name>TableName</Name>
65+
<Mandatory>Yes</Mandatory>
66+
<DataField>TableName</DataField>
67+
<DataSource>ARBTableKeyFieldMetadata</DataSource>
68+
</AxDataEntityViewField>
69+
<AxDataEntityViewField xmlns=""
70+
i:type="AxDataEntityViewMappedField">
71+
<Name>IndexName</Name>
72+
<Mandatory>Yes</Mandatory>
73+
<DataField>IndexName</DataField>
74+
<DataSource>ARBTableKeyFieldMetadata</DataSource>
75+
</AxDataEntityViewField>
76+
<AxDataEntityViewField xmlns=""
77+
i:type="AxDataEntityViewMappedField">
78+
<Name>FieldName</Name>
79+
<Mandatory>Yes</Mandatory>
80+
<DataField>FieldName</DataField>
81+
<DataSource>ARBTableKeyFieldMetadata</DataSource>
82+
</AxDataEntityViewField>
83+
<AxDataEntityViewField xmlns=""
84+
i:type="AxDataEntityViewMappedField">
85+
<Name>KeyType</Name>
86+
<DataField>KeyType</DataField>
87+
<DataSource>ARBTableKeyFieldMetadata</DataSource>
88+
</AxDataEntityViewField>
89+
<AxDataEntityViewField xmlns=""
90+
i:type="AxDataEntityViewMappedField">
91+
<Name>FieldPosition</Name>
92+
<DataField>FieldPosition</DataField>
93+
<DataSource>ARBTableKeyFieldMetadata</DataSource>
94+
</AxDataEntityViewField>
95+
</Fields>
96+
<Keys>
97+
<AxDataEntityViewKey>
98+
<Name>EntityKey</Name>
99+
<Fields>
100+
<AxDataEntityViewKeyField>
101+
<DataField>TableName</DataField>
102+
</AxDataEntityViewKeyField>
103+
<AxDataEntityViewKeyField>
104+
<DataField>IndexName</DataField>
105+
</AxDataEntityViewKeyField>
106+
<AxDataEntityViewKeyField>
107+
<DataField>FieldName</DataField>
108+
</AxDataEntityViewKeyField>
109+
</Fields>
110+
</AxDataEntityViewKey>
111+
</Keys>
112+
<Mappings />
113+
<Ranges />
114+
<Relations />
115+
<StateMachines />
116+
<ViewMetadata>
117+
<Name>Metadata</Name>
118+
<SourceCode>
119+
<Methods />
120+
</SourceCode>
121+
<DataSources>
122+
<AxQuerySimpleRootDataSource>
123+
<Name>ARBTableKeyFieldMetadata</Name>
124+
<Table>ARBTableKeyFieldMetadata</Table>
125+
<DataSources />
126+
<DerivedDataSources />
127+
<Fields>
128+
<AxQuerySimpleDataSourceField>
129+
<Name>TableName</Name>
130+
<Field>TableName</Field>
131+
</AxQuerySimpleDataSourceField>
132+
<AxQuerySimpleDataSourceField>
133+
<Name>IndexName</Name>
134+
<Field>IndexName</Field>
135+
</AxQuerySimpleDataSourceField>
136+
<AxQuerySimpleDataSourceField>
137+
<Name>FieldName</Name>
138+
<Field>FieldName</Field>
139+
</AxQuerySimpleDataSourceField>
140+
<AxQuerySimpleDataSourceField>
141+
<Name>KeyType</Name>
142+
<Field>KeyType</Field>
143+
</AxQuerySimpleDataSourceField>
144+
<AxQuerySimpleDataSourceField>
145+
<Name>FieldPosition</Name>
146+
<Field>FieldPosition</Field>
147+
</AxQuerySimpleDataSourceField>
148+
</Fields>
149+
<Ranges />
150+
<GroupBy />
151+
<Having />
152+
<OrderBy />
153+
</AxQuerySimpleRootDataSource>
154+
</DataSources>
155+
</ViewMetadata>
156+
</AxDataEntityView>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<AxEdt xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns=""
3+
i:type="AxEdtInt">
4+
<Name>ARBMetadataFieldPosition</Name>
5+
<Label>@AOTBrowser:FieldPosition</Label>
6+
<ArrayElements />
7+
<Relations />
8+
<TableReferences />
9+
</AxEdt>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<AxEdt xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns=""
3+
i:type="AxEdtString">
4+
<Name>ARBMetadataKeyType</Name>
5+
<Label>@AOTBrowser:KeyType</Label>
6+
<ArrayElements />
7+
<Relations />
8+
<TableReferences />
9+
<StringSize>40</StringSize>
10+
</AxEdt>

0 commit comments

Comments
 (0)