locked
How to return output to client side (rest call) from Microsoft.Xrm.Sdk.IPlugin? RRS feed

Answers

  • In this scenario, you need to pass complex input, and return complex data. There are two approaches you can use:

    Make two calls, one to pass the parameters and another to retrieve the data

    1. Use the Create method to store the parameters (the natch_request attributes). You don't need a plugin on the create message, but you do need the Guid return value of this call
    2. Use a Retrieve method to retrieve the record instance you just created. Call the 3rd party API in a plugin (on the post event) on the Retrieve message. Populate the "Entity" OutputParameter with the response data. This can then be read in the client code

    Use just one call

    The only message that you can use via REST that will take a complex input, and return complex data is RetrieveMultiple. The input is the jquery filter, and the output would be one or more records in an EntityCollection. Although it's only one call, this is more complex to implement than the first approach as you'd need to:

    1. In the client, build a jqeury with a filter that contains the data you want to pass
    2. In a post-event plugin on RetrieveMultiple, get the parameters from the Query InputParameter. I think this will be a QueryExpression, but it may be a FetechExpression. Call the 3rd party API, and add an Entity to the "EntityCollection" OutputParameter that has the response data

    Personally, I'd go with the first approach. It means you'll have a record created for each request, but that might not be a bad thing from an audit perspective, and you could create a Bulk Delete job to clear out the data


    Microsoft CRM MVP - http://mscrmuk.blogspot.com/ http://www.excitation.co.uk

    Thursday, April 3, 2014 4:20 PM
    Moderator

