Skip to content

Instantly share code, notes, and snippets.

@jamesikanos
Created June 12, 2020 12:42
Show Gist options
  • Save jamesikanos/5def3e58e73b05f994447d9c915a5518 to your computer and use it in GitHub Desktop.
Save jamesikanos/5def3e58e73b05f994447d9c915a5518 to your computer and use it in GitHub Desktop.
WorkfloPlus Auto Uploader Example
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>workflow_uploader</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.json" Version="12.0.3" />
</ItemGroup>
</Project>
/*
Workflow Upload Tester - James Woodall
-------
This sample shows how to automatically upload and submit a WorkfloPlus workflow zip file.
To use, get an access token from https://accounts.workfloplus.com/manage/dev with Workflow_Create permission.
Run using "> dotnet run"
MIT License
Copyright (c) 2020 Intoware Limited.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace workflow_uploader
{
class Program
{
// CHANGE ME!!
const string AccessToken = "<INSERT ACCESS TOKEN HERE>";
// CHANGE ME!!
const string ZipToUpload = "/path/to/zip/file.zip";
const string Gateway = "https://gateway.workfloplus.com";
const int BlockSize = (1024 * 100); // 100Kb
static async Task Main(string[] args)
{
// 1. Upload the Workflow ZIP
var uploadResult = await UploadZipAsync();
// 2. Commit the Workflow ZIP to the database
using (var client = CreateHttpClient())
{
// 2a. Set the metadata properties for the workflow
var obj = new CreateWorkflowModel
{
FileJobId = uploadResult.JobId,
Description = (uploadResult.workflowDescription ?? "") + "\nAuto Uploaded",
Name = uploadResult.WorkflowName ?? "Auto Uploaded Workflow"
};
Console.WriteLine("Submitting Workflow");
Console.WriteLine(JsonConvert.SerializeObject(obj, Formatting.Indented));
using (var response = await PostAsJsonAsync(client, "workflows/v2", obj))
{
response.EnsureSuccessStatusCode();
var workflowId = (await ReadJsonContentAsync<JObject>(response))["workflowId"]?.ToString();
Console.WriteLine("Workflow Published: " + workflowId);
}
}
}
/// <summary>
/// Uploads the file in blocks
/// </summary>
static async Task<UploadJobModel> UploadBlocksAsync(HttpClient client, string uploadJobId)
{
Console.WriteLine("Starting to upload file");
using (var fs = File.OpenRead(ZipToUpload))
{
// Create a buffer to store the blocks
var buffer = new byte[BlockSize];
var from = 0;
int read;
int count = 0;
// Read the block size and upload each chunk
while ((read = await fs.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
Console.WriteLine("Uploading Block: {0}", ++count);
// File Progress is controlled by a Content-Range header
var contentRangeHeader = new ContentRangeHeaderValue(from, from + read - 1, fs.Length);
var byteContent = new ByteArrayContent(buffer, 0, read);
byteContent.Headers.ContentRange = contentRangeHeader;
using (var response = await client.PostAsync("workflows/v2/upload/" + uploadJobId, byteContent))
{
response.EnsureSuccessStatusCode();
}
from += read;
}
}
Console.WriteLine("Blocks Uploaded, checking status...");
// Wait an initial 1.5 seconds for the job to process
await Task.Delay(1500);
// Get the job status (wait a few seconds for it to complete)
for (int i = 0; i < 5; i++)
{
using (var response = await client.GetAsync("workflows/v2/upload/" + uploadJobId))
{
response.EnsureSuccessStatusCode();
var responseObj = await ReadJsonContentAsync<UploadJobModel>(response);
if (responseObj.Status != "Completed")
{
Console.WriteLine("Status not completed, trying again...");
await Task.Delay(4000);
continue;
}
Console.WriteLine("Workflow Processed Successfully");
return responseObj;
}
}
throw new ApplicationException("Unable to process workflow in time");
}
/// <summary>
/// Uploads the workflow zip file and returns the upload job model
/// </summary>
static async Task<UploadJobModel> UploadZipAsync()
{
using (var client = CreateHttpClient())
{
// Create an upload job
var fileInfo = new FileInfo(ZipToUpload);
Console.WriteLine("Uploading Workflow: {0}" + fileInfo.FullName);
Console.WriteLine("File Size: {0:N} mb", fileInfo.Length / 1024 / 1024);
var obj = new { BlockSize, fileSize = fileInfo.Length, friendlyFilename = fileInfo.Name };
using (var response = await PostAsJsonAsync(client, "workflows/v2/upload", obj))
{
response.EnsureSuccessStatusCode();
// Fetch the Upload Job Id to be used for the upload block process
var uploadJobId = (await ReadJsonContentAsync<JObject>(response))["id"]?.ToString();
return await UploadBlocksAsync(client, uploadJobId);
}
}
}
/// <summary>
/// Helper method to create a HttpClient object with the BaseAddress and Authorization tokens populated
/// </summary>
static HttpClient CreateHttpClient()
{
var client = new HttpClient
{
BaseAddress = new Uri(Gateway)
};
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", AccessToken);
return client;
}
/// <summary>
/// Helper method to serialize and post a JSON object
/// </summary>
/// <param name="client">HTTP Client to use</param>
/// <param name="url">URL to POST to</param>
/// <param name="obj">Object to send</param>
/// <typeparam name="T">Type of object to send</typeparam>
/// <returns>A completed HtttpResponseMessage</returns>
static Task<HttpResponseMessage> PostAsJsonAsync<T>(HttpClient client, string url, T obj)
{
var jStr = JsonConvert.SerializeObject(obj);
var stringContent = new StringContent(jStr);
stringContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
return client.PostAsync(url, stringContent);
}
/// <summary>
/// Helper method to serialize response string into object
/// </summary>
/// <param name="response">Response to serialize</param>
/// <typeparam name="T">JSON Type to serialize to</typeparam>
/// <returns>The serialized type</returns>
static async Task<T> ReadJsonContentAsync<T>(HttpResponseMessage response)
{
var content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(content);
}
}
/// <summary>
/// Model representing a processing/completed Workflow Upload Job
/// </summary>
class UploadJobModel
{
/// <summary>
/// Parsed description from the workflow
/// </summary>
public string workflowDescription { get; set; }
/// <summary>
/// Parsed name of the workflow
/// </summary>
public string WorkflowName { get; set; }
/// <summary>
/// Workflow Upload Job Id
/// </summary>
public string JobId { get; set; }
/// <summary>
/// Status of the process
/// </summary>
public string Status { get; set; }
}
class CreateWorkflowModel
{
public string Name { get; set; }
public string Mode { get; set; } = "live";
public string Description { get; set; }
public string FileJobId { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment