Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

182 key uniqueness #183

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/MetadataGen/MetadataGenerator365/.gitignore

This file was deleted.

3 changes: 2 additions & 1 deletion src/MetadataGen/MetadataGenerator365/App.config
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
<add key="entities" value=""/>
<add key="fetchFromAssemblies" value="false" />
</appSettings>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup></configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup>
</configuration>
14 changes: 14 additions & 0 deletions src/MetadataShared/Arguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ internal static class Arguments {
Abbreviations = new string[] { }
};

public static ArgumentDescription OutputFileName = new ArgumentDescription()
{
Name = "outFileName",
Abbreviations = new string[] { }
};

public static ArgumentDescription IncludeSystemEntities = new ArgumentDescription()
{
Name = "includeSystemEntities",
Abbreviations = new string[] { }
};

public static ArgumentDescription[] ArgList = new ArgumentDescription[] {
Url,
Username,
Expand All @@ -107,6 +119,8 @@ internal static class Arguments {
Entities,
Solutions,
OutDir,
OutputFileName,
IncludeSystemEntities,
UnitTestProjectPath,
fetchFromAssemblies,
Method,
Expand Down
13 changes: 8 additions & 5 deletions src/MetadataShared/DataHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal class DataHelper
private HashSet<string> EntityLogicalNames;
private string[] SolutionNames;

public DataHelper(IOrganizationService service, string entitiesString, string solutionsString, bool fetchFromAssemblies)
public DataHelper(IOrganizationService service, string entitiesString, string solutionsString, bool fetchFromAssemblies,bool includeSystemEntities)
{
this.service = service;
this.SolutionNames = solutionsString.Split(',').Select(x => x.Trim()).ToArray();
Expand All @@ -36,10 +36,13 @@ public DataHelper(IOrganizationService service, string entitiesString, string so
this.EntityLogicalNames = GetLogicalNames(AssemblyGetter.GetAssembliesInBuildPath());

// Add default entities
var defaultEntities = new string[] { "businessunit", "systemuser", "transactioncurrency", "role", "systemuserroles", "teamroles", "activitypointer", "roletemplate" };
foreach (var logicalName in defaultEntities)
if (includeSystemEntities)
{
this.EntityLogicalNames.Add(logicalName);
var defaultEntities = new string[] { "businessunit", "systemuser", "transactioncurrency", "role", "systemuserroles", "teamroles", "activitypointer", "roletemplate" };
foreach (var logicalName in defaultEntities)
{
this.EntityLogicalNames.Add(logicalName);
}
}

// Add specified entities
Expand All @@ -52,7 +55,7 @@ public DataHelper(IOrganizationService service, string entitiesString, string so
}
}

public MetadataSkeleton GetMetadata(string path)
public MetadataSkeleton GetMetadata()
{
var skeleton = new MetadataSkeleton();

Expand Down
12 changes: 9 additions & 3 deletions src/MetadataShared/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,16 @@ static void GenerateMetadata() {
);

Console.WriteLine("Generation of metadata files started");
var generator = new DataHelper(auth.Authenticate(), ParsedArgs[Arguments.Entities], ParsedArgs[Arguments.Solutions], ParsedArgs.GetAsType<bool>(Arguments.fetchFromAssemblies));
bool includeSystemEntities = true;
if (ParsedArgs[Arguments.IncludeSystemEntities] != null)
{
includeSystemEntities = Convert.ToBoolean(ParsedArgs[Arguments.IncludeSystemEntities]);
}
var generator = new DataHelper(auth.Authenticate(), ParsedArgs[Arguments.Entities], ParsedArgs[Arguments.Solutions], ParsedArgs.GetAsType<bool>(Arguments.fetchFromAssemblies),includeSystemEntities);
var outputLocation = ParsedArgs[Arguments.OutDir] ?? Directory.GetCurrentDirectory();
var outputFileName = ParsedArgs[Arguments.OutputFileName] ?? "Metadata.xml";

var skeleton = generator.GetMetadata(AssemblyGetter.GetProjectPath());
var skeleton = generator.GetMetadata();

var serializer = new DataContractSerializer(typeof(MetadataSkeleton));
var workflowSerializer = new DataContractSerializer(typeof(Entity));
Expand All @@ -79,7 +85,7 @@ static void GenerateMetadata() {
Console.WriteLine("Writing files");

Directory.CreateDirectory(outputLocation);
using (var stream = new FileStream(outputLocation + "/Metadata.xml", FileMode.Create)) {
using (var stream = new FileStream(outputLocation + "/" + outputFileName, FileMode.Create)) {
serializer.WriteObject(stream, skeleton);
}

Expand Down
18 changes: 18 additions & 0 deletions src/XrmMockupShared/Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,10 @@ internal OrganizationResponse Execute(OrganizationRequest request, EntityReferen
// System Pre-operation
pluginManager.TriggerSystem(eventOp, ExecutionStage.PreOperation, entityInfo.Item1, preImage, postImage, pluginContext, this);
}
#if !(XRM_MOCKUP_2011 || XRM_MOCKUP_2013)
//perform uniqueness checks for the request
CheckRequestUniqueness(request, userRef);
#endif

// Core operation
OrganizationResponse response = ExecuteRequest(request, userRef, parentPluginContext);
Expand Down Expand Up @@ -836,6 +840,20 @@ private void CheckRequestSecurity(OrganizationRequest request, EntityReference u
throw new NotImplementedException($"CheckRequestSecurity for the request '{request.RequestName}' has not been implemented yet.");
}

#if !(XRM_MOCKUP_2011 || XRM_MOCKUP_2013)
private void CheckRequestUniqueness(OrganizationRequest request, EntityReference userRef)
{
var handler = RequestHandlers.FirstOrDefault(x => x.HandlesRequest(request.RequestName));
if (handler != null)
{
handler.CheckUniqueness(request, userRef);
}
return;

throw new NotImplementedException($"CheckUniqueness for the request '{request.RequestName}' has not been implemented yet.");
}
#endif

private string RequestNameToMessageName(string requestName)
{
switch (requestName)
Expand Down
59 changes: 59 additions & 0 deletions src/XrmMockupShared/Requests/CreateRequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,65 @@ internal CreateRequestHandler(Core core, XrmDb db, MetadataSkeleton metadata, Se
{
}

internal override void CheckUniqueness(OrganizationRequest orgRequest, EntityReference userRef)
{
#if !(XRM_MOCKUP_2011 || XRM_MOCKUP_2013)
var request = MakeRequest<CreateRequest>(orgRequest);
var settings = MockupExecutionContext.GetSettings(request);
var entity = request.Target;
if (entity.LogicalName == null) throw new MockupException("Entity needs a logical name");

var entityMetadata = metadata.EntityMetadata.GetMetadata(entity.LogicalName);
if (!entityMetadata.Keys.Any())
{
return;
}

var currentRows = db.GetDBEntityRows(entity.LogicalName);
if (!currentRows.Any())
{
return;
}

var criteria = new FilterExpression();
foreach (var key in entityMetadata.Keys)
{
//dynamics cheerfully allows null values in keys, so only actually checks duplicates on populated values
//so if the entity does not have all of the key attributes populated it will always be created
bool allPopulated = true;
foreach (var keyAttr in key.KeyAttributes)
{
allPopulated = allPopulated && (entity.Contains(keyAttr) && entity[keyAttr] != null);
}
if (!allPopulated)
{
//skip onto the next key
continue;
}

foreach (var keyAttr in key.KeyAttributes)
{
var condition = new ConditionExpression(keyAttr, ConditionOperator.Equal, entity[keyAttr]);
criteria.Conditions.Add(condition);
}
foreach (var row in currentRows)
{
if (criteria.Conditions.All(c => Utility.EvaluateCondition(row.ToEntity(), c)))
{
string fieldNames = string.Empty;
foreach (var keyAttr in key.KeyAttributes)
{
fieldNames += entityMetadata.Attributes.Single(x => x.LogicalName == keyAttr).DisplayName.UserLocalizedLabel.Label + ", ";
}
fieldNames = fieldNames.Trim().Trim(new char[] { ',' });

throw new FaultException($"A record that has the attribute values {fieldNames} already exists. The entity key {key.DisplayName.UserLocalizedLabel.Label} requires that this set of attributes contains unique values. Select unique values and try again.");
}
}
}
#endif
}

internal override void CheckSecurity(OrganizationRequest orgRequest, EntityReference userRef)
{
var request = MakeRequest<CreateRequest>(orgRequest);
Expand Down
4 changes: 4 additions & 0 deletions src/XrmMockupShared/Requests/RequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ virtual internal void CheckSecurity(OrganizationRequest orgRequest, EntityRefere
{
}

virtual internal void CheckUniqueness(OrganizationRequest orgRequest, EntityReference userRef)
{
}

public static T MakeRequest<T>(OrganizationRequest req) where T : OrganizationRequest {
var typedReq = Activator.CreateInstance<T>();
if (req.RequestName != typedReq.RequestName) {
Expand Down
75 changes: 73 additions & 2 deletions src/XrmMockupShared/Requests/UpdateRequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,82 @@ namespace DG.Tools.XrmMockup
{
internal class UpdateRequestHandler : RequestHandler
{
internal UpdateRequestHandler(Core core, XrmDb db, MetadataSkeleton metadata, Security security) : base(core,
db, metadata, security, "Update")
internal UpdateRequestHandler(Core core, XrmDb db, MetadataSkeleton metadata, Security security)
: base(core,db, metadata, security, "Update")
{
}

internal override void CheckUniqueness(OrganizationRequest orgRequest, EntityReference userRef)
{
#if !(XRM_MOCKUP_2011 || XRM_MOCKUP_2013)
var request = MakeRequest<UpdateRequest>(orgRequest);
var settings = MockupExecutionContext.GetSettings(request);
var entity = request.Target;
if (entity.LogicalName == null) throw new MockupException("Entity needs a logical name");

var entityMetadata = metadata.EntityMetadata.GetMetadata(entity.LogicalName);
if (!entityMetadata.Keys.Any())
{
return;
}

var currentRows = db.GetDBEntityRows(entity.LogicalName);
if (!currentRows.Any())
{
return;
}

var criteria = new FilterExpression();
foreach (var key in entityMetadata.Keys)
{
bool anyPopulated = false;
foreach (var keyAttr in key.KeyAttributes)
{
anyPopulated = anyPopulated | entity.Contains(keyAttr);
}
if (!anyPopulated)
{
continue;
}

foreach (var row in currentRows)
{
if (row.ToEntity().Id == entity.Id)
{
//don't check the row being updated against itself
continue;
}
foreach (var keyAttr in key.KeyAttributes)
{
ConditionExpression condition = null;
if (entity.Contains(keyAttr))
{
condition = new ConditionExpression(keyAttr, ConditionOperator.Equal, entity[keyAttr]);
}
else
{
//get use the current value of the row
condition = new ConditionExpression(keyAttr, ConditionOperator.Equal, row.ToEntity()[keyAttr]);
}
criteria.Conditions.Add(condition);
}

if (criteria.Conditions.All(c => Utility.EvaluateCondition(row.ToEntity(), c)))
{
string fieldNames = string.Empty;
foreach (var keyAttr in key.KeyAttributes)
{
fieldNames += entityMetadata.Attributes.Single(x => x.LogicalName == keyAttr).DisplayName.UserLocalizedLabel.Label + ", ";
}
fieldNames = fieldNames.Trim().Trim(new char[] { ',' });

throw new FaultException($"A record that has the attribute values {fieldNames} already exists. The entity key {key.DisplayName.UserLocalizedLabel.Label} requires that this set of attributes contains unique values. Select unique values and try again.");
}
}
}
#endif
}

internal override void CheckSecurity(OrganizationRequest orgRequest, EntityReference userRef)
{
var request = MakeRequest<UpdateRequest>(orgRequest);
Expand Down
2 changes: 1 addition & 1 deletion src/XrmMockupShared/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ public static bool MatchesCriteria(Entity row, FilterExpression criteria)
criteria.Conditions.Any(c => EvaluateCondition(row, c));
}

private static bool EvaluateCondition(Entity row, ConditionExpression condition)
internal static bool EvaluateCondition(Entity row, ConditionExpression condition)
{
object attr = null;
switch (condition)
Expand Down
Loading