All replies

  • Maybe you can create a PostImage and use this postImage entity to get all extra data.

    Bye

    Alessandro


    Alessandro Graps

    Wednesday, April 2, 2014 5:25 PM
  • Could you share more details what is actual requirement ??

    Our Website | Our Blog | Follow US | My Facebook Page | Microsoft Dynamics CRM 2011 Application Design
    Make sure to "Vote as Helpful" and "Mark As Answer",if you get answer of your question.

    Thursday, April 3, 2014 12:19 AM
    Moderator
  • Thank you gentlemen for your time.

    @Alessandro I will look into the PostImage, I've seen it appear somewhere, but don't know what it really is.

    @Mahender

    I want to integrate data from a 3rd party API into a CRM Form. I don't want to call the API directly from javascript, because that would require CORS and expose details I'd rather not have out there.

    So I found that I could call a plugin from javascript, on the create of my custom entity, to execute the 3rd party API.
    The pugin would then return the data from the API back to the browser. You could say the plugin acts as a proxy.
    If there is another option, not using the api, I'd be happy to try that.

    PS: I also thought about putting the 'proxy' outside of CRM, have CORS enabled on that and call this from the javascript in the form. But I'd rather keep the code together and not have another project/install to maintain.

    Thursday, April 3, 2014 7:08 AM
  • Ok, got it, now when you are saying extra data are referring crm data or data that you are getting from 3rd party api ??

    Our Website | Our Blog | Follow US | My Facebook Page | Microsoft Dynamics CRM 2011 Application Design
    Make sure to "Vote as Helpful" and "Mark As Answer",if you get answer of your question.

    Thursday, April 3, 2014 7:52 AM
    Moderator
  • Sorry about forgetting the details, it's all clear in my head though ;-)

    So the extra data is from the 3rd party API, and it's a web api that's called over http. 

    Thursday, April 3, 2014 8:03 AM
  • Ok so you are consuming third party api in plugin and have created a custom entity, now you are creating record of custom entity which callng your plugin to initiate 3rd party api call, Are you storing that data in your entity so that you can get it using javascript ??, if you are following this process you need to bind plugin for your every type of request for example example let's say create an option set which will define your operation create/update/retrieve and as soon as you will create/update record of your custom entity if will call 3rd party api based on operation type selected.

    Our Website | Our Blog | Follow US | My Facebook Page | Microsoft Dynamics CRM 2011 Application Design
    Make sure to "Vote as Helpful" and "Mark As Answer",if you get answer of your question.

    Thursday, April 3, 2014 8:34 AM
    Moderator
  • In the plugin, I get the data I need to build the request. While in the plugin execution, I do the http call to get the new data from the API. The data returned from the API should be returned by the plugin.

    Here's what I've got (no calling the api yet). As you can see I've tried many different things. I'm sure the plugin is called, because when an exception is throw from the plugin I can see it in javascript/browser.

    // The InputParameters collection contains all the data passed in the message request.
                    var context = localContext.PluginExecutionContext;
                    if (context.InputParameters.Contains("Target") &&
                        context.InputParameters["Target"] is Entity)
                    {
                        // Obtain the target entity from the input parameters.
                        Entity entity = (Entity)context.InputParameters["Target"];
    
                        // Verify that the target entity represents an entity type you are expecting. 
                        // For example, an account. If not, the plug-in was not registered correctly.
                        if (entity.LogicalName != "natch_trendstopapicommand")
                            return;
    
                        var method = entity.GetAttributeValue<string>("natch_RequestMethod");
                        var url = entity.GetAttributeValue<string>("natch_RequestUrl");
                        var body = entity.GetAttributeValue<string>("natch_RequestBody");
                        localContext.TracingService.Trace("method: {0}", method);
                        localContext.TracingService.Trace("url: {0}", url);
                        localContext.TracingService.Trace("body: {0}", body);
    
                        entity.Attributes["natch_ResponseStatusCode"] = 300;
                        entity.Attributes["natch_ResponseBody"] = "{ 'data': 300 }";
    
                        entity.Attributes["ResponseStatusCode"] = 301;
                        entity.Attributes["ResponseBody"] = "{ 'data': 301 }";
    
                        context.OutputParameters["Target0"] = entity;
                        context.OutputParameters["Entity0"] = entity;
    
    
                      
                        Entity entity1 = new Entity("natch_trendstopapicommand");
                        foreach (var key in entity.Attributes.Keys)
                        {
                            if (!entity1.Attributes.ContainsKey(key))
                            {
                                entity1.Attributes.Add(key, entity.Attributes[key]);
                            }
                        }
    
                        //entity1.EntityState = EntityState.Created;
                        //entity1.Attributes.Add("statecode", 0);
                        //entity1.Attributes.Add("statuscode", 1);
                        ////entity1.Attributes.Add("ownerid", new EntityReference("ownerid", userId));
    
                        ////OptionSetValue state = new OptionSetValue(1);
                        ////OptionSetValue status = new OptionSetValue(2);
                        ////entity1.Attributes.Add("state",state);
                        ////entity1.Attributes.Add("status", status);
    
    
                        ////entity1.Attributes.Add("natch_ResponseStatusCode", 203);
                        ////entity1.Attributes.Add("natch_ResponseBody", "{ 'data': 3 }");
    
                        context.OutputParameters["Target1"] = entity1;
                        context.OutputParameters["Entity1"] = entity1;
    
                        context.OutputParameters["ResponseStatusCode"] = 301;
                        context.OutputParameters["ResponseBody"] = "{ 'data': 301 }";
                        context.OutputParameters["natch_ResponseStatusCode"] = 301;
                        context.OutputParameters["natch_ResponseBody"] = "{ 'data': 301 }";
    
                        ////context.InputParameters["Target"] = entity1;
                    }


    Thursday, April 3, 2014 2:59 PM
  • In this scenario, you need to pass complex input, and return complex data. There are two approaches you can use:

    Make two calls, one to pass the parameters and another to retrieve the data

    1. Use the Create method to store the parameters (the natch_request attributes). You don't need a plugin on the create message, but you do need the Guid return value of this call
    2. Use a Retrieve method to retrieve the record instance you just created. Call the 3rd party API in a plugin (on the post event) on the Retrieve message. Populate the "Entity" OutputParameter with the response data. This can then be read in the client code

    Use just one call

    The only message that you can use via REST that will take a complex input, and return complex data is RetrieveMultiple. The input is the jquery filter, and the output would be one or more records in an EntityCollection. Although it's only one call, this is more complex to implement than the first approach as you'd need to:

    1. In the client, build a jqeury with a filter that contains the data you want to pass
    2. In a post-event plugin on RetrieveMultiple, get the parameters from the Query InputParameter. I think this will be a QueryExpression, but it may be a FetechExpression. Call the 3rd party API, and add an Entity to the "EntityCollection" OutputParameter that has the response data

    Personally, I'd go with the first approach. It means you'll have a record created for each request, but that might not be a bad thing from an audit perspective, and you could create a Bulk Delete job to clear out the data


    Microsoft CRM MVP - http://mscrmuk.blogspot.com/ http://www.excitation.co.uk

    Thursday, April 3, 2014 4:20 PM
    Moderator
  • Finally it works! Thank you so much.

    I tried the first option, but could get it working. The output I added in the plugin was not showing up.
    Moved on to the second option and after a lot of back and forth (deploy, publish, test) I was able to get the whole process working.

    Second option is indeed more complex, so I might give option one another try.

    I wrote this little function to help me figure out the input- and output parameters.

    	private void ThrowParametersForDebug(IPluginExecutionContext context)
            {
                var sb = new StringBuilder();
                foreach (var attribute in context.OutputParameters.Keys)
                {
                    sb.AppendLine("O: " + attribute);
                }
                foreach (var attribute in context.InputParameters.Keys)
                {
                    sb.AppendLine("I: " + attribute);
                }
    
                throw new Exception(sb.ToString());
            }

    Friday, April 4, 2014 11:45 AM
  • Just wanted to give an update on option one. Tried the mentioned flow with create and retrieve, but can not get my extra data out there during the post-operation of the retrieve.

    Tried both adding and setting the attribute and also setting the output param explicitly (because I don't know if "entity" is a reference or not.

    var entity = context.OutputParameters["BusinessEntity"] as Entity;
                entity.Attributes.Add("natch_responsebody", "{ 'ok': 1}");
                //entity.Attributes.Add("natch_responsestatuscode", 808);
                //entity.Attributes["natch_responsebody"] = "{ 'ok': 1}";
                entity.Attributes["natch_responsestatuscode"] = 808;
    
                context.OutputParameters["BusinessEntity"] = entity;

    PS: I'm on holiday for the next 10 days.

    Friday, April 4, 2014 1:11 PM