Skip to content
18 changes: 17 additions & 1 deletion Generator/DTO/AttributeUsage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,20 @@ public record AttributeUsage(
OperationType OperationType,
ComponentType ComponentType,
bool IsFromDependencyAnalysis
);
)
{
public static OperationType MapSdkMessageToOperationType(string sdkMessageName)
{
return sdkMessageName?.ToLowerInvariant() switch
{
"create" => OperationType.Create,
"update" => OperationType.Update,
"delete" => OperationType.Delete,
"retrieve" => OperationType.Read,
"retrievemultiple" => OperationType.List,
"upsert" => OperationType.Update,
"merge" => OperationType.Update,
_ => OperationType.Other
};
}
};
1 change: 1 addition & 0 deletions Generator/DTO/SDKStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ public record SDKStep(
string Name,
string FilteringAttributes,
string PrimaryObjectTypeCode,
string SdkMessageName,
OptionSetValue State) : Analyzeable();
22 changes: 17 additions & 5 deletions Generator/Queries/PluginQueries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ public static async Task<IEnumerable<SDKStep>> GetSDKMessageProcessingStepsAsync
{
Columns = new ColumnSet("primaryobjecttypecode"),
EntityAlias = "filter"
},
new LinkEntity(
"sdkmessageprocessingstep",
"sdkmessage",
"sdkmessageid",
"sdkmessageid",
JoinOperator.Inner)
{
Columns = new ColumnSet("name"),
EntityAlias = "message"
}
//new LinkEntity
//{
Expand All @@ -66,17 +76,19 @@ public static async Task<IEnumerable<SDKStep>> GetSDKMessageProcessingStepsAsync
var steps = result.Entities.Select(e =>
{
var sdkMessageId = e.GetAttributeValue<AliasedValue>("step.sdkmessageid")?.Value as EntityReference;
var sdkMessageName = e.GetAttributeValue<AliasedValue>("step.name")?.Value as string;
var sdkStepName = e.GetAttributeValue<AliasedValue>("step.name")?.Value as string;
var sdkFilterAttributes = e.GetAttributeValue<AliasedValue>("step.filteringattributes")?.Value as string;
var sdkState = e.GetAttributeValue<AliasedValue>("step.statecode")?.Value as OptionSetValue;
var filterTypeCode = e.GetAttributeValue<AliasedValue>("filter.primaryobjecttypecode")?.Value as string;
var sdkMessageName = e.GetAttributeValue<AliasedValue>("message.name")?.Value as string;

return new SDKStep(
sdkMessageId.Id.ToString(),
sdkMessageName ?? "Unknown Name",
sdkMessageId?.Id.ToString() ?? "Unknown",
sdkStepName ?? "Unknown Name",
sdkFilterAttributes ?? "",
filterTypeCode,
sdkState
filterTypeCode ?? "Unknown",
sdkMessageName ?? "Unknown",
sdkState ?? new OptionSetValue(0)
);
});

Expand Down
5 changes: 4 additions & 1 deletion Generator/Services/Plugins/PluginAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ public override async Task AnalyzeComponentAsync(SDKStep sdkStep, Dictionary<str
// Retrieve the logical name of the entity from the linked SDK Message entity
var logicalTableName = sdkStep.PrimaryObjectTypeCode;

// Map SDK message name to operation type
var operationType = AttributeUsage.MapSdkMessageToOperationType(sdkStep.SdkMessageName);

// Populate the attributeUsages dictionary
foreach (var attribute in filteringAttributes)
AddAttributeUsage(attributeUsages, logicalTableName, attribute, new AttributeUsage(pluginName, $"Used in filterattributes", OperationType.Other, SupportedType, false));
AddAttributeUsage(attributeUsages, logicalTableName, attribute, new AttributeUsage(pluginName, $"Used in filterattributes", operationType, SupportedType, false));

}
catch (Exception ex)
Expand Down
2 changes: 1 addition & 1 deletion Website/components/datamodelview/Attributes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export const Attributes = ({ entity, search = "", onVisibleCountChange }: IAttri
filteredAttributes = filteredAttributes.filter(attr => attributeMatchesSearch(attr, query))
}

