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

RetrieveRequestExecutor does not return correct response #142

Open
Zerajima opened this issue Apr 11, 2024 · 6 comments
Open

RetrieveRequestExecutor does not return correct response #142

Zerajima opened this issue Apr 11, 2024 · 6 comments

Comments

@Zerajima
Copy link

Zerajima commented Apr 11, 2024

In response returned by RetrieveRequestExecutor.Execute method, the entity object is stored under "Entity" key.

This is not the same key, that dataverse returns. The actual key should be "BusinessEntity". This behavior is described in https://learn.microsoft.com/en-gb/power-apps/developer/data-platform/understand-the-data-context#outputparameters.

I ran into this issue when writing tests for plugin I wrote, for Retrieve message.

@BetimBeja
Copy link

Hi @Zerajima
I think you are getting confused between a plugin registered in the Retrieve message which contains the BusinessEntity output variable, and the RetrieveResponse type used from the SDK in combination with RetrieveRequest. The RetrieveResponse has an Entity parameter. See here https://learn.microsoft.com/en-us/dotnet/api/microsoft.xrm.sdk.messages.retrieveresponse

I am not sure if @jordimontana82 has implemented something that triggers plugins on the Retrieve message, or RetrieveMultiple message, he can reply here if that is the case :)

@Zerajima
Copy link
Author

You're correct, it's pretty confusing, and we have Microsoft to that for that. RetrieveResponse does actually contain "Entity" field, however during the request execution pipeline, this value is stored in IExecutionContext.OutputParameters, under "BusinessEntity" key. The link I provided in first post describes the differences between message responses and IExecutionContext contents. There's a list of responses, that use different naming in response class and in IExecutionContext.

I'm not sure where exactly this is handled in FakeXrmEasy, I assumed it would be in message executor, but if it's somewhere else, I apologize for misleading you.

This is the code snippet, that works correctly on dataverse, but throws KeyNotFound exception when executed through FakeXrmEasy pipeline.
var resultEntity = context.OutputParameters["BusinessEntity"] as Entity;

@BetimBeja
Copy link

In FakeXrmEasy the MessageExecutor like RetrieveRequestExecutor and the EventPipeline Simulation are two different things. I guess this might be picked up from @jordimontana82 as an improvement.

@jordimontana82
Copy link
Contributor

jordimontana82 commented Apr 11, 2024

I am not sure if @jordimontana82 has implemented something that triggers plugins on the Retrieve message, or RetrieveMultiple message, he can reply here if that is the case :)

The pipeline should support any message (including Retrieve's).

@Zerajima can you post a sample unit test to reproduce, please?

FXE (short for FakeXrmEasy) passes in InputParameters from the incoming request and out to OutputParameters as well. But a unit test might be handy (along with your pipeline setup and how you are registering plugin steps) to reproduce in case we missed anything...

@jordimontana82
Copy link
Contributor

Maybe it's related to this one too #124

@Zerajima
Copy link
Author

Here's a sample test to demonstrate this issue. The IPlugin implementation should work properly on dataverse.

using FakeXrmEasy.Abstractions.Enums;
using FakeXrmEasy.Abstractions.Plugins.Enums;
using FakeXrmEasy.Middleware;
using FakeXrmEasy.Middleware.Crud;
using FakeXrmEasy.Middleware.Pipeline;
using FakeXrmEasy.Pipeline;
using FakeXrmEasy.Plugins.PluginSteps;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Extensions;
using Microsoft.Xrm.Sdk.Query;
using Xunit;

namespace Tests;

public class SampleTests
{
    private const string EntityLogicalName = "testentity";
    private const string NameAttributeLogicalName = "name";

    [Fact]
    public void TestPlugin()
    {
        var context = MiddlewareBuilder
            .New()
            .AddCrud()
            .AddPipelineSimulation()
            .UsePipelineSimulation()
            .UseCrud()
            .SetLicense(FakeXrmEasyLicense.NonCommercial)
            .Build();

        context.RegisterPluginStep<PluginHandler>(
            new PluginStepDefinition()
            {
                MessageName = "Retrieve",
                EntityLogicalName = EntityLogicalName,
                Stage = ProcessingStepStage.Postoperation,
                Mode = ProcessingStepMode.Synchronous,
                Rank = 1,
            });

        var seededEntity = new TestEntity()
        {
            Id = Guid.NewGuid(),
            Name = "Seeded name",
        };
        context.AddEntity(seededEntity);

        var service = context.GetOrganizationService();
        var actualEntity = service.Retrieve(EntityLogicalName, seededEntity.Id, new ColumnSet(NameAttributeLogicalName));

        Assert.Equal(actualEntity.Attributes[NameAttributeLogicalName], "Modified name");
    }

    [EntityLogicalName(EntityLogicalName)]
    public class TestEntity : Entity
    {
        public TestEntity()
            : base(EntityLogicalName)
        {
        }

        [AttributeLogicalName(NameAttributeLogicalName)]
        public string? Name
        {
            get => GetAttributeValue<string?>(NameAttributeLogicalName);
            set => SetAttributeValue(NameAttributeLogicalName, value);
        }
    }

    public class PluginHandler : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            var context = serviceProvider.Get<IPluginExecutionContext>();

            var entity = context.OutputParameters["BusinessEntity"] as Entity; // Throws KeyNotFoundException
            entity.Attributes[NameAttributeLogicalName] = "Modified name";
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants