Month: September 2018

K2 Blackpearl Re-Resolve Group to Users

K2 Blackpearl Re-Resolve Group to Users

We have a system built around the K2 Blackpearl workflow engine that routes requests to the appropriate destinations. We switched a number of users from one active directory domain to an entirely different one, which K2’s cache wasn’t aware of. This caused some users to be missed when resolving the workflow destination active directory group to its members.

Force K2 Workflow Activity to Re-Resolve AD Group Destination to Members

The first thing we need to do is force K2 to refresh its cache for the problematic active directory group.

  1. Navigate to the server where K2 is hosted.
  2. Find the ForceIdentityServiceRefresh application shipped with K2. For us, that’s in the C:\K2 folder.
  3. Enter the Group name, select the “Group” object type and check each checkbox to force a full cache refresh for the given group.K2 Cache Force Identity Refresh
  4. Click the Clear Identity Cache button.

Now that the cache is cleared K2 will have a reference to the latest members associated with the group in question. It’s time to make the K2 workflow re-resolve its destination:

  1. Open the K2 Workspace.
  2. Navigate to the Management Console.
  3. Within the Management Console menu tree, navigate to the Instances node of the workflow process that has a problematic instance, which is typically under the following path:

    K2ServerName -> Workflow Server -> Processes -> Your.Process.Name -> Your.Workflow.Name -> Instances

  4. Search for the workflow instance you want to re-resolve destinations for, select it, then click Goto Activity.K2 Workflow Instance
  5. Within the Goto Activity screen you’ll see a list of all available activities associated with the workflow. Select the same activity the workflow is currently at, then press OK.K2 Workflow GoTo Activity

The result of following the above steps will force the given instance of your K2 workflow process to re-evaluate the selected activity, effectively causing a re-resolution of the destination group to its members from K2’s refreshed cache. 

Download File with jQuery and Web API 2.0 IHttpActionResult

Download File with jQuery and Web API 2.0 IHttpActionResult

I came across a need to download a file through an AJAX request to a Web API 2.0 endpoint. A quick search revealed a fairly straightforward way to tackle this problem, which is to implement a custom IHttpActionResult (available in Web API 2.0) that sets up the HttpResponse in a way that can be digested by the client.

Create a Custom Web API 2.0 IHttpActionResult

The first step was to implement the IHttpActionResult as outlined below.

    public class ApiFileResult : IHttpActionResult {
        public ApiFileResult(string filePath, string contentType, string fileName = null) {
            if (string.IsNullOrWhiteSpace(filePath)) {
                throw new ArgumentNullException(nameof(filePath));
            }

            this.contentType = contentType ?? MimeMapping.GetMimeMapping(Path.GetExtension(filePath));
            var fileStream = File.OpenRead(filePath);
            var fileBytes = new byte[fileStream.Length];
            fileStream.Read(fileBytes, 0, fileBytes.Length);
            stream = new MemoryStream(fileBytes);

            this.fileName = fileName ?? Path.GetFileName(filePath);
        }

        public ApiFileResult(MemoryStream stream, string contentType, string fileName) {
            if (stream == null) {
                throw new ArgumentNullException(nameof(stream));
            }
            if (contentType == null) {
                throw new ArgumentNullException(nameof(contentType));
            }
            
            this.stream = stream;
            this.contentType = contentType;
            this.fileName = fileName;
        }

        private readonly string contentType;
        private readonly string fileName;
        private readonly MemoryStream stream;

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) {
            var bytes = stream.ToArray();
            var memoryStream = new MemoryStream(bytes);

            var response = new HttpResponseMessage(HttpStatusCode.OK) {
                Content = new StreamContent(memoryStream)
            };

            response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
            response.Content.Headers.ContentLength = memoryStream.Length;
            response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
                FileName = fileName
            };

            return Task.FromResult(response);
        }
    }

Once that’s in place we can use the new IHttpActionResult as our API’s return.

Create an ApiController Action That Returns Custom IHttpActionResult

Next up is creating an instance of this new custom Web API 2.0 IHttpActionResult class and using it as our API’s return type. Below is a fairly lightweight ApiController to demonstrate just that. The injected IBillingExportService class is used to generate a memory stream containing the contents of the file. We then new-up an instance of our custom ApiFileResult object, passing the file stream, file type and file name through.

    [RoutePrefix("api/v1/Billing")]
    public class BillingApiController : ApiController {
        public BillingApiController(IBillingExportService service) {
            this.service = service;
        }

        private readonly IBillingExportService service;

        [HttpPost, Route("Export", Name = "BillingExport")]
        public async Task<IHttpActionResult> GetBillingExport(int id) {
            var exportStream = await service.GetExportFile(id);
            return new ApiFileResult(exportStream, 
                                     "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 
                                     $"Billing Export {DateTime.Today:yyyy-MM-dd}{ExcelExport.WorkbookBuilder.ExcelFileExtension}");
        }
    }

