-
Notifications
You must be signed in to change notification settings - Fork 0
/
DojoTablesConnector.cs
158 lines (136 loc) · 7.2 KB
/
DojoTablesConnector.cs
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
using System.Net.WebSockets;
using System.Text;
using Dojo.Net.EPOS.Server.Exceptions;
using Dojo.Net.EPOS.Server.JsonRpc;
using Dojo.Net.EPOS.Server.Schema;
using Dojo.Net.EPOS.Server.Serialization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
namespace Dojo.Net.EPOS.Server
{
public class DojoTablesConnector
{
private const int ReconnectTimeout = 5000;
private const string StagingUrl = "wss://staging-api.dojo.dev/ws/v1/tables/epos";
private const string ProductionUrl = "wss://api.dojo.tech/ws/v1/tables/epos";
public string? ResellerId { get; init; }
public ILogger Logger { get; init; } = NullLogger.Instance;
public string SoftwareHouseId { get; }
public bool IsSandbox { get; }
private string Token { get; }
private JsonSerializerSettings jsonSerializerSettings { get; }
public DojoTablesConnector(string accountId, string apiKey, string softwareHouseId, bool isSandbox)
{
if (accountId == null)
throw new ArgumentNullException(nameof(accountId));
if (apiKey == null)
throw new ArgumentNullException(nameof(apiKey));
this.Token = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{accountId}:{apiKey}"));
this.IsSandbox = isSandbox;
this.SoftwareHouseId = softwareHouseId ?? throw new ArgumentNullException(nameof(softwareHouseId));
this.Logger.LogInformation($"Dojo Tables Connector initialized for account {accountId} and software house {softwareHouseId} in {(isSandbox ? "sandbox" : "production")} mode");
this.jsonSerializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
};
jsonSerializerSettings.Converters.Add(new StringEnumConverter());
}
public async Task StartAsync(ITablesAPIServer serverHandler, CancellationToken cancellationToken)
{
do
{
using var client = new ClientWebSocket();
client.Options.SetRequestHeader("Authorization", $"Basic {this.Token}");
if (!string.IsNullOrEmpty(this.ResellerId))
{
client.Options.SetRequestHeader("reseller-id", this.ResellerId);
}
if (!string.IsNullOrEmpty(this.SoftwareHouseId))
{
client.Options.SetRequestHeader("software-house-id", this.SoftwareHouseId);
}
try
{
var uri = new Uri(this.IsSandbox ? StagingUrl : ProductionUrl);
await client.ConnectAsync(uri, cancellationToken);
this.Logger.LogInformation($"Connected to Dojo Tables API at {uri}");
var receiveBuffer = new ArraySegment<byte>(new byte[1024]);
while (client.State == WebSocketState.Open)
{
WebSocketReceiveResult result;
using var ms = new MemoryStream();
do
{
result = await client.ReceiveAsync(receiveBuffer, cancellationToken);
ms.Write(receiveBuffer.Array!, receiveBuffer.Offset, result.Count);
}
while (!result.EndOfMessage);
ms.Seek(0, SeekOrigin.Begin);
if (result.MessageType == WebSocketMessageType.Text)
{
using var reader = new StreamReader(ms, Encoding.UTF8);
var message = await reader.ReadToEndAsync();
if (message == null)
{
this.Logger.LogError($"Failed to read message from Dojo");
continue;
}
JsonRpcRequestEnvelope? requestEnvelope = JsonConvert.DeserializeObject<JsonRpcRequestEnvelope>(message, new JsonRpcEnvelopeConverter());
JsonRpcResponseEnvelope? response = null;
string requestId = string.Empty;
try
{
if (requestEnvelope == null)
{
requestId = "";
this.Logger.LogError($"Failed to parse request from Dojo, message: {message}");
throw new TablesException(TablesErrorCode.ErrorParseError, "Invalid request");
}
requestId = requestEnvelope.Id ?? "";
this.Logger.LogInformation($"Received request {requestEnvelope.Method} with id {requestId}");
response = await requestEnvelope.AcceptMessageAsync(serverHandler);
}
catch (TablesException ex)
{
response = new ErrorResponse(requestId, ex.ErrorCode, ex.Message);
this.Logger.LogWarning($"Sending error response. ErrorCode: {ex.ErrorCode}, ErrorReason: {ex.Message}");
}
catch (Exception ex)
{
response = new ErrorResponse(requestId, TablesErrorCode.ErrorInternalPosError, ex.Message);
this.Logger.LogError(ex, "Error processing request");
}
if (response != null)
{
var responseMessage = JsonConvert.SerializeObject(response, jsonSerializerSettings);
await client.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(responseMessage)), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
}
}
catch (OperationCanceledException)
{
this.Logger.LogInformation("Connection to Dojo Tables API cancelled");
return;
}
catch (WebSocketException ex)
{
// TODO: check when authorization fails, we need to cancel attempt to connect
this.Logger.LogError(ex, "Error while connecting to Dojo Tables API");
}
catch (Exception ex)
{
this.Logger.LogError(ex, "Unkown error while connecting to Dojo Tables API");
}
await Task.Delay(ReconnectTimeout, cancellationToken);
} while (!cancellationToken.IsCancellationRequested);
}
}
}