diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2d15e48 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,20 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.vs +**/.vscode +**/*.*proj.user +**/azds.yaml +**/charts +**/bin +**/obj +**/Dockerfile +**/Dockerfile.develop +**/docker-compose.yml +**/docker-compose.*.yml +**/*.dbmdl +**/*.jfm +**/secrets.dev.yaml +**/values.dev.yaml +**/.toolstarget \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore index 3e759b7..4ce6fdd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files +*.rsuser *.suo *.user *.userosscache @@ -19,6 +20,8 @@ [Rr]eleases/ x64/ x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ @@ -52,7 +55,6 @@ BenchmarkDotNet.Artifacts/ project.lock.json project.fragment.lock.json artifacts/ -**/Properties/launchSettings.json # StyleCop StyleCopReport.xml @@ -60,7 +62,7 @@ StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c -*_i.h +*_h.h *.ilk *.meta *.obj @@ -77,6 +79,7 @@ StyleCopReport.xml *.tlh *.tmp *.tmp_proj +*_wpftmp.csproj *.log *.vspscc *.vssscc @@ -208,7 +211,7 @@ _pkginfo.txt # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache -!*.[Cc]ache/ +!?*.[Cc]ache/ # Others ClientBin/ @@ -221,7 +224,7 @@ ClientBin/ *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk +# Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk @@ -252,6 +255,7 @@ ServiceFabricBackup/ *.bim.layout *.bim_*.settings *.rptproj.rsuser +*- Backup*.rdl # Microsoft Fakes FakesAssemblies/ @@ -291,8 +295,8 @@ paket-files/ .idea/ *.sln.iml -# CodeRush -.cr/ +# CodeRush personal settings +.cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ @@ -317,7 +321,7 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ -# Azure Stream Analytics local run output +# Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log @@ -326,5 +330,11 @@ ASALocalRun/ # NVidia Nsight GPU debugger configuration file *.nvuser -# MFractors (Xamarin productivity tool) working folder +# MFractors (Xamarin productivity tool) working folder .mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb \ No newline at end of file diff --git a/DomainModel/HealthChecks.Configuration/HealthChecks.Configuration.csproj b/DomainModel/HealthChecks.Configuration/HealthChecks.Configuration.csproj new file mode 100644 index 0000000..8d5269c --- /dev/null +++ b/DomainModel/HealthChecks.Configuration/HealthChecks.Configuration.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.2 + + + + + + + + + + + + + + + + diff --git a/DomainModel/HealthChecks.Configuration/IdentityConfiguration.cs b/DomainModel/HealthChecks.Configuration/IdentityConfiguration.cs new file mode 100644 index 0000000..e91c961 --- /dev/null +++ b/DomainModel/HealthChecks.Configuration/IdentityConfiguration.cs @@ -0,0 +1,15 @@ +namespace HealthChecks.Configuration +{ + /// + /// Represents the identity and Access Control Configuration + /// + public class IdentityConfiguration + { + public bool Enabled { get; set; } + public string ServerUrl { get; set; } + public string Audience { get; set; } + public bool RequireHttpsMetadata { get; set; } + + public string TransportStorageDirectory { get; set; } + } +} diff --git a/DomainModel/HealthChecks.Configuration/LocalStackConfiguration.cs b/DomainModel/HealthChecks.Configuration/LocalStackConfiguration.cs new file mode 100644 index 0000000..4c9d870 --- /dev/null +++ b/DomainModel/HealthChecks.Configuration/LocalStackConfiguration.cs @@ -0,0 +1,7 @@ +namespace HealthChecks.Configuration +{ + public class LocalStackConfiguration + { + public string SqsUrl { get; set; } + } +} \ No newline at end of file diff --git a/DomainModel/HealthChecks.Configuration/Logging/LoggingExtensions.cs b/DomainModel/HealthChecks.Configuration/Logging/LoggingExtensions.cs new file mode 100644 index 0000000..24219b6 --- /dev/null +++ b/DomainModel/HealthChecks.Configuration/Logging/LoggingExtensions.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace HealthChecks.Configuration.Logging +{ + /// + /// Extension methods to log in our standard JSON format + /// + public static class LoggingExtensions + { + /// + /// Log a message in JSON format + /// + /// + /// Message to log + /// Data to log with message + /// Log with date/level or raw data + public static void LogInformationJson(this ILogger logger, string message, dynamic data = null, bool withMetaData = true) + { + var formattedMessage = GetFormattedMessage( + logger, + LogLevel.Information, + withMetaData, + message, + data + ); + + if(formattedMessage != null) + { + logger.LogInformation(message = formattedMessage); + } + } + + /// + /// Log a message in JSON format + /// + /// + /// Message to log + /// Data to log with message + /// Log with date/level or raw data + public static void LogDebugJson(this ILogger logger, string message, object data = null, bool withMetaData = true) + { + var formattedMessage = GetFormattedMessage( + logger, + LogLevel.Debug, + withMetaData, + message, + data + ); + + if (formattedMessage != null) + { + logger.LogDebug(message = formattedMessage); + } + } + + /// + /// Log a message in JSON format + /// + /// + /// Message to log + /// Data to log with message + /// Log with date/level or raw data + public static void LogWarningJson(this ILogger logger, string message, dynamic data = null, bool withMetaData = true) + { + var formattedMessage = GetFormattedMessage( + logger, + LogLevel.Warning, + withMetaData, + message, + data + ); + + if (formattedMessage != null) + { + logger.LogWarning(message = formattedMessage); + } + } + + /// + /// Log a message in JSON format + /// + /// + /// Message to log + /// Data to log with message + /// Log with date/level or raw data + public static void LogTraceJson(this ILogger logger, string message, dynamic data = null, bool withMetaData = true) + { + var formattedMessage = GetFormattedMessage( + logger, + LogLevel.Trace, + withMetaData, + message, + data + ); + + if (formattedMessage != null) + { + logger.LogTrace(message = formattedMessage); + } + } + + /// + /// Log a message in JSON format + /// + /// + /// Message to log + /// Data to log with message + /// Log with date/level or raw data + public static void LogCriticalJson(this ILogger logger, string message, dynamic data = null, bool withMetaData = true) + { + var formattedMessage = GetFormattedMessage( + logger, + LogLevel.Critical, + withMetaData, + message, + data + ); + + if (formattedMessage != null) + { + logger.LogCritical(message = formattedMessage); + } + } + + /// + /// Log a message in JSON format + /// + /// + /// Message to log + /// Data to log with message + /// Log with date/level or raw data + public static void LogErrorJson(this ILogger logger, string message, dynamic data = null, bool withMetaData = true) + { + var formattedMessage = GetFormattedMessage( + logger, + LogLevel.Error, + withMetaData, + message, + data + ); + + if (formattedMessage != null) + { + logger.LogError(message = formattedMessage); + } + } + + private static string GetFormattedMessage( + ILogger logger, + LogLevel level, + bool withMetaData, + string message, + dynamic data = null) + { + if (withMetaData) + { + data = new + { + LongDate = DateTime.Now, + LongDateUtc = DateTime.UtcNow, + Level = level.ToString(), + Message = message, + Data = data + }; + } + else + { + data = new + { + Message = message, + Data = data + }; + } + + return SerializeMessage(logger, data); + } + + private static string SerializeMessage(ILogger logger, dynamic message) + { + var serializationErrors = new List(); + + var serializedMessage = JsonConvert.SerializeObject( + message, + new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + StringEscapeHandling = StringEscapeHandling.EscapeNonAscii, + Error = delegate (object sender, ErrorEventArgs args) + { + serializationErrors.Add(args.ErrorContext.Error.Message); + // Mark the serialization error as handled so it does not throw an exception up the call stack + args.ErrorContext.Handled = true; + } + }); + + if (serializationErrors.Count > 0) + { + logger.LogWarning(JsonConvert.SerializeObject(serializationErrors)); + } + + return serializedMessage; + } + } +} diff --git a/DomainModel/HealthChecks.ServiceBus/Base/SqsListener.cs b/DomainModel/HealthChecks.ServiceBus/Base/SqsListener.cs new file mode 100644 index 0000000..080ce79 --- /dev/null +++ b/DomainModel/HealthChecks.ServiceBus/Base/SqsListener.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Amazon.SQS; +using Amazon.SQS.Model; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; + +namespace HealthChecks.ServiceBus.Base +{ + /// + /// The SqsListener represents a long-running service in your API which is + /// SQS queue aware. It will listen to messages of type T which are routed on the queue + /// Remember that this contract is OWNED by the API, and should not be a shared contract between the + /// recipient and sender, as this leads to tight-coupling. + /// + /// The type of class to receive from your queue + public abstract class SqsListener : IHostedService, IDisposable where T: class + { + private readonly string _queueName; + protected readonly ILogger> Logger; + private readonly IAmazonSQS _sqsClient; + private GetQueueUrlResponse _queueDetails; + + public bool Shutdown { get; set; } + public bool Started { get; set; } + + public abstract Task HandleMessageAsync( + T constructedMessage); + + protected SqsListener( + string queueName, + IAmazonSQS sqsClient, + ILogger> logger) + { + Logger = logger; + _queueName = queueName; + _sqsClient = sqsClient; + } + + #region Implementation of IHostedService + + public Task StartAsync(CancellationToken cancellationToken) + { + return Task.Run(StartListeningAsync, cancellationToken); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + Shutdown = true; + return Task.CompletedTask; + } + + #endregion + + protected void GetQueueDetailsFromAws() + { + Logger.LogDebug("Getting SQS queue URL..."); + _queueDetails = _sqsClient.GetQueueUrlAsync(_queueName).Result; + Logger.LogDebug($"SQS queue URL retrieved: {_queueDetails.QueueUrl}"); + } + + public async Task StartListeningAsync() + { + Logger.LogDebug("Starting SQS listener..."); + + GetQueueDetailsFromAws(); + + var request = new ReceiveMessageRequest + { + AttributeNames = new List { "All" }, + MaxNumberOfMessages = 5, + QueueUrl = _queueDetails.QueueUrl, + VisibilityTimeout = (int)TimeSpan.FromMinutes(2).TotalSeconds, + WaitTimeSeconds = (int)TimeSpan.FromSeconds(20).TotalSeconds + }; + + Started = true; + + while (!Shutdown) + { + Logger.LogDebug("Checking for new SQS messages..."); + var response = _sqsClient.ReceiveMessageAsync(request).Result; + + if (response.Messages.Count > 0) + { + Logger.LogDebug($"Received {response.Messages.Count} messages from SQS"); + foreach (var message in response.Messages) + { + Logger.LogDebug($"Processing message {message.MessageId}"); + Logger.LogDebug($"Message contents: {message.Body.Replace("{", "{{").Replace("}", "}}")}"); + + T constructedMessage; + try + { + Logger.LogDebug($"Deserialising message body..."); + constructedMessage = JsonConvert.DeserializeObject(message.Body); + Logger.LogDebug($"Deserialised message {message.MessageId} successfully."); + Logger.LogDebug("Sanity check re-serialised:"); + Logger.LogDebug(JsonConvert.SerializeObject(constructedMessage).Replace("{", "{{").Replace("}", "}}")); + } + catch (JsonReaderException ex) + { + Logger.LogError(ex, "Unknown message type."); + + // Bad message, delete it from the queue. + Logger.LogError($"Deleting bad message from queue with ID {message.MessageId}"); + Logger.LogError("Message body:"); + Logger.LogError(message.Body); + var deleteMessageRequest = new DeleteMessageRequest(_queueDetails.QueueUrl, message.ReceiptHandle); + _sqsClient.DeleteMessageAsync(deleteMessageRequest).Wait(); + + continue; + } + + try + { + await HandleMessageAsync(constructedMessage); + // Message handled, delete it from the queue. + var deleteMessageRequest = new DeleteMessageRequest(_queueDetails.QueueUrl, message.ReceiptHandle); + await _sqsClient.DeleteMessageAsync(deleteMessageRequest); + } + catch (Exception ex) + { + Logger.LogDebug($"Processing of message failed. Release message {message.MessageId} back to queue."); + Logger.LogDebug($"Error was: {ex.Message}"); + Logger.LogDebug(ex.StackTrace); + + var changeMessageRequest = new ChangeMessageVisibilityRequest(_queueDetails.QueueUrl, message.ReceiptHandle, 0); + await _sqsClient.ChangeMessageVisibilityAsync(changeMessageRequest); + } + } + } + else + { + Logger.LogDebug("No sqs messages received."); + } + } + + Logger.LogDebug("Shutting down"); + Started = false; + + await Task.CompletedTask; + } + + public void Dispose() + { + _sqsClient?.Dispose(); + } + } +} diff --git a/DomainModel/HealthChecks.ServiceBus/Base/SqsSender.cs b/DomainModel/HealthChecks.ServiceBus/Base/SqsSender.cs new file mode 100644 index 0000000..9786572 --- /dev/null +++ b/DomainModel/HealthChecks.ServiceBus/Base/SqsSender.cs @@ -0,0 +1,59 @@ +using System; +using System.Threading.Tasks; +using Amazon.SQS; +using Amazon.SQS.Model; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; + +namespace HealthChecks.ServiceBus.Base +{ + /// + /// A sender of messages to an SQS queue of type T. + /// + /// The type of class to send to your queue + public abstract class SqsSender + where T : class + { + private readonly string _queueName; + private readonly ILogger> _logger; + private readonly IAmazonSQS _sqsClient; + private GetQueueUrlResponse _queueDetails; + + protected SqsSender( + string queueName, + IAmazonSQS amazonSqs, + ILogger> logger) + { + _queueName = queueName; + _sqsClient = amazonSqs; + _logger = logger; + GetQueueDetailsFromAwsAsync().Wait(); + } + + protected async Task GetQueueDetailsFromAwsAsync() + { + try { + _logger.LogDebug("Getting SQS queue URL..."); + _queueDetails = await _sqsClient.GetQueueUrlAsync(_queueName); + _logger.LogDebug($"SQS queue URL retrieved: {_queueDetails.QueueUrl}"); + } catch(Exception ex) + { + throw ex; + } + } + + + public async Task Send(T message) + { + var messageBody = JsonConvert.SerializeObject(message); + _logger.LogDebug($"Sending SQS message: {messageBody} on queue {_queueDetails.QueueUrl}"); + var sendMessageRequest = new SendMessageRequest + { + MessageBody = messageBody, + QueueUrl = _queueDetails.QueueUrl + }; + + await _sqsClient.SendMessageAsync(sendMessageRequest); + } + } +} \ No newline at end of file diff --git a/DomainModel/HealthChecks.ServiceBus/HealthChecks.ServiceBus.csproj b/DomainModel/HealthChecks.ServiceBus/HealthChecks.ServiceBus.csproj new file mode 100644 index 0000000..b58dab5 --- /dev/null +++ b/DomainModel/HealthChecks.ServiceBus/HealthChecks.ServiceBus.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + + + + + + + + + + diff --git a/HealthChecks.SomeModelService/AppServices/SomeModel/ISomeModelApplicationService.cs b/HealthChecks.SomeModelService/AppServices/SomeModel/ISomeModelApplicationService.cs new file mode 100644 index 0000000..40c7c16 --- /dev/null +++ b/HealthChecks.SomeModelService/AppServices/SomeModel/ISomeModelApplicationService.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using HealthChecks.SomeModelService.Contracts.Models.SomeModel; + +namespace HealthChecks.SomeModelService.AppServices.SomeModel +{ + public interface ISomeModelApplicationService + { + Task CreateAsync(SomeModelContract model); + + Task UpdateAsync( + SomeModelContract model); + + Task GetAsync(long id); + + Task> ListAllAsync(); + + Task DeleteAsync(long id); + + } +} \ No newline at end of file diff --git a/HealthChecks.SomeModelService/AppServices/SomeModel/SomeModelApplicationService.cs b/HealthChecks.SomeModelService/AppServices/SomeModel/SomeModelApplicationService.cs new file mode 100644 index 0000000..a1fcd2f --- /dev/null +++ b/HealthChecks.SomeModelService/AppServices/SomeModel/SomeModelApplicationService.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using HealthChecks.SomeModelService.Contracts.Models.SomeModel; +using HealthChecks.SomeModelService.Repositories.SomeModel; +using Microsoft.Extensions.Logging; +using Nelibur.ObjectMapper; +using Newtonsoft.Json; +using SomeModelModel = HealthChecks.SomeModelService.Models.SomeModel.SomeModel; + +namespace HealthChecks.SomeModelService.AppServices.SomeModel +{ + public class SomeModelApplicationService : ISomeModelApplicationService + { + private readonly ILogger _logger; + + private readonly ISomeModelRepository _somemodelRepository; + + + public SomeModelApplicationService( + ILogger logger, + ISomeModelRepository somemodelRepository) + { + _logger = logger; + _somemodelRepository = somemodelRepository; + } + + public async Task CreateAsync(SomeModelContract model) + { + var mappedData = CastToDomainModel(model); + var returnedData = await _somemodelRepository.CreateAsync(mappedData); + return CastToContract(returnedData); + } + + public async Task UpdateAsync( + SomeModelContract model) + { + _logger.LogTrace( + $"Updating a SomeModel with Id {model.Id} based on the command data sent: " + + $"{JsonConvert.SerializeObject(model)}"); + + var storedModel = _somemodelRepository.GetAsync(model.Id); + if (storedModel == null) + { + _logger.LogWarning($"No such SomeModel model with Id {model.Id} found. Doing nothing."); + return model; + } + + var result = await _somemodelRepository.UpdateAsync(CastToDomainModel(model)); + + return CastToContract(result); + } + + public async Task GetAsync(long id) + { + _logger.LogDebug($"Retrieving SomeModel with Id of {id}"); + var somemodelFound = await _somemodelRepository.GetAsync(id); + if(somemodelFound != null) + { + _logger.LogDebug($"No SomeModel with Id of {id} found. Returning null"); + } + + return CastToContract(somemodelFound); + } + + public async Task> ListAllAsync() + { + var results = await _somemodelRepository.ListAllAsync(); + return await Task.FromResult(CastAllToContracts(results)); + } + + public async Task DeleteAsync(long id) + { + _logger.LogDebug($"Deleting SomeModel with Id of {id}"); + await _somemodelRepository.DeleteAsync(id); + } + + public static IEnumerable CastAllToContracts( + IEnumerable models) + { + return models.Select(CastToContract).ToList(); + } + + public static SomeModelContract CastToContract( + SomeModelModel model) + { + TinyMapper.Bind(); + TinyMapper.Bind(); + return TinyMapper.Map(model); + } + + public static SomeModelModel CastToDomainModel( + SomeModelContract model) + { + TinyMapper.Bind(); + TinyMapper.Bind(); + return TinyMapper.Map(model); + } + } +} \ No newline at end of file diff --git a/HealthChecks.SomeModelService/Contracts/Models/SomeModel/SomeModelContract.cs b/HealthChecks.SomeModelService/Contracts/Models/SomeModel/SomeModelContract.cs new file mode 100644 index 0000000..8c34cff --- /dev/null +++ b/HealthChecks.SomeModelService/Contracts/Models/SomeModel/SomeModelContract.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace HealthChecks.SomeModelService.Contracts.Models.SomeModel +{ + public class SomeModelContract + { + [Required] + public long Id { get; set; } + } +} diff --git a/HealthChecks.SomeModelService/Controllers/SomeModelController.cs b/HealthChecks.SomeModelService/Controllers/SomeModelController.cs new file mode 100644 index 0000000..0d68ec6 --- /dev/null +++ b/HealthChecks.SomeModelService/Controllers/SomeModelController.cs @@ -0,0 +1,100 @@ +using System.Threading.Tasks; +using HealthChecks.Configuration.Logging; +using HealthChecks.SomeModelService.AppServices.SomeModel; +using HealthChecks.SomeModelService.Contracts.Models.SomeModel; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace HealthChecks.SomeModelService.Controllers +{ + [Route("somemodels")] + [ApiController] + public class SomeModelController : ControllerBase + { + private readonly ISomeModelApplicationService _somemodelApplicationService; + + private readonly ILogger _logger; + + public SomeModelController( + ISomeModelApplicationService somemodelApplicationService, + ILogger logger) + { + _somemodelApplicationService = somemodelApplicationService; + _logger = logger; + } + + + [HttpPost] + public async Task CreateAsync(SomeModelContract model) + { + _logger.LogTraceJson("Starting SomeModel create", model); + if (!ModelState.IsValid) + { + _logger.LogErrorJson( + "SomeModel model is not valid", + model); + + return BadRequest(ModelState); + } + + var newSomeModel = await _somemodelApplicationService.CreateAsync(model); + _logger.LogTraceJson("Completing SomeModel create"); + return Ok(newSomeModel); + } + + [HttpGet] + public async Task ListAllAsync() + { + _logger.LogTraceJson("ListAll SomeModels"); + var SomeModels = await _somemodelApplicationService.ListAllAsync(); + _logger.LogTraceJson("Completing ListAll SomeModels", SomeModels); + return Ok(SomeModels); + } + + [HttpGet] + [Route("{id}")] + public async Task GetAsync([FromRoute]long id) + { + _logger.LogTraceJson($"Starting Get SomeModel: {id}"); + var SomeModel = await _somemodelApplicationService.GetAsync(id); + + if (SomeModel == null) + { + _logger.LogTraceJson($"SomeModel not found: {id}"); + return NotFound(); + } + _logger.LogTraceJson($"Completing Get SomeModel: {id}"); + return Ok(SomeModel); + } + + + [HttpPut] + public async Task UpdateAsync( + [FromBody] SomeModelContract model) + { + _logger.LogTraceJson("Starting SomeModel update", model); + if (!ModelState.IsValid) + { + _logger.LogErrorJson( + "SomeModel model is not valid", + model); + + return BadRequest(ModelState); + } + + var updatedSomeModel = await _somemodelApplicationService.UpdateAsync(model); + _logger.LogTraceJson("Completing SomeModel update"); + return Ok(updatedSomeModel); + } + + [HttpDelete] + [Route("{id}")] + public async Task DeleteAsync(long id) + { + _logger.LogTraceJson($"Starting deletion of SomeModel: {id}"); + await _somemodelApplicationService.DeleteAsync(id); + _logger.LogTraceJson($"Completing deletion of SomeModel: {id}"); + return Ok(); + } + } +} \ No newline at end of file diff --git a/HealthChecks.SomeModelService/DependencyModule.cs b/HealthChecks.SomeModelService/DependencyModule.cs new file mode 100644 index 0000000..3d14393 --- /dev/null +++ b/HealthChecks.SomeModelService/DependencyModule.cs @@ -0,0 +1,16 @@ +using Autofac; +using HealthChecks.SomeModelService.AppServices.SomeModel; +using HealthChecks.SomeModelService.Repositories.SomeModel; + +namespace HealthChecks.SomeModelService +{ + public class DependencyModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As(); + builder.RegisterType().As(); + + } + } +} diff --git a/HealthChecks.SomeModelService/Dockerfile b/HealthChecks.SomeModelService/Dockerfile new file mode 100644 index 0000000..d6a034c --- /dev/null +++ b/HealthChecks.SomeModelService/Dockerfile @@ -0,0 +1,19 @@ +FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base +WORKDIR /app +EXPOSE 54411 + +FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build +WORKDIR /src +COPY ["HealthChecks.SomeModelService/HealthChecks.SomeModelService.csproj", "HealthChecks.SomeModelService/"] +RUN dotnet restore "HealthChecks.SomeModelService/HealthChecks.SomeModelService.csproj" +COPY . . +WORKDIR "/src/HealthChecks.SomeModelService" +RUN dotnet build "HealthChecks.SomeModelService.csproj" -c Release -o /app + +FROM build AS publish +RUN dotnet publish "HealthChecks.SomeModelService.csproj" -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "HealthChecks.SomeModelService.dll"] diff --git a/HealthChecks.SomeModelService/Health/ApiHealthCheck.cs b/HealthChecks.SomeModelService/Health/ApiHealthCheck.cs new file mode 100644 index 0000000..b6eb77f --- /dev/null +++ b/HealthChecks.SomeModelService/Health/ApiHealthCheck.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace HealthChecks.SomeModelService.Health +{ + public class ApiHealthCheck : IHealthCheck + { + public Task CheckHealthAsync( + HealthCheckContext context, + CancellationToken cancellationToken = new CancellationToken()) + { + //TODO: Implement your own healthcheck logic here + var isHealthy = true; + if(isHealthy) + { + return Task.FromResult(HealthCheckResult.Healthy("healthy")); + } + + return Task.FromResult(HealthCheckResult.Unhealthy("unhealthy")); + } + } +} \ No newline at end of file diff --git a/HealthChecks.SomeModelService/HealthChecks.SomeModelService.csproj b/HealthChecks.SomeModelService/HealthChecks.SomeModelService.csproj new file mode 100644 index 0000000..82e1a33 --- /dev/null +++ b/HealthChecks.SomeModelService/HealthChecks.SomeModelService.csproj @@ -0,0 +1,46 @@ + + + + netcoreapp2.2 + InProcess + Linux + ..\docker-compose.dcproj + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/HealthChecks.SomeModelService/Models/SomeModel/ISomeModel.cs b/HealthChecks.SomeModelService/Models/SomeModel/ISomeModel.cs new file mode 100644 index 0000000..66aab5b --- /dev/null +++ b/HealthChecks.SomeModelService/Models/SomeModel/ISomeModel.cs @@ -0,0 +1,7 @@ +namespace HealthChecks.SomeModelService.Models.SomeModel +{ + public interface ISomeModel + { + long Id { get; set; } + } +} \ No newline at end of file diff --git a/HealthChecks.SomeModelService/Models/SomeModel/SomeModel.cs b/HealthChecks.SomeModelService/Models/SomeModel/SomeModel.cs new file mode 100644 index 0000000..dbea1e3 --- /dev/null +++ b/HealthChecks.SomeModelService/Models/SomeModel/SomeModel.cs @@ -0,0 +1,7 @@ +namespace HealthChecks.SomeModelService.Models.SomeModel +{ + public class SomeModel : ISomeModel + { + public long Id { get; set; } + } +} \ No newline at end of file diff --git a/HealthChecks.SomeModelService/NLog.Release.config b/HealthChecks.SomeModelService/NLog.Release.config new file mode 100644 index 0000000..df7ec2e --- /dev/null +++ b/HealthChecks.SomeModelService/NLog.Release.config @@ -0,0 +1,70 @@ + + + + + + + #{if Log.File.Enable} + + + + + + + + + + + + + + + + + + + + + + #{/if} + + #{if Log.EventLog.Enable} + + + #{/if} + + + + + + #{if Log.File.Enable} + + + + + + + #{/if} + + #{if Log.EventLog.Enable} + + #{/if} + + \ No newline at end of file diff --git a/HealthChecks.SomeModelService/NLog.config b/HealthChecks.SomeModelService/NLog.config new file mode 100644 index 0000000..f0956fe --- /dev/null +++ b/HealthChecks.SomeModelService/NLog.config @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/HealthChecks.SomeModelService/Program.cs b/HealthChecks.SomeModelService/Program.cs new file mode 100644 index 0000000..db0457e --- /dev/null +++ b/HealthChecks.SomeModelService/Program.cs @@ -0,0 +1,61 @@ +using System; +using System.IO; +using Autofac.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using NLog.Web; + +namespace HealthChecks.SomeModelService +{ + public class Program + { + public static void Main(string[] args) + { + var logger = NLogBuilder.ConfigureNLog("NLog.config").GetCurrentClassLogger(); + try + { + logger.Debug("inititalising Microservice.Restful"); + var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); + + var host = new WebHostBuilder() + .UseKestrel() + .ConfigureServices(services => services.AddAutofac()) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(Directory.GetCurrentDirectory()); + config.AddJsonFile("appsettings.json",true,reloadOnChange: true); + config.AddJsonFile($"appsettings.{environmentName}.json",optional: true, reloadOnChange:true); + }) + .ConfigureLogging((hostingContext, logging) => + { + logging.ClearProviders(); + logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); + logging.AddConsole(); + logging.AddDebug(); + logging.SetMinimumLevel(LogLevel.Trace); + }) + .UseNLog() + .UseStartup() + .Build(); + + host.Run(); + } + catch (Exception ex) + { + //NLog: catch setup errors + logger.Error(ex, "Stopped program because of exception"); + throw; + } + finally + { + // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux) + NLog.LogManager.Shutdown(); + } + + } + + } +} diff --git a/HealthChecks.SomeModelService/Properties/launchSettings.json b/HealthChecks.SomeModelService/Properties/launchSettings.json new file mode 100644 index 0000000..a3fadd4 --- /dev/null +++ b/HealthChecks.SomeModelService/Properties/launchSettings.json @@ -0,0 +1,35 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:50939", + "sslPort": 0 + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "SomeModels", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "HealthChecks.ExampleCrud.Api": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "SomeModels", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:5000" + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/SomeModels" + } + } +} \ No newline at end of file diff --git a/HealthChecks.SomeModelService/Repositories/SomeModel/ISomeModelRepository.cs b/HealthChecks.SomeModelService/Repositories/SomeModel/ISomeModelRepository.cs new file mode 100644 index 0000000..121a0ff --- /dev/null +++ b/HealthChecks.SomeModelService/Repositories/SomeModel/ISomeModelRepository.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using SomeModelModel = HealthChecks.SomeModelService.Models.SomeModel.SomeModel; + +namespace HealthChecks.SomeModelService.Repositories.SomeModel +{ + public interface ISomeModelRepository + { + Task CreateAsync(SomeModelModel model); + + Task UpdateAsync( + SomeModelModel model); + + Task GetAsync(long id); + + Task> ListAllAsync(); + + Task DeleteAsync(long id); + + } +} \ No newline at end of file diff --git a/HealthChecks.SomeModelService/Repositories/SomeModel/SomeModelRepository.cs b/HealthChecks.SomeModelService/Repositories/SomeModel/SomeModelRepository.cs new file mode 100644 index 0000000..c162f0a --- /dev/null +++ b/HealthChecks.SomeModelService/Repositories/SomeModel/SomeModelRepository.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using SomeModelModel = HealthChecks.SomeModelService.Models.SomeModel.SomeModel; + +namespace HealthChecks.SomeModelService.Repositories.SomeModel +{ + public class SomeModelRepository : ISomeModelRepository + { + private static readonly List SomeModels = new List(); + + private readonly ILogger _logger; + + public SomeModelRepository( + ILogger logger) + { + _logger = logger; + } + + public async Task CreateAsync(SomeModelModel model) + { + + var nextId = SomeModels.Count + 1; + model.Id = nextId; + _logger.LogTrace( + $"Creating an SomeModel with Id {nextId} based on the command data sent: " + + $"{JsonConvert.SerializeObject(model)}"); + SomeModels.Add(model); + + return model; + } + + public async Task UpdateAsync( + SomeModelModel model) + { + _logger.LogTrace( + $"Updating a SomeModel with Id {model.Id} based on the command data sent: " + + $"{JsonConvert.SerializeObject(model)}"); + + var storedModel = SomeModels.Find(g => g.Id.Equals(model.Id)); + if (storedModel == null) + { + _logger.LogWarning($"No such SomeModel model with Id {model.Id} found. Doing nothing."); + return model; + } + + SomeModels.Remove(storedModel); + + SomeModels.Add(model); + + return model; + } + + public async Task GetAsync(long id) + { + _logger.LogDebug($"Retrieving SomeModel with Id of {id}"); + var model = SomeModels.FirstOrDefault(i => i.Id.Equals(id)); + + if (model != null) + { + return model; + } + _logger.LogDebug($"No SomeModel with Id of {id} found. Returning null"); + return null; + } + + public async Task> ListAllAsync() + { + return SomeModels.AsEnumerable(); + } + + public async Task DeleteAsync(long id) + { + _logger.LogDebug($"Deleting SomeModel with Id of {id}"); + var modelFound = SomeModels.Find(g => g.Id.Equals(id)); + if (modelFound != null) + { + SomeModels.Remove(modelFound); + _logger.LogDebug($"SomeModel with Id of {id} Deleted"); + } + else + { + _logger.LogDebug($"No SomeModel with Id of {id} found to delete. Returning"); + } + } + + } +} \ No newline at end of file diff --git a/HealthChecks.SomeModelService/Startup.cs b/HealthChecks.SomeModelService/Startup.cs new file mode 100644 index 0000000..486e12c --- /dev/null +++ b/HealthChecks.SomeModelService/Startup.cs @@ -0,0 +1,118 @@ +using Autofac; +using HealthChecks.Configuration; +using HealthChecks.SomeModelService.Health; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using IHostingEnvironment = Microsoft.AspNetCore.Hosting.IHostingEnvironment; + +namespace HealthChecks.SomeModelService +{ + public class Startup + { + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + /// + /// Set up any services and other general configuration here + /// + /// + public void ConfigureServices(IServiceCollection services) + { + services.AddCors(); + services.AddHealthChecks() + .AddCheck("api"); + //add additional healthchecks here, as needed + //as described here https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks + + + //Api Base Configuration + services.AddMvcCore() + .AddAuthorization() + .AddJsonFormatters(); + + //add the overall configuration to the IoC + services.AddSingleton(Configuration); + + //add our specific configurations to the IoC + var identityConfiguration = new IdentityConfiguration(); + Configuration.GetSection("Identity").Bind(identityConfiguration); + services.AddSingleton(identityConfiguration); + + if (identityConfiguration.Enabled) + { + services + .AddAuthentication("Bearer") + .AddJwtBearer( + "Bearer", + options => + { + options.Authority = identityConfiguration.ServerUrl; + options.RequireHttpsMetadata = identityConfiguration.RequireHttpsMetadata; + options.Audience = identityConfiguration.Audience; + }); + } + + var awsOptions = Configuration.GetAWSOptions(); + services.AddDefaultAWSOptions(awsOptions); + ConfigureAwsServices(services); + } + + private void ConfigureAwsServices( + IServiceCollection services) + { + var localStackConfiguration = new LocalStackConfiguration(); + Configuration.GetSection("LocalStack").Bind(localStackConfiguration); + + //SQS + var awsSqsOptions = Configuration.GetAWSOptions(); + //if (localStackConfiguration.SqsUrl != null) + //{ + // awsSqsOptions.DefaultClientConfig.ServiceURL = localStackConfiguration.SqsUrl; + //} + } + + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime applicationLifetime) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseCors(builder => + builder + .AllowAnyOrigin() + .AllowAnyHeader() + .AllowAnyMethod() + ); + + app.UseHealthChecks("/health", new HealthCheckOptions() + { + Predicate = _ => true + }); + + //Use Identity and Access Control + app.UseAuthentication(); + + //Use MVC / API + app.UseMvc(); + } + + /// + /// Used to configure your container as needed. + /// + /// + public void ConfigureContainer(ContainerBuilder builder) + { + builder.RegisterModule(new DependencyModule()); + } + } +} diff --git a/HealthChecks.SomeModelService/appsettings.Development.json b/HealthChecks.SomeModelService/appsettings.Development.json new file mode 100644 index 0000000..0c5f7d6 --- /dev/null +++ b/HealthChecks.SomeModelService/appsettings.Development.json @@ -0,0 +1,23 @@ +{ + "AWS": { + "Profile": "default", + "Region": "us-west-2", + "ProfileLocation": "/root/.aws" + }, + "LocalStack": { + "SqsUrl": "http://localstack:4576" + }, + "Identity": { + "Enabled": false, + "ServerUrl": "http://localhost:5000", + "RequireHttpsMetadata": false, + "Audience": "Api1" + }, + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/HealthChecks.SomeModelService/appsettings.json b/HealthChecks.SomeModelService/appsettings.json new file mode 100644 index 0000000..1a0acdb --- /dev/null +++ b/HealthChecks.SomeModelService/appsettings.json @@ -0,0 +1,16 @@ +{ + "Identity": { + "Enabled": false, + "ServerUrl": "http://localhost:5000", + "RequireHttpsMetadata": false, + "Audience": "Api1" + }, + + "Logging": { + "LogLevel": { + "Default": "Trace" + } + }, + + "AllowedHosts": "*" +} diff --git a/HealthChecks.sln b/HealthChecks.sln new file mode 100644 index 0000000..e2668ff --- /dev/null +++ b/HealthChecks.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29020.237 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tooling", "Tooling", "{A205C45D-DE59-4833-A3C0-81414F1D9E76}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.Configuration", "DomainModel\HealthChecks.Configuration\HealthChecks.Configuration.csproj", "{3D99E2E2-8501-4A95-9A6A-054DB3B3DD29}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.ServiceBus", "DomainModel\HealthChecks.ServiceBus\HealthChecks.ServiceBus.csproj", "{95953674-DAE8-4DD0-8DE2-3FDF3A8FD590}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.SomeModelService", "HealthChecks.SomeModelService\HealthChecks.SomeModelService.csproj", "{7050B3EB-FBAC-4BD2-9526-44859073F32E}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{5C57D5E1-F715-47CA-827E-A92DF9880E41}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3D99E2E2-8501-4A95-9A6A-054DB3B3DD29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D99E2E2-8501-4A95-9A6A-054DB3B3DD29}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D99E2E2-8501-4A95-9A6A-054DB3B3DD29}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D99E2E2-8501-4A95-9A6A-054DB3B3DD29}.Release|Any CPU.Build.0 = Release|Any CPU + {95953674-DAE8-4DD0-8DE2-3FDF3A8FD590}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {95953674-DAE8-4DD0-8DE2-3FDF3A8FD590}.Debug|Any CPU.Build.0 = Debug|Any CPU + {95953674-DAE8-4DD0-8DE2-3FDF3A8FD590}.Release|Any CPU.ActiveCfg = Release|Any CPU + {95953674-DAE8-4DD0-8DE2-3FDF3A8FD590}.Release|Any CPU.Build.0 = Release|Any CPU + {7050B3EB-FBAC-4BD2-9526-44859073F32E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7050B3EB-FBAC-4BD2-9526-44859073F32E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7050B3EB-FBAC-4BD2-9526-44859073F32E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7050B3EB-FBAC-4BD2-9526-44859073F32E}.Release|Any CPU.Build.0 = Release|Any CPU + {5C57D5E1-F715-47CA-827E-A92DF9880E41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C57D5E1-F715-47CA-827E-A92DF9880E41}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C57D5E1-F715-47CA-827E-A92DF9880E41}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C57D5E1-F715-47CA-827E-A92DF9880E41}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3D99E2E2-8501-4A95-9A6A-054DB3B3DD29} = {A205C45D-DE59-4833-A3C0-81414F1D9E76} + {95953674-DAE8-4DD0-8DE2-3FDF3A8FD590} = {A205C45D-DE59-4833-A3C0-81414F1D9E76} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {068E1943-FABE-4C1B-A4E9-CE3050BE7BF0} + EndGlobalSection +EndGlobal diff --git a/Tests/HealthChecks.ComponentTests/ComponentTest.cs b/Tests/HealthChecks.ComponentTests/ComponentTest.cs new file mode 100644 index 0000000..84ffa11 --- /dev/null +++ b/Tests/HealthChecks.ComponentTests/ComponentTest.cs @@ -0,0 +1,18 @@ +using NUnit.Framework; + +namespace HealthChecks.ExampleCrud.ComponentTests +{ + public class ComponentTest + { + [SetUp] + public void Setup() + { + } + + [Test] + public void Test1() + { + Assert.Pass(); + } + } +} \ No newline at end of file diff --git a/Tests/HealthChecks.ComponentTests/HealthChecks.ComponentTests.csproj b/Tests/HealthChecks.ComponentTests/HealthChecks.ComponentTests.csproj new file mode 100644 index 0000000..ba1718d --- /dev/null +++ b/Tests/HealthChecks.ComponentTests/HealthChecks.ComponentTests.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + diff --git a/Tests/HealthChecks.IntegrationTests/HealthChecks.IntegrationTests.csproj b/Tests/HealthChecks.IntegrationTests/HealthChecks.IntegrationTests.csproj new file mode 100644 index 0000000..ba1718d --- /dev/null +++ b/Tests/HealthChecks.IntegrationTests/HealthChecks.IntegrationTests.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + diff --git a/Tests/HealthChecks.Tests/ApplicationServices/create-all-appservice-tests.cmd b/Tests/HealthChecks.Tests/ApplicationServices/create-all-appservice-tests.cmd new file mode 100644 index 0000000..c215652 --- /dev/null +++ b/Tests/HealthChecks.Tests/ApplicationServices/create-all-appservice-tests.cmd @@ -0,0 +1 @@ +dotnet new ptml-appserv-tests --model SomeModel --name SomeModel --base-namespace HealthChecks.ExampleCrud --force \ No newline at end of file diff --git a/Tests/HealthChecks.Tests/Controllers/create-all-controller-tests.cmd b/Tests/HealthChecks.Tests/Controllers/create-all-controller-tests.cmd new file mode 100644 index 0000000..32384a3 --- /dev/null +++ b/Tests/HealthChecks.Tests/Controllers/create-all-controller-tests.cmd @@ -0,0 +1 @@ +dotnet new ptml-controller-tests --model SomeModel --name SomeModel --base-namespace HealthChecks.ExampleCrud --force \ No newline at end of file diff --git a/Tests/HealthChecks.Tests/HealthChecks.Tests.csproj b/Tests/HealthChecks.Tests/HealthChecks.Tests.csproj new file mode 100644 index 0000000..09e93f5 --- /dev/null +++ b/Tests/HealthChecks.Tests/HealthChecks.Tests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + diff --git a/Tests/HealthChecks.Tests/Repositories/create-all-repo-tests.cmd b/Tests/HealthChecks.Tests/Repositories/create-all-repo-tests.cmd new file mode 100644 index 0000000..117d29d --- /dev/null +++ b/Tests/HealthChecks.Tests/Repositories/create-all-repo-tests.cmd @@ -0,0 +1 @@ +dotnet new ptml-repo-tests --model SomeModel --name SomeModel --base-namespace HealthChecks.ExampleCrud --force \ No newline at end of file diff --git a/create-my-apis.cmd b/create-my-apis.cmd new file mode 100644 index 0000000..553a8c9 --- /dev/null +++ b/create-my-apis.cmd @@ -0,0 +1,5 @@ +dotnet new ptml-micro-ddd --model-name SomeModel --name HealthChecks.ExampleCrud.SomeModelService --base-namespace HealthChecks.ExampleCrud --force + +dotnet sln ./HealthChecks.ExampleCrud.sln add ./HealthChecks.ExampleCrud.SomeModelService/HealthChecks.ExampleCrud.SomeModelService.csproj + +dotnet add ./Tests/HealthChecks.ExampleCrud.Tests/HealthChecks.ExampleCrud.Tests.csproj reference ./HealthChecks.ExampleCrud.SomeModelService/HealthChecks.ExampleCrud.SomeModelService.csproj \ No newline at end of file diff --git a/docker-compose.dcproj b/docker-compose.dcproj new file mode 100644 index 0000000..7b0379c --- /dev/null +++ b/docker-compose.dcproj @@ -0,0 +1,18 @@ + + + + 2.1 + Linux + 5c57d5e1-f715-47ca-827e-a92df9880e41 + LaunchBrowser + {Scheme}://localhost:{ServicePort}/SomeModels + healthchecks.somemodelservice + + + + docker-compose.yml + + + + + \ No newline at end of file diff --git a/docker-compose.override.yml b/docker-compose.override.yml new file mode 100644 index 0000000..70c7dfe --- /dev/null +++ b/docker-compose.override.yml @@ -0,0 +1,7 @@ +version: '3.4' +services: + healthchecks.somemodelservice: + environment: + - ASPNETCORE_ENVIRONMENT=Development + ports: + - 54411:80 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d8e1802 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +version: '3.4' + +services: + healthchecks.somemodelservice: + image: ${DOCKER_REGISTRY-}healthcheckssomemodelservice + build: + context: . + dockerfile: HealthChecks.SomeModelService/Dockerfile \ No newline at end of file