Permalink
Browse files

derivative webhook with signalR

  • Loading branch information...
augustogoncalves committed Dec 13, 2018
1 parent 226793b commit 101baee6e581f39108ccc0a8673a3affa39c06f5
@@ -23,12 +23,17 @@
using Microsoft.AspNetCore.Mvc;
using Autodesk.Forge;
using Autodesk.Forge.Model;
using Newtonsoft.Json.Linq;
using Microsoft.AspNetCore.SignalR;

namespace forgeSample.Controllers
{
[ApiController]
public class ModelDerivativeController : ControllerBase
{
private IHubContext<ModelDerivativeHub> _hubContext;
public ModelDerivativeController(IHubContext<ModelDerivativeHub> hubContext) { _hubContext = hubContext; }

/// <summary>
/// Start the translation job for a give bucketKey/objectName
/// </summary>
@@ -40,6 +45,32 @@ public async Task<dynamic> TranslateObject([FromBody]ObjectModel objModel)
{
dynamic oauth = await OAuthController.GetInternalAsync();

// prepare the webhook callback
DerivativeWebhooksApi webhook = new DerivativeWebhooksApi();
webhook.Configuration.AccessToken = oauth.access_token;
dynamic existingHooks = await webhook.GetHooksAsync(DerivativeWebhookEvent.ExtractionFinished);

// get the callback from your settings (e.g. web.config)
string callbackUlr = OAuthController.GetAppSetting("FORGE_WEBHOOK_CALLBACK_HOST") + "/api/forge/callback/modelderivative";

bool createHook = true; // need to create, we don't know if our hook is already there...
foreach (KeyValuePair<string, dynamic> hook in new DynamicDictionaryItems(existingHooks.data))
{
if (hook.Value.scope.workflow.Equals(objModel.connectionId))
{
// ok, found one hook with the same workflow, no need to create...
createHook = false;
if (!hook.Value.callbackUrl.Equals(callbackUlr))
{
await webhook.DeleteHookAsync(DerivativeWebhookEvent.ExtractionFinished, new System.Guid(hook.Value.hookId));
createHook = true; // ops, the callback URL is outdated, so delete and prepare to create again
}
}
}

// need to (re)create the hook?
if (createHook) await webhook.CreateHookAsync(DerivativeWebhookEvent.ExtractionFinished, callbackUlr, objModel.connectionId);

// prepare the payload
List<JobPayloadItem> outputs = new List<JobPayloadItem>()
{
@@ -53,9 +84,9 @@ public async Task<dynamic> TranslateObject([FromBody]ObjectModel objModel)
};
JobPayload job;
if (string.IsNullOrEmpty(objModel.rootFilename))
job = new JobPayload(new JobPayloadInput(objModel.objectName), new JobPayloadOutput(outputs));
job = new JobPayload(new JobPayloadInput(objModel.objectName), new JobPayloadOutput(outputs), new JobPayloadMisc(objModel.connectionId));
else
job = new JobPayload(new JobPayloadInput(objModel.objectName, true, objModel.rootFilename), new JobPayloadOutput(outputs));
job = new JobPayload(new JobPayloadInput(objModel.objectName, true, objModel.rootFilename), new JobPayloadOutput(outputs), new JobPayloadMisc(objModel.connectionId));


// start the translation
@@ -65,6 +96,14 @@ public async Task<dynamic> TranslateObject([FromBody]ObjectModel objModel)
return jobPosted;
}

[HttpPost]
[Route("/api/forge/callback/modelderivative")]
public async Task<IActionResult> DerivativeCallback([FromBody]JObject body)
{
await ModelDerivativeHub.ExtractionFinished(_hubContext, body);
return Ok();
}

[HttpDelete]
[Route("api/forge/modelderivative/manifest")]
public async Task<IActionResult> DeleteObjectAsync([FromBody]ObjectModel objectModel)
@@ -85,5 +124,23 @@ public class ObjectModel
public string bucketKey { get; set; }
public string objectName { get; set; }
public string rootFilename { get; set; }
public string connectionId { get; set; }
}

/// <summary>
/// Class uses for SignalR
/// </summary>
public class ModelDerivativeHub : Microsoft.AspNetCore.SignalR.Hub
{
public string GetConnectionId() { return Context.ConnectionId; }

/// <summary>
/// Notify the client that the workitem is complete
/// </summary>
public async static Task ExtractionFinished(IHubContext<ModelDerivativeHub> context, JObject body)
{
string connectionId = body["hook"]["scope"]["workflow"].Value<String>();
await context.Clients.Client(connectionId).SendAsync("extractionFinished", body);
}
}
}
@@ -82,7 +82,7 @@ private static async Task<dynamic> Get2LeggedTokenAsync(Scope[] scopes)
/// <summary>
/// Reads appsettings from web.config
/// </summary>
private static string GetAppSetting(string settingKey)
public static string GetAppSetting(string settingKey)
{
return Environment.GetEnvironmentVariable(settingKey);
}
@@ -26,6 +26,7 @@ public Startup(IConfiguration configuration)
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSignalR();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@@ -40,6 +41,11 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
app.UseHsts();
}