if (hideStandardFields) filteredAttributes = filteredAttributes.filter(attr => attr.IsCustomAttribute || attr.IsStandardFieldModified);
if (hideStandardFields) filteredAttributes = filteredAttributes.filter(attr => (attr.IsCustomAttribute || attr.IsStandardFieldModified) && !attr.SchemaName.endsWith("Base"));

if (!sortColumn || !sortDirection) return filteredAttributes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function AttributeDetails({ entityName, attribute, isEntityAuditEnabled }
}

if (attribute.IsPrimaryName) {
details.push({ icon: <BadgeRounded className="h-4 w-4" />, tooltip: "Primary Name" });
details.push({ icon: <BadgeRounded className="h-4 w-4" />, tooltip: "Primary column: Its value is shown in the header of forms for this table, and as the display value of lookup-fields pointing to this table" });
}

switch (attribute.RequiredLevel) {
Expand Down
30 changes: 15 additions & 15 deletions Website/components/datamodelview/entity/SecurityRoles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function SecurityRoles({ roles }: { roles: SecurityRole[] }) {

function SecurityRoleRow({ role }: { role: SecurityRole }) {
const theme = useTheme();

return (
<Paper
variant="outlined"
Expand All @@ -27,16 +27,16 @@ function SecurityRoleRow({ role }: { role: SecurityRole }) {
justifyContent: 'space-between',
gap: 1,
p: 2,
backgroundColor: theme.palette.mode === 'dark'
? 'rgba(255, 255, 255, 0.02)'
backgroundColor: theme.palette.mode === 'dark'
? 'rgba(255, 255, 255, 0.02)'
: 'rgba(0, 0, 0, 0.02)',
borderColor: 'border.main',
width: '100%',
}}
>
<Typography
variant="subtitle1"
sx={{
<Typography
variant="subtitle1"
sx={{
fontWeight: 700,
wordWrap: 'break-word',
maxWidth: { xs: '100%', sm: '180px', md: '240px' },
Expand All @@ -45,12 +45,12 @@ function SecurityRoleRow({ role }: { role: SecurityRole }) {
>
{role.Name}
</Typography>
<Box
sx={{
display: 'flex',
flexWrap: { xs: 'wrap', sm: 'nowrap' },
gap: 1,
alignItems: 'flex-end'
<Box
sx={{
display: 'flex',
flexWrap: { xs: 'wrap', sm: 'nowrap' },
gap: 1,
alignItems: 'flex-end'
}}
>
<PrivilegeIcon privilege="Create" name="Create" depth={role.Create} />
Expand Down Expand Up @@ -106,7 +106,7 @@ function GetDepthIcon({ privilege, depth }: { privilege: string, depth: Privileg
const getTooltipText = (priv: string, d: PrivilegeDepth): string => {
const depthDescriptions: Record<PrivilegeDepth, string> = {
[PrivilegeDepth.None]: "No access",
[PrivilegeDepth.Basic]: "User - Only records owned by the user",
[PrivilegeDepth.Basic]: "User (or team) - Only records owned by the user themselves or owned by teams the user is a member of. This doesn't give access to rows owned by other members of those teams.",
[PrivilegeDepth.Local]: "Business Unit - Records owned by the user's business unit",
[PrivilegeDepth.Deep]: "Parent: Child Business Units - Records owned by the user's business unit and all child business units",
[PrivilegeDepth.Global]: "Organization - All records in the organization"
Expand All @@ -117,8 +117,8 @@ function GetDepthIcon({ privilege, depth }: { privilege: string, depth: Privileg
"Read": "View records",
"Write": "Modify existing records",
"Delete": "Remove records",
"Append": "Attach other records to this record (e.g., add notes, activities)",
"AppendTo": "Attach this record to other records (e.g., be selected in a lookup)",
"Append": "Access to attach other tables to me. (e.g. fill a lookup on table record with other table record)",
"AppendTo": "Access to attach me to other tables. (e.g. this table can be selected in a lookup from another table)",
"Assign": "Change the owner of records",
"Share": "Share records with other users or teams"
};
Expand Down
36 changes: 18 additions & 18 deletions Website/components/insightsview/overview/InsightsOverviewView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ const InsightsOverviewView = ({ }: InsightsOverviewViewProps) => {

return [
{
category: 'Entities',
category: 'Tables',
standard: standardEntities.length,
custom: customEntities.length,
},
{
category: 'Attributes',
category: 'Columns',
standard: standardAttributes.length,
custom: customAttributes.length,
},
Expand Down Expand Up @@ -251,21 +251,21 @@ const InsightsOverviewView = ({ }: InsightsOverviewViewProps) => {
<Grid size={{ xs: 12, md: 5 }}>
<Paper elevation={0} className="p-4 flex rounded-2xl">
<Stack className="w-full" direction="column" spacing={2} alignItems="center" justifyContent="center">
{/* Ungrouped Entities */}
<Tooltip title={"Entities: " + ungroupedEntities.map(entity => entity.SchemaName).join(", ")}>
{/* Ungrouped Tables */}
<Tooltip title={"Tables: " + ungroupedEntities.map(entity => entity.SchemaName).join(", ")}>
<Box className="text-center px-4 py-1 rounded-lg flex items-center w-full" gap={2} sx={{ backgroundColor: 'background.default' }}>
<Box className="h-8 w-8" sx={{ color: 'error.main' }}>{WarningIcon}</Box>
<Typography variant="h4" className="font-semibold p-0 m-0" sx={{ color: 'text.primary' }}>{ungroupedEntities.length}</Typography>
<Typography variant="body2" className="p-0 m-0" sx={{ color: 'text.secondary' }}>Entities ungrouped</Typography>
<Typography variant="body2" className="p-0 m-0" sx={{ color: 'text.secondary' }}>Tables ungrouped</Typography>
</Box>
</Tooltip>

{/* No Icon Entities */}
<Tooltip title={"Entities: " + missingIconEntities.map(entity => entity.SchemaName).join(", ")}>
{/* No Icon Tables */}
<Tooltip title={"Tables: " + missingIconEntities.map(entity => entity.SchemaName).join(", ")}>
<Box className="text-center px-4 py-1 rounded-lg flex items-center w-full" gap={2} sx={{ backgroundColor: 'background.default' }}>
<Box className="h-8 w-8" sx={{ color: 'error.main' }}>{WarningIcon}</Box>
<Typography variant="h4" className="font-semibold p-0 m-0" sx={{ color: 'text.primary' }}>{missingIconEntities.length}</Typography>
<Typography variant="body2" className="p-0 m-0" sx={{ color: 'text.secondary' }}>Entities without icons</Typography>
<Typography variant="body2" className="p-0 m-0" sx={{ color: 'text.secondary' }}>Tables without icons</Typography>
</Box>
</Tooltip>

Expand Down Expand Up @@ -302,7 +302,7 @@ const InsightsOverviewView = ({ }: InsightsOverviewViewProps) => {
<Grid size={{ xs: 12, md: 4 }}>
<InfoCard
color="warning.main"
title="Attribute Process Dependencies"
title="Column Process Dependencies"
value={totalAttributeUsageCount}
iconSrc={ProcessesIcon}
/>
Expand All @@ -314,7 +314,7 @@ const InsightsOverviewView = ({ }: InsightsOverviewViewProps) => {
<Typography variant="h6" sx={{ color: 'text.primary' }}>
Data Model Distribution: Standard vs Custom
</Typography>
<Tooltip title="Shows the distribution of standard (out-of-the-box) versus custom entities, attributes, and relationships in your Dataverse environment. This helps identify customization levels across your data model." arrow placement="left">
<Tooltip title="Shows the distribution of standard (out-of-the-box) versus custom tables, columns, and relationships in your Dataverse environment. This helps identify customization levels across your data model." arrow placement="left">
<IconButton size="small" sx={{ color: 'text.secondary' }}>
<Box sx={{ width: 20, height: 20 }}>{InfoIcon}</Box>
</IconButton>
Expand Down Expand Up @@ -618,9 +618,9 @@ const InsightsOverviewView = ({ }: InsightsOverviewViewProps) => {
<Paper elevation={2} className="p-6 rounded-2xl">
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
<Typography variant="h6" sx={{ color: 'text.primary' }}>
Entity Features Distribution
Table Features Distribution
</Typography>
<Tooltip title="Shows the distribution of key entity features enabled in your data model, including audit tracking, activity entities, and notes functionality. This provides insight into which capabilities are being utilized." arrow placement="left">
<Tooltip title="Shows the distribution of key table features enabled in your data model, including audit tracking, activity tables, and notes functionality. This provides insight into which capabilities are being utilized." arrow placement="left">
<IconButton size="small" sx={{ color: 'text.secondary' }}>
<Box sx={{ width: 20, height: 20 }}>{InfoIcon}</Box>
</IconButton>
Expand Down Expand Up @@ -674,9 +674,9 @@ const InsightsOverviewView = ({ }: InsightsOverviewViewProps) => {
<Paper elevation={2} className="p-6 rounded-2xl">
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
<Typography variant="h6" sx={{ color: 'text.primary' }}>
Attribute Types Distribution
Column Types Distribution
</Typography>
<Tooltip title="Breaks down all attributes by their data type (e.g., String, Integer, Lookup, DateTime). This helps understand the composition of your data model and identify commonly used field types." arrow placement="left">
<Tooltip title="Breaks down all columns by their data type (e.g., String, Integer, Lookup, DateTime). This helps understand the composition of your data model and identify commonly used field types." arrow placement="left">
<IconButton size="small" sx={{ color: 'text.secondary' }}>
<Box sx={{ width: 20, height: 20 }}>{InfoIcon}</Box>
</IconButton>
Expand Down Expand Up @@ -730,9 +730,9 @@ const InsightsOverviewView = ({ }: InsightsOverviewViewProps) => {
<Paper elevation={2} className="p-6 rounded-2xl">
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
<Typography variant="h6" sx={{ color: 'text.primary' }}>
Attribute Process Dependencies by Type
Column Process Dependencies by Type
</Typography>
<Tooltip title="Shows which types of components (plugins, flows, web resources, etc.) are using attributes from your data model. This identifies where attributes are referenced in business logic and automation." arrow placement="left">
<Tooltip title="Shows which types of components (plugins, flows, web resources, etc.) are using columns from your data model. This identifies where columns are referenced in business logic and automation." arrow placement="left">
<IconButton size="small" sx={{ color: 'text.secondary' }}>
<Box sx={{ width: 20, height: 20 }}>{InfoIcon}</Box>
</IconButton>
Expand Down Expand Up @@ -786,9 +786,9 @@ const InsightsOverviewView = ({ }: InsightsOverviewViewProps) => {
<Paper elevation={2} className="p-6 rounded-2xl">
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
<Typography variant="h6" sx={{ color: 'text.primary' }}>
Attribute Process Dependencies by Detection Source
Column Process Dependencies by Detection Source
</Typography>
<Tooltip title="Compares attribute usages found by the analyzer (scanning component source code) versus those detected through dependency analysis. This shows the effectiveness of different detection methods." arrow placement="left">
<Tooltip title="Compares column usages found by the analyzer (scanning component source code) versus those detected through dependency analysis. This shows the effectiveness of different detection methods." arrow placement="left">
<IconButton size="small" sx={{ color: 'text.secondary' }}>
<Box sx={{ width: 20, height: 20 }}>{InfoIcon}</Box>
</IconButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,9 @@ const InsightsSolutionView = ({ }: InsightsSolutionViewProps) => {
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
{component.Name} ({
component.ComponentType === SolutionComponentTypeEnum.Entity
? 'Entity'
? 'Table'
: component.ComponentType === SolutionComponentTypeEnum.Attribute
? 'Attribute'
? 'Column'
: component.ComponentType === SolutionComponentTypeEnum.Relationship
? 'Relationship'
: 'Unknown'
Expand Down
Loading
Loading