Skip to content

Commit f3095b6

Browse files
Ticket #909 : Optimize the algorithm used to enrich the representation & build hier attributes
1 parent 645c7c1 commit f3095b6

3 files changed

Lines changed: 186 additions & 40 deletions

File tree

src/Scim/SimpleIdServer.Scim.Domains/SCIMRepresentation.cs

Lines changed: 110 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ namespace SimpleIdServer.Scim.Domains
99
{
1010
public class SCIMRepresentation : BaseDomain, IEquatable<SCIMRepresentation>
1111
{
12+
private Dictionary<string, SCIMSchema> _schemaByAttributeIdCache;
13+
private Dictionary<string, SCIMSchema> _schemaByIdCache;
14+
1215
public SCIMRepresentation()
1316
{
1417
Schemas = new List<SCIMSchema>();
@@ -81,12 +84,65 @@ public SCIMSchema GetSchema(SCIMRepresentationAttribute attribute)
8184

8285
public SCIMSchema GetSchema(SCIMSchemaAttribute attribute)
8386
{
84-
return Schemas.FirstOrDefault(s => s.HasAttribute(attribute));
87+
EnsureSchemaCacheInitialized();
88+
89+
if (_schemaByAttributeIdCache.TryGetValue(attribute.Id, out var schema))
90+
{
91+
return schema;
92+
}
93+
94+
return null;
8595
}
8696

8797
public SCIMSchema GetSchema(string id)
8898
{
89-
return Schemas.FirstOrDefault(s => s.Id == id);
99+
EnsureSchemaCacheInitialized();
100+
101+
if (_schemaByIdCache.TryGetValue(id, out var schema))
102+
{
103+
return schema;
104+
}
105+
106+
return null;
107+
}
108+
109+
public SCIMSchema GetSchemaByAttributeId(string attributeId)
110+
{
111+
EnsureSchemaCacheInitialized();
112+
113+
if (_schemaByAttributeIdCache.TryGetValue(attributeId, out var schema))
114+
{
115+
return schema;
116+
}
117+
118+
return null;
119+
}
120+
121+
private void EnsureSchemaCacheInitialized()
122+
{
123+
if (_schemaByAttributeIdCache != null && _schemaByIdCache != null)
124+
{
125+
return;
126+
}
127+
128+
_schemaByAttributeIdCache = new Dictionary<string, SCIMSchema>();
129+
_schemaByIdCache = new Dictionary<string, SCIMSchema>();
130+
131+
foreach (var schema in Schemas)
132+
{
133+
_schemaByIdCache[schema.Id] = schema;
134+
135+
foreach (var attribute in schema.Attributes)
136+
{
137+
_schemaByAttributeIdCache[attribute.Id] = schema;
138+
}
139+
}
140+
}
141+
142+
public void InvalidateSchemaCache()
143+
{
144+
_schemaByAttributeIdCache = null;
145+
_schemaByIdCache = null;
90146
}
91147

92148
public void AddAttribute(SCIMRepresentationAttribute attribute)
@@ -384,37 +440,71 @@ public static List<SCIMRepresentationAttribute> BuildFlatAttributes(ICollection<
384440
public static List<SCIMRepresentationAttribute> BuildHierarchicalAttributes(IEnumerable<SCIMRepresentationAttribute> attributes)
385441
{
386442
var rootId = string.Empty;
387-
if (attributes.Count() == 0) return new List<SCIMRepresentationAttribute>();
443+
var attributesList = attributes as List<SCIMRepresentationAttribute> ?? attributes.ToList();
444+
if (attributesList.Count == 0) return new List<SCIMRepresentationAttribute>();
445+
388446
var parentsDictionary = new Dictionary<string, List<SCIMRepresentationAttribute>>();
389-
var treeNodes = new List<SCIMRepresentationAttribute>();
390-
foreach (var scimRepresentationAttribute in attributes)
447+
var existingIds = new HashSet<string>(attributesList.Count);
448+
var emptyList = new List<SCIMRepresentationAttribute>();
449+
450+
foreach (var attr in attributesList)
391451
{
392-
var parentIdKey = scimRepresentationAttribute.ParentAttributeId ?? rootId;
393-
treeNodes.Add(scimRepresentationAttribute);
394-
if (!parentsDictionary.ContainsKey(parentIdKey))
452+
existingIds.Add(attr.Id);
453+
var parentIdKey = attr.ParentAttributeId ?? rootId;
454+
if (!parentsDictionary.TryGetValue(parentIdKey, out var children))
395455
{
396-
parentsDictionary[parentIdKey] = new List<SCIMRepresentationAttribute>() { scimRepresentationAttribute };
397-
continue;
456+
children = new List<SCIMRepresentationAttribute>();
457+
parentsDictionary[parentIdKey] = children;
398458
}
399-
400-
parentsDictionary[parentIdKey].Add(scimRepresentationAttribute);
459+
children.Add(attr);
401460
}
402461

403-
foreach(var node in treeNodes)
462+
foreach (var node in attributesList)
404463
{
405-
if(parentsDictionary.ContainsKey(node.Id))
464+
if (parentsDictionary.TryGetValue(node.Id, out var children))
406465
{
407-
node.CachedChildren = parentsDictionary[node.Id];
408-
node.Children = parentsDictionary[node.Id];
466+
node.CachedChildren = children;
467+
node.Children = children;
409468
}
410469
else
411470
{
412-
var lst = new List<SCIMRepresentationAttribute>();
413-
node.CachedChildren = lst;
414-
node.Children = lst;
471+
node.CachedChildren = emptyList;
472+
node.Children = emptyList;
415473
}
416474
}
417-
var attrWithNoParentLst = attributes.Where(a => a.ParentAttributeId == rootId || !attributes.Any(c => c.Id == a.ParentAttributeId)).ToList();
475+
476+
List<SCIMRepresentationAttribute> attrWithNoParentLst;
477+
if (parentsDictionary.TryGetValue(rootId, out var rootChildren))
478+
{
479+
attrWithNoParentLst = new List<SCIMRepresentationAttribute>(rootChildren.Count);
480+
foreach (var attr in rootChildren)
481+
{
482+
attrWithNoParentLst.Add(attr);
483+
}
484+
485+
foreach (var attr in attributesList)
486+
{
487+
if (!string.IsNullOrEmpty(attr.ParentAttributeId) &&
488+
attr.ParentAttributeId != rootId &&
489+
!existingIds.Contains(attr.ParentAttributeId))
490+
{
491+
attrWithNoParentLst.Add(attr);
492+
}
493+
}
494+
}
495+
else
496+
{
497+
attrWithNoParentLst = new List<SCIMRepresentationAttribute>();
498+
foreach (var attr in attributesList)
499+
{
500+
if (string.IsNullOrEmpty(attr.ParentAttributeId) ||
501+
!existingIds.Contains(attr.ParentAttributeId))
502+
{
503+
attrWithNoParentLst.Add(attr);
504+
}
505+
}
506+
}
507+
418508
foreach (var attrWithNoParent in attrWithNoParentLst)
419509
{
420510
attrWithNoParent.ComputeValueIndex();

src/Scim/SimpleIdServer.Scim/Extensions/SCIMRepresentationExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ public static JsonObject ToResponse(this SCIMRepresentation representation, stri
180180
representation.ApplyEmptyArray();
181181
}
182182

183-
var attributes = representation.HierarchicalAttributes
183+
var attrs = representation.HierarchicalAttributes.ToList();
184+
var attributes = attrs
184185
.Where(p => string.IsNullOrWhiteSpace(p.ParentAttributeId))
185186
.Select(a =>
186187
{

src/Scim/SimpleIdServer.Scim/Helpers/AttributeReferenceEnricher.cs

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,86 @@ public async Task Enrich(string resourceType, IEnumerable<SCIMRepresentation> re
3030
return;
3131
}
3232

33-
foreach (var attributeMapping in attributeMappingLst)
33+
var controllerNameCache = attributeMappingLst
34+
.GroupBy(m => m.TargetResourceType)
35+
.ToDictionary(
36+
g => g.Key,
37+
g => _resourceTypeResolver.ResolveByResourceType(g.Key).ControllerName
38+
);
39+
40+
foreach (var representation in representationLst)
3441
{
35-
foreach(var representation in representationLst)
36-
{
37-
var controllerName = _resourceTypeResolver.ResolveByResourceType(attributeMapping.TargetResourceType).ControllerName;
42+
var referenceAttributeCache = new Dictionary<string, SCIMSchemaAttribute>();
43+
foreach (var schema in representation.Schemas)
44+
{
45+
foreach (var attr in schema.Attributes.Where(a => !string.IsNullOrEmpty(a.ParentId)))
46+
{
47+
if (attr.Name == "$ref" && !referenceAttributeCache.ContainsKey(attr.ParentId))
48+
{
49+
referenceAttributeCache[attr.ParentId] = attr;
50+
}
51+
}
52+
}
53+
54+
var childrenByParentId = new Dictionary<string, List<SCIMRepresentationAttribute>>();
55+
foreach (var attr in representation.FlatAttributes)
56+
{
57+
if (!string.IsNullOrEmpty(attr.ParentAttributeId))
58+
{
59+
if (!childrenByParentId.ContainsKey(attr.ParentAttributeId))
60+
{
61+
childrenByParentId[attr.ParentAttributeId] = new List<SCIMRepresentationAttribute>();
62+
}
63+
childrenByParentId[attr.ParentAttributeId].Add(attr);
64+
}
65+
}
66+
67+
foreach (var attributeMapping in attributeMappingLst)
68+
{
69+
var controllerName = controllerNameCache[attributeMapping.TargetResourceType];
3870
var attrs = representation.GetAttributesByAttrSchemaId(attributeMapping.SourceAttributeId).ToList();
39-
foreach(var attr in attrs)
40-
{
41-
var values = representation.GetChildren(attr);
42-
var value = values.FirstOrDefault(v => v.SchemaAttribute.Name == "value");
43-
var type = values.FirstOrDefault(v => v.SchemaAttribute.Name == "type");
44-
var reference = values.FirstOrDefault(v => v.SchemaAttribute.Name == "$ref");
45-
var schema = representation.GetSchema(attr);
46-
var referenceAttribute = schema?.GetChildren(attr.SchemaAttribute).FirstOrDefault(v => v.Name == "$ref");
47-
if (value == null || string.IsNullOrWhiteSpace(value.ValueString) || reference != null || referenceAttribute == null || type == null || type.ValueString != attributeMapping.TargetResourceType)
48-
{
71+
72+
foreach (var attr in attrs)
73+
{
74+
if (!childrenByParentId.TryGetValue(attr.Id, out var children))
75+
{
4976
continue;
50-
}
51-
52-
representation.AddAttribute(attr, new SCIMRepresentationAttribute(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), referenceAttribute, referenceAttribute.SchemaId)
77+
}
78+
79+
var childrenLookup = children.ToLookup(v => v.SchemaAttribute.Name);
80+
var value = childrenLookup["value"].FirstOrDefault();
81+
var type = childrenLookup["type"].FirstOrDefault();
82+
var reference = childrenLookup["$ref"].FirstOrDefault();
83+
if (value == null ||
84+
string.IsNullOrWhiteSpace(value.ValueString) ||
85+
reference != null ||
86+
type == null ||
87+
type.ValueString != attributeMapping.TargetResourceType)
88+
{
89+
continue;
90+
}
91+
92+
var schema = representation.GetSchemaByAttributeId(attr.SchemaAttributeId);
93+
if (schema == null)
94+
{
95+
continue;
96+
}
97+
98+
if (!referenceAttributeCache.TryGetValue(attr.SchemaAttributeId, out var referenceAttribute))
99+
{
100+
continue;
101+
}
102+
103+
representation.AddAttribute(attr, new SCIMRepresentationAttribute(
104+
Guid.NewGuid().ToString(),
105+
Guid.NewGuid().ToString(),
106+
referenceAttribute,
107+
referenceAttribute.SchemaId)
53108
{
54109
ValueReference = $"{baseUrl}/{controllerName}/{value.ValueString}"
55110
});
56-
}
57-
}
111+
}
112+
}
58113
}
59114
}
60115
}

0 commit comments

Comments
 (0)