Getting Started Guide¶
Creating a New Project¶
This getting started guide will show you how to create a custom AspNetCore web application and use the agent framework to create connections and send basic messages. We are going to start from scratch. All you need to have installed in Visual Studio and the .NET Core SDK.
Prerequisites¶
- If you haven’t already done so, install Visual Studio community and the .NET Core 2.2.300 SDK
You should be running an instance of Windows or MacOS for this particular demo.
Create an AspNetCore Project¶
Open Visual Studio and select new project, then choose Web Application (Model-View-Controller):
Select the .NET Core 2.2, then name your project WebAgent.
Installing the Required Packages¶
Use one of the three methods below to load the AgentFramework.Core packages into your project using nuget.
Package Manager CLI:
Install-Package AgentFramework.Core -Source https://www.myget.org/F/agent-framework/api/v3/index.json -v 4.0.0-preview.662
.NET CLI:
dotnet add package AgentFramework.Core -s https://www.myget.org/F/agent-framework/api/v3/index.json -v 4.0.0-preview.662
NOTE: For these first two CLI options, make sure to install AgentFramework.AspNetCore
and AgentFramework.Core.Handlers
by replacing the package name above with these two names as well.
Visual Studio Nuget Package Manager:
- click on your new project WebAgent in the side bar
- go to the project tab in your menu, select
add nuget packages...
- click on the dropdown that says nuget.org, and select
configure sources...
- click add, and then name the new source
Agent Framework Beta
and paste the urlhttps://www.myget.org/F/agent-framework/api/v3/index.json
into the location field.- check the
show pre-release packages
box on the bottom left- choose the
Agent Framework Beta
source from the dropdown- select the AgentFramework.AspNetCore package from the list. Make sure it is on a 4.0.0 version
- click add.
We will also use one other package from Nuget called
Jdenticon
. Add that from the nuget.org repository list.
Installing the libindy SDK on your computer¶
Windows¶
You can download binaries of libindy and all dependencies from the Sovrin repo. The dependencies are under deps
folder and libindy
under one of streams (rc, master, stable). There are two options to link the DLLs
- Unzip all files in a directory and add that to your PATH variable (recommended for development)
- Or copy all DLL files in the publish directory (recommended for published deployments)
More details at the Indy documentation for setting up Windows environment.
MacOS¶
Check Setup Indy SDK build environment for MacOS.
Copy libindy.a
and libindy.dylib
to the /usr/local/lib/
directory.
Configuring your own Agent¶
In this section, we’ll walk through some sections of the WebAgent sample to understand how the AgentFramework can be used in a running application.
The WebAgent sample was created using the same steps as listed above. It may help to follow along to the steps below in your own new project.
However, you may want to also open and try to run the fully working WebAgent sample in Visual Studio first to see if all the dependancies are working as they should.
When the project was created, Startup.cs
and Program.cs
files were built using a template. These control how your webserver starts. We will need to
edit them to use the agent framework.
Startup.cs¶
Our first goal is to edit the Startup file. Copy and paste the below code into your Startup.cs
file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | using System;
using AgentFramework.AspNetCore;
using AgentFramework.Core.Models.Wallets;
using Jdenticon.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WebAgent.Messages;
using WebAgent.Protocols.BasicMessage;
using WebAgent.Utils;
namespace WebAgent
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddLogging();
// Register agent framework dependency services and handlers
services.AddAgentFramework(builder =>
{
builder.AddBasicAgent<SimpleWebAgent>(c =>
{
c.OwnerName = Environment.GetEnvironmentVariable("AGENT_NAME") ?? NameGenerator.GetRandomName();
c.EndpointUri = new Uri(Environment.GetEnvironmentVariable("ENDPOINT_HOST") ?? Environment.GetEnvironmentVariable("ASPNETCORE_URLS"));
c.WalletConfiguration = new WalletConfiguration { Id = "WebAgentWallet" };
c.WalletCredentials = new WalletCredentials { Key = "MyWalletKey" };
});
});
// Register custom handlers with DI pipeline
services.AddSingleton<BasicMessageHandler>();
services.AddSingleton<TrustPingMessageHandler>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
// Register agent middleware
app.UseAgentFramework();
// fun identicons
app.UseJdenticon();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
|
In this file, we congigure and add the Agent Framework to the project. If you are building the project from scratch, make sure to comment out lines 44-45 until you have created these services. Line 72 specifies how the API to trigger actions should be called. ————-
Program.cs¶
Next, we will edit the Program.cs
file. Copy and paste this code too:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace WebAgent
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseStartup<Startup>()
.Build();
}
}
|
Once you have finished with this code, take a moment to look over the changes that we’ve made.
SimpleWebAgent.cs¶
Now create a file name SimpleWebAgent.cs
in the main directory
This file will inherit from the AgentBase class in the AgentFramework, and it extends the IAgent Interface.
This interface includes only one function named Task<MessageResponse>ProcessAsync(IAgentContext context, MessageContext messageContext)
This will process any message that is sent to the agent’s endpoint.
Copy and paste the below code into the file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | using System;
using AgentFramework.Core.Handlers;
using WebAgent.Messages;
using WebAgent.Protocols.BasicMessage;
namespace WebAgent
{
public class SimpleWebAgent : AgentBase
{
public SimpleWebAgent(IServiceProvider serviceProvider)
: base(serviceProvider)
{
}
protected override void ConfigureHandlers()
{
AddConnectionHandler();
AddForwardHandler();
AddHandler<BasicMessageHandler>();
AddHandler<TrustPingMessageHandler>();
}
}
}
|
bundleconfig.json¶
Create a bundleconfig.json file in your project root directory, and paste this json array into to it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // Configure bundling and minification for the project.
// More info at https://go.microsoft.com/fwlink/?LinkId=808241
[
{
"outputFileName": "wwwroot/css/site.min.css",
// An array of relative input file paths. Globbing patterns supported
"inputFiles": [
"wwwroot/css/site.css"
]
},
{
"outputFileName": "wwwroot/js/site.min.js",
"inputFiles": [
"wwwroot/js/site.js"
],
// Optionally specify minification options
"minify": {
"enabled": true,
"renameLocals": true
},
// Optionally generate .map file
"sourceMap": false
}
]
|
launchSettings.json¶
Edit the Property/launchSettings.json
1 2 3 4 5 6 7 8 9 10 11 12 | {
"profiles": {
"WebAgent": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:5000/"
}
}
}
|
Finally, to get this program to run we will need to add a couple of utility files.
First, add a Utils folder. Add these two files to the Utils folder (Open these links in new tabs):
NameGenerator.cs Extensions.cs
Click run, you should see your template home page will appear in your web browser at http://localhost:5000. Congratulations! You’ve successully included the Agent framework into your project. Continue on to see how you might use it in your project.
WebAgent Walkthrough¶
We will learn how to create a web agent in this section. If you have created your own agent, you should be able to use that here. You may want to download the View files and import them into your project to save time on copy/paste: We’re assuming that you are already familiar with the high level concepts of Indy and Sovrin.
The first thing is to understand about Wallets. Read about wallets here: Insert link/paragraphs about wallet infra based on hipe: Wallet HIPE
Opening a wallet in Agent Framework¶
We open a wallet in Agent Framework by provisioning an agent. This process of provisioning
agents will create and configure an agent wallet and initialize the agent configuration.
The framework will generate a random Did and Verkey, unless you specify AgentSeed
which is used if you need determinism. Length of seed must be 32 characters.
await _provisioningService.ProvisionAgentAsync(
new ProvisioningConfiguration
{
EndpointUri = "http://localhost:5000",
OwnerName = "My Agent"
});
We provision this agent in the Startup.cs
file by building it directly with the AgentBuilder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | using System;
using AgentFramework.AspNetCore;
using AgentFramework.Core.Models.Wallets;
using Jdenticon.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WebAgent.Messages;
using WebAgent.Protocols.BasicMessage;
using WebAgent.Utils;
namespace WebAgent
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddLogging();
// Register agent framework dependency services and handlers
services.AddAgentFramework(builder =>
{
builder.AddBasicAgent<SimpleWebAgent>(c =>
{
c.OwnerName = Environment.GetEnvironmentVariable("AGENT_NAME") ?? NameGenerator.GetRandomName();
c.EndpointUri = new Uri(Environment.GetEnvironmentVariable("ENDPOINT_HOST") ?? Environment.GetEnvironmentVariable("ASPNETCORE_URLS"));
c.WalletConfiguration = new WalletConfiguration { Id = "WebAgentWallet" };
c.WalletCredentials = new WalletCredentials { Key = "MyWalletKey" };
});
});
// Register custom handlers with DI pipeline
services.AddSingleton<BasicMessageHandler>();
services.AddSingleton<TrustPingMessageHandler>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
// Register agent middleware
app.UseAgentFramework();
// fun identicons
app.UseJdenticon();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
|
You now have a functioning wallet in your agent, ready to store all your secrets.
The Agent framework abstracts the main workflows of Indy into a state machine. For our agent, we will show how to create and receive invitations with other people. Although simple, it builds the foundation for all other potential agent communications like credentials and proofs.
TODO: Basic message and routing info
Connections¶
Every connection is a unique relationship with another agent. The Agent Framework represents this relationship with a ConnectionRecord
, this entity describes the pairwise relationship with another party.
The states for this record are:
Invited
- initially, when creating invitations to connect, the record will be set to this state.Negotating
- set after accepting an invitation and sending a request to connectConnected
- set when both parties have acknowledged the connection and have a pairwise record of each others DID’s
For us to send basic messages back and forth, we will first need to establish that protocol. We will walk through how a message is processed in the WebAgent.
First, we need to define the basic message structure in the Protocols folder. These are all child classes of the AgentMessage class that is in the AgentFramework.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using AgentFramework.Core.Exceptions;
using AgentFramework.Core.Extensions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace AgentFramework.Core.Messages
{
/// <summary>
/// Represents an abstract base class of a content message.
/// </summary>
public abstract class AgentMessage
{
/// <summary>
/// Internal JObject representation of an agent message.
/// </summary>
[JsonIgnore]
private IList<JProperty> _decorators = new List<JProperty>();
/// <summary>
/// Gets or sets the message id.
/// </summary>
/// <value>
/// The message id.
/// </value>
[JsonProperty("@id")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>
/// The type.
/// </value>
[JsonProperty("@type")]
public string Type { get; set; }
/// <summary>
/// Gets the decorators on the message.
/// </summary>
/// <returns>The decorators as a JObject.</returns>
public IReadOnlyList<JProperty> GetDecorators() => new ReadOnlyCollection<JProperty>(_decorators);
/// <summary>
/// Internal set method for setting the collection of decorators attached to the message.
/// </summary>
/// <param name="decorators">JObject of decorators attached to the message.</param>
internal void SetDecorators(IList<JProperty> decorators) => _decorators = decorators;
/// <summary>
/// Gets the decorator.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name">The name.</param>
/// <returns></returns>
/// <exception cref="AgentFrameworkException">ErrorCode.InvalidMessage Cannot deserialize packed message or unable to find decorator on message.</exception>
public T GetDecorator<T>(string name) where T : class
{
try
{
var decorator = _decorators.First(_ => _.Name == $"~{name}");
return decorator.Value.ToObject<T>();
}
catch (Exception e)
{
throw new AgentFrameworkException(ErrorCode.InvalidMessage, "Failed to extract decorator from message", e);
}
}
/// <summary>
/// Finds the decorator with the specified name or returns <code>null</code>.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name">The name.</param>
/// <returns>The requested decorator or null</returns>
public T FindDecorator<T>(string name) where T : class
{
var decorator = _decorators.FirstOrDefault(_ => _.Name == $"~{name}");
return decorator?.Value.ToObject<T>();
}
/// <summary>
/// Adds the decorator.
/// </summary>
/// <param name="decorator">The decorator.</param>
/// <param name="name">The decorator name.</param>
public void AddDecorator<T>(T decorator, string name) where T : class =>
_decorators.Add(new JProperty($"~{name}", JsonConvert.DeserializeObject<JToken>(decorator.ToJson())));
/// <summary>
/// Sets the decorator overriding any existing values
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="decorator">The decorator.</param>
/// <param name="name">The name.</param>
public void SetDecorator<T>(T decorator, string name) where T : class
{
_decorators.Remove(_decorators.FirstOrDefault(x => x.Name == $"~{name}"));
AddDecorator(decorator, name);
}
}
}
|
The BasicMessage uses the AgentMessage attributes as shown in the class above but adds “content” and “sent_time” attributes as well. This will allow something like text messages to be sent between agents.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | using System;
using AgentFramework.Core.Messages;
using WebAgent.Messages;
using Newtonsoft.Json;
namespace WebAgent.Protocols.BasicMessage
{
public class BasicMessage : AgentMessage
{
public BasicMessage()
{
Id = Guid.NewGuid().ToString();
Type = CustomMessageTypes.BasicMessageType;
}
[JsonProperty("content")]
public string Content { get; set; }
[JsonProperty("sent_time")]
public string SentTime { get; set; }
}
}
|
When your agent receives a message, it gets put directly into the wallet by the basic message handler.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | using System;
using System.Threading.Tasks;
using AgentFramework.Core.Contracts;
using AgentFramework.Core.Handlers;
using AgentFramework.Core.Messages;
namespace WebAgent.Protocols.BasicMessage
{
public class BasicMessageHandler : MessageHandlerBase<BasicMessage>
{
private readonly IWalletRecordService _recordService;
public BasicMessageHandler(IWalletRecordService recordService)
{
_recordService = recordService;
}
protected override async Task<AgentMessage> ProcessAsync(BasicMessage message, IAgentContext agentContext, MessageContext messageContext)
{
Console.WriteLine($"Processing message by {messageContext.Connection.Id}");
await _recordService.AddAsync(agentContext.Wallet, new BasicMessageRecord
{
Id = Guid.NewGuid().ToString(),
ConnectionId = messageContext.Connection.Id,
Text = message.Content,
SentTime = DateTime.TryParse(message.SentTime, out var dateTime) ? dateTime : DateTime.UtcNow,
Direction = MessageDirection.Incoming
});
return null;
}
}
}
|
Then when the view is reloaded, the Controller class takes over.
This is following the MVC pattern of ASPNetCore.
Here is the ConnectionsController class that will show a BasicMessage if one is sent or received:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | using System;
using System.Globalization;
using System.Reactive.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using AgentFramework.Core.Contracts;
using AgentFramework.Core.Extensions;
using AgentFramework.Core.Handlers;
using AgentFramework.Core.Handlers.Agents;
using AgentFramework.Core.Messages.Connections;
using AgentFramework.Core.Models;
using AgentFramework.Core.Models.Connections;
using AgentFramework.Core.Models.Events;
using AgentFramework.Core.Models.Records.Search;
using AgentFramework.Core.Utils;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using WebAgent.Messages;
using WebAgent.Models;
using WebAgent.Protocols.BasicMessage;
namespace WebAgent.Controllers
{
public class ConnectionsController : Controller
{
private readonly IEventAggregator _eventAggregator;
private readonly IConnectionService _connectionService;
private readonly IWalletService _walletService;
private readonly IWalletRecordService _recordService;
private readonly IProvisioningService _provisioningService;
private readonly IAgentProvider _agentContextProvider;
private readonly IMessageService _messageService;
private readonly WalletOptions _walletOptions;
public ConnectionsController(
IEventAggregator eventAggregator,
IConnectionService connectionService,
IWalletService walletService,
IWalletRecordService recordService,
IProvisioningService provisioningService,
IAgentProvider agentContextProvider,
IMessageService messageService,
IOptions<WalletOptions> walletOptions)
{
_eventAggregator = eventAggregator;
_connectionService = connectionService;
_walletService = walletService;
_recordService = recordService;
_provisioningService = provisioningService;
_agentContextProvider = agentContextProvider;
_messageService = messageService;
_walletOptions = walletOptions.Value;
}
[HttpGet]
public async Task<IActionResult> Index()
{
var context = await _agentContextProvider.GetContextAsync();
return View(new ConnectionsViewModel
{
Connections = await _connectionService.ListAsync(context)
});
}
[HttpGet]
public async Task<IActionResult> CreateInvitation()
{
var context = await _agentContextProvider.GetContextAsync();
var (invitation, _) = await _connectionService.CreateInvitationAsync(context, new InviteConfiguration { AutoAcceptConnection = true });
ViewData["Invitation"] = $"{(await _provisioningService.GetProvisioningAsync(context.Wallet)).Endpoint.Uri}?c_i={EncodeInvitation(invitation)}";
return View();
}
[HttpPost]
public async Task<IActionResult> AcceptInvitation(AcceptConnectionViewModel model)
{
var context = await _agentContextProvider.GetContextAsync();
var invite = MessageUtils.DecodeMessageFromUrlFormat<ConnectionInvitationMessage>(model.InvitationDetails);
var (request, record) = await _connectionService.CreateRequestAsync(context, invite);
await _messageService.SendAsync(context.Wallet, request, record, invite.RecipientKeys[0]);
return RedirectToAction("Index");
}
[HttpPost]
public IActionResult ViewInvitation(AcceptConnectionViewModel model)
{
if (!ModelState.IsValid)
{
return Redirect(Request.Headers["Referer"].ToString());
}
ViewData["InvitationDetails"] = model.InvitationDetails;
var invite = MessageUtils.DecodeMessageFromUrlFormat<ConnectionInvitationMessage>(model.InvitationDetails);
return View(invite);
}
[HttpPost]
public async Task<IActionResult> SendTrustPing(string connectionId)
{
var context = await _agentContextProvider.GetContextAsync();
var connection = await _connectionService.GetAsync(context, connectionId);
var message = new TrustPingMessage
{
ResponseRequested = true,
Comment = "Hello"
};
var slim = new SemaphoreSlim(0, 1);
var success = false;
using (var subscription = _eventAggregator.GetEventByType<ServiceMessageProcessingEvent>()
.Where(_ => _.MessageType == CustomMessageTypes.TrustPingResponseMessageType)
.Subscribe(_ => { success = true; slim.Release(); }))
{
await _messageService.SendAsync(context.Wallet, message, connection);
await slim.WaitAsync(TimeSpan.FromSeconds(5));
return RedirectToAction("Details", new { id = connectionId, trustPingSuccess = success });
}
}
[HttpGet]
public async Task<IActionResult> Details(string id, bool? trustPingSuccess = null)
{
var context = new AgentContext
{
Wallet = await _walletService.GetWalletAsync(_walletOptions.WalletConfiguration,
_walletOptions.WalletCredentials)
};
var model = new ConnectionDetailsViewModel
{
Connection = await _connectionService.GetAsync(context, id),
Messages = await _recordService.SearchAsync<BasicMessageRecord>(context.Wallet,
SearchQuery.Equal(nameof(BasicMessageRecord.ConnectionId), id), null, 10),
TrustPingSuccess = trustPingSuccess
};
return View(model);
}
[HttpPost]
public async Task<IActionResult> SendMessage(string connectionId, string text)
{
if (string.IsNullOrEmpty(text)) return RedirectToAction("Details", new { id = connectionId });
var context = new AgentContext
{
Wallet = await _walletService.GetWalletAsync(_walletOptions.WalletConfiguration,
_walletOptions.WalletCredentials)
};
var sentTime = DateTime.UtcNow;
var messageRecord = new BasicMessageRecord
{
Id = Guid.NewGuid().ToString(),
Direction = MessageDirection.Outgoing,
Text = text,
SentTime = sentTime,
ConnectionId = connectionId
};
var message = new BasicMessage
{
Content = text,
SentTime = sentTime.ToString("s", CultureInfo.InvariantCulture)
};
var connection = await _connectionService.GetAsync(context, connectionId);
// Save the outgoing message to the local wallet for chat history purposes
await _recordService.AddAsync(context.Wallet, messageRecord);
// Send an agent message using the secure connection
await _messageService.SendAsync(context.Wallet, message, connection);
return RedirectToAction("Details", new {id = connectionId});
}
[HttpPost]
public IActionResult LaunchApp(LaunchAppViewModel model)
{
return Redirect($"{model.UriSchema}{Uri.EscapeDataString(model.InvitationDetails)}");
}
/// <summary>
/// Encodes the invitation to a base64 string which can be presented to the user as QR code or a deep link Url
/// </summary>
/// <returns>The invitation.</returns>
/// <param name="invitation">Invitation.</param>
public string EncodeInvitation(ConnectionInvitationMessage invitation)
{
return invitation.ToJson().ToBase64();
}
/// <summary>
/// Decodes the invitation from base64 to strongly typed object
/// </summary>
/// <returns>The invitation.</returns>
/// <param name="invitation">Invitation.</param>
public ConnectionInvitationMessage DecodeInvitation(string invitation)
{
return JsonConvert.DeserializeObject<ConnectionInvitationMessage>(Encoding.UTF8.GetString(Convert.FromBase64String(invitation)));
}
}
}
|
Read through the rest of the ConnectionsController class to understand how the Agent Framework can be used to implement the other website features.