app.UseSignalR(routes =>
{
routes.MapHub<Controllers.ModelDerivativeHub>("/api/signalr/modelderivative");
});

app.UseDefaultFiles();
app.UseStaticFiles();
app.UseHttpsRedirection();
@@ -9,7 +9,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Autodesk.Forge" Version="1.4.0" />
<PackageReference Include="Autodesk.Forge" Version="1.5.0" />
<PackageReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

@@ -14,8 +14,10 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.1/js/bootstrap-select.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.1/css/bootstrap-select.min.css" />
<!-- Autodesk Forge Viewer files -->
<link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/style.min.css?v=v6.0" type="text/css">
<script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/viewer3D.min.js?v=v6.0"></script>
<link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/style.min.css?v=v6.*" type="text/css">
<script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/viewer3D.min.js?v=v6.*"></script>
<!-- .NET SignalR -->
<script src="//unpkg.com/@aspnet/signalr@1.1.0/dist/browser/signalr.min.js"></script>
<!-- this project files -->
<link href="css/main.css" rel="stylesheet" />
<script src="js/ForgeTree.js"></script>
@@ -78,7 +80,7 @@ <h4 class="modal-title" id="myModalLabel">Create new bucket</h4>
</select>
<input type="text" id="newBucketKey" class="form-control">
</div><!-- /input-group -->
For demonstration purpouses, objects (files) are not automatically translated. After you upload, right click
For demonstration purposes, objects (files) are not automatically translated. After you upload, right click
on the object and select "Translate".
</div>
<div class="modal-footer">
@@ -247,7 +247,7 @@ function translateObject(node) {
jQuery.post({
url: '/api/forge/modelderivative/jobs',
contentType: 'application/json',
data: JSON.stringify({ 'bucketKey': bucketKey, 'objectName': objectKey, 'rootFilename': $("#rootFilename").val() }),
data: JSON.stringify({ 'bucketKey': bucketKey, 'objectName': objectKey, 'rootFilename': $("#rootFilename").val(), 'connectionId': connectionId }),
success: function (res) {
$("#forgeViewer").html('Translation started! Please try again in a moment.');
},
@@ -258,9 +258,9 @@ function translateObject(node) {
jQuery.post({
url: '/api/forge/modelderivative/jobs',
contentType: 'application/json',
data: JSON.stringify({ 'bucketKey': bucketKey, 'objectName': objectKey }),
data: JSON.stringify({ 'bucketKey': bucketKey, 'objectName': objectKey, 'connectionId': connectionId }),
success: function (res) {
$("#forgeViewer").html('Translation started! Please try again in a moment.');
$("#forgeViewer").html('Translation started! Model will load when ready...');
},
});
}
@@ -74,4 +74,28 @@ function getForgeToken(callback) {
callback(res.access_token, res.expires_in)
}
});
}

var connection;
var connectionId;

$(document).ready(function () {
startConnection();
});

function startConnection(onReady) {
if (connection && connection.connectionState) { if (onReady) onReady(); return; }
connection = new signalR.HubConnectionBuilder().withUrl("/api/signalr/modelderivative").build();
connection.start()
.then(function () {
connection.invoke('getConnectionId')
.then(function (id) {
connectionId = id; // we'll need this...
if (onReady) onReady();
});
});

connection.on("extractionFinished", function (data) {
launchViewer(data.resourceUrn);
});
}

0 comments on commit 101baee

Please sign in to comment.