@@ -61,6 +61,7 @@ public async Task<IEnumerable<Record>> GetFilteredMetadata()
6161 ) . ToList ( ) ) ;
6262
6363 var logicalNameToSecurityRoles = await GetSecurityRoles ( rolesInSolution , entitiesInSolutionMetadata . ToDictionary ( x => x . LogicalName , x => x . Privileges ) ) ;
64+ var pluginStepAttributeMap = await GetPluginStepAttributes ( ) ;
6465 var entityLogicalNamesInSolution = entitiesInSolutionMetadata . Select ( e => e . LogicalName ) . ToHashSet ( ) ;
6566
6667 logger . LogInformation ( "There are {Count} entities in the solution." , entityLogicalNamesInSolution . Count ) ;
@@ -118,6 +119,7 @@ public async Task<IEnumerable<Record>> GetFilteredMetadata()
118119 securityRoles ?? [ ] ,
119120 keys ?? [ ] ,
120121 entityIconMap ,
122+ pluginStepAttributeMap ,
121123 configuration ) ;
122124 } ) ;
123125 }
@@ -132,13 +134,16 @@ private static Record MakeRecord(
132134 List < SecurityRole > securityRoles ,
133135 List < Key > keys ,
134136 Dictionary < string , string > entityIconMap ,
137+ Dictionary < string , HashSet < string > > pluginStepAttributeMap ,
135138 IConfiguration configuration )
136139 {
137140 var attributes =
138141 relevantAttributes
139142 . Select ( metadata =>
140143 {
141- var attr = GetAttribute ( metadata , entity , logicalToSchema , logger ) ;
144+ pluginStepAttributeMap . TryGetValue ( entity . LogicalName , out var entityPluginAttributes ) ;
145+ var hasPluginStep = entityPluginAttributes ? . Contains ( metadata . LogicalName ) == true ;
146+ var attr = GetAttribute ( metadata , entity , logicalToSchema , hasPluginStep , logger ) ;
142147 attr . IsStandardFieldModified = MetadataExtensions . StandardFieldHasChanged ( metadata , entity . DisplayName . UserLocalizedLabel ? . Label ?? string . Empty ) ;
143148 return attr ;
144149 } )
@@ -212,9 +217,9 @@ private static Record MakeRecord(
212217 iconBase64 ) ;
213218 }
214219
215- private static Attribute GetAttribute ( AttributeMetadata metadata , EntityMetadata entity , Dictionary < string , ExtendedEntityInformation > logicalToSchema , ILogger < DataverseService > logger )
220+ private static Attribute GetAttribute ( AttributeMetadata metadata , EntityMetadata entity , Dictionary < string , ExtendedEntityInformation > logicalToSchema , bool hasPluginStep , ILogger < DataverseService > logger )
216221 {
217- return metadata switch
222+ Attribute attr = metadata switch
218223 {
219224 PicklistAttributeMetadata picklist => new ChoiceAttribute ( picklist ) ,
220225 MultiSelectPicklistAttributeMetadata multiSelect => new ChoiceAttribute ( multiSelect ) ,
@@ -230,6 +235,8 @@ private static Attribute GetAttribute(AttributeMetadata metadata, EntityMetadata
230235 FileAttributeMetadata fileAttribute => new FileAttribute ( fileAttribute ) ,
231236 _ => new GenericAttribute ( metadata )
232237 } ;
238+ attr . HasPluginStep = hasPluginStep ;
239+ return attr ;
233240 }
234241
235242 private static ( string ? Group , string ? Description ) GetGroupAndDescription ( EntityMetadata entity , IDictionary < string , string > tableGroups )
@@ -539,6 +546,112 @@ private static string GetCoreUrl(string url)
539546 return $ "{ uri . Scheme } ://{ uri . Host } ";
540547 }
541548
549+ private async Task < Dictionary < string , HashSet < string > > > GetPluginStepAttributes ( )
550+ {
551+ logger . LogInformation ( "Retrieving plugin step attributes..." ) ;
552+
553+ var pluginStepAttributeMap = new Dictionary < string , HashSet < string > > ( ) ;
554+
555+ try
556+ {
557+ // Query sdkmessageprocessingstep table for steps with filtering attributes
558+ var stepQuery = new QueryExpression ( "sdkmessageprocessingstep" )
559+ {
560+ ColumnSet = new ColumnSet ( "filteringattributes" , "sdkmessagefilterid" ) ,
561+ Criteria = new FilterExpression
562+ {
563+ Conditions =
564+ {
565+ new ConditionExpression ( "filteringattributes" , ConditionOperator . NotNull ) ,
566+ new ConditionExpression ( "filteringattributes" , ConditionOperator . NotEqual , "" ) ,
567+ new ConditionExpression ( "statecode" , ConditionOperator . Equal , 0 ) // Only active steps
568+ }
569+ } ,
570+ LinkEntities =
571+ {
572+ new LinkEntity
573+ {
574+ LinkFromEntityName = "sdkmessageprocessingstep" ,
575+ LinkFromAttributeName = "sdkmessagefilterid" ,
576+ LinkToEntityName = "sdkmessagefilter" ,
577+ LinkToAttributeName = "sdkmessagefilterid" ,
578+ Columns = new ColumnSet ( "primaryobjecttypecode" ) ,
579+ EntityAlias = "filter"
580+ }
581+ }
582+ } ;
583+
584+ var stepResults = await client . RetrieveMultipleAsync ( stepQuery ) ;
585+
586+ // Build a simple type code to entity name mapping using known common entities
587+ var typeCodeToLogicalName = new Dictionary < int , string >
588+ {
589+ { 1 , "account" } ,
590+ { 2 , "contact" } ,
591+ { 3 , "opportunity" } ,
592+ { 4 , "lead" } ,
593+ { 5 , "note" } ,
594+ { 6 , "businessunit" } ,
595+ { 8 , "systemuser" } ,
596+ { 9 , "team" } ,
597+ { 10 , "businessunitnewsarticle" } ,
598+ { 14 , "incident" } ,
599+ { 112 , "case" } ,
600+ { 4200 , "campaignactivity" } ,
601+ { 4201 , "list" } ,
602+ { 4202 , "campaign" } ,
603+ { 4204 , "fax" } ,
604+ { 4207 , "letter" } ,
605+ { 4210 , "phonecall" } ,
606+ { 4212 , "task" } ,
607+ { 4214 , "service" } ,
608+ { 4216 , "contract" } ,
609+ { 4220 , "workflow" }
610+ } ;
611+
612+ foreach ( var step in stepResults . Entities )
613+ {
614+ var filteringAttributes = step . GetAttributeValue < string > ( "filteringattributes" ) ;
615+ var entityTypeCode = step . GetAttributeValue < AliasedValue > ( "filter.primaryobjecttypecode" ) ? . Value as int ? ;
616+
617+ if ( string . IsNullOrEmpty ( filteringAttributes ) || ! entityTypeCode . HasValue )
618+ continue ;
619+
620+ // Try to get entity logical name from our known mapping first
621+ string ? logicalName = null ;
622+ if ( typeCodeToLogicalName . ContainsKey ( entityTypeCode . Value ) )
623+ {
624+ logicalName = typeCodeToLogicalName [ entityTypeCode . Value ] ;
625+ }
626+ else
627+ {
628+ // For unknown type codes, we'll skip them for now
629+ // In a full implementation, you'd want to query the entity metadata
630+ logger . LogDebug ( "Unknown entity type code: {TypeCode}" , entityTypeCode . Value ) ;
631+ continue ;
632+ }
633+
634+ if ( ! pluginStepAttributeMap . ContainsKey ( logicalName ) )
635+ pluginStepAttributeMap [ logicalName ] = new HashSet < string > ( ) ;
636+
637+ // Parse comma-separated attribute names
638+ var attributeNames = filteringAttributes . Split ( ',' , StringSplitOptions . RemoveEmptyEntries ) ;
639+ foreach ( var attributeName in attributeNames )
640+ {
641+ pluginStepAttributeMap [ logicalName ] . Add ( attributeName . Trim ( ) ) ;
642+ }
643+ }
644+
645+ logger . LogInformation ( "Found {Count} entities with plugin step attributes." , pluginStepAttributeMap . Count ) ;
646+ }
647+ catch ( Exception ex )
648+ {
649+ logger . LogWarning ( "Failed to retrieve plugin step attributes: {Message}" , ex . Message ) ;
650+ }
651+
652+ return pluginStepAttributeMap ;
653+ }
654+
542655 private static async Task < AccessToken > FetchAccessToken ( TokenCredential credential , string scope , ILogger logger )
543656 {
544657 var tokenRequestContext = new TokenRequestContext ( new [ ] { scope } ) ;
0 commit comments