From 31d0af908abeb8c259b77de0a6a0d83a1a972062 Mon Sep 17 00:00:00 2001 From: Gregory Schilsson Date: Thu, 22 Apr 2021 11:44:42 -0400 Subject: [PATCH 1/2] Updated gitignore --- .gitignore | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/.gitignore b/.gitignore index 6704566..29fcc59 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,65 @@ dist # TernJS port file .tern-port + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# SQL Server files +*.mdf +*.ldf From 7b843f91f0e1e764010774ef9f745faabb0edb55 Mon Sep 17 00:00:00 2001 From: Gregory Schilsson Date: Thu, 22 Apr 2021 11:45:44 -0400 Subject: [PATCH 2/2] Sample Console Application written in dotnet core 3.1 --- src/Program.cs | 108 +++++++++++ src/VBrick.ConsoleApp/AppOptions.cs | 48 +++++ src/VBrick.ConsoleApp/Models/VideoReport.cs | 62 ++++++ .../Processing/DateTimeOffsetJsonConverter.cs | 35 ++++ .../Processing/TimeSpanJsonConverter.cs | 35 ++++ src/VBrick.ConsoleApp/Program.cs | 70 +++++++ .../Properties/launchSettings.json | 8 + src/VBrick.ConsoleApp/RecordsProcessor.cs | 179 ++++++++++++++++++ .../VBrick.ConsoleApp.csproj | 31 +++ .../appsettings.StagingTest.json | 19 ++ src/VBrick.ConsoleApp/appsettings.json | 19 ++ src/VBrick.Samples.sln | 45 +++++ src/src.csproj | 8 + 13 files changed, 667 insertions(+) create mode 100644 src/Program.cs create mode 100644 src/VBrick.ConsoleApp/AppOptions.cs create mode 100644 src/VBrick.ConsoleApp/Models/VideoReport.cs create mode 100644 src/VBrick.ConsoleApp/Processing/DateTimeOffsetJsonConverter.cs create mode 100644 src/VBrick.ConsoleApp/Processing/TimeSpanJsonConverter.cs create mode 100644 src/VBrick.ConsoleApp/Program.cs create mode 100644 src/VBrick.ConsoleApp/Properties/launchSettings.json create mode 100644 src/VBrick.ConsoleApp/RecordsProcessor.cs create mode 100644 src/VBrick.ConsoleApp/VBrick.ConsoleApp.csproj create mode 100644 src/VBrick.ConsoleApp/appsettings.StagingTest.json create mode 100644 src/VBrick.ConsoleApp/appsettings.json create mode 100644 src/VBrick.Samples.sln create mode 100644 src/src.csproj diff --git a/src/Program.cs b/src/Program.cs new file mode 100644 index 0000000..856458a --- /dev/null +++ b/src/Program.cs @@ -0,0 +1,108 @@ +using System; + +namespace src +{ + class Program + { + static readonly HttpClient httpClient = new HttpClient(); + + public static async Task Main(string[] args) + { + int returnValue = 0; + using (var host = CreateHostBuilder(args).Build()) + { + host.Services.GetService().Restart(); + using (var scope = host.Services.CreateScope()) + { + await host.StartAsync(); + InitializeMapping(scope); + /* TODO: Code Goes Here */ + RecordsProcessor recordsProcessor = scope.ServiceProvider.GetService(); + recordsProcessor.SetClientProperties(httpClient); + /* TODO: Retry to get token */ + var token = await recordsProcessor.UserLoginAsync(httpClient); + /* TODO: Retry to get results */ + returnValue = await recordsProcessor.ProcessRecords(httpClient, token); + await host.StopAsync(); + } + await Console.Out.WriteLineAsync($"FinishTime: {host.Services.GetService().FinishTime()}"); + } + return returnValue; + } + + internal static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((hostingContext, config) => + { + var switchMapping = new Dictionary + { + { "-g", "GenerateReportOnly" }, + { "--generate-report-only", "GenerateReportOnly" }, + { "-pw", "ApiPassword" }, + { "-api-password", "ApiPassword" } + }; + + config.AddCommandLine(args, switchMapping); + }) + .ConfigureServices((hostContext, services) => + { + /* + * The next four code statements are equivalent to the line + * `services.AddHydrogenApplication>();` + * in a Web Startup.cs file. + * */ + services.Configure(options => + options.AddMaps(new[] { + typeof(AppMappingModule).Assembly, + typeof(OrganizationMappingModule).Assembly + })); + + services.RegisterModule(module: + new LogicDependencyModule() + ); + + /* Organization mapping and module dependencies */ + services.RegisterModule(module: + new OrganizationDependencyModule() + ); + + var configuration = hostContext.Configuration; + services.Configure(configuration); + services.Configure(configuration); + services.Configure(configuration); + services.Configure(configuration); + services.AddOptions(); + + // Register identity + services.AddScoped, IdentityProvider>(); + services.AddScoped, KeyPersonIdentityProvider>(); + services.AddScoped, BasicPersonIdentityProvider>(); + services.AddScoped(); + + // Other dependencies + services.AddScoped(); + services.AddScoped(); + services.AddScoped(s => + { + return new RootDirectoryProvider() + { + Path = Directory.GetCurrentDirectory() + }; + }); + services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); + + services.AddSingleton(); + }); + + internal static void InitializeMapping(IServiceScope scope) + { + var options = scope.ServiceProvider.GetService>(); + Mapper.Initialize(options.Value); + } + } +} diff --git a/src/VBrick.ConsoleApp/AppOptions.cs b/src/VBrick.ConsoleApp/AppOptions.cs new file mode 100644 index 0000000..7680612 --- /dev/null +++ b/src/VBrick.ConsoleApp/AppOptions.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Text; + +namespace VBrick.ConsoleApp +{ + /// + /// Class used with to extract + /// settings from an appsettings.json file. + /// + public class AppOptions + { + /// + /// Base address for the VBrick server + /// + public string ApiBaseAddress { get; set; } + /// + /// VBrick account to use against API + /// + public string ApiUsername { get; set; } + /// + /// VBrick password. + /// This should be passed by console argument. + /// For Visual Studio, this can be added by right-clicking the Console Project, + /// Properties > Debug tab then adding the string -pw "YOUR_PASSWORD" to the arguments. + /// + public string ApiPassword { get; set; } + /// + /// HttpClient timeout, in seconds + /// + public int ApiClientTimeout { get; set; } + /// + /// When hooked up to a database, the limit to which to add records + /// before flushing to the database. + /// + /// For example, Entity Framework Core + /// can add records via a DbSet Add, then call SaveAsync() to flush the + /// results to the database. + /// + public int BulkLimit { get; set; } + /// + /// When hooked up to a database, informs the console application + /// not to write to the database but write instead to the logger. + /// + public bool? GenerateReportOnly { get; set; } + } +} diff --git a/src/VBrick.ConsoleApp/Models/VideoReport.cs b/src/VBrick.ConsoleApp/Models/VideoReport.cs new file mode 100644 index 0000000..4e0bf83 --- /dev/null +++ b/src/VBrick.ConsoleApp/Models/VideoReport.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; +using VBrick.ConsoleApp.Processing; + +namespace VBrick.ConsoleApp.Models +{ + public class VideoReport + { + /// + /// Primary key for storing record into database + /// + [JsonIgnore] + public Guid Id { get; set; } + [JsonPropertyName("videoId")] + public Guid VideoId { get; set; } + [JsonPropertyName("title")] + public string Title { get; set; } + [JsonPropertyName("username")] + public string Username { get; set; } + [JsonPropertyName("firstName")] + public string FirstName { get; set; } + [JsonPropertyName("lastName")] + public string LastName { get; set; } + [JsonPropertyName("emailAddress")] + public string EmailAddress { get; set; } + [JsonIgnore] + public string DepartmentOrSector { get; set; } + [JsonIgnore] + public string Group { get; set; } + [JsonIgnore] + public string Section { get; set; } + [JsonPropertyName("completed")] + public bool Completed { get; set; } + [JsonPropertyName("zone")] + public string Zone { get; set; } + [JsonPropertyName("device")] + public string Device { get; set; } + [JsonPropertyName("browser")] + public string Browser { get; set; } + [JsonPropertyName("userDeviceType")] + public string UserDeviceType { get; set; } + [JsonPropertyName("playbackUrl")] + public string PlaybackUrl { get; set; } + [JsonPropertyName("dateViewed")] + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset DateViewed { get; set; } + [JsonPropertyName("viewingTime")] + [JsonConverter(typeof(TimeSpanJsonConverter))] + public TimeSpan ViewingTime { get; set; } + [JsonPropertyName("viewingStartTime")] + public DateTimeOffset ViewingStartTime { get; set; } + [JsonPropertyName("viewingEndTime")] + public DateTimeOffset ViewingEndTime { get; set; } + + public override string ToString() + { + return String.Format("{{\"Id\":\"{0}\", \"Title\":\"{1}\", \"DateViewed\":\"{2}\"}}", this.Id, this.Title, this.DateViewed.ToString("O")); + } + } +} diff --git a/src/VBrick.ConsoleApp/Processing/DateTimeOffsetJsonConverter.cs b/src/VBrick.ConsoleApp/Processing/DateTimeOffsetJsonConverter.cs new file mode 100644 index 0000000..67f3967 --- /dev/null +++ b/src/VBrick.ConsoleApp/Processing/DateTimeOffsetJsonConverter.cs @@ -0,0 +1,35 @@ +/* + * Reference + * https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-core-3-1#sample-basic-converter + * */ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace VBrick.ConsoleApp.Processing +{ + /// + /// Converts between a DateTimeOffset and a specific date string format. + /// + public class DateTimeOffsetJsonConverter : JsonConverter + { + private const string DATE_FORMAT = "MM/dd/yyyy HH:mm:ss"; + + public override DateTimeOffset Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) => + DateTimeOffset.ParseExact(reader.GetString(), + DATE_FORMAT, CultureInfo.InvariantCulture); + + public override void Write( + Utf8JsonWriter writer, + DateTimeOffset dateTimeValue, + JsonSerializerOptions options) => + writer.WriteStringValue(dateTimeValue.ToString( + DATE_FORMAT, CultureInfo.InvariantCulture)); + } +} diff --git a/src/VBrick.ConsoleApp/Processing/TimeSpanJsonConverter.cs b/src/VBrick.ConsoleApp/Processing/TimeSpanJsonConverter.cs new file mode 100644 index 0000000..e4c348c --- /dev/null +++ b/src/VBrick.ConsoleApp/Processing/TimeSpanJsonConverter.cs @@ -0,0 +1,35 @@ +/* + * Reference + * https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-core-3-1#sample-basic-converter + * */ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace VBrick.ConsoleApp.Processing +{ + /// + /// Converts between a TimeSpan and a specific time string format. + /// + public class TimeSpanJsonConverter : JsonConverter + { + private const string TIME_FORMAT = "c"; + + public override TimeSpan Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) => + TimeSpan.ParseExact(reader.GetString(), + TIME_FORMAT, CultureInfo.InvariantCulture); + + public override void Write( + Utf8JsonWriter writer, + TimeSpan timeSpanValue, + JsonSerializerOptions options) => + writer.WriteStringValue(timeSpanValue.ToString( + TIME_FORMAT, CultureInfo.InvariantCulture)); + } +} diff --git a/src/VBrick.ConsoleApp/Program.cs b/src/VBrick.ConsoleApp/Program.cs new file mode 100644 index 0000000..ab6ea1e --- /dev/null +++ b/src/VBrick.ConsoleApp/Program.cs @@ -0,0 +1,70 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace VBrick.ConsoleApp +{ + class Program + { + public static async Task Main(string[] args) + { + int returnValue = 0; + using (var host = CreateHostBuilder(args).Build()) + { + host.Services.GetService().Start(); + using (var scope = host.Services.CreateScope()) + { + await host.StartAsync(); + RecordsProcessor recordsProcessor = scope.ServiceProvider.GetService(); + /* TODO: Retry to get token */ + var token = await recordsProcessor.UserLoginAsync(); + /* TODO: Retry to get results */ + returnValue = await recordsProcessor.ProcessRecords(token); + await host.StopAsync(); + } + host.Services.GetService().Stop(); + await Console.Out.WriteLineAsync($"Elapsed: {host.Services.GetService().ElapsedMilliseconds} ms"); + } + return returnValue; + } + + internal static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((hostingContext, config) => + { + /* + * Switch Mapping maps command line arguments to an IOptions class. + * */ + var switchMapping = new Dictionary + { + { "-g", "GenerateReportOnly" }, + { "--generate-report-only", "GenerateReportOnly" }, + { "-pw", "ApiPassword" }, + { "-api-password", "ApiPassword" } + }; + + config.AddCommandLine(args, switchMapping); + }) + .ConfigureServices((hostContext, services) => + { + services.Configure(hostContext.Configuration) + .AddOptions(); + + // Other dependencies + services.AddScoped(); + /* Reference: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#how-to-use-typed-clients-with-ihttpclientfactory */ + services.AddHttpClient(client => + { + client.BaseAddress = new Uri(hostContext.Configuration["ApiBaseAddress"]); + client.Timeout = new TimeSpan(0, 0, hostContext.Configuration.GetValue("ApiClientTimeout")); + }); + + services.AddSingleton(); + }); + + } +} diff --git a/src/VBrick.ConsoleApp/Properties/launchSettings.json b/src/VBrick.ConsoleApp/Properties/launchSettings.json new file mode 100644 index 0000000..7967c1b --- /dev/null +++ b/src/VBrick.ConsoleApp/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "VBrick.ConsoleApp": { + "commandName": "Project", + "commandLineArgs": "--environment \"StagingTest\" -pw \"YOUR_PASSWORD_HERE\"" + } + } +} \ No newline at end of file diff --git a/src/VBrick.ConsoleApp/RecordsProcessor.cs b/src/VBrick.ConsoleApp/RecordsProcessor.cs new file mode 100644 index 0000000..254588b --- /dev/null +++ b/src/VBrick.ConsoleApp/RecordsProcessor.cs @@ -0,0 +1,179 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using VBrick.ConsoleApp.Models; + +namespace VBrick.ConsoleApp +{ + class RecordsProcessor + { + private readonly HttpClient httpClient; + private readonly IOptions options; + private readonly ILogger logger; + + public RecordsProcessor(HttpClient httpClient, + IOptions options, + ILogger logger) + { + this.httpClient = httpClient; + this.options = options; + this.logger = logger; + } + + internal async Task ProcessRecords(string token) + { + if (options.Value.GenerateReportOnly.HasValue && + options.Value.GenerateReportOnly.Value) + { + var jsonString = await this.VideosReportAsync(token); + logger?.LogInformation($"{{ \"data\": {jsonString} }}"); + } + else + { + _ = await this.AddVideoReportsAsync(token); + } + /* TODO: try-catch and return something other than 0. */ + return 0; + } + + /// + /// User login code for generating a token for use with other calls. + /// + /// + /// + internal async Task UserLoginAsync() + { + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "/api/v2/user/login"); + + var jsonString = JsonSerializer.Serialize(new { username = options.Value.ApiUsername, password = options.Value.ApiPassword }); + request.Content = new StringContent(jsonString, Encoding.UTF8, "application/json"); + + try + { + HttpResponseMessage response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + + var jsonResponse = await response.Content.ReadAsStringAsync(); + logger?.LogDebug("User login token received."); + var jsonObject = JsonSerializer.Deserialize(jsonResponse); + return jsonObject.token; + } + catch (HttpRequestException ex) + { + logger?.LogError(ex, "Error trying to login."); + return string.Empty; + } + catch (UriFormatException ex) + { + logger?.LogError(ex, "Bad setting: ApiBaseAddress"); + return string.Empty; + } + } + + /// + /// On-screen report for receiving and parsing API data. + /// + internal async Task VideosReportAsync(string token) + { + var utcNow = DateTime.UtcNow; + var thisMonth = new DateTime(utcNow.Year, utcNow.Month, 1); + var lastMonth = thisMonth.AddMonths(-1); + var testUri = string.Format("/api/v2/videos/report?after={0}&before={1}", lastMonth.AddDays(1).ToString("O"), lastMonth.AddDays(2).ToString("O")); + logger?.LogTrace($"testUri: {testUri}"); + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, testUri); + request.Headers.Authorization = new AuthenticationHeaderValue("VBrick", token); + + try + { + HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + response.EnsureSuccessStatusCode(); + logger?.LogDebug("Response received with success status code."); + + await response.Content.LoadIntoBufferAsync(); + + StringBuilder sb = new StringBuilder("["); + using (var stream = new MemoryStream()) + { + await response.Content.CopyToAsync(stream); + foreach (var record in await JsonSerializer.DeserializeAsync>(stream)) + { + sb.Append(JsonSerializer.Serialize(record)); + sb.Append(","); + logger?.LogTrace($"record.ToString(): {record}"); + } + /* Remove trailing comma, then close JSON array */ + sb.Remove(sb.Length - 1, 1); + } + sb.Append("]"); + + return sb.ToString(); + } + catch (HttpRequestException ex) + { + logger?.LogError(ex, "Error: Possibly exceeded timeout or response size limit."); + return string.Empty; + } + catch (UriFormatException ex) + { + logger?.LogError(ex, "Bad setting: ApiBaseAddress"); + return string.Empty; + } + catch (IOException ex) + { + logger?.LogError(ex, "Error retrieving content from response"); + return string.Empty; + } + } + + /// + /// Stub for adding records to database processed from API. + /// + internal async Task AddVideoReportsAsync(string token) + { + //var lastMonth = DateTime.UtcNow.AddMonths(-1); + //var testUri = string.Format("/api/v2/videos/report?after={0}", new DateTime(lastMonth.Year, lastMonth.Month, 1).ToString("O")); + var utcNow = DateTime.UtcNow; + var thisMonth = new DateTime(utcNow.Year, utcNow.Month, 1); + var lastMonth = thisMonth.AddMonths(-1); + var testUri = string.Format("/api/v2/videos/report?after={0}&before={1}", lastMonth.ToString("O"), thisMonth.ToString("O")); + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, testUri); + request.Headers.Authorization = new AuthenticationHeaderValue("VBrick", token); + + try + { + HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + response.EnsureSuccessStatusCode(); + + var bulkLimit = options.Value.BulkLimit; + foreach (var record in await JsonSerializer.DeserializeAsync>(await response.Content.ReadAsStreamAsync())) + { + logger?.LogTrace($"record.ToString(): {record}"); + /* Logic for bulk adding to database */ + } + return 0; + } + catch (HttpRequestException ex) + { + logger?.LogError(ex, "Error trying to login."); + return 1; + } + catch (UriFormatException ex) + { + logger?.LogError(ex, "Bad setting: ApiBaseAddress"); + return 1; + } + } + + private class SimpleToken + { + public string token { get; set; } + } + } +} diff --git a/src/VBrick.ConsoleApp/VBrick.ConsoleApp.csproj b/src/VBrick.ConsoleApp/VBrick.ConsoleApp.csproj new file mode 100644 index 0000000..3f4a06a --- /dev/null +++ b/src/VBrick.ConsoleApp/VBrick.ConsoleApp.csproj @@ -0,0 +1,31 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + + + + diff --git a/src/VBrick.ConsoleApp/appsettings.StagingTest.json b/src/VBrick.ConsoleApp/appsettings.StagingTest.json new file mode 100644 index 0000000..5991606 --- /dev/null +++ b/src/VBrick.ConsoleApp/appsettings.StagingTest.json @@ -0,0 +1,19 @@ +{ + "exclude": [ + "**/bin", + "**/obj" + ], + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "LabCastMetrics.ConsoleApp.RecordsProcessor": "Debug" + } + }, + "ApiBaseAddress": "https://YOUR.SERVER.HERE.com/", + "ApiUsername": "svc-YOUR-SERVICE-ACOUNT-api", + "ApiClientTimeout": 660, + "BulkLimit": 1000, + "GenerateReportOnly": true +} diff --git a/src/VBrick.ConsoleApp/appsettings.json b/src/VBrick.ConsoleApp/appsettings.json new file mode 100644 index 0000000..aa45080 --- /dev/null +++ b/src/VBrick.ConsoleApp/appsettings.json @@ -0,0 +1,19 @@ +{ + "exclude": [ + "**/bin", + "**/obj" + ], + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "LabCastMetrics.ConsoleApp.RecordsProcessor": "Debug" + } + }, + "ApiBaseAddress": "https://vbrick.yourdomain.com/", + "ApiUsername": "svc-vbrick-api", + "ApiClientTimeout": 660, + "BulkLimit": 1000, + "GenerateReportOnly": true +} diff --git a/src/VBrick.Samples.sln b/src/VBrick.Samples.sln new file mode 100644 index 0000000..856de0c --- /dev/null +++ b/src/VBrick.Samples.sln @@ -0,0 +1,45 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.6.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VBrick.ConsoleApp", "VBrick.ConsoleApp\VBrick.ConsoleApp.csproj", "{8115EF8C-7B2D-4829-A129-7ED983CE32C0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Repository Items", "Repository Items", "{2C13CA35-04B2-4725-807D-8EB7DF57B87D}" + ProjectSection(SolutionItems) = preProject + ..\.gitignore = ..\.gitignore + ..\LICENSE = ..\LICENSE + ..\README.md = ..\README.md + ..\vbrick_postman_collection.json = ..\vbrick_postman_collection.json + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8115EF8C-7B2D-4829-A129-7ED983CE32C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8115EF8C-7B2D-4829-A129-7ED983CE32C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8115EF8C-7B2D-4829-A129-7ED983CE32C0}.Debug|x64.ActiveCfg = Debug|Any CPU + {8115EF8C-7B2D-4829-A129-7ED983CE32C0}.Debug|x64.Build.0 = Debug|Any CPU + {8115EF8C-7B2D-4829-A129-7ED983CE32C0}.Debug|x86.ActiveCfg = Debug|Any CPU + {8115EF8C-7B2D-4829-A129-7ED983CE32C0}.Debug|x86.Build.0 = Debug|Any CPU + {8115EF8C-7B2D-4829-A129-7ED983CE32C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8115EF8C-7B2D-4829-A129-7ED983CE32C0}.Release|Any CPU.Build.0 = Release|Any CPU + {8115EF8C-7B2D-4829-A129-7ED983CE32C0}.Release|x64.ActiveCfg = Release|Any CPU + {8115EF8C-7B2D-4829-A129-7ED983CE32C0}.Release|x64.Build.0 = Release|Any CPU + {8115EF8C-7B2D-4829-A129-7ED983CE32C0}.Release|x86.ActiveCfg = Release|Any CPU + {8115EF8C-7B2D-4829-A129-7ED983CE32C0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6D70C701-ECF3-46DC-A0CB-163965080653} + EndGlobalSection +EndGlobal diff --git a/src/src.csproj b/src/src.csproj new file mode 100644 index 0000000..c73e0d1 --- /dev/null +++ b/src/src.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp3.1 + + +