global: add null safety checks

This commit is contained in:
Abdullah Atta
2025-10-14 21:15:51 +05:00
parent be432dfd24
commit 6e35edb715
109 changed files with 452 additions and 590 deletions

View File

@@ -1,33 +0,0 @@
/*
This file is part of the Notesnook Sync Server project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the Affero GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Affero GNU General Public License for more details.
You should have received a copy of the Affero GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Text.Json.Serialization;
namespace Streetwriters.Common.Attributes
{
[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false)]
public class JsonInterfaceConverterAttribute : JsonConverterAttribute
{
public JsonInterfaceConverterAttribute(Type converterType)
: base(converterType)
{
}
}
}

View File

@@ -33,7 +33,7 @@ namespace Streetwriters.Common
{
Id = "notesnook",
Name = "Notesnook",
SenderEmail = Constants.NOTESNOOK_SENDER_EMAIL,
SenderEmail = Constants.NOTESNOOK_SENDER_EMAIL ?? "noreply@notesnook.com",
SenderName = "Notesnook",
Type = ApplicationType.NOTESNOOK,
AppId = ApplicationType.NOTESNOOK,
@@ -58,14 +58,15 @@ namespace Streetwriters.Common
{ "notesnook", Notesnook }
};
public static Client FindClientById(string id)
public static Client? FindClientById(string? id)
{
if (!IsValidClient(id)) return null;
if (string.IsNullOrEmpty(id) || !IsValidClient(id)) return null;
return ClientsMap[id];
}
public static Client FindClientByAppId(ApplicationType appId)
public static Client? FindClientByAppId(ApplicationType? appId)
{
if (appId is null) return null;
switch (appId)
{
case ApplicationType.NOTESNOOK:

View File

@@ -29,54 +29,54 @@ namespace Streetwriters.Common
public static string INSTANCE_NAME => Environment.GetEnvironmentVariable("INSTANCE_NAME") ?? "default";
// S3 related
public static string S3_ACCESS_KEY => Environment.GetEnvironmentVariable("S3_ACCESS_KEY");
public static string S3_ACCESS_KEY_ID => Environment.GetEnvironmentVariable("S3_ACCESS_KEY_ID");
public static string S3_SERVICE_URL => Environment.GetEnvironmentVariable("S3_SERVICE_URL");
public static string S3_REGION => Environment.GetEnvironmentVariable("S3_REGION");
public static string S3_BUCKET_NAME => Environment.GetEnvironmentVariable("S3_BUCKET_NAME");
public static string S3_INTERNAL_BUCKET_NAME => Environment.GetEnvironmentVariable("S3_INTERNAL_BUCKET_NAME");
public static string S3_INTERNAL_SERVICE_URL => Environment.GetEnvironmentVariable("S3_INTERNAL_SERVICE_URL");
public static string? S3_ACCESS_KEY => Environment.GetEnvironmentVariable("S3_ACCESS_KEY");
public static string? S3_ACCESS_KEY_ID => Environment.GetEnvironmentVariable("S3_ACCESS_KEY_ID");
public static string? S3_SERVICE_URL => Environment.GetEnvironmentVariable("S3_SERVICE_URL");
public static string? S3_REGION => Environment.GetEnvironmentVariable("S3_REGION");
public static string? S3_BUCKET_NAME => Environment.GetEnvironmentVariable("S3_BUCKET_NAME");
public static string? S3_INTERNAL_BUCKET_NAME => Environment.GetEnvironmentVariable("S3_INTERNAL_BUCKET_NAME");
public static string? S3_INTERNAL_SERVICE_URL => Environment.GetEnvironmentVariable("S3_INTERNAL_SERVICE_URL");
// SMTP settings
public static string SMTP_USERNAME => Environment.GetEnvironmentVariable("SMTP_USERNAME");
public static string SMTP_PASSWORD => Environment.GetEnvironmentVariable("SMTP_PASSWORD");
public static string SMTP_HOST => Environment.GetEnvironmentVariable("SMTP_HOST");
public static string SMTP_PORT => Environment.GetEnvironmentVariable("SMTP_PORT");
public static string SMTP_REPLYTO_EMAIL => Environment.GetEnvironmentVariable("SMTP_REPLYTO_EMAIL");
public static string NOTESNOOK_SENDER_EMAIL => Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_EMAIL") ?? Environment.GetEnvironmentVariable("SMTP_USERNAME");
public static string? SMTP_USERNAME => Environment.GetEnvironmentVariable("SMTP_USERNAME");
public static string? SMTP_PASSWORD => Environment.GetEnvironmentVariable("SMTP_PASSWORD");
public static string? SMTP_HOST => Environment.GetEnvironmentVariable("SMTP_HOST");
public static string? SMTP_PORT => Environment.GetEnvironmentVariable("SMTP_PORT");
public static string? SMTP_REPLYTO_EMAIL => Environment.GetEnvironmentVariable("SMTP_REPLYTO_EMAIL");
public static string? NOTESNOOK_SENDER_EMAIL => Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_EMAIL") ?? Environment.GetEnvironmentVariable("SMTP_USERNAME");
public static string NOTESNOOK_APP_HOST => Environment.GetEnvironmentVariable("NOTESNOOK_APP_HOST");
public static string NOTESNOOK_API_SECRET => Environment.GetEnvironmentVariable("NOTESNOOK_API_SECRET");
public static string? NOTESNOOK_APP_HOST => Environment.GetEnvironmentVariable("NOTESNOOK_APP_HOST");
public static string NOTESNOOK_API_SECRET => Environment.GetEnvironmentVariable("NOTESNOOK_API_SECRET") ?? throw new InvalidOperationException("NOTESNOOK_API_SECRET is required");
// MessageBird is used for SMS sending
public static string TWILIO_ACCOUNT_SID => Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID");
public static string TWILIO_AUTH_TOKEN => Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");
public static string TWILIO_SERVICE_SID => Environment.GetEnvironmentVariable("TWILIO_SERVICE_SID");
public static string? TWILIO_ACCOUNT_SID => Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID");
public static string? TWILIO_AUTH_TOKEN => Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");
public static string? TWILIO_SERVICE_SID => Environment.GetEnvironmentVariable("TWILIO_SERVICE_SID");
// Server discovery
public static int NOTESNOOK_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("NOTESNOOK_SERVER_PORT") ?? "80");
public static string NOTESNOOK_SERVER_HOST => Environment.GetEnvironmentVariable("NOTESNOOK_SERVER_HOST");
public static string NOTESNOOK_CERT_PATH => Environment.GetEnvironmentVariable("NOTESNOOK_CERT_PATH");
public static string NOTESNOOK_CERT_KEY_PATH => Environment.GetEnvironmentVariable("NOTESNOOK_CERT_KEY_PATH");
public static string? NOTESNOOK_SERVER_HOST => Environment.GetEnvironmentVariable("NOTESNOOK_SERVER_HOST");
public static string? NOTESNOOK_CERT_PATH => Environment.GetEnvironmentVariable("NOTESNOOK_CERT_PATH");
public static string? NOTESNOOK_CERT_KEY_PATH => Environment.GetEnvironmentVariable("NOTESNOOK_CERT_KEY_PATH");
public static int IDENTITY_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("IDENTITY_SERVER_PORT") ?? "80");
public static string IDENTITY_SERVER_HOST => Environment.GetEnvironmentVariable("IDENTITY_SERVER_HOST");
public static Uri IDENTITY_SERVER_URL => new(Environment.GetEnvironmentVariable("IDENTITY_SERVER_URL"));
public static string IDENTITY_CERT_PATH => Environment.GetEnvironmentVariable("IDENTITY_CERT_PATH");
public static string IDENTITY_CERT_KEY_PATH => Environment.GetEnvironmentVariable("IDENTITY_CERT_KEY_PATH");
public static string? IDENTITY_SERVER_HOST => Environment.GetEnvironmentVariable("IDENTITY_SERVER_HOST");
public static Uri? IDENTITY_SERVER_URL => Environment.GetEnvironmentVariable("IDENTITY_SERVER_URL") is string url ? new Uri(url) : null;
public static string? IDENTITY_CERT_PATH => Environment.GetEnvironmentVariable("IDENTITY_CERT_PATH");
public static string? IDENTITY_CERT_KEY_PATH => Environment.GetEnvironmentVariable("IDENTITY_CERT_KEY_PATH");
public static int SSE_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("SSE_SERVER_PORT") ?? "80");
public static string SSE_SERVER_HOST => Environment.GetEnvironmentVariable("SSE_SERVER_HOST");
public static string SSE_CERT_PATH => Environment.GetEnvironmentVariable("SSE_CERT_PATH");
public static string SSE_CERT_KEY_PATH => Environment.GetEnvironmentVariable("SSE_CERT_KEY_PATH");
public static string? SSE_SERVER_HOST => Environment.GetEnvironmentVariable("SSE_SERVER_HOST");
public static string? SSE_CERT_PATH => Environment.GetEnvironmentVariable("SSE_CERT_PATH");
public static string? SSE_CERT_KEY_PATH => Environment.GetEnvironmentVariable("SSE_CERT_KEY_PATH");
// internal
public static string WEBRISK_API_URI => Environment.GetEnvironmentVariable("WEBRISK_API_URI");
public static string MONGODB_CONNECTION_STRING => Environment.GetEnvironmentVariable("MONGODB_CONNECTION_STRING");
public static string MONGODB_DATABASE_NAME => Environment.GetEnvironmentVariable("MONGODB_DATABASE_NAME");
public static string? WEBRISK_API_URI => Environment.GetEnvironmentVariable("WEBRISK_API_URI");
public static string MONGODB_CONNECTION_STRING => Environment.GetEnvironmentVariable("MONGODB_CONNECTION_STRING") ?? throw new ArgumentNullException("MONGODB_CONNECTION_STRING environment variable is not set");
public static string MONGODB_DATABASE_NAME => Environment.GetEnvironmentVariable("MONGODB_DATABASE_NAME") ?? throw new ArgumentNullException("MONGODB_DATABASE_NAME environment variable is not set");
public static int SUBSCRIPTIONS_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("SUBSCRIPTIONS_SERVER_PORT") ?? "80");
public static string SUBSCRIPTIONS_SERVER_HOST => Environment.GetEnvironmentVariable("SUBSCRIPTIONS_SERVER_HOST");
public static string SUBSCRIPTIONS_CERT_PATH => Environment.GetEnvironmentVariable("SUBSCRIPTIONS_CERT_PATH");
public static string SUBSCRIPTIONS_CERT_KEY_PATH => Environment.GetEnvironmentVariable("SUBSCRIPTIONS_CERT_KEY_PATH");
public static string? SUBSCRIPTIONS_SERVER_HOST => Environment.GetEnvironmentVariable("SUBSCRIPTIONS_SERVER_HOST");
public static string? SUBSCRIPTIONS_CERT_PATH => Environment.GetEnvironmentVariable("SUBSCRIPTIONS_CERT_PATH");
public static string? SUBSCRIPTIONS_CERT_KEY_PATH => Environment.GetEnvironmentVariable("SUBSCRIPTIONS_CERT_KEY_PATH");
public static string[] NOTESNOOK_CORS_ORIGINS => Environment.GetEnvironmentVariable("NOTESNOOK_CORS")?.Split(",") ?? new string[] { };
}
}

View File

@@ -1,54 +0,0 @@
/*
This file is part of the Notesnook Sync Server project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the Affero GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Affero GNU General Public License for more details.
You should have received a copy of the Affero GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Streetwriters.Common.Converters
{
/// <summary>
/// Converts simple interface into an object (assumes that there is only one class of TInterface)
/// </summary>
/// <typeparam name="TInterface">Interface type</typeparam>
/// <typeparam name="TClass">Class type</typeparam>
public class InterfaceConverter<TInterface, TClass> : JsonConverter<TInterface> where TClass : TInterface
{
public override TInterface Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<TClass>(ref reader, options);
}
public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options)
{
switch (value)
{
case null:
JsonSerializer.Serialize(writer, null, options);
break;
default:
{
var type = value.GetType();
JsonSerializer.Serialize(writer, value, type, options);
break;
}
}
}
}
}

View File

@@ -42,7 +42,7 @@ namespace Streetwriters.Common.Extensions
var data = new Dictionary<string, object>
{
{ "version", Constants.COMPATIBILITY_VERSION },
{ "id", server.Id },
{ "id", server.Id ?? "unknown" },
{ "instance", Constants.INSTANCE_NAME }
};
await context.Response.WriteAsync(JsonSerializer.Serialize(data));
@@ -70,12 +70,12 @@ namespace Streetwriters.Common.Extensions
return app;
}
public static T GetService<T>(this IApplicationBuilder app)
public static T GetService<T>(this IApplicationBuilder app) where T : notnull
{
return app.ApplicationServices.GetRequiredService<T>();
}
public static T GetScopedService<T>(this IApplicationBuilder app)
public static T GetScopedService<T>(this IApplicationBuilder app) where T : notnull
{
using (var scope = app.ApplicationServices.CreateScope())
{

View File

@@ -17,6 +17,7 @@ You should have received a copy of the Affero GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
@@ -28,7 +29,7 @@ namespace Streetwriters.Common.Extensions
{
public static class HttpClientExtensions
{
public static async Task<T> SendRequestAsync<T>(this HttpClient httpClient, string url, IHeaderDictionary headers, HttpMethod method, HttpContent content = null) where T : IResponse, new()
public static async Task<T> SendRequestAsync<T>(this HttpClient httpClient, string url, IHeaderDictionary? headers, HttpMethod method, HttpContent? content = null) where T : IResponse, new()
{
var request = new HttpRequestMessage(method, url);
@@ -51,22 +52,23 @@ namespace Streetwriters.Common.Extensions
}
var response = await httpClient.SendAsync(request);
if (response.Content.Headers.ContentLength > 0 && response.Content.Headers.ContentType.ToString().Contains("application/json"))
if (response.Content.Headers.ContentLength > 0 && response.Content.Headers.ContentType?.ToString()?.Contains("application/json") == true)
{
var res = await response.Content.ReadFromJsonAsync<T>();
res.Success = response.IsSuccessStatusCode;
res.StatusCode = (int)response.StatusCode;
return res;
}
else
{
return new T { Success = response.IsSuccessStatusCode, StatusCode = (int)response.StatusCode, Content = response.Content };
if (res != null)
{
res.Success = response.IsSuccessStatusCode;
res.StatusCode = (int)response.StatusCode;
return res;
}
}
return new T { Success = response.IsSuccessStatusCode, StatusCode = (int)response.StatusCode, Content = response.Content };
}
public static Task<T> ForwardAsync<T>(this HttpClient httpClient, IHttpContextAccessor accessor, string url, HttpMethod method) where T : IResponse, new()
{
var httpContext = accessor.HttpContext;
var httpContext = accessor.HttpContext ?? throw new InvalidOperationException("HttpContext is not available");
var content = new StreamContent(httpContext.Request.BodyReader.AsStream());
return httpClient.SendRequestAsync<T>(url, httpContext.Request.Headers, method, content);
}

View File

@@ -27,7 +27,7 @@ namespace Streetwriters.Common.Extensions
{
public static IServiceCollection AddRepository<T>(this IServiceCollection services, string collectionName, string database) where T : class
{
services.AddSingleton((provider) => MongoDbContext.GetMongoCollection<T>(provider.GetService<MongoDB.Driver.IMongoClient>(), database, collectionName));
services.AddSingleton((provider) => MongoDbContext.GetMongoCollection<T>(provider.GetRequiredService<MongoDB.Driver.IMongoClient>(), database, collectionName));
services.AddScoped<Repository<T>>();
return services;
}

View File

@@ -33,6 +33,6 @@ namespace Streetwriters.Common.Interfaces
string SenderName { get; set; }
string EmailConfirmedRedirectURL { get; }
string AccountRecoveryRedirectURL { get; }
Func<string, Task> OnEmailConfirmed { get; set; }
Func<string, Task>? OnEmailConfirmed { get; set; }
}
}

View File

@@ -17,11 +17,13 @@ You should have received a copy of the Affero GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using MongoDB.Bson;
namespace Streetwriters.Common.Interfaces
{
public interface IDocument
{
string Id
ObjectId Id
{
get; set;
}

View File

@@ -12,8 +12,8 @@ namespace Streetwriters.Common.Interfaces
string email,
EmailTemplate template,
IClient client,
GnuPGContext gpgContext = null,
Dictionary<string, byte[]> attachments = null
GnuPGContext? gpgContext = null,
Dictionary<string, byte[]>? attachments = null
);
}
}

View File

@@ -1,32 +0,0 @@
/*
This file is part of the Notesnook Sync Server project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the Affero GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Affero GNU General Public License for more details.
You should have received a copy of the Affero GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Models;
namespace Streetwriters.Common.Interfaces
{
public interface IOffer : IDocument
{
ApplicationType AppId { get; set; }
string PromoCode { get; set; }
PromoCode[] Codes { get; set; }
}
}

View File

@@ -25,6 +25,6 @@ namespace Streetwriters.Common.Interfaces
{
bool Success { get; set; }
int StatusCode { get; set; }
HttpContent Content { get; set; }
HttpContent? Content { get; set; }
}
}

View File

@@ -8,7 +8,7 @@ namespace Streetwriters.Common.Interfaces
public interface IUserSubscriptionService
{
[WampProcedure("co.streetwriters.subscriptions.subscriptions.get_user_subscription")]
Task<Subscription> GetUserSubscriptionAsync(string clientId, string userId);
Task<Subscription?> GetUserSubscriptionAsync(string clientId, string userId);
Subscription TransformUserSubscription(Subscription subscription);
}
}

View File

@@ -28,7 +28,7 @@ namespace Streetwriters.Common.Messages
public class CreateSubscriptionMessage
{
[JsonPropertyName("userId")]
public string UserId { get; set; }
public required string UserId { get; set; }
[JsonPropertyName("provider")]
public SubscriptionProvider Provider { get; set; }
@@ -46,19 +46,19 @@ namespace Streetwriters.Common.Messages
public long ExpiryTime { get; set; }
[JsonPropertyName("orderId")]
public string OrderId { get; set; }
public string? OrderId { get; set; }
[JsonPropertyName("updateURL")]
public string UpdateURL { get; set; }
public string? UpdateURL { get; set; }
[JsonPropertyName("cancelURL")]
public string CancelURL { get; set; }
public string? CancelURL { get; set; }
[JsonPropertyName("subscriptionId")]
public string SubscriptionId { get; set; }
public string? SubscriptionId { get; set; }
[JsonPropertyName("productId")]
public string ProductId { get; set; }
public string? ProductId { get; set; }
[JsonPropertyName("extend")]
public bool Extend { get; set; }

View File

@@ -28,7 +28,7 @@ namespace Streetwriters.Common.Messages
public class CreateSubscriptionMessageV2
{
[JsonPropertyName("userId")]
public string UserId { get; set; }
public required string UserId { get; set; }
[JsonPropertyName("provider")]
public SubscriptionProvider Provider { get; set; }
@@ -49,13 +49,13 @@ namespace Streetwriters.Common.Messages
public long ExpiryTime { get; set; }
[JsonPropertyName("orderId")]
public string OrderId { get; set; }
public string? OrderId { get; set; }
[JsonPropertyName("subscriptionId")]
public string SubscriptionId { get; set; }
public string? SubscriptionId { get; set; }
[JsonPropertyName("productId")]
public string ProductId { get; set; }
public string? ProductId { get; set; }
[JsonPropertyName("timestamp")]
public long Timestamp { get; set; }

View File

@@ -27,7 +27,7 @@ namespace Streetwriters.Common.Messages
public class DeleteSubscriptionMessage
{
[JsonPropertyName("userId")]
public string UserId { get; set; }
public required string UserId { get; set; }
[JsonPropertyName("appId")]
public ApplicationType AppId { get; set; }

View File

@@ -27,6 +27,6 @@ namespace Streetwriters.Common.Messages
public class DeleteUserMessage
{
[JsonPropertyName("userId")]
public string UserId { get; set; }
public required string UserId { get; set; }
}
}

View File

@@ -26,10 +26,10 @@ namespace Streetwriters.Common.Messages
public class Message
{
[JsonPropertyName("type")]
public string Type { get; set; }
public required string Type { get; set; }
[JsonPropertyName("data")]
public string Data { get; set; }
public string? Data { get; set; }
}
public class SendSSEMessage
{
@@ -37,10 +37,10 @@ namespace Streetwriters.Common.Messages
public bool SendToAll { get; set; }
[JsonPropertyName("userId")]
public string UserId { get; set; }
public required string UserId { get; set; }
[JsonPropertyName("message")]
public Message Message { get; set; }
public required Message Message { get; set; }
[JsonPropertyName("originTokenId")]
public string? OriginTokenId { get; set; }

View File

@@ -31,15 +31,15 @@ namespace Streetwriters.Common.Models
{
public class Client : IClient
{
public string Id { get; set; }
public string Name { get; set; }
public required string Id { get; set; }
public required string Name { get; set; }
public ApplicationType Type { get; set; }
public ApplicationType AppId { get; set; }
public string SenderEmail { get; set; }
public string SenderName { get; set; }
public string EmailConfirmedRedirectURL { get; set; }
public string AccountRecoveryRedirectURL { get; set; }
public required string SenderEmail { get; set; }
public required string SenderName { get; set; }
public required string EmailConfirmedRedirectURL { get; set; }
public required string AccountRecoveryRedirectURL { get; set; }
public Func<string, Task> OnEmailConfirmed { get; set; }
public Func<string, Task>? OnEmailConfirmed { get; set; }
}
}

View File

@@ -3,9 +3,9 @@ namespace Streetwriters.Common.Models
public class EmailTemplate
{
public int? Id { get; set; }
public object Data { get; set; }
public string Subject { get; set; }
public string Html { get; set; }
public string Text { get; set; }
public object? Data { get; set; }
public required string Subject { get; set; }
public required string Html { get; set; }
public required string Text { get; set; }
}
}

View File

@@ -10,12 +10,12 @@ namespace Streetwriters.Common.Models
public partial class GetCustomerResponse : PaddleResponse
{
[JsonPropertyName("data")]
public PaddleCustomer Customer { get; set; }
public PaddleCustomer? Customer { get; set; }
}
public class PaddleCustomer
{
[JsonPropertyName("email")]
public string Email { get; set; }
public string? Email { get; set; }
}
}

View File

@@ -10,7 +10,7 @@ namespace Streetwriters.Common.Models
public partial class GetSubscriptionResponse : PaddleResponse
{
[JsonPropertyName("data")]
public Data Data { get; set; }
public Data? Data { get; set; }
}
public partial class Data
@@ -22,7 +22,7 @@ namespace Streetwriters.Common.Models
// public string Status { get; set; }
[JsonPropertyName("customer_id")]
public string CustomerId { get; set; }
public string? CustomerId { get; set; }
// [JsonPropertyName("address_id")]
// public string AddressId { get; set; }
@@ -64,7 +64,7 @@ namespace Streetwriters.Common.Models
// public CurrentBillingPeriod CurrentBillingPeriod { get; set; }
[JsonPropertyName("billing_cycle")]
public BillingCycle BillingCycle { get; set; }
public BillingCycle? BillingCycle { get; set; }
// [JsonPropertyName("scheduled_change")]
// public object ScheduledChange { get; set; }
@@ -76,7 +76,7 @@ namespace Streetwriters.Common.Models
// public object CustomData { get; set; }
[JsonPropertyName("management_urls")]
public ManagementUrls ManagementUrls { get; set; }
public ManagementUrls? ManagementUrls { get; set; }
// [JsonPropertyName("discount")]
// public object Discount { get; set; }
@@ -91,7 +91,7 @@ namespace Streetwriters.Common.Models
public long Frequency { get; set; }
[JsonPropertyName("interval")]
public string Interval { get; set; }
public string? Interval { get; set; }
}
// public partial class CurrentBillingPeriod
@@ -206,9 +206,9 @@ namespace Streetwriters.Common.Models
public partial class ManagementUrls
{
[JsonPropertyName("update_payment_method")]
public Uri UpdatePaymentMethod { get; set; }
public Uri? UpdatePaymentMethod { get; set; }
[JsonPropertyName("cancel")]
public Uri Cancel { get; set; }
public Uri? Cancel { get; set; }
}
}

View File

@@ -10,12 +10,12 @@ namespace Streetwriters.Common.Models
public class GetTransactionInvoiceResponse : PaddleResponse
{
[JsonPropertyName("data")]
public Invoice Invoice { get; set; }
public Invoice? Invoice { get; set; }
}
public partial class Invoice
{
[JsonPropertyName("url")]
public string Url { get; set; }
public string? Url { get; set; }
}
}

View File

@@ -10,6 +10,6 @@ namespace Streetwriters.Common.Models
public partial class GetTransactionResponse : PaddleResponse
{
[JsonPropertyName("data")]
public TransactionV2 Transaction { get; set; }
public TransactionV2? Transaction { get; set; }
}
}

View File

@@ -9,14 +9,14 @@ namespace Streetwriters.Common.Models
{
public GiftCard()
{
Id = ObjectId.GenerateNewId().ToString();
Id = ObjectId.GenerateNewId();
}
public string Code { get; set; }
public string OrderId { get; set; }
public string OrderIdType { get; set; }
public string ProductId { get; set; }
public string RedeemedBy { get; set; }
public required string Code { get; set; }
public required string OrderId { get; set; }
public required string OrderIdType { get; set; }
public required string ProductId { get; set; }
public string? RedeemedBy { get; set; }
public long RedeemedAt { get; set; }
public long Timestamp { get; set; }
public long Term { get; set; }
@@ -24,6 +24,6 @@ namespace Streetwriters.Common.Models
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
[JsonIgnore]
public string Id { get; set; }
public ObjectId Id { get; set; }
}
}

View File

@@ -9,7 +9,7 @@ namespace Streetwriters.Common.Models
public bool Success { get; set; }
[JsonPropertyName("response")]
public Payment[] Payments { get; set; }
public Payment[]? Payments { get; set; }
}
public partial class Payment
@@ -24,10 +24,10 @@ namespace Streetwriters.Common.Models
public double Amount { get; set; }
[JsonPropertyName("currency")]
public string Currency { get; set; }
public string? Currency { get; set; }
[JsonPropertyName("payout_date")]
public string PayoutDate { get; set; }
public string? PayoutDate { get; set; }
[JsonPropertyName("is_paid")]
public short IsPaid { get; set; }
@@ -36,6 +36,6 @@ namespace Streetwriters.Common.Models
public bool IsOneOffCharge { get; set; }
[JsonPropertyName("receipt_url")]
public string ReceiptUrl { get; set; }
public string? ReceiptUrl { get; set; }
}
}

View File

@@ -9,31 +9,31 @@ namespace Streetwriters.Common.Models
public bool Success { get; set; }
[JsonPropertyName("response")]
public Transaction[] Transactions { get; set; }
public Transaction[]? Transactions { get; set; }
}
public partial class Transaction
{
[JsonPropertyName("order_id")]
public string OrderId { get; set; }
public string? OrderId { get; set; }
[JsonPropertyName("checkout_id")]
public string CheckoutId { get; set; }
public string? CheckoutId { get; set; }
[JsonPropertyName("amount")]
public string Amount { get; set; }
public string? Amount { get; set; }
[JsonPropertyName("currency")]
public string Currency { get; set; }
public string? Currency { get; set; }
[JsonPropertyName("status")]
public string Status { get; set; }
public string? Status { get; set; }
[JsonPropertyName("created_at")]
public string CreatedAt { get; set; }
public string? CreatedAt { get; set; }
[JsonPropertyName("passthrough")]
public object Passthrough { get; set; }
public object? Passthrough { get; set; }
[JsonPropertyName("product_id")]
public long ProductId { get; set; }
@@ -45,13 +45,13 @@ namespace Streetwriters.Common.Models
public bool IsOneOff { get; set; }
[JsonPropertyName("subscription")]
public PaddleSubscription Subscription { get; set; }
public PaddleSubscription? Subscription { get; set; }
[JsonPropertyName("user")]
public PaddleTransactionUser User { get; set; }
public PaddleTransactionUser? User { get; set; }
[JsonPropertyName("receipt_url")]
public string ReceiptUrl { get; set; }
public string? ReceiptUrl { get; set; }
}
public partial class PaddleSubscription
@@ -60,7 +60,7 @@ namespace Streetwriters.Common.Models
public long SubscriptionId { get; set; }
[JsonPropertyName("status")]
public string Status { get; set; }
public string? Status { get; set; }
}
public partial class PaddleTransactionUser
@@ -69,7 +69,7 @@ namespace Streetwriters.Common.Models
public long UserId { get; set; }
[JsonPropertyName("email")]
public string Email { get; set; }
public string? Email { get; set; }
[JsonPropertyName("marketing_consent")]
public bool MarketingConsent { get; set; }

View File

@@ -10,19 +10,19 @@ namespace Streetwriters.Common.Models
public partial class ListTransactionsResponseV2 : PaddleResponse
{
[JsonPropertyName("data")]
public TransactionV2[] Transactions { get; set; }
public TransactionV2[]? Transactions { get; set; }
}
public partial class TransactionV2
{
[JsonPropertyName("id")]
public string Id { get; set; }
public string? Id { get; set; }
[JsonPropertyName("status")]
public string Status { get; set; }
public string? Status { get; set; }
[JsonPropertyName("customer_id")]
public string CustomerId { get; set; }
public string? CustomerId { get; set; }
// [JsonPropertyName("address_id")]
// public string AddressId { get; set; }
@@ -31,10 +31,10 @@ namespace Streetwriters.Common.Models
// public object BusinessId { get; set; }
[JsonPropertyName("custom_data")]
public Dictionary<string, string> CustomData { get; set; }
public Dictionary<string, string>? CustomData { get; set; }
[JsonPropertyName("origin")]
public string Origin { get; set; }
public string? Origin { get; set; }
// [JsonPropertyName("collection_mode")]
// public string CollectionMode { get; set; }
@@ -49,10 +49,10 @@ namespace Streetwriters.Common.Models
// public string InvoiceNumber { get; set; }
[JsonPropertyName("billing_details")]
public BillingDetails BillingDetails { get; set; }
public BillingDetails? BillingDetails { get; set; }
[JsonPropertyName("billing_period")]
public BillingPeriod BillingPeriod { get; set; }
public BillingPeriod? BillingPeriod { get; set; }
// [JsonPropertyName("currency_code")]
// public string CurrencyCode { get; set; }
@@ -70,10 +70,10 @@ namespace Streetwriters.Common.Models
public DateTimeOffset? BilledAt { get; set; }
[JsonPropertyName("items")]
public Item[] Items { get; set; }
public Item[]? Items { get; set; }
[JsonPropertyName("details")]
public Details Details { get; set; }
public Details? Details { get; set; }
// [JsonPropertyName("payments")]
// public Payment[] Payments { get; set; }
@@ -88,7 +88,7 @@ namespace Streetwriters.Common.Models
// public bool EnableCheckout { get; set; }
[JsonPropertyName("payment_terms")]
public PaymentTerms PaymentTerms { get; set; }
public PaymentTerms? PaymentTerms { get; set; }
// [JsonPropertyName("purchase_order_number")]
// public string PurchaseOrderNumber { get; set; }
@@ -100,7 +100,7 @@ namespace Streetwriters.Common.Models
public partial class PaymentTerms
{
[JsonPropertyName("interval")]
public string Interval { get; set; }
public string? Interval { get; set; }
[JsonPropertyName("frequency")]
public long Frequency { get; set; }
@@ -127,7 +127,7 @@ namespace Streetwriters.Common.Models
// public TaxRatesUsed[] TaxRatesUsed { get; set; }
[JsonPropertyName("totals")]
public Totals Totals { get; set; }
public Totals? Totals { get; set; }
// [JsonPropertyName("adjusted_totals")]
// public AdjustedTotals AdjustedTotals { get; set; }
@@ -139,7 +139,7 @@ namespace Streetwriters.Common.Models
// public AdjustedTotals AdjustedPayoutTotals { get; set; }
[JsonPropertyName("line_items")]
public LineItem[] LineItems { get; set; }
public LineItem[]? LineItems { get; set; }
}
public partial class Totals
@@ -175,7 +175,7 @@ namespace Streetwriters.Common.Models
// public object Earnings { get; set; }
[JsonPropertyName("currency_code")]
public string CurrencyCode { get; set; }
public string? CurrencyCode { get; set; }
}
// public partial class AdjustedTotals
// {
@@ -225,10 +225,10 @@ namespace Streetwriters.Common.Models
public partial class LineItem
{
[JsonPropertyName("id")]
public string Id { get; set; }
public string? Id { get; set; }
[JsonPropertyName("price_id")]
public string PriceId { get; set; }
public string? PriceId { get; set; }
// [JsonPropertyName("quantity")]
// public long Quantity { get; set; }
@@ -247,7 +247,7 @@ namespace Streetwriters.Common.Models
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("proration")]
public Proration Proration { get; set; }
public Proration? Proration { get; set; }
}
// public partial class Product
@@ -322,7 +322,7 @@ namespace Streetwriters.Common.Models
public partial class Proration
{
[JsonPropertyName("billing_period")]
public BillingPeriod BillingPeriod { get; set; }
public BillingPeriod? BillingPeriod { get; set; }
}
// public partial class Totals
@@ -356,20 +356,20 @@ namespace Streetwriters.Common.Models
public partial class Item
{
[JsonPropertyName("price")]
public Price Price { get; set; }
public Price? Price { get; set; }
[JsonPropertyName("quantity")]
public long Quantity { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("proration")]
public Proration Proration { get; set; }
public Proration? Proration { get; set; }
}
public partial class Price
{
[JsonPropertyName("id")]
public string Id { get; set; }
public string? Id { get; set; }
// [JsonPropertyName("description")]
// public string Description { get; set; }
@@ -378,7 +378,7 @@ namespace Streetwriters.Common.Models
// public TypeEnum Type { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
public string? Name { get; set; }
// [JsonPropertyName("product_id")]
// public string ProductId { get; set; }
@@ -500,7 +500,7 @@ namespace Streetwriters.Common.Models
public long PerPage { get; set; }
[JsonPropertyName("next")]
public Uri Next { get; set; }
public Uri? Next { get; set; }
[JsonPropertyName("has_more")]
public bool HasMore { get; set; }

View File

@@ -9,7 +9,7 @@ namespace Streetwriters.Common.Models
public bool Success { get; set; }
[JsonPropertyName("response")]
public PaddleUser[] Users { get; set; }
public PaddleUser[]? Users { get; set; }
}
public class PaddleUser
@@ -24,22 +24,22 @@ namespace Streetwriters.Common.Models
public long UserId { get; set; }
[JsonPropertyName("user_email")]
public string UserEmail { get; set; }
public string? UserEmail { get; set; }
[JsonPropertyName("marketing_consent")]
public bool MarketingConsent { get; set; }
[JsonPropertyName("update_url")]
public string UpdateUrl { get; set; }
public string? UpdateUrl { get; set; }
[JsonPropertyName("cancel_url")]
public string CancelUrl { get; set; }
public string? CancelUrl { get; set; }
[JsonPropertyName("state")]
public string State { get; set; }
public string? State { get; set; }
[JsonPropertyName("signup_date")]
public string SignupDate { get; set; }
public string? SignupDate { get; set; }
[JsonPropertyName("quantity")]
public long Quantity { get; set; }

View File

@@ -22,8 +22,8 @@ namespace Streetwriters.Common.Models
public class MFAConfig
{
public bool IsEnabled { get; set; }
public string PrimaryMethod { get; set; }
public string SecondaryMethod { get; set; }
public required string PrimaryMethod { get; set; }
public string? SecondaryMethod { get; set; }
public int RemainingValidCodes { get; set; }
}
}

View File

@@ -29,25 +29,25 @@ using Streetwriters.Common.Interfaces;
namespace Streetwriters.Common.Models
{
public class Offer : IOffer
public class Offer
{
public Offer()
{
Id = ObjectId.GenerateNewId().ToString();
Id = ObjectId.GenerateNewId();
}
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
[JsonPropertyName("id")]
public string Id { get; set; }
public ObjectId Id { get; set; }
[JsonPropertyName("appId")]
public ApplicationType AppId { get; set; }
[JsonPropertyName("promoCode")]
public string PromoCode { get; set; }
public required string PromoCode { get; set; }
[JsonPropertyName("codes")]
public PromoCode[] Codes { get; set; }
public required PromoCode[] Codes { get; set; }
}
}

View File

@@ -10,7 +10,7 @@ namespace Streetwriters.Common.Models
public partial class PaddleResponse
{
[JsonPropertyName("error")]
public PaddleError Error { get; set; }
public PaddleError? Error { get; set; }
}
public class PaddleError

View File

@@ -35,6 +35,6 @@ namespace Streetwriters.Common.Models
public SubscriptionProvider Provider { get; set; }
[JsonPropertyName("code")]
public string Code { get; set; }
public required string Code { get; set; }
}
}

View File

@@ -9,7 +9,7 @@ namespace Streetwriters.Common.Models
public bool Success { get; set; }
[JsonPropertyName("response")]
public Refund Refund { get; set; }
public required Refund Refund { get; set; }
}
public partial class Refund

View File

@@ -30,6 +30,6 @@ namespace Streetwriters.Common.Models
public bool Success { get; set; }
public int StatusCode { get; set; }
[JsonIgnore]
public HttpContent Content { get; set; }
public HttpContent? Content { get; set; }
}
}

View File

@@ -65,8 +65,12 @@ namespace Streetwriters.Common.Models
[BsonRepresentation(BsonType.Int32)]
[JsonPropertyName("type")]
[Obsolete("Use SubscriptionPlan and SubscriptionStatus instead.")]
public SubscriptionType Type { get; set; }
public SubscriptionType Type
{
get;
[Obsolete("Use SubscriptionPlan and SubscriptionStatus instead.")]
set;
}
[JsonPropertyName("cancelURL")]
public string? CancelURL { get; set; }

View File

@@ -10,40 +10,40 @@ namespace Streetwriters.Common.Models
public partial class SubscriptionPreviewResponse : PaddleResponse
{
[JsonPropertyName("data")]
public SubscriptionPreviewData Data { get; set; }
public SubscriptionPreviewData? Data { get; set; }
}
public partial class SubscriptionPreviewData
{
[JsonPropertyName("currency_code")]
public string CurrencyCode { get; set; }
public string? CurrencyCode { get; set; }
[JsonPropertyName("billing_cycle")]
public BillingCycle BillingCycle { get; set; }
public BillingCycle? BillingCycle { get; set; }
[JsonPropertyName("update_summary")]
public UpdateSummary UpdateSummary { get; set; }
public UpdateSummary? UpdateSummary { get; set; }
[JsonPropertyName("immediate_transaction")]
public TransactionV2 ImmediateTransaction { get; set; }
public TransactionV2? ImmediateTransaction { get; set; }
[JsonPropertyName("next_transaction")]
public TransactionV2 NextTransaction { get; set; }
public TransactionV2? NextTransaction { get; set; }
[JsonPropertyName("recurring_transaction_details")]
public Details RecurringTransactionDetails { get; set; }
public Details? RecurringTransactionDetails { get; set; }
}
public partial class UpdateSummary
{
[JsonPropertyName("charge")]
public UpdateSummaryItem Charge { get; set; }
public UpdateSummaryItem? Charge { get; set; }
[JsonPropertyName("credit")]
public UpdateSummaryItem Credit { get; set; }
public UpdateSummaryItem? Credit { get; set; }
[JsonPropertyName("result")]
public UpdateSummaryItem Result { get; set; }
public UpdateSummaryItem? Result { get; set; }
}
public partial class UpdateSummaryItem

View File

@@ -24,13 +24,13 @@ namespace Streetwriters.Common.Models
public class UserModel
{
[JsonPropertyName("id")]
public string UserId { get; set; }
public required string UserId { get; set; }
[JsonPropertyName("email")]
public string Email { get; set; }
public required string Email { get; set; }
[JsonPropertyName("phoneNumber")]
public string PhoneNumber { get; set; }
public string? PhoneNumber { get; set; }
[JsonPropertyName("isEmailConfirmed")]
public bool IsEmailConfirmed { get; set; }
@@ -39,7 +39,7 @@ namespace Streetwriters.Common.Models
public bool MarketingConsent { get; set; }
[JsonPropertyName("mfa")]
public MFAConfig MFA { get; set; }
public required MFAConfig MFA { get; set; }
}
}

View File

@@ -30,16 +30,16 @@ namespace Streetwriters.Common
{
public class Server
{
public Server(string originCertPath = null, string originCertKeyPath = null)
public Server(string? originCertPath = null, string? originCertKeyPath = null)
{
if (!string.IsNullOrEmpty(originCertPath) && !string.IsNullOrEmpty(originCertKeyPath))
this.SSLCertificate = X509Certificate2.CreateFromPemFile(originCertPath, originCertKeyPath);
}
public string Id { get; set; }
public string? Id { get; set; }
public int Port { get; set; }
public string Hostname { get; set; }
public Uri PublicURL { get; set; }
public X509Certificate2 SSLCertificate { get; }
public required string Hostname { get; set; }
public Uri? PublicURL { get; set; }
public X509Certificate2? SSLCertificate { get; }
public bool IsSecure { get => this.SSLCertificate != null; }
public override string ToString()
@@ -93,14 +93,14 @@ namespace Streetwriters.Common
public static Server NotesnookAPI { get; } = new(Constants.NOTESNOOK_CERT_PATH, Constants.NOTESNOOK_CERT_KEY_PATH)
{
Port = Constants.NOTESNOOK_SERVER_PORT,
Hostname = Constants.NOTESNOOK_SERVER_HOST,
Hostname = Constants.NOTESNOOK_SERVER_HOST ?? "localhost",
Id = "notesnook-sync"
};
public static Server MessengerServer { get; } = new(Constants.SSE_CERT_PATH, Constants.SSE_CERT_KEY_PATH)
{
Port = Constants.SSE_SERVER_PORT,
Hostname = Constants.SSE_SERVER_HOST,
Hostname = Constants.SSE_SERVER_HOST ?? "localhost",
Id = "sse"
};
@@ -108,14 +108,14 @@ namespace Streetwriters.Common
{
PublicURL = Constants.IDENTITY_SERVER_URL,
Port = Constants.IDENTITY_SERVER_PORT,
Hostname = Constants.IDENTITY_SERVER_HOST,
Hostname = Constants.IDENTITY_SERVER_HOST ?? "localhost",
Id = "auth"
};
public static Server SubscriptionServer { get; } = new(Constants.SUBSCRIPTIONS_CERT_PATH, Constants.SUBSCRIPTIONS_CERT_KEY_PATH)
{
Port = Constants.SUBSCRIPTIONS_SERVER_PORT,
Hostname = Constants.SUBSCRIPTIONS_SERVER_HOST,
Hostname = Constants.SUBSCRIPTIONS_SERVER_HOST ?? "localhost",
Id = "subscription"
};
}

View File

@@ -28,8 +28,8 @@ namespace Streetwriters.Common.Services
string email,
EmailTemplate template,
IClient client,
GnuPGContext gpgContext = null,
Dictionary<string, byte[]> attachments = null
GnuPGContext? gpgContext = null,
Dictionary<string, byte[]>? attachments = null
)
{
if (!mailClient.IsConnected)
@@ -78,8 +78,8 @@ namespace Streetwriters.Common.Services
EmailTemplate template,
IClient client,
MailboxAddress sender,
GnuPGContext gpgContext = null,
Dictionary<string, byte[]> attachments = null
GnuPGContext? gpgContext = null,
Dictionary<string, byte[]>? attachments = null
)
{
var builder = new BodyBuilder();

View File

@@ -129,7 +129,7 @@ namespace Streetwriters.Common.Services
public async Task<GetCustomerResponse?> FindCustomerFromTransactionAsync(string transactionId)
{
var transaction = await GetTransactionAsync(transactionId);
if (transaction == null) return null;
if (transaction?.Transaction?.CustomerId == null) return null;
var url = $"{PADDLE_BASE_URI}/customers/{transaction.Transaction.CustomerId}";
var response = await httpClient.GetFromJsonAsync<GetCustomerResponse>(url);
return response;

View File

@@ -18,7 +18,7 @@ namespace Streetwriters.Common.Services
HttpClient httpClient = new HttpClient();
public async Task<ListUsersResponse> ListUsersAsync(
public async Task<ListUsersResponse?> ListUsersAsync(
string subscriptionId,
int results
)
@@ -41,7 +41,7 @@ namespace Streetwriters.Common.Services
return await response.Content.ReadFromJsonAsync<ListUsersResponse>();
}
public async Task<ListPaymentsResponse> ListPaymentsAsync(
public async Task<ListPaymentsResponse?> ListPaymentsAsync(
string subscriptionId,
long planId
)
@@ -66,7 +66,7 @@ namespace Streetwriters.Common.Services
return await response.Content.ReadFromJsonAsync<ListPaymentsResponse>();
}
public async Task<ListTransactionsResponse> ListTransactionsAsync(
public async Task<ListTransactionsResponse?> ListTransactionsAsync(
string subscriptionId
)
{
@@ -86,7 +86,7 @@ namespace Streetwriters.Common.Services
return await response.Content.ReadFromJsonAsync<ListTransactionsResponse>();
}
public async Task<PaddleTransactionUser> FindUserFromOrderAsync(string orderId)
public async Task<PaddleTransactionUser?> FindUserFromOrderAsync(string orderId)
{
var url = $"{PADDLE_BASE_URI}/2.0/order/{orderId}/transactions";
var httpClient = new HttpClient();
@@ -101,7 +101,7 @@ namespace Streetwriters.Common.Services
)
);
var transactions = await response.Content.ReadFromJsonAsync<ListTransactionsResponse>();
if (transactions.Transactions.Length == 0) return null;
if (transactions?.Transactions == null || transactions.Transactions.Length == 0) return null;
return transactions.Transactions[0].User;
}
@@ -123,7 +123,7 @@ namespace Streetwriters.Common.Services
);
var refundResponse = await response.Content.ReadFromJsonAsync<RefundPaymentResponse>();
return refundResponse.Success;
return refundResponse?.Success ?? false;
}
public async Task<bool> CancelSubscriptionAsync(string subscriptionId)

View File

@@ -32,14 +32,14 @@ namespace Streetwriters.Common
{
private readonly ConcurrentDictionary<string, IWampRealmProxy> Channels = new();
public string Endpoint { get; set; }
public string Address { get; set; }
public required string Endpoint { get; set; }
public required string Address { get; set; }
public T Topics { get; set; } = new T();
public string Realm { get; set; }
public required string Realm { get; set; }
private async Task<IWampRealmProxy> GetChannelAsync(string topic)
{
if (!Channels.TryGetValue(topic, out IWampRealmProxy channel) || !channel.Monitor.IsConnected)
if (!Channels.TryGetValue(topic, out IWampRealmProxy? channel) || channel == null || !channel.Monitor.IsConnected)
{
channel = await WampHelper.OpenWampChannelAsync(Address, Realm);
Channels.AddOrUpdate(topic, (key) => channel, (key, old) => channel);