Now that we have the API endpoint in place we can make a jQuery AJAX request to it, as follows:

    $.ajax({
            type: 'POST',
            cache: false,
            url: exportScheduledBillingUrl,
            data: serializeScheduledBillingGrid(),
            xhrFields: {
                    // make sure the response knows we're expecting a binary type in return.
                    // this is important, without it the excel file is marked corrupted.
                    responseType: 'arraybuffer'
                }
        })
    .done(function (data, status, xmlHeaderRequest) {
        var downloadLink = document.createElement('a');
        var blob = new Blob([data],
            {
                type: xmlHeaderRequest.getResponseHeader('Content-Type')
            });
        var url = window.URL || window.webkitURL;
        var downloadUrl = url.createObjectURL(blob);
        var fileName = '';

        // get the file name from the content disposition
        var disposition = xmlHttpRequest.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) {
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) {
                fileName = matches[1].replace(/['"]/g, '');
            }
        }

        // Blob download logic taken from: https://stackoverflow.com/questions/16086162/handle-file-download-from-ajax-post
        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE workaround for "HTML7007" and "Access Denied" error.
            window.navigator.msSaveBlob(blob, fileName);
        } else {
            if (fileName) {
                if (typeof downloadLink.download === 'undefined') {
                    window.location = downloadUrl;
                } else {
                    downloadLink.href = downloadUrl;
                    downloadLink.download = fileName;
                    document.body.appendChild(downloadLink);
                    downloadLink.click();
                }
            } else {
                window.location = downloadUrl;
            }

            setTimeout(function () {
                    url.revokeObjectURL(downloadUrl);
                },
                100);
        }
    });

When the above Ajax call is triggered (i.e. a user clicks a button on your page) it’ll return the new custom IHttpActionResult you created. We then use the information provided by that result to build a Blob object. This Blob object enables us to generate an object URL which can be used to download the contents of our file.

Xamarin iOS MT1007 Failed to Launch the Application

Xamarin iOS MT1007 Failed to Launch the Application

After going through the free provisioning profile article I’ve been able to debug my Xamarin iOS app on a physical device. I wanted to verify what happens when the application gets installed for the first time, so I deleted the app from my phone. After running the Visual Studio debugger, the application was reinstalled successfully on my device, but failed to launch the debugger with the following message in the output log:

The app could not be launched on ‘iPhone’. Error: error MT1007: Failed to launch the application ‘/path/to/my/iOS.app’ on the device ‘iPhone’: Failed to launch the application ‘My.App.Namespace’ on the device ‘iPhone’: ESecurity. You can still launch the application manually by tapping on it.

Resolve the Xamarin iOS MT1007 Failed to Launch Error

The message sent to the Visual Studio output log was fairly vague. A quick Google search brought me to several different posts suggesting things like restarting my phone, updating Xcode and so on. Restarting wasn’t the solution and Xcode was already up-to-date. 

Enabling the Device Log prior to debugging gave me a bit more insight: after the error was raised, the following was output to the device log:

The request was denied by service delegate (SBMainWorkspace) for reason: Security (“Unable to launch my.application.namespace because it has an invalid code signature, inadequate entitlements or its profile has not been explicitly trusted by the user”).

The line “its profile has not been explicitly trusted by the user” caught my interest, and made me believe something relate to my provisioning profile was undone. I plugged my phone into the Mac and opened my provisioning profile Xcode project. I then selected my device as the target and ran the application, which greeted me with an error message stating that my device does not trust the developer profile associated with the app.

You Just Need a Little Trust

While the Xamarin iOS MT1007 error can mean many things, in this case it boils down to trust. The solution to this trust issue is the following:

  1. On your iPhone, navigate to Settings -> General -> Device Management.
  2. Within Device Management, you should see your developer provisioning profile, click it.
  3. Trust the developer profile.

After trusting the profile you’ll see a callout stating:

Apps from developer “Your Profile” are trusted on this iPhone and will be trusted until all apps from the developer are deleted.”

Those last few words sum it all up. The problem was introduced the moment I uninstalled the app from my device, and rightfully so according to the relationship my device has with the developer profile.