From 940794927f9f710867ffa27b804acdd4550f4475 Mon Sep 17 00:00:00 2001 From: adrianLIrobotics Date: Fri, 3 Mar 2023 13:24:53 +0100 Subject: [PATCH 01/31] Created InstanceRunningDto and InstanceRunningModel --- src/Models/Domain/ActionRunningModel.cs | 12 ++++++ src/Models/Domain/InstanceRunningModel.cs | 46 +++++++++++++++++++++++ src/Models/Dto/InstanceRunningDto.cs | 44 ++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 src/Models/Domain/ActionRunningModel.cs create mode 100644 src/Models/Domain/InstanceRunningModel.cs create mode 100644 src/Models/Dto/InstanceRunningDto.cs diff --git a/src/Models/Domain/ActionRunningModel.cs b/src/Models/Domain/ActionRunningModel.cs new file mode 100644 index 00000000..7c6cdf26 --- /dev/null +++ b/src/Models/Domain/ActionRunningModel.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Middleware.Models.Domain +{ + internal class ActionRunningModel + { + } +} diff --git a/src/Models/Domain/InstanceRunningModel.cs b/src/Models/Domain/InstanceRunningModel.cs new file mode 100644 index 00000000..1a0c98ba --- /dev/null +++ b/src/Models/Domain/InstanceRunningModel.cs @@ -0,0 +1,46 @@ +using System.Text.Json.Serialization; +using Middleware.Models.Dto; +using Middleware.Models.Dto.Hardware; +using Middleware.Models.Enums; + + +namespace Middleware.Models.Domain; + +public class InstanceRunningModel : BaseModel +{ + [JsonPropertyName("Id")] + public override Guid Id { get; set; } + + [JsonPropertyName("Name")] + public override string Name { get; set; } + + [JsonPropertyName("ServiceInstanceId")] + public Guid ServiceInstanceId { get; set; } + + [JsonPropertyName("ServiceType")] + + public string ServiceType { get; set; } + + [JsonPropertyName("ServiceUrl")] + public string ServiceUrl { get; set; } + + [JsonPropertyName("ServiceStatus")] + public string ServiceStatus { get; set; } //updated every 10 sec + + [JsonPropertyName("DeployedTime")] + public DateTime DeployedTime { get; set; } // Compulsory field + + public override Dto.Dto ToDto() + { + var domain = this; + return new InstanceRunningDto() + { + Id = domain.Id.ToString(), + Name = domain.Name, + ServiceType = domain.ServiceType, + ServiceInstanceId = domain.ServiceInstanceId.ToString(), + ServiceUrl = domain.ServiceUrl, + ServiceStatus = domain.ServiceStatus + }; + } +} diff --git a/src/Models/Dto/InstanceRunningDto.cs b/src/Models/Dto/InstanceRunningDto.cs new file mode 100644 index 00000000..1ac42ac7 --- /dev/null +++ b/src/Models/Dto/InstanceRunningDto.cs @@ -0,0 +1,44 @@ +using Middleware.Models.Domain; +using Middleware.Models.Dto.Hardware; +using Redis.OM.Modeling; +using Middleware.Models.Dto.Ros; + +namespace Middleware.Models.Dto; + +[Document(IndexName = "instanceRunning-idx", StorageType = StorageType.Json, Prefixes = new[] { InstanceRunningDto.Prefix })] +public class InstanceRunningDto : Dto +{ + public const string Prefix = "instanceRunning"; + [Indexed] + [RedisIdField] + public override string Id { get; set; } = default!; + [Indexed] + public string? Name { get; set; } = default!; + [Indexed] + public string? ServiceInstanceId { get; set; } = default!; + [Indexed] + public string ServiceType { get; set; } = default!; + [Indexed] + public string ServiceUrl { get; set; } = default!; + [Indexed] + public string ServiceStatus { get; set; } = default!; + + [Indexed(Sortable = true)] + public DateTime DeployedTime { get; set; } + + public override BaseModel ToModel() + { + var dto = this; + return new InstanceRunningModel() + { + Id = Guid.Parse(dto.Id.Replace(Prefix, "")), + Name = dto.Name, + ServiceType = dto.ServiceType, + ServiceUrl = dto.ServiceUrl, + ServiceInstanceId = Guid.Parse(dto.ServiceInstanceId), + ServiceStatus = dto.ServiceStatus, + DeployedTime = dto.DeployedTime + + }; + } +} From eaa5b5e75c5d279700ee6fc0b9771bfd0c26513b Mon Sep 17 00:00:00 2001 From: adrianLIrobotics Date: Mon, 6 Mar 2023 11:39:37 +0100 Subject: [PATCH 02/31] Added controller for ActionRunning --- .../DataAccessExtensionMethods.cs | 2 + .../Abstract/IActionRunningRepository.cs | 9 + .../Redis/RedisActionRunningRepository.cs | 68 +++++ .../Redis/RedisInstanceRunningRepository.cs | 12 + src/Models/Domain/ActionPlanModel.cs | 2 +- src/Models/Domain/ActionRunningModel.cs | 67 ++++- src/Models/Dto/ActionRunningDto.cs | 42 +++ .../Controllers/ActionRunningController.cs | 277 ++++++++++++++++++ .../Abstract/IActionRunningService.cs | 9 + .../Services/ActionRunningService.cs | 27 ++ 10 files changed, 511 insertions(+), 4 deletions(-) create mode 100644 src/DataAccess/Repositories/Abstract/IActionRunningRepository.cs create mode 100644 src/DataAccess/Repositories/Redis/RedisActionRunningRepository.cs create mode 100644 src/DataAccess/Repositories/Redis/RedisInstanceRunningRepository.cs create mode 100644 src/Models/Dto/ActionRunningDto.cs create mode 100644 src/RedisInterface/Controllers/ActionRunningController.cs create mode 100644 src/RedisInterface/Services/Abstract/IActionRunningService.cs create mode 100644 src/RedisInterface/Services/ActionRunningService.cs diff --git a/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs b/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs index 21f87745..5c326610 100644 --- a/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs +++ b/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs @@ -54,6 +54,8 @@ public static IServiceCollection RegisterRepositories(this IServiceCollection se services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + return services; } diff --git a/src/DataAccess/Repositories/Abstract/IActionRunningRepository.cs b/src/DataAccess/Repositories/Abstract/IActionRunningRepository.cs new file mode 100644 index 00000000..7f48001c --- /dev/null +++ b/src/DataAccess/Repositories/Abstract/IActionRunningRepository.cs @@ -0,0 +1,9 @@ +using Middleware.Models.Domain; + +namespace Middleware.DataAccess.Repositories.Abstract +{ + public interface IActionRunningRepository: IBaseRepository, IRelationRepository + { + Task PatchActionAsync(Guid id, ActionRunningModel patch); + } +} diff --git a/src/DataAccess/Repositories/Redis/RedisActionRunningRepository.cs b/src/DataAccess/Repositories/Redis/RedisActionRunningRepository.cs new file mode 100644 index 00000000..3daeef48 --- /dev/null +++ b/src/DataAccess/Repositories/Redis/RedisActionRunningRepository.cs @@ -0,0 +1,68 @@ +using Middleware.DataAccess.Repositories.Abstract; +using Middleware.Models.Domain; +using Middleware.Models.Dto; +using Redis.OM; +using Redis.OM.Contracts; +using RedisGraphDotNet.Client; +using Serilog; + +namespace Middleware.DataAccess.Repositories; + +public class RedisActionRunningRepository : RedisRepository, IActionRunningRepository +{ + public RedisActionRunningRepository(IRedisConnectionProvider provider, IRedisGraphClient redisGraph, ILogger logger) : base(provider, redisGraph, true, logger) + { + + } + + public async Task PatchActionAsync(Guid id, ActionRunningModel patch) + { + ActionRunningModel? currentModel = await GetByIdAsync(id); + if (currentModel == null) + { + return null; + } + if (!string.IsNullOrEmpty(patch.Name)) + { + currentModel.Name = patch.Name; + } + if (patch.Tags != null) + { + currentModel.Tags = patch.Tags; + } + if (!string.IsNullOrEmpty(patch.Order.ToString())) + { + currentModel.Order = patch.Order; + } + if (!string.IsNullOrEmpty(patch.Placement)) + { + currentModel.Placement = patch.Placement; + } + if (!string.IsNullOrEmpty(patch.PlacementType)) + { + currentModel.PlacementType = patch.PlacementType; + } + if (!string.IsNullOrEmpty(patch.ActionPriority)) + { + currentModel.ActionPriority = patch.ActionPriority; + } + if (!string.IsNullOrEmpty(patch.ActionStatus)) + { + currentModel.ActionStatus = patch.ActionStatus; + } + if (patch.Services != null) + { + currentModel.Services = patch.Services; + } + if (!string.IsNullOrEmpty(patch.MinimumRam.ToString())) + { + currentModel.MinimumRam = patch.MinimumRam; + } + if (!string.IsNullOrEmpty(patch.MinimumNumCores.ToString())) + { + currentModel.MinimumNumCores = patch.MinimumNumCores; + } + await UpdateAsync(currentModel); + return currentModel; + } +} diff --git a/src/DataAccess/Repositories/Redis/RedisInstanceRunningRepository.cs b/src/DataAccess/Repositories/Redis/RedisInstanceRunningRepository.cs new file mode 100644 index 00000000..8d2b3ac6 --- /dev/null +++ b/src/DataAccess/Repositories/Redis/RedisInstanceRunningRepository.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Middleware.DataAccess.Repositories.Redis +{ + internal class RedisInstanceRunningRepository + { + } +} diff --git a/src/Models/Domain/ActionPlanModel.cs b/src/Models/Domain/ActionPlanModel.cs index e8c30511..7c1017f2 100644 --- a/src/Models/Domain/ActionPlanModel.cs +++ b/src/Models/Domain/ActionPlanModel.cs @@ -27,7 +27,7 @@ public sealed class ActionPlanModel : BaseModel public DateTime LastStatusChange { get; set; } // AL 2022-05-10: Not sure we need this one or how to use it. [JsonPropertyName("ActionSequence")] - public List ActionSequence { get; set; } + public List ActionSequence { get; set; } [JsonPropertyName("RobotId")] public Guid RobotId { get; set; } diff --git a/src/Models/Domain/ActionRunningModel.cs b/src/Models/Domain/ActionRunningModel.cs index 7c6cdf26..82e8b47c 100644 --- a/src/Models/Domain/ActionRunningModel.cs +++ b/src/Models/Domain/ActionRunningModel.cs @@ -1,12 +1,73 @@ -using System; +using Middleware.Models.Dto.Hardware; +using Middleware.Models.Dto; +using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.Json.Serialization; using System.Threading.Tasks; -namespace Middleware.Models.Domain +namespace Middleware.Models.Domain; + +public class ActionRunningModel : BaseModel { - internal class ActionRunningModel + [JsonPropertyName("Id")] + public override Guid Id { get; set; } + + [JsonPropertyName("ActionParentId")] + public Guid ActionParentId { get; set; } // This is the Id of normal Actio from which the running action is based. + + [JsonPropertyName("ActionPlanId")] + public Guid ActionPlanId { get; set; } + + [JsonPropertyName("Name")] + public override string Name { get; set; } + + + [JsonPropertyName("Tags")] + public List? Tags { get; set; } + + [JsonPropertyName("Order")] + public int Order { get; set; } + + [JsonPropertyName("Placement")] + public string? Placement { get; set; } + + [JsonPropertyName("PlacementType")] + public string? PlacementType { get; set; } // Either edge or cloud. + + [JsonPropertyName("ActionPriority")] + public string? ActionPriority { get; set; } + + [JsonPropertyName("ActionStatus")] + public string? ActionStatus { get; set; } + + [JsonPropertyName("Services")] + //[JsonIgnore] + public List? Services { get; set; } + + [JsonPropertyName("MinimumRam")] + public int MinimumRam { get; set; } + + [JsonPropertyName("MinimumNumCores")] + public int MinimumNumCores { get; set; } + + public override Dto.Dto ToDto() { + var domain = this; + return new ActionRunningDto() + { + Id = domain.Id.ToString(), + ActionPriority = domain.ActionPriority, + Name = domain.Name, + ActionParentId = domain.ActionParentId.ToString(), + ActionPlanId = domain.ActionPlanId.ToString(), + HardwareRequirements = new HardwareRequirements() + { + MinimumRam = domain.MinimumRam, + MinimumNumCores = domain.MinimumNumCores + }, + Tags = domain.Tags + }; } } diff --git a/src/Models/Dto/ActionRunningDto.cs b/src/Models/Dto/ActionRunningDto.cs new file mode 100644 index 00000000..8b4bc6fc --- /dev/null +++ b/src/Models/Dto/ActionRunningDto.cs @@ -0,0 +1,42 @@ +using Middleware.Models.Domain; +using Middleware.Models.Dto.Hardware; +using Redis.OM.Modeling; + +namespace Middleware.Models.Dto; + +[Document(IndexName = "actionRunning-idx", StorageType = StorageType.Json, Prefixes = new[] { ActionRunningDto.Prefix })] +public class ActionRunningDto : Dto +{ + public const string Prefix = "ActionRunning"; + [Indexed] + [RedisIdField] + public override string Id { get; set; } = default!; + [Indexed] + public string ActionParentId { get; init; } = default!; // This is the ID in which the actionRunning us based from the normal Action + [Indexed] + public string ActionPlanId { get; init; } = default!; + [Indexed] + public string Name { get; init; } = default!; + [Indexed] + public List Tags { get; init; } = new(); + [Indexed] + public string ActionPriority { get; init; } = default!; + + public HardwareRequirements HardwareRequirements { get; init; } = new(); + + public override BaseModel ToModel() + { + var dto = this; + return new ActionRunningModel() + { + Id = Guid.Parse(dto.Id.Replace(Prefix, "")), + ActionParentId = Guid.Parse(dto.ActionParentId.Replace(Prefix, "")), + ActionPlanId = Guid.Parse(dto.ActionParentId.Replace(Prefix, "")), + Name = dto.Name, + Tags = dto.Tags, + MinimumRam = dto.HardwareRequirements.MinimumRam, + MinimumNumCores = dto.HardwareRequirements.MinimumNumCores, + ActionPriority = dto.ActionPriority + }; + } +} \ No newline at end of file diff --git a/src/RedisInterface/Controllers/ActionRunningController.cs b/src/RedisInterface/Controllers/ActionRunningController.cs new file mode 100644 index 00000000..85d1f756 --- /dev/null +++ b/src/RedisInterface/Controllers/ActionRunningController.cs @@ -0,0 +1,277 @@ +using System.Net; +using Microsoft.AspNetCore.Mvc; +using Middleware.Common.Responses; +using Middleware.DataAccess.Repositories.Abstract; +using Middleware.Models.Domain; +using Middleware.RedisInterface.Services.Abstract; + +namespace Middleware.RedisInterface.Controllers +{ + [Route("api/v1/[controller]")] + [ApiController] + public class ActionRunningController : ControllerBase + { + private readonly IActionRunningRepository _actionRunningRepository; + private readonly ILogger _logger; + + + public ActionRunningController(IActionRunningRepository actionRunningRepository, ILogger logger) + { + _actionRunningRepository = actionRunningRepository ?? throw new ArgumentNullException(nameof(actionRunningRepository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + } + + /// + /// Get all the ActionRunningModels entities + /// + /// the list of ActionModel entities + [HttpGet(Name = "ActionRunningGetAll")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task>> GetAllAsync() + { + try + { + + List models = await _actionRunningRepository.GetAllAsync(); + if (models.Any() == false) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No actions were found.")); + } + return Ok(models); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + //New end points for depends_on property for actions. + + /// + /// Get an ActionRunningModel entity by id + /// + /// + /// the ActionRunningModel entity for the specified id + [HttpGet] + [Route("{id}", Name = "ActionRunningGetById")] + [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetByIdAsync(Guid id) + { + try + { + ActionRunningModel model = await _actionRunningRepository.GetByIdAsync(id); + if (model == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, $"Action with id: '{id}' was not found.")); + } + return Ok(model); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Add a new ActionRunningModel entity + /// + /// + /// the newly created ActionModel entity + [HttpPost(Name = "ActionRunningAdd")] + [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task> AddAsync([FromBody] ActionRunningModel model) + { + if (model == null) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Parameters were not specified.")); + } + try + { + model = await _actionRunningRepository.AddAsync(model); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + return Ok(model); + } + + /// + /// Partially update an existing ActionRunningModel entity + /// + /// + /// + /// the modified ActionModel entity + [HttpPatch] + [Route("{id}", Name = "ActionRunningPatch")] + [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task PatchActionAsync([FromBody] ActionRunningModel patch, [FromRoute] Guid id) + { + try + { + ActionRunningModel model = await _actionRunningRepository.PatchActionAsync(id, patch); + if (model == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object to be updated was not found.")); + } + return Ok(model); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + + /// + /// Delete an ActionRunningModel entity for the given id + /// + /// + /// no return + [HttpDelete] + [Route("{id}", Name = "ActionRunningDelete")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task DeleteByIdAsync(Guid id) + { + try + { + var deleted = await _actionRunningRepository.DeleteByIdAsync(id); + if (deleted == false) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified Action has not been found.")); + } + return Ok(); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Creates a new relation between two models + /// + /// + /// + [HttpPost] + [Route("AddRelation", Name = "ActionRunningAddRelation")] + [ProducesResponseType(typeof(RelationModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task> AddRelationAsync([FromBody] RelationModel model) + { + if (model == null) + { + return BadRequest("Parameters were not specified."); + } + try + { + bool isValid = await _actionRunningRepository.AddRelationAsync(model); + if (!isValid) + { + return StatusCode((int)HttpStatusCode.InternalServerError, new ApiResponse((int)HttpStatusCode.InternalServerError, "The relation was not created")); + } + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + return Ok(model); + } + + /// + /// Retrieves a single relation by name + /// + /// + /// + /// + [HttpGet] + [Route("relation/{name}", Name = "ActionRunningGetRelationByName")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetRelationAsync(Guid id, string name) //Guid of node and name of relationship + { + if (string.IsNullOrWhiteSpace(name)) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); + } + + if (id == Guid.Empty) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); + } + try + { + var relations = await _actionRunningRepository.GetRelation(id, name); + if (!relations.Any()) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found.")); + } + return Ok(relations); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Retrieves two relations by their names + /// + /// + /// + /// + /// + [HttpGet] + [Route("relations/{firstName}/{secondName}", Name = "ActionRunningGetRelationsByName")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetRelationsAsync(Guid id, string firstName, string secondName) + { + try + { + List relationNames = new List() { firstName, secondName }; + var relations = await _actionRunningRepository.GetRelations(id, relationNames); + if (!relations.Any()) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found")); + } + return Ok(relations); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + } +} diff --git a/src/RedisInterface/Services/Abstract/IActionRunningService.cs b/src/RedisInterface/Services/Abstract/IActionRunningService.cs new file mode 100644 index 00000000..0a052a6c --- /dev/null +++ b/src/RedisInterface/Services/Abstract/IActionRunningService.cs @@ -0,0 +1,9 @@ +using Middleware.Models.Domain; + +namespace Middleware.RedisInterface.Services.Abstract; + +public interface IActionRunningService +{ + Task GetByIdAsync(Guid id); + Task AddAsync(ActionRunningModel model); +} diff --git a/src/RedisInterface/Services/ActionRunningService.cs b/src/RedisInterface/Services/ActionRunningService.cs new file mode 100644 index 00000000..fa92a5e8 --- /dev/null +++ b/src/RedisInterface/Services/ActionRunningService.cs @@ -0,0 +1,27 @@ +using Middleware.DataAccess.Repositories.Abstract; +using Middleware.DataAccess.Repositories.Redis; +using Middleware.Models.Domain; +using Middleware.RedisInterface.Services.Abstract; + +namespace Middleware.RedisInterface.Services; + +public class ActionRunningService : IActionRunningService +{ + private readonly IActionRunningRepository _actionRunningRepository; + public ActionRunningService(IActionRunningRepository actionRunningRepository) + { + _actionRunningRepository = actionRunningRepository; + } + public async Task AddAsync(ActionRunningModel model) + { + var action = await _actionRunningRepository.AddAsync(model); + return action; + } + + public async Task GetByIdAsync(Guid id) + { + var action = await _actionRunningRepository.GetByIdAsync(id); + + return action; + } +} \ No newline at end of file From 2844fddc936f15800e070e34baee2ce308e2a04c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= Date: Mon, 6 Mar 2023 11:51:30 +0100 Subject: [PATCH 03/31] Removed unused ConnectionMultiplexer to old redis DB --- .../DataAccessExtensionMethods.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs b/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs index 21f87745..09c2c9bf 100644 --- a/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs +++ b/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs @@ -23,21 +23,13 @@ public static class DataAccessExtensionMethods public static WebApplicationBuilder RegisterRedis(this WebApplicationBuilder builder) { var config = builder.Configuration.GetSection(RedisConfig.ConfigName).Get(); - - ConnectionMultiplexer multiplexer = ConnectionMultiplexer.Connect( - config.HostName, - (c) => - { - c.Password = config.Password; - }); - //For the redis-cluster master node, port 6380 should be used, using port 6379 will point to the replicas of the redis-cluster - var mux2 = ConnectionMultiplexer.Connect($"{config.ClusterHostname}:6380", c => c.Password = config.Password); - IRedisConnectionProvider provider = new RedisConnectionProvider(mux2); + var mux = ConnectionMultiplexer.Connect($"{config.ClusterHostname}:6380", c => c.Password = config.Password); + IRedisConnectionProvider provider = new RedisConnectionProvider(mux); builder.Services.AddSingleton(provider); - builder.Services.AddSingleton(mux2); - RedisGraphClient redisGraphClient = new RedisGraphClient(mux2); + builder.Services.AddSingleton(mux); + RedisGraphClient redisGraphClient = new RedisGraphClient(mux); builder.Services.AddSingleton(redisGraphClient); return builder; } From 84250d98a9263b343eaf47b4bf06cd0276c2200e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= Date: Mon, 6 Mar 2023 13:24:56 +0100 Subject: [PATCH 04/31] Replace static env variables with k8s service variables --- .vscode/launch.json | 2 +- .vscode/tasks.json | 6 ++-- docker-compose.override.yml | 12 ++++---- k8s/orchestrator/orchestrator.yaml | 4 ++- k8s/task-planner/task_planner_deployment.yaml | 7 +---- .../ApiReference/ApiClientBuilder.cs | 7 +++-- .../Deployment/DeploymentService.cs | 8 ++--- .../Responses/actionSequenceREsponse.cs | 29 ------------------- .../ApiReference/ApiClientBuilder.cs | 8 ++--- .../ApiReference/ApiClientBuilder.cs | 14 +++++---- 10 files changed, 34 insertions(+), 63 deletions(-) delete mode 100644 src/RedisInterface/Responses/actionSequenceREsponse.cs diff --git a/.vscode/launch.json b/.vscode/launch.json index 914d4605..af88c7c1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,7 +15,7 @@ "stopAtEntry": false, "env": { "ASPNETCORE_ENVIRONMENT": "Development", - "ASPNETCORE_URLS": "http://+:3001" + "ASPNETCORE_URLS": "http://+:69" }, "sourceFileMap": { "/Views": "${workspaceFolder}/Views" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7ed80361..1574dd6b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,11 +7,11 @@ "resource": "orchestrator-api", "resourceType": "service", "ports": [ - 3001 + 69 ], - "targetCluster": "microk8s-cluster", + "targetCluster": "minikube", "targetNamespace": "middleware", - "useKubernetesServiceEnvironmentVariables": false + "useKubernetesServiceEnvironmentVariables": true }, { "label": "bridge-to-kubernetes.compound", diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 4ca673af..2d126f48 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -29,7 +29,7 @@ services: - ASPNETCORE_HTTPS_PORT=7026 #- ASPNETCORE_Kestrel__Certificates__Default__Password=ros #- ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx - - REDIS_INTERFACE_ADDRESS=http://redisinterface.api + - REDIS_INTERFACE_API_SERVICE_HOST=redisinterface.api - AWS_IMAGE_REGISTRY=${AWS_IMAGE_REGISTRY} - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} @@ -51,9 +51,9 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=https://+:443;http://+:80 - - REDIS_INTERFACE_ADDRESS=http://redisinterface.api - - RESOURCE_PLANNER_ADDRESS=http://resourceplanner.api - - ORCHESTRATOR_ADDRESS=http://orchestrator.api + - REDIS_INTERFACE_API_SERVICE_HOST=redisinterface.api + - RESOURCE_PLANNER_ADDRESS=resourceplanner.api + - ORCHESTRATOR_API_SERVICE_HOST=orchestrator.api - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - Middleware__Organization=5G-ERA-DEV @@ -70,8 +70,8 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=https://+:443;http://+:80 - - REDIS_INTERFACE_ADDRESS=http://redisinterface.api - - ORCHESTRATOR_ADDRESS=http://orchestrator.api + - REDIS_INTERFACE_API_SERVICE_HOST=redisinterface.api + - ORCHESTRATOR_API_SERVICE_HOST=orchestrator.api - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - Middleware__Organization=5G-ERA-DEV diff --git a/k8s/orchestrator/orchestrator.yaml b/k8s/orchestrator/orchestrator.yaml index 317c6027..78166d6a 100644 --- a/k8s/orchestrator/orchestrator.yaml +++ b/k8s/orchestrator/orchestrator.yaml @@ -31,9 +31,11 @@ spec: automountServiceAccountToken: true nodeSelector: kubernetes.io/os: linux + imagePullSecrets: + - name: awsecr-cred containers: - name: orchestrator-api - image: 394603622351.dkr.ecr.eu-west-1.amazonaws.com/orchestrator-api:v0.2 + image: 394603622351.dkr.ecr.eu-west-1.amazonaws.com/orchestrator-api:v0.2.1-rc1 imagePullPolicy: Always resources: {} env: diff --git a/k8s/task-planner/task_planner_deployment.yaml b/k8s/task-planner/task_planner_deployment.yaml index dfcc50a0..473182b1 100644 --- a/k8s/task-planner/task_planner_deployment.yaml +++ b/k8s/task-planner/task_planner_deployment.yaml @@ -17,9 +17,4 @@ spec: - name: task-planner-api image: 394603622351.dkr.ecr.eu-west-1.amazonaws.com/task-planner-api:latest imagePullPolicy: Always - resources: {} - env: - - name: REDIS_INTERFACE_ADDRESS - value: http://redis-interface-api - - name: ORCHESTRATOR_ADDRESS - value: http://orchestrator-api \ No newline at end of file + resources: {} \ No newline at end of file diff --git a/src/Orchestrator/ApiReference/ApiClientBuilder.cs b/src/Orchestrator/ApiReference/ApiClientBuilder.cs index 818a0c09..b5a3bb0f 100644 --- a/src/Orchestrator/ApiReference/ApiClientBuilder.cs +++ b/src/Orchestrator/ApiReference/ApiClientBuilder.cs @@ -21,10 +21,11 @@ public ApiClientBuilder(IHttpClientFactory httpClientFactory, IEnvironment env) /// public RedisApiClient CreateRedisApiClient() { - var address = _env.GetEnvVariable("REDIS_INTERFACE_ADDRESS") ?? - throw new ArgumentNullException("REDIS_INTERFACE_ADDRESS", "REDIS_INTERFACE_ADDRESS environment variable not specified"); + var address = _env.GetEnvVariable("REDIS_INTERFACE_API_SERVICE_HOST") ?? + throw new ArgumentNullException("REDIS_INTERFACE_API_SERVICE_HOST", "REDIS_INTERFACE_API_SERVICE_HOST environment variable not specified"); + var url = $"http://{address}"; var client = _httpClientFactory.CreateClient(AppConfig.RedisApiClientName); - return new RedisApiClient($"{address}", client); + return new RedisApiClient(url, client); } /// diff --git a/src/Orchestrator/Deployment/DeploymentService.cs b/src/Orchestrator/Deployment/DeploymentService.cs index 2bcd9550..07e0ce6f 100644 --- a/src/Orchestrator/Deployment/DeploymentService.cs +++ b/src/Orchestrator/Deployment/DeploymentService.cs @@ -375,10 +375,10 @@ public V1Deployment CreateStartupDeployment(string name, string tag) }; var envList = new List { - new("REDIS_INTERFACE_ADDRESS", $"http://redis-interface-api"), - new("ORCHESTRATOR_ADDRESS", $"http://orchestrator-api"), - new("TASK_PLANNER_ADDRESS", $"http://task-planner-api"), - new("RESOURCE_PLANNER_ADDRESS", $"http://resource-planner-api"), + // new("REDIS_INTERFACE_ADDRESS", $"http://redis-interface-api"), + // new("ORCHESTRATOR_ADDRESS", $"http://orchestrator-api_SERVICE_HOST"), + // new("TASK_PLANNER_ADDRESS", $"http://task-planner-api"), + // new("RESOURCE_PLANNER_ADDRESS", $"http://resource-planner-api"), new("Middleware__Organization", mwConfig.Organization), new("Middleware__InstanceName", mwConfig.InstanceName), new("Middleware__InstanceType", mwConfig.InstanceType) diff --git a/src/RedisInterface/Responses/actionSequenceREsponse.cs b/src/RedisInterface/Responses/actionSequenceREsponse.cs deleted file mode 100644 index 5608a052..00000000 --- a/src/RedisInterface/Responses/actionSequenceREsponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Middleware.RedisInterface.Responses -{ - public record ActionSequenceResponse - { - [JsonPropertyName("taskName")] - public string TaskName { get; set; } - - [JsonPropertyName("taskId")] - public Guid TaskId { get; set; } - - [JsonPropertyName("actions")] - public List Actions { get; set; } - - public ActionSequenceResponse() - { - - } - - public ActionSequenceResponse(string taskName, Guid taskId, List actions) - { - TaskName = taskName; - TaskId = taskId; - Actions = actions; - } - } - -} diff --git a/src/ResourcePlanner/ApiReference/ApiClientBuilder.cs b/src/ResourcePlanner/ApiReference/ApiClientBuilder.cs index f83ddd39..4dc64a20 100644 --- a/src/ResourcePlanner/ApiReference/ApiClientBuilder.cs +++ b/src/ResourcePlanner/ApiReference/ApiClientBuilder.cs @@ -21,16 +21,16 @@ public ApiClientBuilder(IHttpClientFactory httpClientFactory, IEnvironment env) /// public RedisApiClient CreateRedisApiClient() { - var address = _env.GetEnvVariable("REDIS_INTERFACE_ADDRESS") ?? - throw new ArgumentNullException("REDIS_INTERFACE_ADDRESS", "REDIS_INTERFACE_ADDRESS environment variable not specified"); + var address = _env.GetEnvVariable("REDIS_INTERFACE_API_SERVICE_HOST") ?? + throw new ArgumentNullException("REDIS_INTERFACE_API_SERVICE_HOST", "REDIS_INTERFACE_API_SERVICE_HOST environment variable not specified"); var client = _httpClientFactory.CreateClient(AppConfig.RedisApiClientName); return new RedisApiClient($"{address}", client); } public OrchestratorApiClient CreateOrchestratorApiClient() { - var address = _env.GetEnvVariable("ORCHESTRATOR_ADDRESS") ?? - throw new ArgumentNullException("ORCHESTRATOR_ADDRESS", "ORCHESTRATOR_ADDRESS environment variable not specified"); + var address = _env.GetEnvVariable("ORCHESTRATOR_API_SERVICE_HOST") ?? + throw new ArgumentNullException("ORCHESTRATOR_API_SERVICE_HOST", "ORCHESTRATOR_API_SERVICE_HOST environment variable not specified"); var client = _httpClientFactory.CreateClient(AppConfig.OrchestratorApiClientName); return new OrchestratorApiClient($"{address}", client); } diff --git a/src/TaskPlanner/ApiReference/ApiClientBuilder.cs b/src/TaskPlanner/ApiReference/ApiClientBuilder.cs index 0d2d90c1..862b4f8c 100644 --- a/src/TaskPlanner/ApiReference/ApiClientBuilder.cs +++ b/src/TaskPlanner/ApiReference/ApiClientBuilder.cs @@ -20,19 +20,21 @@ public ApiClientBuilder(IHttpClientFactory httpClientFactory, IEnvironment env) /// public ResourcePlannerApiClient CreateResourcePlannerApiClient() { - var address = _env.GetEnvVariable("RESOURCE_PLANNER_ADDRESS") ?? - throw new ArgumentNullException("RESOURCE_PLANNER_ADDRESS", "RESOURCE_PLANNER_ADDRESS environment variable not specified"); + var address = _env.GetEnvVariable("RESOURCE_PLANNER_API_SERVICE_HOST") ?? + throw new ArgumentNullException("RESOURCE_PLANNER_API_SERVICE_HOST", "RESOURCE_PLANNER_API_SERVICE_HOST environment variable not specified"); + var url = $"http://{address}"; var client = _httpClientFactory.CreateClient("resourcePlannerApiClient"); - return new ResourcePlannerApiClient(address, client); + return new ResourcePlannerApiClient(url, client); } /// public OrchestratorApiClient CreateOrchestratorApiClient() { - var address = _env.GetEnvVariable("ORCHESTRATOR_ADDRESS") ?? - throw new ArgumentNullException("ORCHESTRATOR_ADDRESS", "ORCHESTRATOR_ADDRESS environment variable not specified"); + var address = _env.GetEnvVariable("ORCHESTRATOR_API_SERVICE_HOST") ?? + throw new ArgumentNullException("ORCHESTRATOR_API_SERVICE_HOST", "ORCHESTRATOR_API_SERVICE_HOST environment variable not specified"); + var url = $"http://{address}"; var client = _httpClientFactory.CreateClient("orchestratorApiClient"); - return new OrchestratorApiClient(address, client); + return new OrchestratorApiClient(url, client); } } From 9847de995f5e0e14b0bd01bb2fc1bd4b5731408e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= Date: Mon, 6 Mar 2023 14:54:21 +0100 Subject: [PATCH 05/31] Correct remaining address; Configuration change for orchestrator --- .vscode/launch.json | 4 ++-- .vscode/tasks.json | 2 +- src/Common/ExtensionMethods/CommonExtensions.cs | 2 +- src/Orchestrator/appsettings.json | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index af88c7c1..24fef950 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,13 +9,13 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "bridge-to-kubernetes.compound", - "program": "${workspaceFolder}/src/Orchestrator/bin/Debug/net6.0/Orchestrator.dll", + "program": "${workspaceFolder}/src/Orchestrator/bin/Debug/net6.0/net6.0/Orchestrator.dll", "args": [], "cwd": "${workspaceFolder}", "stopAtEntry": false, "env": { "ASPNETCORE_ENVIRONMENT": "Development", - "ASPNETCORE_URLS": "http://+:69" + "ASPNETCORE_URLS": "http://+:3001" }, "sourceFileMap": { "/Views": "${workspaceFolder}/Views" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1574dd6b..99cef77b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,7 +7,7 @@ "resource": "orchestrator-api", "resourceType": "service", "ports": [ - 69 + 3001 ], "targetCluster": "minikube", "targetNamespace": "middleware", diff --git a/src/Common/ExtensionMethods/CommonExtensions.cs b/src/Common/ExtensionMethods/CommonExtensions.cs index f4f69d69..8abcb5ac 100644 --- a/src/Common/ExtensionMethods/CommonExtensions.cs +++ b/src/Common/ExtensionMethods/CommonExtensions.cs @@ -19,7 +19,7 @@ public static IServiceCollection RegisterCommonServices(this IServiceCollection services.AddHttpClient(AppConfig.RedisApiClientName, (a) => { - a.BaseAddress = new Uri(Environment.GetEnvironmentVariable("REDIS_INTERFACE_ADDRESS")); + a.BaseAddress = new Uri(Environment.GetEnvironmentVariable("REDIS_INTERFACE_API_SERVICE_HOST")); a.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json")); }); diff --git a/src/Orchestrator/appsettings.json b/src/Orchestrator/appsettings.json index 5632d4e3..88d6bda7 100644 --- a/src/Orchestrator/appsettings.json +++ b/src/Orchestrator/appsettings.json @@ -6,7 +6,8 @@ }, "Redis": { "HostName": "", - "Password": "" + "Password": "", + "ClusterHostname": "" }, "CustomLogger": { "LoggerName": "", From bcbb39ae145b790639ec6108c1acfbaced3ce1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= Date: Mon, 6 Mar 2023 15:13:48 +0100 Subject: [PATCH 06/31] Accessing RedisInterface address --- src/Common/ExtensionMethods/CommonExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Common/ExtensionMethods/CommonExtensions.cs b/src/Common/ExtensionMethods/CommonExtensions.cs index 8abcb5ac..a927e135 100644 --- a/src/Common/ExtensionMethods/CommonExtensions.cs +++ b/src/Common/ExtensionMethods/CommonExtensions.cs @@ -16,10 +16,10 @@ public static class CommonExtensions public static IServiceCollection RegisterCommonServices(this IServiceCollection services) { services.AddSingleton(); - + var address = Environment.GetEnvironmentVariable("REDIS_INTERFACE_API_SERVICE_HOST"); services.AddHttpClient(AppConfig.RedisApiClientName, (a) => { - a.BaseAddress = new Uri(Environment.GetEnvironmentVariable("REDIS_INTERFACE_API_SERVICE_HOST")); + a.BaseAddress = new Uri($"http://{address}"); a.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json")); }); From 3c9ffb63a9317e6f0812b328d659ec546ffa658b Mon Sep 17 00:00:00 2001 From: adrianLIrobotics Date: Mon, 6 Mar 2023 15:33:34 +0100 Subject: [PATCH 07/31] InstanceRunningController implemented and running --- .../DataAccessExtensionMethods.cs | 2 +- .../Abstract/IInstanceRunningRepository.cs | 10 + .../Redis/RedisInstanceRunningRepository.cs | 68 +++- src/Models/Domain/InstanceRunningModel.cs | 3 +- src/Models/Dto/InstanceRunningDto.cs | 4 +- .../Controllers/InstanceRunningController.cs | 301 ++++++++++++++++++ 6 files changed, 377 insertions(+), 11 deletions(-) create mode 100644 src/DataAccess/Repositories/Abstract/IInstanceRunningRepository.cs create mode 100644 src/RedisInterface/Controllers/InstanceRunningController.cs diff --git a/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs b/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs index 5c326610..3f727a0b 100644 --- a/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs +++ b/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs @@ -55,7 +55,7 @@ public static IServiceCollection RegisterRepositories(this IServiceCollection se services.AddScoped(); services.AddScoped(); services.AddScoped(); - + services.AddScoped(); return services; } diff --git a/src/DataAccess/Repositories/Abstract/IInstanceRunningRepository.cs b/src/DataAccess/Repositories/Abstract/IInstanceRunningRepository.cs new file mode 100644 index 00000000..59a94386 --- /dev/null +++ b/src/DataAccess/Repositories/Abstract/IInstanceRunningRepository.cs @@ -0,0 +1,10 @@ +using Middleware.Models.Domain; + +namespace Middleware.DataAccess.Repositories.Abstract +{ + public interface IInstanceRunningRepository : IBaseRepository, IRelationRepository + { + Task PatchInstanceAsync(Guid id, InstanceRunningModel patch); + + } +} diff --git a/src/DataAccess/Repositories/Redis/RedisInstanceRunningRepository.cs b/src/DataAccess/Repositories/Redis/RedisInstanceRunningRepository.cs index 8d2b3ac6..65b62d53 100644 --- a/src/DataAccess/Repositories/Redis/RedisInstanceRunningRepository.cs +++ b/src/DataAccess/Repositories/Redis/RedisInstanceRunningRepository.cs @@ -1,12 +1,66 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Microsoft.IdentityModel.Tokens; +using Middleware.DataAccess.Repositories.Abstract; +using Middleware.Models.Domain; +using Middleware.Models.Dto; +using Redis.OM.Contracts; +using RedisGraphDotNet.Client; +using Serilog; -namespace Middleware.DataAccess.Repositories.Redis +namespace Middleware.DataAccess.Repositories { - internal class RedisInstanceRunningRepository + public class RedisInstanceRunningRepository : RedisRepository, IInstanceRunningRepository { + /// + /// Default constructor + /// + /// + /// + /// + public RedisInstanceRunningRepository(IRedisConnectionProvider provider, IRedisGraphClient redisGraph, ILogger logger) : base(provider, redisGraph, true, logger) + { + } + + /// + /// Patching properties for InstaceModel + /// + /// + /// + /// Patched model + public async Task PatchInstanceAsync(Guid id, InstanceRunningModel patch) + { + InstanceRunningModel? currentModel = await GetByIdAsync(id); + if (currentModel == null) + { + return null; + } + if (!string.IsNullOrEmpty(patch.Name)) + { + currentModel.Name = patch.Name; + } + if (!string.IsNullOrEmpty(patch.ServiceType)) + { + currentModel.ServiceType = patch.ServiceType; + } + if (!string.IsNullOrEmpty(patch.ServiceInstanceId.ToString())) + { + currentModel.ServiceInstanceId = patch.ServiceInstanceId; + } + if (!string.IsNullOrEmpty(patch.ServiceUrl)) + { + currentModel.ServiceUrl = patch.ServiceUrl; + } + if (!string.IsNullOrEmpty(patch.ServiceStatus)) + { + currentModel.ServiceStatus = patch.ServiceStatus; + } + if (!string.IsNullOrEmpty(patch.DeployedTime.ToString())) + { + currentModel.DeployedTime = patch.DeployedTime; + } + + await UpdateAsync(currentModel); + return currentModel; + } + } } diff --git a/src/Models/Domain/InstanceRunningModel.cs b/src/Models/Domain/InstanceRunningModel.cs index 1a0c98ba..59366b5e 100644 --- a/src/Models/Domain/InstanceRunningModel.cs +++ b/src/Models/Domain/InstanceRunningModel.cs @@ -40,7 +40,8 @@ public override Dto.Dto ToDto() ServiceType = domain.ServiceType, ServiceInstanceId = domain.ServiceInstanceId.ToString(), ServiceUrl = domain.ServiceUrl, - ServiceStatus = domain.ServiceStatus + ServiceStatus = domain.ServiceStatus, + DeployedTime = domain.DeployedTime == default ? DateTimeOffset.Now : domain.DeployedTime }; } } diff --git a/src/Models/Dto/InstanceRunningDto.cs b/src/Models/Dto/InstanceRunningDto.cs index 1ac42ac7..39e4e4d2 100644 --- a/src/Models/Dto/InstanceRunningDto.cs +++ b/src/Models/Dto/InstanceRunningDto.cs @@ -24,7 +24,7 @@ public class InstanceRunningDto : Dto public string ServiceStatus { get; set; } = default!; [Indexed(Sortable = true)] - public DateTime DeployedTime { get; set; } + public DateTimeOffset DeployedTime { get; set; } public override BaseModel ToModel() { @@ -37,7 +37,7 @@ public override BaseModel ToModel() ServiceUrl = dto.ServiceUrl, ServiceInstanceId = Guid.Parse(dto.ServiceInstanceId), ServiceStatus = dto.ServiceStatus, - DeployedTime = dto.DeployedTime + DeployedTime = dto.DeployedTime.DateTime }; } diff --git a/src/RedisInterface/Controllers/InstanceRunningController.cs b/src/RedisInterface/Controllers/InstanceRunningController.cs new file mode 100644 index 00000000..d6f5b9d1 --- /dev/null +++ b/src/RedisInterface/Controllers/InstanceRunningController.cs @@ -0,0 +1,301 @@ +using System.Net; +using Microsoft.AspNetCore.Mvc; +using Middleware.Common.Responses; +using Middleware.DataAccess.Repositories.Abstract; +using Middleware.Models.Domain; + +namespace Middleware.RedisInterface.Controllers +{ + [Route("api/v1/[controller]")] + [ApiController] + public class InstanceRunningController : ControllerBase + { + private readonly IInstanceRunningRepository _instanceRunningRepository; + private readonly ILogger _logger; + + public InstanceRunningController(IInstanceRunningRepository instanceRunningRepository, ILogger logger) + { + _instanceRunningRepository = instanceRunningRepository ?? throw new ArgumentNullException(nameof(instanceRunningRepository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + /// Get all the InstanceModel entities + /// + /// the list of InstanceModel entities + [HttpGet(Name = "InstanceRunningGetAll")] + [ProducesResponseType(typeof(InstanceRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task>> GetAllAsync() + { + try + { + List models = await _instanceRunningRepository.GetAllAsync(); + if (models.Any() == false) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Objects were not found.")); + } + return Ok(models); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Get an InstanceModel entity by id + /// + /// + /// the InstanceModel entity for the specified id + [HttpGet] + [Route("{id}", Name = "InstanceRunningGetById")] + [ProducesResponseType(typeof(InstanceRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetByIdAsync(Guid id) + { + try + { + InstanceRunningModel model = await _instanceRunningRepository.GetByIdAsync(id); + if (model == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); + } + return Ok(model); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Add a new InstanceModel entity + /// + /// + /// the newly created InstanceModel entity + [HttpPost(Name = "InstanceRunningAdd")] + [ProducesResponseType(typeof(InstanceRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task> AddAsync([FromBody] InstanceRunningModel model) + { + if (model == null) + { + return BadRequest("Parameters were not specified."); + } + try + { + InstanceRunningModel instance = await _instanceRunningRepository.AddAsync(model); + if (instance is null) + { + return StatusCode((int)HttpStatusCode.InternalServerError, + new ApiResponse((int)HttpStatusCode.InternalServerError, + "Could not add the instance to the data store")); + } + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + return Ok(model); + } + + /// + /// Partially update an existing InstanceModel entity + /// + /// + /// + /// the modified InstanceModel entity + [HttpPatch] + [Route("{id}", Name = "InstanceRunningPatch")] + [ProducesResponseType(typeof(InstanceRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task PatchInstanceAsync([FromBody] InstanceRunningModel patch, [FromRoute] Guid id) + { + try + { + InstanceRunningModel model = await _instanceRunningRepository.PatchInstanceAsync(id, patch); + if (model == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object to be updated was not found.")); + } + return Ok(model); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Delete an InstanceModel entity for the given id + /// + /// + /// no return + [HttpDelete] + [Route("{id}", Name = "InstanceRunningDelete")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task DeleteByIdAsync(Guid id) + { + try + { + var deleted = await _instanceRunningRepository.DeleteByIdAsync(id); + if (deleted == false) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified Instance has not been found.")); + } + return Ok(); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Creates a new relation between two models + /// + /// + /// + [HttpPost] + [Route("AddRelation", Name = "InstanceRunningAddRelation")] + [ProducesResponseType(typeof(RelationModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task> AddRelationAsync([FromBody] RelationModel model) + { + if (model == null) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Parameters were not specified.")); + } + try + { + bool isValid = await _instanceRunningRepository.AddRelationAsync(model); + if (!isValid) + { + return StatusCode((int)HttpStatusCode.InternalServerError, new ApiResponse((int)HttpStatusCode.NotFound, "The relation was not created")); + } + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + return Ok(model); + } + + + /// + /// Deletes a new relation between two models + /// + /// + /// + [HttpDelete] + [Route("DeleteRelation", Name = "InstanceRunningDeleteRelation")] + [ProducesResponseType(typeof(RelationModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task DeleteRelationAsync([FromBody] RelationModel model) + { + if (model == null) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Parameters were not specified.")); + } + try + { + bool isValid = await _instanceRunningRepository.DeleteRelationAsync(model); + if (!isValid) + { + return StatusCode((int)HttpStatusCode.InternalServerError, new ApiResponse((int)HttpStatusCode.NotFound, "The relation was not deleted")); + } + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + return Ok(); + } + + /// + /// Retrieves a single relation by name + /// + /// + /// + /// + [HttpGet] + [Route("relation/{name}", Name = "InstanceRunningGetRelationByName")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetRelationAsync(Guid id, string name) + { + try + { + var relations = await _instanceRunningRepository.GetRelation(id, name); + if (!relations.Any()) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found.")); + } + return Ok(relations); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Retrieves two relations by their names + /// + /// + /// + /// + /// + [HttpGet] + [Route("relations/{firstName}/{secondName}", Name = "InstanceRunningGetRelationsByName")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetRelationsAsync(Guid id, string firstName, string secondName) + { + try + { + List relationNames = new List() { firstName, secondName }; + var relations = await _instanceRunningRepository.GetRelations(id, relationNames); + if (!relations.Any()) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found")); + } + return Ok(relations); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + } +} From 86c93541546d3bc93cadb921bab6994f4cb10695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= Date: Mon, 6 Mar 2023 15:34:06 +0100 Subject: [PATCH 08/31] Specify port in k8s service --- src/Common/ExtensionMethods/CommonExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Common/ExtensionMethods/CommonExtensions.cs b/src/Common/ExtensionMethods/CommonExtensions.cs index a927e135..56db5c61 100644 --- a/src/Common/ExtensionMethods/CommonExtensions.cs +++ b/src/Common/ExtensionMethods/CommonExtensions.cs @@ -16,10 +16,10 @@ public static class CommonExtensions public static IServiceCollection RegisterCommonServices(this IServiceCollection services) { services.AddSingleton(); - var address = Environment.GetEnvironmentVariable("REDIS_INTERFACE_API_SERVICE_HOST"); + var address = Environment.GetEnvironmentVariable("REDIS_INTERFACE_API_PORT"); services.AddHttpClient(AppConfig.RedisApiClientName, (a) => { - a.BaseAddress = new Uri($"http://{address}"); + a.BaseAddress = new Uri(address.Replace("tcp", "http", StringComparison.InvariantCultureIgnoreCase)); a.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json")); }); From 4dcbeb8c5f5a8453c0a60b31d4361ff4f07ffd1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= Date: Mon, 6 Mar 2023 17:45:54 +0100 Subject: [PATCH 09/31] Use simple planning for resource planning --- src/TaskPlanner/Controllers/PlanController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TaskPlanner/Controllers/PlanController.cs b/src/TaskPlanner/Controllers/PlanController.cs index a5ad7e52..1e70a916 100644 --- a/src/TaskPlanner/Controllers/PlanController.cs +++ b/src/TaskPlanner/Controllers/PlanController.cs @@ -67,7 +67,7 @@ public async Task> GetPlan([FromBody] CreatePlanRequest Task = tmpTaskSend }; ResourcePlanner.TaskModel tmpFinalTask = - await _resourcePlannerClient.GetResourcePlanAsync(resourceInput); + await _resourcePlannerClient.ResourceAsync(resourceInput); TaskModel resourcePlan = _mapper.Map(tmpFinalTask); if (dryRun) From 8af5b8afb0cc88609412cfd91f99bfe3c3771bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= Date: Mon, 6 Mar 2023 21:58:19 +0100 Subject: [PATCH 10/31] Add nullable properties to InstanceModel --- .vscode/launch.json | 20 +++++ .vscode/tasks.json | 82 +++++++++++++------ k8s/orchestrator/orchestrator.yaml | 6 +- .../ExtensionMethods/CommonExtensions.cs | 8 +- src/Models/Domain/InstanceModel.cs | 22 ++--- src/Orchestrator/.vscode/launch.json | 45 ++++++++++ src/Orchestrator/.vscode/tasks.json | 25 ++++++ 7 files changed, 169 insertions(+), 39 deletions(-) create mode 100644 src/Orchestrator/.vscode/launch.json create mode 100644 src/Orchestrator/.vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 24fef950..d8a443fe 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -21,6 +21,26 @@ "/Views": "${workspaceFolder}/Views" } }, + { + "name": "TaskPlanner .NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/src/TaskPlanner/bin/Debug/net6.0/TaskPlanner.dll", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, { "name": "Orchestrator .NET Core Launch (web)", "type": "coreclr", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 99cef77b..58699800 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,25 +1,61 @@ { - "version": "2.0.0", - "tasks": [ - { - "label": "bridge-to-kubernetes.resource", - "type": "bridge-to-kubernetes.resource", - "resource": "orchestrator-api", - "resourceType": "service", - "ports": [ - 3001 - ], - "targetCluster": "minikube", - "targetNamespace": "middleware", - "useKubernetesServiceEnvironmentVariables": true - }, - { - "label": "bridge-to-kubernetes.compound", - "dependsOn": [ - "bridge-to-kubernetes.resource", - "build" - ], - "dependsOrder": "sequence" - } - ] + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/src/Orchestrator/Orchestrator.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/src/Orchestrator/Orchestrator.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/src/Orchestrator/Orchestrator.csproj" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "bridge-to-kubernetes.resource", + "type": "bridge-to-kubernetes.resource", + "resource": "orchestrator-api", + "resourceType": "service", + "ports": [ + 3001 + ], + "targetCluster": "minikube", + "targetNamespace": "middleware", + "useKubernetesServiceEnvironmentVariables": true + }, + { + "label": "bridge-to-kubernetes.compound", + "dependsOn": [ + "bridge-to-kubernetes.resource", + "build" + ], + "dependsOrder": "sequence" + } + ] } \ No newline at end of file diff --git a/k8s/orchestrator/orchestrator.yaml b/k8s/orchestrator/orchestrator.yaml index 78166d6a..12586354 100644 --- a/k8s/orchestrator/orchestrator.yaml +++ b/k8s/orchestrator/orchestrator.yaml @@ -35,14 +35,14 @@ spec: - name: awsecr-cred containers: - name: orchestrator-api - image: 394603622351.dkr.ecr.eu-west-1.amazonaws.com/orchestrator-api:v0.2.1-rc1 + image: 394603622351.dkr.ecr.eu-west-1.amazonaws.com/orchestrator-api:v0.2.1-rc6 imagePullPolicy: Always resources: {} env: - name: AWS_IMAGE_REGISTRY value: 394603622351.dkr.ecr.eu-west-1.amazonaws.com # to be replaced with real value - - name: REDIS_INTERFACE_ADDRESS - value: http://redis-interface-api # to be replaced with real value + - name: REDIS_INTERFACE_API_PORT + value: TCP://redis-interface-api.middleware.cluster.local # to be replaced with real value - name: Middleware__Organization value: 5G-ERA # to be replaced with real value - name: Middleware__InstanceName diff --git a/src/Common/ExtensionMethods/CommonExtensions.cs b/src/Common/ExtensionMethods/CommonExtensions.cs index 56db5c61..e3cb84e6 100644 --- a/src/Common/ExtensionMethods/CommonExtensions.cs +++ b/src/Common/ExtensionMethods/CommonExtensions.cs @@ -16,10 +16,14 @@ public static class CommonExtensions public static IServiceCollection RegisterCommonServices(this IServiceCollection services) { services.AddSingleton(); - var address = Environment.GetEnvironmentVariable("REDIS_INTERFACE_API_PORT"); services.AddHttpClient(AppConfig.RedisApiClientName, (a) => { - a.BaseAddress = new Uri(address.Replace("tcp", "http", StringComparison.InvariantCultureIgnoreCase)); + // get the value within the method to evaluate it when the service is already working + // to respond to any changes in the values + var host = Environment.GetEnvironmentVariable("REDIS_INTERFACE_API_SERVICE_HOST"); + var port = Environment.GetEnvironmentVariable("REDIS_INTERFACE_API_SERVICE_PORT"); + var address = $"http://{host}:{port}"; + a.BaseAddress = new Uri(address); a.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json")); }); diff --git a/src/Models/Domain/InstanceModel.cs b/src/Models/Domain/InstanceModel.cs index c620bb95..823effb4 100644 --- a/src/Models/Domain/InstanceModel.cs +++ b/src/Models/Domain/InstanceModel.cs @@ -17,40 +17,40 @@ public class InstanceModel : BaseModel public Guid ServiceInstanceId { get; set; } [JsonPropertyName("ServiceType")] - public string ServiceType { get; set; } + public string? ServiceType { get; set; } [JsonPropertyName("IsReusable")] public bool? IsReusable { get; set; } [JsonPropertyName("DesiredStatus")] - public string DesiredStatus { get; set; } + public string? DesiredStatus { get; set; } [JsonPropertyName("ServiceUrl")] - public string ServiceUrl { get; set; } + public string? ServiceUrl { get; set; } [JsonPropertyName("RosTopicsPub")] - public List RosTopicsPub { get; set; } // compulsory field + public List RosTopicsPub { get; set; } = new();// compulsory field [JsonPropertyName("RosTopicsSub")] - public List RosTopicsSub { get; set; } // compulsory field + public List RosTopicsSub { get; set; } = new();// compulsory field [JsonPropertyName("ROSVersion")] public int RosVersion { get; set; } // compulsory field [JsonPropertyName("ROSDistro")] - public string ROSDistro { get; set; } // compulsory field + public string? ROSDistro { get; set; } // compulsory field [JsonPropertyName("Tags")] - public List Tags { get; set; } + public List? Tags { get; set; } [JsonPropertyName("InstanceFamily")] - public string InstanceFamily { get; set; } // Compulsory field + public string? InstanceFamily { get; set; } // Compulsory field [JsonPropertyName("SuccessRate")] public int SuccessRate { get; set; } [JsonPropertyName("ServiceStatus")] - public string ServiceStatus { get; set; } //updated every 10 sec + public string? ServiceStatus { get; set; } //updated every 10 sec [JsonPropertyName("ContainerImage")] [JsonIgnore] @@ -78,8 +78,8 @@ public bool IsValid() if (RosVersion == 0) return false; if (string.IsNullOrEmpty(MinimumRam.ToString())) return false; if (string.IsNullOrEmpty(MinimumNumCores.ToString())) return false; - if (string.IsNullOrEmpty(ROSDistro.ToString())) return false; - if (string.IsNullOrEmpty(InstanceFamily.ToString())) return false; + if (string.IsNullOrEmpty(ROSDistro?.ToString())) return false; + if (string.IsNullOrEmpty(InstanceFamily?.ToString())) return false; if (!rosDistrosEnum.Contains(ROSDistro)) return false; // if (string.IsNullOrEmpty(RosTopicsPub.ToString())) return false; // if (string.IsNullOrEmpty(RosTopicsSub.ToString())) return false; diff --git a/src/Orchestrator/.vscode/launch.json b/src/Orchestrator/.vscode/launch.json new file mode 100644 index 00000000..10a831c6 --- /dev/null +++ b/src/Orchestrator/.vscode/launch.json @@ -0,0 +1,45 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Orchestrator .NET Core Launch (web) with Kubernetes", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "bridge-to-kubernetes.compound", + "program": "${workspaceFolder}/bin/Debug/net6.0/net6.0/Orchestrator.dll", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://+:3001" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": "Orchestrator .NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}Development/middleware/src/Orchestrator/bin/Debug/net6.0/net6.0/Orchestrator.dll", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + } + ] +} \ No newline at end of file diff --git a/src/Orchestrator/.vscode/tasks.json b/src/Orchestrator/.vscode/tasks.json new file mode 100644 index 00000000..99cef77b --- /dev/null +++ b/src/Orchestrator/.vscode/tasks.json @@ -0,0 +1,25 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "bridge-to-kubernetes.resource", + "type": "bridge-to-kubernetes.resource", + "resource": "orchestrator-api", + "resourceType": "service", + "ports": [ + 3001 + ], + "targetCluster": "minikube", + "targetNamespace": "middleware", + "useKubernetesServiceEnvironmentVariables": true + }, + { + "label": "bridge-to-kubernetes.compound", + "dependsOn": [ + "bridge-to-kubernetes.resource", + "build" + ], + "dependsOrder": "sequence" + } + ] +} \ No newline at end of file From 6cf6663c8001acc5bfe61cdeb08e3db56e852637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= Date: Mon, 6 Mar 2023 23:12:58 +0100 Subject: [PATCH 11/31] Final touches to the delpoyment process --- k8s/orchestrator/orchestrator.yaml | 8 +++++--- .../Services/RedisInterfaceClientService.cs | 15 ++++++++------- src/Models/Domain/ActionPlanModel.cs | 3 ++- .../Controllers/OrchestrateController.cs | 4 ++-- src/Orchestrator/Deployment/DeploymentService.cs | 14 +++++++------- src/Orchestrator/Jobs/UpdateStatusJob.cs | 2 +- src/Orchestrator/Models/DeploymentPairModel.cs | 7 ++++++- 7 files changed, 31 insertions(+), 22 deletions(-) diff --git a/k8s/orchestrator/orchestrator.yaml b/k8s/orchestrator/orchestrator.yaml index 12586354..3476a9ce 100644 --- a/k8s/orchestrator/orchestrator.yaml +++ b/k8s/orchestrator/orchestrator.yaml @@ -35,14 +35,16 @@ spec: - name: awsecr-cred containers: - name: orchestrator-api - image: 394603622351.dkr.ecr.eu-west-1.amazonaws.com/orchestrator-api:v0.2.1-rc6 + image: 394603622351.dkr.ecr.eu-west-1.amazonaws.com/orchestrator-api:v0.2.1-rc7 imagePullPolicy: Always resources: {} env: - name: AWS_IMAGE_REGISTRY value: 394603622351.dkr.ecr.eu-west-1.amazonaws.com # to be replaced with real value - - name: REDIS_INTERFACE_API_PORT - value: TCP://redis-interface-api.middleware.cluster.local # to be replaced with real value + - name: REDIS_INTERFACE_API_SERVICE_HOST + value: redis-interface-api.middleware.cluster.local # to be replaced with real value + - name: REDIS_INTERFACE_API_SERVICE_PORT + value: "80" - name: Middleware__Organization value: 5G-ERA # to be replaced with real value - name: Middleware__InstanceName diff --git a/src/Common/Services/RedisInterfaceClientService.cs b/src/Common/Services/RedisInterfaceClientService.cs index 0317a1f3..578f2a17 100644 --- a/src/Common/Services/RedisInterfaceClientService.cs +++ b/src/Common/Services/RedisInterfaceClientService.cs @@ -1,4 +1,5 @@ -using System.Net.Http.Json; +using System.Net; +using System.Net.Http.Json; using System.Web; using Microsoft.Extensions.Logging; using Middleware.Common.Config; @@ -98,10 +99,8 @@ public async Task ActionPlanGetByIdAsync(Guid id, CancellationT { var result = await _httpClient.GetAsync(url, token); - if (result.IsSuccessStatusCode == false) - { - throw new InvalidOperationException(); - } + if (result.StatusCode == HttpStatusCode.NotFound) + return null; var body = await result.Content.ReadAsStringAsync(token); ActionPlanModel actionPlan = JsonConvert.DeserializeObject(body); @@ -155,8 +154,10 @@ public async Task ActionPlanAddAsync(ActionPlanModel actionPlan, Cancellat string url = $"/api/v1/Action/plan"; try { - var result = await _httpClient.PostAsJsonAsync(url, JsonConvert.SerializeObject(actionPlan)); - + var result = await _httpClient.PostAsJsonAsync(url, actionPlan); + //var resp = result. + var response = await result.Content.ReadAsStringAsync(); + _logger.LogInformation("httpResponse: {0}", response); return result.IsSuccessStatusCode; } catch (Exception ex) diff --git a/src/Models/Domain/ActionPlanModel.cs b/src/Models/Domain/ActionPlanModel.cs index e8c30511..8bb61f72 100644 --- a/src/Models/Domain/ActionPlanModel.cs +++ b/src/Models/Domain/ActionPlanModel.cs @@ -39,9 +39,10 @@ public ActionPlanModel() { } - public ActionPlanModel(Guid id, string name, List actionSequence, Guid robotId) + public ActionPlanModel(Guid id, Guid taskId, string name, List actionSequence, Guid robotId) { Id = id; + TaskId = taskId; Name = name; ActionSequence = actionSequence; RobotId = robotId; diff --git a/src/Orchestrator/Controllers/OrchestrateController.cs b/src/Orchestrator/Controllers/OrchestrateController.cs index a6651463..f78d18ba 100644 --- a/src/Orchestrator/Controllers/OrchestrateController.cs +++ b/src/Orchestrator/Controllers/OrchestrateController.cs @@ -160,7 +160,7 @@ public async Task DeletePlanById(Guid id) var isSuccess = await _deploymentService.DeletePlanAsync(actionPlan); //Delete the LOCATED_AT relationships between instance and edge/cloud. - List actionTempList = actionPlan.ActionSequence; + /*List actionTempList = actionPlan.ActionSequence; foreach (ActionModel action in actionTempList) { BaseModel placement; @@ -187,7 +187,7 @@ public async Task DeletePlanById(Guid id) //delete all the located_at relationships between all instances of 1 action and the resources been edge/cloud await _redisInterfaceClient.DeleteRelationAsync(instance, placement, "LOCATED_AT"); } - } + }*/ // //Delete the relationship OWNS between the robot and the task that has been completed. // RelationModel deleteRelationRobotOwnsTask = new RelationModel(); diff --git a/src/Orchestrator/Deployment/DeploymentService.cs b/src/Orchestrator/Deployment/DeploymentService.cs index 07e0ce6f..a0d52d89 100644 --- a/src/Orchestrator/Deployment/DeploymentService.cs +++ b/src/Orchestrator/Deployment/DeploymentService.cs @@ -68,13 +68,13 @@ public async Task DeployAsync(TaskModel task, Guid robotId) var deploymentNames = deployments.Items.Select(d => d.Metadata.Name).OrderBy(d => d).ToArray(); _logger.LogDebug("Current deployments: {deployments}", string.Join(", ", deploymentNames)); - foreach (var seq in task.ActionSequence) + foreach (var seq in task.ActionSequence!) { //BaseModel location; //location = seq.PlacementType.ToLower().Contains("cloud") // ? await _redisInterfaceClient.GetCloudByNameAsync(seq.Placement) // : await _redisInterfaceClient.GetEdgeByNameAsync(seq.Placement); - foreach (var service in seq.Services) + foreach (var service in seq.Services!) { try { @@ -125,12 +125,12 @@ public async Task DeployAsync(TaskModel task, Guid robotId) /// private async Task SaveActionSequence(TaskModel task, Guid robotId) { - var actionPlan = new ActionPlanModel(task.ActionPlanId, task.Name, task.ActionSequence, robotId); + var actionPlan = new ActionPlanModel(task.ActionPlanId, task.Id, task.Name, task.ActionSequence!, robotId); actionPlan.SetStatus("active"); var result = await _redisInterfaceClient.ActionPlanAddAsync(actionPlan); - return result != null; + return result; } private async Task DeployService(IKubernetes k8SClient, InstanceModel service, string[] deploymentNames) @@ -160,7 +160,7 @@ private async Task DeployService(IKubernetes k8SClient, InstanceModel service, s var deployedPair = await Deploy(k8SClient, cim); service.ServiceStatus = ServiceStatus.Idle.GetStringValue(); - service.ServiceInstanceId = Guid.Parse(deployedPair.Deployment.GetLabel("serviceId")); + service.ServiceInstanceId = deployedPair.InstanceId; _logger.LogDebug("Deployed the image {Name} with the Id {ServiceInstanceId}", service.Name, service.ServiceInstanceId); @@ -183,7 +183,7 @@ public async Task Deploy(IKubernetes k8SClient, ContainerIm var deployment = await Deploy(k8SClient, cim.K8SDeployment, cim.Name, instanceId, nameof(cim.K8SDeployment)); - return new DeploymentPairModel(deployment, service); + return new DeploymentPairModel(deployment, service, instanceId); } /// @@ -292,7 +292,7 @@ public async Task DeletePlanAsync(ActionPlanModel actionPlan) var k8sClient = _kubernetesBuilder.CreateKubernetesClient(); foreach (var action in actionPlan.ActionSequence) { - foreach (var srv in action.Services) + foreach (var srv in action.Services!) { retVal &= await DeleteInstance(k8sClient, srv); } diff --git a/src/Orchestrator/Jobs/UpdateStatusJob.cs b/src/Orchestrator/Jobs/UpdateStatusJob.cs index 4cb9e177..f31368d0 100644 --- a/src/Orchestrator/Jobs/UpdateStatusJob.cs +++ b/src/Orchestrator/Jobs/UpdateStatusJob.cs @@ -87,7 +87,7 @@ protected override async Task ExecuteJobAsync(IJobExecutionContext context) //check if all instances are down for at least half an hour, then terminate foreach (var action in seq.ActionSequence) { - if (action.Placement != _middlewareConfig.InstanceName) + if (action.Placement != $"{_middlewareConfig.InstanceName}-{_middlewareConfig.InstanceType}") continue; foreach (var instance in action.Services) diff --git a/src/Orchestrator/Models/DeploymentPairModel.cs b/src/Orchestrator/Models/DeploymentPairModel.cs index 6421601f..2d7ce637 100644 --- a/src/Orchestrator/Models/DeploymentPairModel.cs +++ b/src/Orchestrator/Models/DeploymentPairModel.cs @@ -12,10 +12,15 @@ public class DeploymentPairModel /// Deployed service /// public V1Service Service { get; set; } + /// + /// Identifier of the deployed instance + /// + public Guid InstanceId { get; set; } - public DeploymentPairModel(V1Deployment deployment, V1Service service) + public DeploymentPairModel(V1Deployment deployment, V1Service service, Guid instanceId) { Deployment = deployment; Service = service; + InstanceId = instanceId; } } \ No newline at end of file From cb808363367abf824949dc7a4b324a0fba06e603 Mon Sep 17 00:00:00 2001 From: adrianLIrobotics Date: Tue, 7 Mar 2023 10:02:58 +0100 Subject: [PATCH 12/31] Orchestrator: implement SaveActionSequence --- src/Common/Enums/ActionStatusEnum.cs | 10 ++++ .../Abstract/IRedisInterfaceClientService.cs | 4 ++ .../Services/RedisInterfaceClientService.cs | 48 ++++++++++++++++ src/Models/Domain/InstanceRunningModel.cs | 2 +- src/Models/Dto/InstanceRunningDto.cs | 4 +- .../Deployment/DeploymentService.cs | 56 ++++++++++++++++++- src/TaskPlanner/Controllers/PlanController.cs | 4 +- .../Controllers/RePlanController.cs | 2 +- 8 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 src/Common/Enums/ActionStatusEnum.cs diff --git a/src/Common/Enums/ActionStatusEnum.cs b/src/Common/Enums/ActionStatusEnum.cs new file mode 100644 index 00000000..4cf8ecd8 --- /dev/null +++ b/src/Common/Enums/ActionStatusEnum.cs @@ -0,0 +1,10 @@ +namespace Middleware.Common.Enums +{ + public enum ActionStatusEnum + { + Unknown, + Running, + Idle, + Off + } +} diff --git a/src/Common/Services/Abstract/IRedisInterfaceClientService.cs b/src/Common/Services/Abstract/IRedisInterfaceClientService.cs index 2b2d78c2..c855b0d6 100644 --- a/src/Common/Services/Abstract/IRedisInterfaceClientService.cs +++ b/src/Common/Services/Abstract/IRedisInterfaceClientService.cs @@ -168,4 +168,8 @@ Task DeleteRelationAsync(TSource source, TDirection d Task> ContainerImageGetForInstanceAsync(Guid id); Task> ContainerImageGetForInstanceAsync(Guid id, CancellationToken token); + + Task ActionRunningAddAsync(ActionRunningModel actionRunning); + + Task InstanceRunningAddAsync(InstanceRunningModel instanceRunning); } \ No newline at end of file diff --git a/src/Common/Services/RedisInterfaceClientService.cs b/src/Common/Services/RedisInterfaceClientService.cs index 0317a1f3..79197bb1 100644 --- a/src/Common/Services/RedisInterfaceClientService.cs +++ b/src/Common/Services/RedisInterfaceClientService.cs @@ -512,5 +512,53 @@ public async Task GetCloudByNameAsync(string name, CancellationToken throw; } } + + public async Task ActionRunningAddAsync(ActionRunningModel actionRunning) + { + return await ActionRunningAddAsync(actionRunning, CancellationToken.None); + } + + public async Task ActionRunningAddAsync(ActionRunningModel actionRunning, CancellationToken token) + { + if (actionRunning is null) + throw new ArgumentNullException(nameof(actionRunning)); + + string url = $"/api/v1/ActionRunning"; + try + { + var result = await _httpClient.PostAsJsonAsync(url, JsonConvert.SerializeObject(actionRunning)); + + return result.IsSuccessStatusCode; + } + catch (Exception ex) + { + _logger.LogError(ex, "There was en error while creating action plan: {plan}", actionRunning); + throw; + } + } + + public async Task InstanceRunningAddAsync(InstanceRunningModel instanceRunning) + { + return await InstanceRunningAddAsync(instanceRunning, CancellationToken.None); + } + + public async Task InstanceRunningAddAsync(InstanceRunningModel instanceRunning, CancellationToken token) + { + if (instanceRunning is null) + throw new ArgumentNullException(nameof(instanceRunning)); + + string url = $"/api/v1/InstanceRunning"; + try + { + var result = await _httpClient.PostAsJsonAsync(url, JsonConvert.SerializeObject(instanceRunning)); + + return result.IsSuccessStatusCode; + } + catch (Exception ex) + { + _logger.LogError(ex, "There was en error while creating action plan: {plan}", instanceRunning); + throw; + } + } } } \ No newline at end of file diff --git a/src/Models/Domain/InstanceRunningModel.cs b/src/Models/Domain/InstanceRunningModel.cs index 59366b5e..58ef4fd6 100644 --- a/src/Models/Domain/InstanceRunningModel.cs +++ b/src/Models/Domain/InstanceRunningModel.cs @@ -41,7 +41,7 @@ public override Dto.Dto ToDto() ServiceInstanceId = domain.ServiceInstanceId.ToString(), ServiceUrl = domain.ServiceUrl, ServiceStatus = domain.ServiceStatus, - DeployedTime = domain.DeployedTime == default ? DateTimeOffset.Now : domain.DeployedTime + DeployedTime = domain.DeployedTime == default ? DateTimeOffset.Now : domain.DeployedTime, }; } } diff --git a/src/Models/Dto/InstanceRunningDto.cs b/src/Models/Dto/InstanceRunningDto.cs index 39e4e4d2..ec73ff7f 100644 --- a/src/Models/Dto/InstanceRunningDto.cs +++ b/src/Models/Dto/InstanceRunningDto.cs @@ -26,6 +26,8 @@ public class InstanceRunningDto : Dto [Indexed(Sortable = true)] public DateTimeOffset DeployedTime { get; set; } + + public override BaseModel ToModel() { var dto = this; @@ -37,7 +39,7 @@ public override BaseModel ToModel() ServiceUrl = dto.ServiceUrl, ServiceInstanceId = Guid.Parse(dto.ServiceInstanceId), ServiceStatus = dto.ServiceStatus, - DeployedTime = dto.DeployedTime.DateTime + DeployedTime = dto.DeployedTime.DateTime, }; } diff --git a/src/Orchestrator/Deployment/DeploymentService.cs b/src/Orchestrator/Deployment/DeploymentService.cs index 2bcd9550..39fc1d7a 100644 --- a/src/Orchestrator/Deployment/DeploymentService.cs +++ b/src/Orchestrator/Deployment/DeploymentService.cs @@ -119,7 +119,8 @@ public async Task DeployAsync(TaskModel task, Guid robotId) } /// - /// Saves the specified task to the redis as an action plan + /// Saves the specified task to the redis as an action plan and add the actionRunnings and InstanceRunning + /// with their corresponding relationships. /// /// /// @@ -127,9 +128,58 @@ private async Task SaveActionSequence(TaskModel task, Guid robotId) { var actionPlan = new ActionPlanModel(task.ActionPlanId, task.Name, task.ActionSequence, robotId); actionPlan.SetStatus("active"); - + RobotModel robot = await _redisInterfaceClient.RobotGetByIdAsync(robotId); + await _redisInterfaceClient.AddRelationAsync(robot, actionPlan, "OWNS"); + + //Add the actionPlan to redis var result = await _redisInterfaceClient.ActionPlanAddAsync(actionPlan); + //Add the actionRunning to redis + foreach(ActionModel action in task.ActionSequence) + { + ActionRunningModel ActionRunningTemp = new ActionRunningModel(); + ActionRunningTemp.ActionPlanId = actionPlan.Id; + ActionRunningTemp.ActionParentId = action.Id; + ActionRunningTemp.Name = action.Name; + ActionRunningTemp.Tags = action.Tags; + ActionRunningTemp.Order = action.Order; + ActionRunningTemp.Placement = action.Placement; + ActionRunningTemp.PlacementType = action.PlacementType; + ActionRunningTemp.ActionPriority = action.ActionPriority; + ActionRunningTemp.ActionStatus = ActionStatusEnum.Running.ToString(); + ActionRunningTemp.Services = action.Services.Select(x=> new InstanceRunningModel() { Name = x.Name, ServiceInstanceId = x.ServiceInstanceId, + ServiceType = x.ServiceType, + ServiceUrl = x.ServiceUrl, + ServiceStatus = x.ServiceStatus, + DeployedTime = DateTime.Now + }).ToList(); + ActionRunningTemp.MinimumRam = action.MinimumRam; + ActionRunningTemp.MinimumNumCores = action.MinimumNumCores; + await _redisInterfaceClient.ActionRunningAddAsync(ActionRunningTemp); + await _redisInterfaceClient.AddRelationAsync(actionPlan, ActionRunningTemp, "CONSISTS_OF"); + + + //Add the InstanceRunning to redis + foreach (InstanceRunningModel runningInstance in ActionRunningTemp.Services) + { + await _redisInterfaceClient.InstanceRunningAddAsync(runningInstance); + await _redisInterfaceClient.AddRelationAsync(ActionRunningTemp, runningInstance, "CONSISTS_OF"); + + if (action.PlacementType == "CLOUD") + { + CloudModel cloud = await _redisInterfaceClient.GetCloudByNameAsync(action.Placement); + await _redisInterfaceClient.AddRelationAsync(runningInstance, cloud, "LOCATED_AT"); + } + if (action.PlacementType == "EDGE") + { + EdgeModel edge = await _redisInterfaceClient.GetEdgeByNameAsync(action.Placement); + await _redisInterfaceClient.AddRelationAsync(runningInstance, edge, "LOCATED_AT"); + } + + } + + } + return result != null; } @@ -312,7 +362,7 @@ public async Task DeletePlanAsync(ActionPlanModel actionPlan) _logger.LogError(ex, "The deletion of the Action Plan has failed!"); retVal = false; } - + //actionPlan.SetStatus("inactive"); return retVal; } diff --git a/src/TaskPlanner/Controllers/PlanController.cs b/src/TaskPlanner/Controllers/PlanController.cs index a5ad7e52..b6e9d045 100644 --- a/src/TaskPlanner/Controllers/PlanController.cs +++ b/src/TaskPlanner/Controllers/PlanController.cs @@ -73,7 +73,7 @@ public async Task> GetPlan([FromBody] CreatePlanRequest if (dryRun) return Ok(resourcePlan); - await _redisInterfaceClient.AddRelationAsync(robot2, resourcePlan, "OWNS"); + await _redisInterfaceClient.AddRelationAsync(robot2, resourcePlan, "WANTS_TO_RUN"); await _actionPlanner.PublishPlanAsync(resourcePlan, robot2); @@ -146,7 +146,7 @@ public async Task> GetSemanticPlan([FromBody] CreatePlan if (dryRun) // Will skip the orchestrator if true (will not deploy the actual plan.) return Ok(resourcePlan); - await _redisInterfaceClient.AddRelationAsync(robot2, resourcePlan, "OWNS"); + await _redisInterfaceClient.AddRelationAsync(robot2, resourcePlan, "WANTS_TO_RUN"); await _actionPlanner.PublishPlanAsync(resourcePlan, robot2); diff --git a/src/TaskPlanner/Controllers/RePlanController.cs b/src/TaskPlanner/Controllers/RePlanController.cs index e30c3b63..330415d3 100644 --- a/src/TaskPlanner/Controllers/RePlanController.cs +++ b/src/TaskPlanner/Controllers/RePlanController.cs @@ -77,7 +77,7 @@ public async Task> GetReplan([FromBody] CreateRePlanRequ //Delete previous plan in orchestrator and graph. --> TODO: AL 06/11/22 The replan is not shown in the graph, only in the ActionPlanModel index db. await _orchestratorClient.DeletePlanByIdAsync(oldPlan.ActionPlanId); - await _redisInterfaceClient.AddRelationAsync(robot, plan, "OWNS"); + //await _redisInterfaceClient.AddRelationAsync(robot, plan, "OWNS"); // Deploy replan Orchestrator.OrchestratorResourceInput tmpTaskOrchestratorSend = new Orchestrator.OrchestratorResourceInput() From c420c23b3b3aaabb422cea7c712f6616ac599e6b Mon Sep 17 00:00:00 2001 From: adrianLIrobotics Date: Tue, 7 Mar 2023 11:08:18 +0100 Subject: [PATCH 13/31] Fixes --- .../Deployment/DeploymentService.cs | 7 +- src/RedisInterface/RedisInterfaceSpec.json | 4025 ++++++++++++----- src/TaskPlanner/Controllers/PlanController.cs | 2 +- 3 files changed, 2829 insertions(+), 1205 deletions(-) diff --git a/src/Orchestrator/Deployment/DeploymentService.cs b/src/Orchestrator/Deployment/DeploymentService.cs index 39fc1d7a..bfb7d262 100644 --- a/src/Orchestrator/Deployment/DeploymentService.cs +++ b/src/Orchestrator/Deployment/DeploymentService.cs @@ -128,14 +128,15 @@ private async Task SaveActionSequence(TaskModel task, Guid robotId) { var actionPlan = new ActionPlanModel(task.ActionPlanId, task.Name, task.ActionSequence, robotId); actionPlan.SetStatus("active"); - RobotModel robot = await _redisInterfaceClient.RobotGetByIdAsync(robotId); - await _redisInterfaceClient.AddRelationAsync(robot, actionPlan, "OWNS"); + RobotModel robot = await _redisInterfaceClient.RobotGetByIdAsync(robotId); //Add the actionPlan to redis var result = await _redisInterfaceClient.ActionPlanAddAsync(actionPlan); + await _redisInterfaceClient.AddRelationAsync(robot, actionPlan, "OWNS"); + //Add the actionRunning to redis - foreach(ActionModel action in task.ActionSequence) + foreach (ActionModel action in task.ActionSequence) { ActionRunningModel ActionRunningTemp = new ActionRunningModel(); ActionRunningTemp.ActionPlanId = actionPlan.Id; diff --git a/src/RedisInterface/RedisInterfaceSpec.json b/src/RedisInterface/RedisInterfaceSpec.json index 669ccc5f..b0e623d8 100644 --- a/src/RedisInterface/RedisInterfaceSpec.json +++ b/src/RedisInterface/RedisInterfaceSpec.json @@ -1309,29 +1309,38 @@ } } }, - "/api/v1/Cloud": { + "/api/v1/ActionRunning": { "get": { "tags": [ - "Cloud" + "ActionRunning" ], - "operationId": "CloudGetAll", + "operationId": "ActionRunningGetAll", "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/ActionRunningModel" + } } }, "application/json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/ActionRunningModel" + } } }, "text/json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/ActionRunningModel" + } } } } @@ -1380,29 +1389,29 @@ }, "post": { "tags": [ - "Cloud" + "ActionRunning" ], - "operationId": "CloudAdd", + "operationId": "ActionRunningAdd", "requestBody": { "content": { "application/json-patch+json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } }, "application/*+json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } } } @@ -1413,17 +1422,17 @@ "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } } } @@ -1471,12 +1480,12 @@ } } }, - "/api/v1/Cloud/{id}": { + "/api/v1/ActionRunning/{id}": { "get": { "tags": [ - "Cloud" + "ActionRunning" ], - "operationId": "CloudGetById", + "operationId": "ActionRunningGetById", "parameters": [ { "name": "id", @@ -1494,17 +1503,17 @@ "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } } } @@ -1553,9 +1562,9 @@ }, "patch": { "tags": [ - "Cloud" + "ActionRunning" ], - "operationId": "CloudPatch", + "operationId": "ActionRunningPatch", "parameters": [ { "name": "id", @@ -1571,22 +1580,22 @@ "content": { "application/json-patch+json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } }, "application/*+json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } } } @@ -1597,17 +1606,17 @@ "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/CloudModel" + "$ref": "#/components/schemas/ActionRunningModel" } } } @@ -1656,9 +1665,9 @@ }, "delete": { "tags": [ - "Cloud" + "ActionRunning" ], - "operationId": "CloudDelete", + "operationId": "ActionRunningDelete", "parameters": [ { "name": "id", @@ -1717,12 +1726,12 @@ } } }, - "/api/v1/Cloud/AddRelation": { + "/api/v1/ActionRunning/AddRelation": { "post": { "tags": [ - "Cloud" + "ActionRunning" ], - "operationId": "CloudAddRelation", + "operationId": "ActionRunningAddRelation", "requestBody": { "content": { "application/json-patch+json": { @@ -1811,12 +1820,12 @@ } } }, - "/api/v1/Cloud/relation/{name}": { + "/api/v1/ActionRunning/relation/{name}": { "get": { "tags": [ - "Cloud" + "ActionRunning" ], - "operationId": "CloudGetRelationByName", + "operationId": "ActionRunningGetRelationByName", "parameters": [ { "name": "id", @@ -1908,12 +1917,12 @@ } } }, - "/api/v1/Cloud/relations/{firstName}/{secondName}": { + "/api/v1/ActionRunning/relations/{firstName}/{secondName}": { "get": { "tags": [ - "Cloud" + "ActionRunning" ], - "operationId": "CloudGetRelationsByName", + "operationId": "ActionRunningGetRelationsByName", "parameters": [ { "name": "id", @@ -2013,22 +2022,12 @@ } } }, - "/name/{name}": { + "/api/v1/Cloud": { "get": { "tags": [ "Cloud" ], - "operationId": "CloudGetDataByName", - "parameters": [ - { - "name": "name", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], + "operationId": "CloudGetAll", "responses": { "200": { "description": "Success", @@ -2091,46 +2090,32 @@ } } } - } - }, - "/api/v1/Cloud/free": { - "get": { + }, + "post": { "tags": [ "Cloud" ], - "operationId": "GetFreeCloudIds", + "operationId": "CloudAdd", "requestBody": { "content": { "application/json-patch+json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } + "$ref": "#/components/schemas/CloudModel" } }, "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } + "$ref": "#/components/schemas/CloudModel" } }, "text/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } + "$ref": "#/components/schemas/CloudModel" } }, "application/*+json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } + "$ref": "#/components/schemas/CloudModel" } } } @@ -2141,46 +2126,17 @@ "content": { "text/plain": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } - } - }, - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } - } - }, - "text/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" + "$ref": "#/components/schemas/CloudModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/ApiResponse" + "$ref": "#/components/schemas/CloudModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/ApiResponse" + "$ref": "#/components/schemas/CloudModel" } } } @@ -2228,74 +2184,40 @@ } } }, - "/api/v1/Cloud/lessBusy": { + "/api/v1/Cloud/{id}": { "get": { "tags": [ "Cloud" ], - "operationId": "GetLessBusyClouds", - "requestBody": { - "content": { - "application/json-patch+json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } - } - }, - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } - } - }, - "text/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } - } - }, - "application/*+json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } - } + "operationId": "CloudGetById", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" } } - }, + ], "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } + "$ref": "#/components/schemas/CloudModel" } }, "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } + "$ref": "#/components/schemas/CloudModel" } }, "text/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CloudModel" - } + "$ref": "#/components/schemas/CloudModel" } } } @@ -2320,26 +2242,6 @@ } } }, - "400": { - "description": "Bad Request", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, "500": { "description": "Server Error", "content": { @@ -2361,44 +2263,64 @@ } } } - } - }, - "/api/v1/Cloud/{name}/containers/count": { - "get": { + }, + "patch": { "tags": [ "Cloud" ], - "operationId": "GetNumCloudContainersByName", + "operationId": "CloudPatch", "parameters": [ { - "name": "name", + "name": "id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/CloudModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/CloudModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/CloudModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/CloudModel" + } + } + } + }, "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/CloudModel" } }, "application/json": { "schema": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/CloudModel" } }, "text/json": { "schema": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/CloudModel" } } } @@ -2423,26 +2345,6 @@ } } }, - "400": { - "description": "Bad Request", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, "500": { "description": "Server Error", "content": { @@ -2464,58 +2366,49 @@ } } } - } - }, - "/api/v1/Cloud/{id}/containers/count": { - "get": { + }, + "delete": { "tags": [ "Cloud" ], - "operationId": "GetNumCloudContainersById", + "operationId": "CloudDelete", "parameters": [ - { - "name": "cloudId", - "in": "query", - "schema": { - "type": "string", - "format": "uuid" - } - }, { "name": "id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], "responses": { "200": { - "description": "Success", + "description": "Success" + }, + "404": { + "description": "Not Found", "content": { "text/plain": { "schema": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/ApiResponse" } }, "application/json": { "schema": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/ApiResponse" } }, "text/json": { "schema": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/ApiResponse" } } } }, - "404": { - "description": "Not Found", + "500": { + "description": "Server Error", "content": { "text/plain": { "schema": { @@ -2533,6 +2426,60 @@ } } } + } + } + } + }, + "/api/v1/Cloud/AddRelation": { + "post": { + "tags": [ + "Cloud" + ], + "operationId": "CloudAddRelation", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + } + } }, "400": { "description": "Bad Request", @@ -2577,13 +2524,21 @@ } } }, - "/api/v1/Cloud/{name}/busy": { + "/api/v1/Cloud/relation/{name}": { "get": { "tags": [ "Cloud" ], - "operationId": "IsBusyCloudByName", + "operationId": "CloudGetRelationByName", "parameters": [ + { + "name": "id", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, { "name": "name", "in": "path", @@ -2599,20 +2554,26 @@ "content": { "text/plain": { "schema": { - "type": "integer", - "format": "int32" + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } } }, "application/json": { "schema": { - "type": "integer", - "format": "int32" + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } } }, "text/json": { "schema": { - "type": "integer", - "format": "int32" + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } } } } @@ -2637,26 +2598,6 @@ } } }, - "400": { - "description": "Bad Request", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, "500": { "description": "Server Error", "content": { @@ -2680,15 +2621,15 @@ } } }, - "/api/v1/Cloud/{id}/busy": { + "/api/v1/Cloud/relations/{firstName}/{secondName}": { "get": { "tags": [ "Cloud" ], - "operationId": "isBusyCloudById", + "operationId": "CloudGetRelationsByName", "parameters": [ { - "name": "cloudId", + "name": "id", "in": "query", "schema": { "type": "string", @@ -2696,7 +2637,15 @@ } }, { - "name": "id", + "name": "firstName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "secondName", "in": "path", "required": true, "schema": { @@ -2710,20 +2659,26 @@ "content": { "text/plain": { "schema": { - "type": "integer", - "format": "int32" + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } } }, "application/json": { "schema": { - "type": "integer", - "format": "int32" + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } } }, "text/json": { "schema": { - "type": "integer", - "format": "int32" + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } } } } @@ -2748,26 +2703,6 @@ } } }, - "400": { - "description": "Bad Request", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, "500": { "description": "Server Error", "content": { @@ -2791,29 +2726,39 @@ } } }, - "/api/v1/ContainerImage": { + "/name/{name}": { "get": { "tags": [ - "ContainerImage" + "Cloud" + ], + "operationId": "CloudGetDataByName", + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } ], - "operationId": "ContainerImageGetAll", "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "$ref": "#/components/schemas/CloudModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "$ref": "#/components/schemas/CloudModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "$ref": "#/components/schemas/CloudModel" } } } @@ -2859,32 +2804,46 @@ } } } - }, - "post": { + } + }, + "/api/v1/Cloud/free": { + "get": { "tags": [ - "ContainerImage" + "Cloud" ], - "operationId": "ContainerImageAdd", + "operationId": "GetFreeCloudIds", "requestBody": { "content": { "application/json-patch+json": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } } }, "application/json": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } } }, "text/json": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } } }, "application/*+json": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } } } } @@ -2895,17 +2854,46 @@ "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } } }, "application/json": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } } }, "text/json": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" } } } @@ -2953,40 +2941,74 @@ } } }, - "/api/v1/ContainerImage/{id}": { + "/api/v1/Cloud/lessBusy": { "get": { "tags": [ - "ContainerImage" + "Cloud" ], - "operationId": "ContainerImageGetbyId", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "operationId": "GetLessBusyClouds", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } + } + }, + "application/*+json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } + } } } - ], + }, "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } } }, "application/json": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } } }, "text/json": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/CloudModel" + } } } } @@ -3011,6 +3033,26 @@ } } }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, "500": { "description": "Server Error", "content": { @@ -3032,64 +3074,44 @@ } } } - }, - "patch": { + } + }, + "/api/v1/Cloud/{name}/containers/count": { + "get": { "tags": [ - "ContainerImage" + "Cloud" ], - "operationId": "ContainerImagePatch", + "operationId": "GetNumCloudContainersByName", "parameters": [ { - "name": "id", + "name": "name", "in": "path", "required": true, "schema": { - "type": "string", - "format": "uuid" + "type": "string" } } ], - "requestBody": { - "content": { - "application/json-patch+json": { - "schema": { - "$ref": "#/components/schemas/ContainerImageModel" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ContainerImageModel" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ContainerImageModel" - } - }, - "application/*+json": { - "schema": { - "$ref": "#/components/schemas/ContainerImageModel" - } - } - } - }, "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "integer", + "format": "int32" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "integer", + "format": "int32" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/ContainerImageModel" + "type": "integer", + "format": "int32" } } } @@ -3114,6 +3136,26 @@ } } }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, "500": { "description": "Server Error", "content": { @@ -3135,26 +3177,55 @@ } } } - }, - "delete": { + } + }, + "/api/v1/Cloud/{id}/containers/count": { + "get": { "tags": [ - "ContainerImage" + "Cloud" ], - "operationId": "ContainerImageDelete", + "operationId": "GetNumCloudContainersById", "parameters": [ + { + "name": "cloudId", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, { "name": "id", "in": "path", "required": true, "schema": { - "type": "string", - "format": "uuid" + "type": "string" } } ], "responses": { "200": { - "description": "Success" + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "text/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } }, "404": { "description": "Not Found", @@ -3176,8 +3247,8 @@ } } }, - "500": { - "description": "Server Error", + "400": { + "description": "Bad Request", "content": { "text/plain": { "schema": { @@ -3195,57 +3266,86 @@ } } } - } - } - } - }, - "/api/v1/ContainerImage/AddRelation": { - "post": { - "tags": [ - "ContainerImage" - ], - "operationId": "ContainerImageAddRelation", - "requestBody": { - "content": { - "application/json-patch+json": { - "schema": { - "$ref": "#/components/schemas/RelationModel" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/RelationModel" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/RelationModel" - } - }, - "application/*+json": { - "schema": { - "$ref": "#/components/schemas/RelationModel" + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } } } } - }, + } + } + }, + "/api/v1/Cloud/{name}/busy": { + "get": { + "tags": [ + "Cloud" + ], + "operationId": "IsBusyCloudByName", + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/RelationModel" + "type": "integer", + "format": "int32" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/RelationModel" + "type": "integer", + "format": "int32" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/RelationModel" + "type": "integer", + "format": "int32" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" } } } @@ -3293,15 +3393,15 @@ } } }, - "/api/v1/ContainerImage/relation/{name}": { + "/api/v1/Cloud/{id}/busy": { "get": { "tags": [ - "ContainerImage" + "Cloud" ], - "operationId": "ContainerImageGetRelationByName", + "operationId": "isBusyCloudById", "parameters": [ { - "name": "id", + "name": "cloudId", "in": "query", "schema": { "type": "string", @@ -3309,7 +3409,7 @@ } }, { - "name": "name", + "name": "id", "in": "path", "required": true, "schema": { @@ -3323,26 +3423,20 @@ "content": { "text/plain": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationModel" - } + "type": "integer", + "format": "int32" } }, "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationModel" - } + "type": "integer", + "format": "int32" } }, "text/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationModel" - } + "type": "integer", + "format": "int32" } } } @@ -3367,6 +3461,26 @@ } } }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, "500": { "description": "Server Error", "content": { @@ -3390,64 +3504,29 @@ } } }, - "/api/v1/ContainerImage/relations/{firstName}/{secondName}": { + "/api/v1/ContainerImage": { "get": { "tags": [ "ContainerImage" ], - "operationId": "ContainerImageGetRelationsByName", - "parameters": [ - { - "name": "id", - "in": "query", - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "firstName", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "secondName", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], + "operationId": "ContainerImageGetAll", "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationModel" - } + "$ref": "#/components/schemas/ContainerImageModel" } }, "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationModel" - } + "$ref": "#/components/schemas/ContainerImageModel" } }, "text/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationModel" - } + "$ref": "#/components/schemas/ContainerImageModel" } } } @@ -3493,57 +3572,59 @@ } } } - } - }, - "/api/v1/ContainerImage/instance/{id}": { - "get": { + }, + "post": { "tags": [ "ContainerImage" ], - "operationId": "ContainerImageGetForInstance", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" + "operationId": "ContainerImageAdd", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ContainerImageModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContainerImageModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ContainerImageModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ContainerImageModel" + } } } - ], + }, "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ContainerImageModel" - } + "$ref": "#/components/schemas/ContainerImageModel" } }, "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ContainerImageModel" - } + "$ref": "#/components/schemas/ContainerImageModel" } }, "text/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ContainerImageModel" - } + "$ref": "#/components/schemas/ContainerImageModel" } } } }, - "404": { - "description": "Not Found", + "400": { + "description": "Bad Request", "content": { "text/plain": { "schema": { @@ -3585,26 +3666,20 @@ } } }, - "/api/v1/Dashboard/tasks": { + "/api/v1/ContainerImage/{id}": { "get": { "tags": [ - "Dashboard" + "ContainerImage" ], + "operationId": "ContainerImageGetbyId", "parameters": [ { - "name": "PageNumber", - "in": "query", + "name": "id", + "in": "path", + "required": true, "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "PageSize", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" + "type": "string", + "format": "uuid" } } ], @@ -3614,17 +3689,37 @@ "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/TaskRobotResponseListPagedResponse" + "$ref": "#/components/schemas/ContainerImageModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/TaskRobotResponseListPagedResponse" + "$ref": "#/components/schemas/ContainerImageModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/TaskRobotResponseListPagedResponse" + "$ref": "#/components/schemas/ContainerImageModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" } } } @@ -3650,48 +3745,84 @@ } } } - } - }, - "/api/v1/Dashboard/locations": { - "get": { + }, + "patch": { "tags": [ - "Dashboard" + "ContainerImage" ], + "operationId": "ContainerImagePatch", "parameters": [ { - "name": "PageNumber", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "PageSize", - "in": "query", + "name": "id", + "in": "path", + "required": true, "schema": { - "type": "integer", - "format": "int32" + "type": "string", + "format": "uuid" } } ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/ContainerImageModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContainerImageModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ContainerImageModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/ContainerImageModel" + } + } + } + }, "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/LocationStatusResponseListPagedResponse" + "$ref": "#/components/schemas/ContainerImageModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/LocationStatusResponseListPagedResponse" + "$ref": "#/components/schemas/ContainerImageModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/LocationStatusResponseListPagedResponse" + "$ref": "#/components/schemas/ContainerImageModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" } } } @@ -3717,30 +3848,43 @@ } } } - } - }, - "/api/v1/Dashboard/tasks/actions": { - "get": { + }, + "delete": { "tags": [ - "Dashboard" + "ContainerImage" + ], + "operationId": "ContainerImageDelete", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } ], "responses": { "200": { - "description": "Success", + "description": "Success" + }, + "404": { + "description": "Not Found", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/ActionSequenceResponse" + "$ref": "#/components/schemas/ApiResponse" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/ActionSequenceResponse" + "$ref": "#/components/schemas/ApiResponse" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/ActionSequenceResponse" + "$ref": "#/components/schemas/ApiResponse" } } } @@ -3768,28 +3912,73 @@ } } }, - "/api/v1/Dashboard/types": { - "get": { + "/api/v1/ContainerImage/AddRelation": { + "post": { "tags": [ - "Dashboard" + "ContainerImage" ], + "operationId": "ContainerImageAddRelation", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + } + } + }, "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/ActionSequenceResponse" + "$ref": "#/components/schemas/RelationModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/ActionSequenceResponse" + "$ref": "#/components/schemas/RelationModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/ActionSequenceResponse" + "$ref": "#/components/schemas/RelationModel" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" } } } @@ -3817,52 +4006,1491 @@ } } }, - "/api/v1/Dashboard/netApps": { + "/api/v1/ContainerImage/relation/{name}": { "get": { "tags": [ - "Dashboard" + "ContainerImage" ], + "operationId": "ContainerImageGetRelationByName", "parameters": [ { - "name": "PageNumber", + "name": "id", "in": "query", "schema": { - "type": "integer", - "format": "int32" + "type": "string", + "format": "uuid" } }, { - "name": "PageSize", - "in": "query", + "name": "name", + "in": "path", + "required": true, "schema": { - "type": "integer", - "format": "int32" + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/ContainerImage/relations/{firstName}/{secondName}": { + "get": { + "tags": [ + "ContainerImage" + ], + "operationId": "ContainerImageGetRelationsByName", + "parameters": [ + { + "name": "id", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "firstName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "secondName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/ContainerImage/instance/{id}": { + "get": { + "tags": [ + "ContainerImage" + ], + "operationId": "ContainerImageGetForInstance", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContainerImageModel" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContainerImageModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContainerImageModel" + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Dashboard/tasks": { + "get": { + "tags": [ + "Dashboard" + ], + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/TaskRobotResponseListPagedResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaskRobotResponseListPagedResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/TaskRobotResponseListPagedResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Dashboard/locations": { + "get": { + "tags": [ + "Dashboard" + ], + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/LocationStatusResponseListPagedResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/LocationStatusResponseListPagedResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/LocationStatusResponseListPagedResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Dashboard/tasks/actions": { + "get": { + "tags": [ + "Dashboard" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ActionSequenceResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionSequenceResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ActionSequenceResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Dashboard/types": { + "get": { + "tags": [ + "Dashboard" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ActionSequenceResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ActionSequenceResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ActionSequenceResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Dashboard/netApps": { + "get": { + "tags": [ + "Dashboard" + ], + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/NetAppsDetailsResponseListPagedResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/NetAppsDetailsResponseListPagedResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/NetAppsDetailsResponseListPagedResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Dashboard/robots": { + "get": { + "tags": [ + "Dashboard" + ], + "parameters": [ + { + "name": "PageNumber", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "PageSize", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/RobotResponseListPagedResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/RobotResponseListPagedResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RobotResponseListPagedResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Dashboard/graph": { + "get": { + "tags": [ + "Dashboard" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/GraphResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/GraphResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/GraphResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Edge": { + "get": { + "tags": [ + "Edge" + ], + "operationId": "EdgeGetAll", + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + }, + "post": { + "tags": [ + "Edge" + ], + "operationId": "EdgeAdd", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Edge/{id}": { + "get": { + "tags": [ + "Edge" + ], + "operationId": "EdgeGetById", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + }, + "patch": { + "tags": [ + "Edge" + ], + "operationId": "EdgePatch", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/EdgeModel" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Edge" + ], + "operationId": "EdgeDelete", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success" + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Edge/AddRelation": { + "post": { + "tags": [ + "Edge" + ], + "operationId": "EdgeAddRelation", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Edge/relation/{name}": { + "get": { + "tags": [ + "Edge" + ], + "operationId": "EdgeGetRelationByName", + "parameters": [ + { + "name": "id", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Edge/relations/{firstName}/{secondName}": { + "get": { + "tags": [ + "Edge" + ], + "operationId": "EdgeGetRelationsByName", + "parameters": [ + { + "name": "id", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "firstName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "secondName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + } + }, + "/api/v1/Edge/free": { + "get": { + "tags": [ + "Edge" + ], + "operationId": "GetFreeEdgesIds", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } + } + }, + "application/*+json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } + } } } - ], + }, "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/NetAppsDetailsResponseListPagedResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } } }, "application/json": { "schema": { - "$ref": "#/components/schemas/NetAppsDetailsResponseListPagedResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } } }, "text/json": { "schema": { - "$ref": "#/components/schemas/NetAppsDetailsResponseListPagedResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } } } } }, - "500": { - "description": "Server Error", + "404": { + "description": "Not Found", "content": { "text/plain": { "schema": { @@ -3880,50 +5508,23 @@ } } } - } - } - } - }, - "/api/v1/Dashboard/robots": { - "get": { - "tags": [ - "Dashboard" - ], - "parameters": [ - { - "name": "PageNumber", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - } }, - { - "name": "PageSize", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Success", + "400": { + "description": "Bad Request", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/RobotResponseListPagedResponse" + "$ref": "#/components/schemas/ApiResponse" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/RobotResponseListPagedResponse" + "$ref": "#/components/schemas/ApiResponse" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/RobotResponseListPagedResponse" + "$ref": "#/components/schemas/ApiResponse" } } } @@ -3951,34 +5552,80 @@ } } }, - "/api/v1/Dashboard/graph": { + "/api/v1/Edge/lessBusy": { "get": { "tags": [ - "Dashboard" + "Edge" ], + "operationId": "GetLessBusyEdges", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } + } + }, + "application/*+json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } + } + } + } + }, "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/GraphResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } } }, "application/json": { "schema": { - "$ref": "#/components/schemas/GraphResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } } }, "text/json": { "schema": { - "$ref": "#/components/schemas/GraphResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/EdgeModel" + } } } } }, - "500": { - "description": "Server Error", + "404": { + "description": "Not Found", "content": { "text/plain": { "schema": { @@ -3996,39 +5643,9 @@ } } } - } - } - } - }, - "/api/v1/Edge": { - "get": { - "tags": [ - "Edge" - ], - "operationId": "EdgeGetAll", - "responses": { - "200": { - "description": "Success", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/EdgeModel" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/EdgeModel" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/EdgeModel" - } - } - } }, - "404": { - "description": "Not Found", + "400": { + "description": "Bad Request", "content": { "text/plain": { "schema": { @@ -4068,36 +5685,24 @@ } } } - }, - "post": { + } + }, + "/api/v1/Edge/name/{name}": { + "get": { "tags": [ "Edge" ], - "operationId": "EdgeAdd", - "requestBody": { - "content": { - "application/json-patch+json": { - "schema": { - "$ref": "#/components/schemas/EdgeModel" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/EdgeModel" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/EdgeModel" - } - }, - "application/*+json": { - "schema": { - "$ref": "#/components/schemas/EdgeModel" - } + "operationId": "EdgeGetDataByName", + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" } } - }, + ], "responses": { "200": { "description": "Success", @@ -4119,8 +5724,8 @@ } } }, - "400": { - "description": "Bad Request", + "404": { + "description": "Not Found", "content": { "text/plain": { "schema": { @@ -4162,20 +5767,27 @@ } } }, - "/api/v1/Edge/{id}": { + "/api/v1/Edge/{id}/busy": { "get": { "tags": [ "Edge" ], - "operationId": "EdgeGetById", + "operationId": "isBusyEdgeById", "parameters": [ + { + "name": "edgeId", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, { "name": "id", "in": "path", "required": true, "schema": { - "type": "string", - "format": "uuid" + "type": "string" } } ], @@ -4241,47 +5853,24 @@ } } } - }, - "patch": { + } + }, + "/api/v1/Edge/{name}/busy": { + "get": { "tags": [ "Edge" ], - "operationId": "EdgePatch", + "operationId": "isBusyEdgeByName", "parameters": [ { - "name": "id", + "name": "name", "in": "path", "required": true, "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "requestBody": { - "content": { - "application/json-patch+json": { - "schema": { - "$ref": "#/components/schemas/EdgeModel" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/EdgeModel" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/EdgeModel" - } - }, - "application/*+json": { - "schema": { - "$ref": "#/components/schemas/EdgeModel" - } + "type": "string" } } - }, + ], "responses": { "200": { "description": "Success", @@ -4344,26 +5933,55 @@ } } } - }, - "delete": { + } + }, + "/api/v1/Edge/{id}/containers/count": { + "get": { "tags": [ "Edge" ], - "operationId": "EdgeDelete", + "operationId": "GetNumEdgeContainersById", "parameters": [ + { + "name": "edgeId", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, { "name": "id", "in": "path", "required": true, "schema": { - "type": "string", - "format": "uuid" + "type": "string" } } ], "responses": { "200": { - "description": "Success" + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "text/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } }, "404": { "description": "Not Found", @@ -4385,6 +6003,26 @@ } } }, + "400": { + "description": "Bad Request", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, "500": { "description": "Server Error", "content": { @@ -4408,53 +6046,69 @@ } } }, - "/api/v1/Edge/AddRelation": { - "post": { + "/api/v1/Edge/{name}/containers/count": { + "get": { "tags": [ "Edge" ], - "operationId": "EdgeAddRelation", - "requestBody": { - "content": { - "application/json-patch+json": { - "schema": { - "$ref": "#/components/schemas/RelationModel" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/RelationModel" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/RelationModel" - } - }, - "application/*+json": { - "schema": { - "$ref": "#/components/schemas/RelationModel" - } + "operationId": "GetNumEdgeContainersByName", + "parameters": [ + { + "name": "cloudName", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" } } - }, + ], "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/RelationModel" + "type": "integer", + "format": "int32" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/RelationModel" + "type": "integer", + "format": "int32" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/RelationModel" + "type": "integer", + "format": "int32" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" } } } @@ -4502,56 +6156,72 @@ } } }, - "/api/v1/Edge/relation/{name}": { + "/api/Health": { "get": { "tags": [ - "Edge" + "Health" ], - "operationId": "EdgeGetRelationByName", - "parameters": [ - { - "name": "id", - "in": "query", - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "name", - "in": "path", - "required": true, - "schema": { - "type": "string" + "operationId": "RedisInterfaceHealthCheck", + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/api/Health/spec": { + "get": { + "tags": [ + "Health" + ], + "operationId": "GetRedisInterfaceSpec", + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + }, + "application/json": { + "schema": { + "type": "string" + } + }, + "text/json": { + "schema": { + "type": "string" + } + } } } + } + } + }, + "/api/v1/Instance": { + "get": { + "tags": [ + "Instance" ], + "operationId": "InstanceGetAll", "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationModel" - } + "$ref": "#/components/schemas/InstanceModel" } }, "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationModel" - } + "$ref": "#/components/schemas/InstanceModel" } }, "text/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationModel" - } + "$ref": "#/components/schemas/InstanceModel" } } } @@ -4597,72 +6267,59 @@ } } } - } - }, - "/api/v1/Edge/relations/{firstName}/{secondName}": { - "get": { + }, + "post": { "tags": [ - "Edge" + "Instance" ], - "operationId": "EdgeGetRelationsByName", - "parameters": [ - { - "name": "id", - "in": "query", - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "firstName", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "secondName", - "in": "path", - "required": true, - "schema": { - "type": "string" + "operationId": "InstanceAdd", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/InstanceModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/InstanceModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/InstanceModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/InstanceModel" + } } } - ], + }, "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationModel" - } + "$ref": "#/components/schemas/InstanceModel" } }, "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationModel" - } + "$ref": "#/components/schemas/InstanceModel" } }, "text/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationModel" - } + "$ref": "#/components/schemas/InstanceModel" } } } }, - "404": { - "description": "Not Found", + "400": { + "description": "Bad Request", "content": { "text/plain": { "schema": { @@ -4704,74 +6361,40 @@ } } }, - "/api/v1/Edge/free": { + "/api/v1/Instance/{id}": { "get": { "tags": [ - "Edge" + "Instance" ], - "operationId": "GetFreeEdgesIds", - "requestBody": { - "content": { - "application/json-patch+json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } - } - }, - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } - } - }, - "text/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } - } - }, - "application/*+json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } - } + "operationId": "InstanceGetById", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" } } - }, + ], "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } + "$ref": "#/components/schemas/InstanceModel" } }, "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } + "$ref": "#/components/schemas/InstanceModel" } }, "text/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } + "$ref": "#/components/schemas/InstanceModel" } } } @@ -4796,26 +6419,6 @@ } } }, - "400": { - "description": "Bad Request", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, "500": { "description": "Server Error", "content": { @@ -4837,46 +6440,43 @@ } } } - } - }, - "/api/v1/Edge/lessBusy": { - "get": { + }, + "patch": { "tags": [ - "Edge" + "Instance" + ], + "operationId": "InstancePatch", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } ], - "operationId": "GetLessBusyEdges", "requestBody": { "content": { "application/json-patch+json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } + "$ref": "#/components/schemas/InstanceModel" } }, "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } + "$ref": "#/components/schemas/InstanceModel" } }, "text/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } + "$ref": "#/components/schemas/InstanceModel" } }, "application/*+json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } + "$ref": "#/components/schemas/InstanceModel" } } } @@ -4887,26 +6487,17 @@ "content": { "text/plain": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } + "$ref": "#/components/schemas/InstanceModel" } }, "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } + "$ref": "#/components/schemas/InstanceModel" } }, "text/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EdgeModel" - } + "$ref": "#/components/schemas/InstanceModel" } } } @@ -4931,8 +6522,50 @@ } } }, - "400": { - "description": "Bad Request", + "500": { + "description": "Server Error", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Instance" + ], + "operationId": "InstanceDelete", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success" + }, + "404": { + "description": "Not Found", "content": { "text/plain": { "schema": { @@ -4974,45 +6607,59 @@ } } }, - "/api/v1/Edge/name/{name}": { - "get": { + "/api/v1/Instance/AddRelation": { + "post": { "tags": [ - "Edge" + "Instance" ], - "operationId": "EdgeGetDataByName", - "parameters": [ - { - "name": "name", - "in": "path", - "required": true, - "schema": { - "type": "string" + "operationId": "InstanceAddRelation", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } } } - ], + }, "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/EdgeModel" + "$ref": "#/components/schemas/RelationModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/EdgeModel" + "$ref": "#/components/schemas/RelationModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/EdgeModel" + "$ref": "#/components/schemas/RelationModel" } } } }, - "404": { - "description": "Not Found", + "400": { + "description": "Bad Request", "content": { "text/plain": { "schema": { @@ -5054,53 +6701,59 @@ } } }, - "/api/v1/Edge/{id}/busy": { - "get": { + "/api/v1/Instance/DeleteRelation": { + "delete": { "tags": [ - "Edge" + "Instance" ], - "operationId": "isBusyEdgeById", - "parameters": [ - { - "name": "edgeId", - "in": "query", - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" + "operationId": "InstanceDeleteRelation", + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/RelationModel" + } } } - ], + }, "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/EdgeModel" + "$ref": "#/components/schemas/RelationModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/EdgeModel" + "$ref": "#/components/schemas/RelationModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/EdgeModel" + "$ref": "#/components/schemas/RelationModel" } } } }, - "404": { - "description": "Not Found", + "400": { + "description": "Bad Request", "content": { "text/plain": { "schema": { @@ -5142,13 +6795,21 @@ } } }, - "/api/v1/Edge/{name}/busy": { + "/api/v1/Instance/relation/{name}": { "get": { "tags": [ - "Edge" + "Instance" ], - "operationId": "isBusyEdgeByName", + "operationId": "InstanceGetRelationByName", "parameters": [ + { + "name": "id", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, { "name": "name", "in": "path", @@ -5164,17 +6825,26 @@ "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/EdgeModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } } }, "application/json": { "schema": { - "$ref": "#/components/schemas/EdgeModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } } }, "text/json": { "schema": { - "$ref": "#/components/schemas/EdgeModel" + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } } } } @@ -5222,15 +6892,15 @@ } } }, - "/api/v1/Edge/{id}/containers/count": { + "/api/v1/Instance/relations/{firstName}/{secondName}": { "get": { "tags": [ - "Edge" + "Instance" ], - "operationId": "GetNumEdgeContainersById", + "operationId": "InstanceGetRelationsByName", "parameters": [ { - "name": "edgeId", + "name": "id", "in": "query", "schema": { "type": "string", @@ -5238,7 +6908,15 @@ } }, { - "name": "id", + "name": "firstName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "secondName", "in": "path", "required": true, "schema": { @@ -5252,20 +6930,26 @@ "content": { "text/plain": { "schema": { - "type": "integer", - "format": "int32" + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } } }, "application/json": { "schema": { - "type": "integer", - "format": "int32" + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } } }, "text/json": { "schema": { - "type": "integer", - "format": "int32" + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + } } } } @@ -5290,26 +6974,6 @@ } } }, - "400": { - "description": "Bad Request", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, "500": { "description": "Server Error", "content": { @@ -5333,26 +6997,20 @@ } } }, - "/api/v1/Edge/{name}/containers/count": { + "/api/v1/Instance/alternative/{id}": { "get": { "tags": [ - "Edge" + "Instance" ], - "operationId": "GetNumEdgeContainersByName", + "operationId": "InstanceGetAlternative", "parameters": [ { - "name": "cloudName", - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "name", + "name": "id", "in": "path", "required": true, "schema": { - "type": "string" + "type": "string", + "format": "uuid" } } ], @@ -5362,20 +7020,17 @@ "content": { "text/plain": { "schema": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/InstanceModel" } }, "application/json": { "schema": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/InstanceModel" } }, "text/json": { "schema": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/InstanceModel" } } } @@ -5384,101 +7039,38 @@ "description": "Not Found", "content": { "text/plain": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - } - } - } - }, - "/api/Health": { - "get": { - "tags": [ - "Health" - ], - "operationId": "RedisInterfaceHealthCheck", - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/api/Health/spec": { - "get": { - "tags": [ - "Health" - ], - "operationId": "GetRedisInterfaceSpec", - "responses": { - "200": { - "description": "Success", + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + }, + "500": { + "description": "Server Error", "content": { "text/plain": { "schema": { - "type": "string" + "$ref": "#/components/schemas/ApiResponse" } }, "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/ApiResponse" } }, "text/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/ApiResponse" } } } @@ -5486,29 +7078,29 @@ } } }, - "/api/v1/Instance": { + "/api/v1/InstanceRunning": { "get": { "tags": [ - "Instance" + "InstanceRunning" ], - "operationId": "InstanceGetAll", + "operationId": "InstanceRunningGetAll", "responses": { "200": { "description": "Success", "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } } } @@ -5557,29 +7149,29 @@ }, "post": { "tags": [ - "Instance" + "InstanceRunning" ], - "operationId": "InstanceAdd", + "operationId": "InstanceRunningAdd", "requestBody": { "content": { "application/json-patch+json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "application/*+json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } } } @@ -5590,17 +7182,17 @@ "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } } } @@ -5648,12 +7240,12 @@ } } }, - "/api/v1/Instance/{id}": { + "/api/v1/InstanceRunning/{id}": { "get": { "tags": [ - "Instance" + "InstanceRunning" ], - "operationId": "InstanceGetById", + "operationId": "InstanceRunningGetById", "parameters": [ { "name": "id", @@ -5671,17 +7263,17 @@ "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } } } @@ -5730,9 +7322,9 @@ }, "patch": { "tags": [ - "Instance" + "InstanceRunning" ], - "operationId": "InstancePatch", + "operationId": "InstanceRunningPatch", "parameters": [ { "name": "id", @@ -5748,22 +7340,22 @@ "content": { "application/json-patch+json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "application/*+json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } } } @@ -5774,17 +7366,17 @@ "content": { "text/plain": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "application/json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } }, "text/json": { "schema": { - "$ref": "#/components/schemas/InstanceModel" + "$ref": "#/components/schemas/InstanceRunningModel" } } } @@ -5833,9 +7425,9 @@ }, "delete": { "tags": [ - "Instance" + "InstanceRunning" ], - "operationId": "InstanceDelete", + "operationId": "InstanceRunningDelete", "parameters": [ { "name": "id", @@ -5894,12 +7486,12 @@ } } }, - "/api/v1/Instance/AddRelation": { + "/api/v1/InstanceRunning/AddRelation": { "post": { "tags": [ - "Instance" + "InstanceRunning" ], - "operationId": "InstanceAddRelation", + "operationId": "InstanceRunningAddRelation", "requestBody": { "content": { "application/json-patch+json": { @@ -5988,12 +7580,12 @@ } } }, - "/api/v1/Instance/DeleteRelation": { + "/api/v1/InstanceRunning/DeleteRelation": { "delete": { "tags": [ - "Instance" + "InstanceRunning" ], - "operationId": "InstanceDeleteRelation", + "operationId": "InstanceRunningDeleteRelation", "requestBody": { "content": { "application/json-patch+json": { @@ -6082,12 +7674,12 @@ } } }, - "/api/v1/Instance/relation/{name}": { + "/api/v1/InstanceRunning/relation/{name}": { "get": { "tags": [ - "Instance" + "InstanceRunning" ], - "operationId": "InstanceGetRelationByName", + "operationId": "InstanceRunningGetRelationByName", "parameters": [ { "name": "id", @@ -6179,12 +7771,12 @@ } } }, - "/api/v1/Instance/relations/{firstName}/{secondName}": { + "/api/v1/InstanceRunning/relations/{firstName}/{secondName}": { "get": { "tags": [ - "Instance" + "InstanceRunning" ], - "operationId": "InstanceGetRelationsByName", + "operationId": "InstanceRunningGetRelationsByName", "parameters": [ { "name": "id", @@ -6284,87 +7876,6 @@ } } }, - "/api/v1/Instance/alternative/{id}": { - "get": { - "tags": [ - "Instance" - ], - "operationId": "InstanceGetAlternative", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/InstanceModel" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/InstanceModel" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/InstanceModel" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - }, - "500": { - "description": "Server Error", - "content": { - "text/plain": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - }, - "text/json": { - "schema": { - "$ref": "#/components/schemas/ApiResponse" - } - } - } - } - } - } - }, "/api/v1/Policy": { "post": { "tags": [ @@ -8842,6 +10353,77 @@ "type": "object", "additionalProperties": false }, + "ActionRunningModel": { + "type": "object", + "properties": { + "relations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + }, + "nullable": true + }, + "Id": { + "type": "string", + "format": "uuid" + }, + "ActionParentId": { + "type": "string", + "format": "uuid" + }, + "ActionPlanId": { + "type": "string", + "format": "uuid" + }, + "Name": { + "type": "string", + "nullable": true + }, + "Tags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "Order": { + "type": "integer", + "format": "int32" + }, + "Placement": { + "type": "string", + "nullable": true + }, + "PlacementType": { + "type": "string", + "nullable": true + }, + "ActionPriority": { + "type": "string", + "nullable": true + }, + "ActionStatus": { + "type": "string", + "nullable": true + }, + "Services": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InstanceRunningModel" + }, + "nullable": true + }, + "MinimumRam": { + "type": "integer", + "format": "int32" + }, + "MinimumNumCores": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, "ActionSequenceResponse": { "type": "object", "properties": { @@ -9263,6 +10845,47 @@ }, "additionalProperties": false }, + "InstanceRunningModel": { + "type": "object", + "properties": { + "relations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationModel" + }, + "nullable": true + }, + "Id": { + "type": "string", + "format": "uuid" + }, + "Name": { + "type": "string", + "nullable": true + }, + "ServiceInstanceId": { + "type": "string", + "format": "uuid" + }, + "ServiceType": { + "type": "string", + "nullable": true + }, + "ServiceUrl": { + "type": "string", + "nullable": true + }, + "ServiceStatus": { + "type": "string", + "nullable": true + }, + "DeployedTime": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, "KeyValuePair": { "type": "object", "properties": { diff --git a/src/TaskPlanner/Controllers/PlanController.cs b/src/TaskPlanner/Controllers/PlanController.cs index b6e9d045..f7e0f297 100644 --- a/src/TaskPlanner/Controllers/PlanController.cs +++ b/src/TaskPlanner/Controllers/PlanController.cs @@ -67,7 +67,7 @@ public async Task> GetPlan([FromBody] CreatePlanRequest Task = tmpTaskSend }; ResourcePlanner.TaskModel tmpFinalTask = - await _resourcePlannerClient.GetResourcePlanAsync(resourceInput); + await _resourcePlannerClient.GetResourcePlanAsync(resourceInput); // await _resourcePlannerClient.GetResourcePlanAsync(resourceInput); TaskModel resourcePlan = _mapper.Map(tmpFinalTask); if (dryRun) From bcbc89bb655db931622f79e64cc7fcb0eb561398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= Date: Tue, 7 Mar 2023 23:46:35 +0100 Subject: [PATCH 14/31] Set prefetch cout to 1 --- .../ExtensionMethods/ServiceCollectionExtensions.cs | 5 ++--- .../ExtensionMethods/ServiceCollectionExtensions.cs | 6 +++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Orchestrator/ExtensionMethods/ServiceCollectionExtensions.cs b/src/Orchestrator/ExtensionMethods/ServiceCollectionExtensions.cs index 292f1ee6..e997631c 100644 --- a/src/Orchestrator/ExtensionMethods/ServiceCollectionExtensions.cs +++ b/src/Orchestrator/ExtensionMethods/ServiceCollectionExtensions.cs @@ -30,13 +30,12 @@ public static IServiceCollection RegisterRabbitMqConsumers(this IServiceCollecti mqBusFactoryConfigurator.ReceiveEndpoint("deployments", ec => { ec.ConfigureConsumeTopology = false; + ec.PrefetchCount = 1; ec.Bind(nameof(DeployPlanMessage), b => { - b.RoutingKey = $"{mwConfig.InstanceName}-{mwConfig.InstanceType}"; b.ExchangeType = ExchangeType.Direct; + b.RoutingKey = $"{mwConfig.InstanceName}-{mwConfig.InstanceType}"; }); - - ec.ConfigureConsumer(busRegistrationContext); }); mqBusFactoryConfigurator.ConfigureEndpoints(busRegistrationContext); diff --git a/src/TaskPlanner/ExtensionMethods/ServiceCollectionExtensions.cs b/src/TaskPlanner/ExtensionMethods/ServiceCollectionExtensions.cs index d4030354..58a066c5 100644 --- a/src/TaskPlanner/ExtensionMethods/ServiceCollectionExtensions.cs +++ b/src/TaskPlanner/ExtensionMethods/ServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using k8s.KubeConfigModels; using MassTransit; +using MassTransit.Configuration; using MassTransit.RabbitMqTransport.Configuration; using Middleware.Common.Config; using Middleware.Common.MessageContracts; @@ -29,7 +30,10 @@ public static IServiceCollection RegisterRabbitMqPublishers(this IServiceCollect x.UseRoutingKeyFormatter(t => t.Message.DeploymentLocation); }); mqBusFactoryConfigurator.Message(x => x.SetEntityName(nameof(DeployPlanMessage))); - mqBusFactoryConfigurator.Publish(x => { x.ExchangeType = ExchangeType.Direct; }); + mqBusFactoryConfigurator.Publish(x => + { + x.ExchangeType = ExchangeType.Direct; + }); mqBusFactoryConfigurator.ConfigureEndpoints(busRegistrationContext); }); From ed8b4be16c5cf48906109ad5328d4d7f42a9b02a Mon Sep 17 00:00:00 2001 From: adrianLIrobotics Date: Wed, 8 Mar 2023 10:09:13 +0100 Subject: [PATCH 15/31] Implemented historicalactionPlan --- docker-compose.override.yml | 10 +- .../Abstract/IRedisInterfaceClientService.cs | 8 + .../Services/RedisInterfaceClientService.cs | 26 ++ .../DataAccessExtensionMethods.cs | 1 + .../IHistoricalActionPlanRepository.cs | 10 + .../RedisHistoricalActionPlanRepository.cs | 55 ++++ .../Domain/HistoricalActionPlanModel.cs | 67 +++++ src/Models/Dto/HistoricalActionPlanDto.cs | 50 ++++ .../Deployment/DeploymentService.cs | 47 +++- .../Controllers/ActionController.cs | 255 +++++++++++++++++- .../Services/DashboardService.cs | 7 +- 11 files changed, 524 insertions(+), 12 deletions(-) create mode 100644 src/DataAccess/Repositories/Abstract/IHistoricalActionPlanRepository.cs create mode 100644 src/DataAccess/Repositories/Redis/RedisHistoricalActionPlanRepository.cs create mode 100644 src/Models/Domain/HistoricalActionPlanModel.cs create mode 100644 src/Models/Dto/HistoricalActionPlanDto.cs diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 2d126f48..d0c67462 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -13,7 +13,7 @@ services: - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - Middleware__Organization=5G-ERA-DEV - - Middleware__InstanceName=MiddlewareBED + - Middleware__InstanceName=MiddlewareBEDAL - Middleware__InstanceType=Edge ports: - "5091:80" @@ -36,7 +36,7 @@ services: - REDIS_HOSTNAME=${REDIS_HOSTNAME} - REDIS_PORT=${REDIS_PORT} - Middleware__Organization=5G-ERA-DEV - - Middleware__InstanceName=MiddlewareBED + - Middleware__InstanceName=MiddlewareBEDAL - Middleware__InstanceType=Edge ports: @@ -52,12 +52,12 @@ services: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=https://+:443;http://+:80 - REDIS_INTERFACE_API_SERVICE_HOST=redisinterface.api - - RESOURCE_PLANNER_ADDRESS=resourceplanner.api + - RESOURCE_PLANNER_API_SERVICE_HOST=resourceplanner.api - ORCHESTRATOR_API_SERVICE_HOST=orchestrator.api - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - Middleware__Organization=5G-ERA-DEV - - Middleware__InstanceName=MiddlewareBED + - Middleware__InstanceName=MiddlewareBEDAL - Middleware__InstanceType=Edge ports: - "5143:80" @@ -75,7 +75,7 @@ services: - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - Middleware__Organization=5G-ERA-DEV - - Middleware__InstanceName=MiddlewareBED + - Middleware__InstanceName=MiddlewareBEDAL - Middleware__InstanceType=Edge ports: - "5263:80" diff --git a/src/Common/Services/Abstract/IRedisInterfaceClientService.cs b/src/Common/Services/Abstract/IRedisInterfaceClientService.cs index c855b0d6..8ea424d2 100644 --- a/src/Common/Services/Abstract/IRedisInterfaceClientService.cs +++ b/src/Common/Services/Abstract/IRedisInterfaceClientService.cs @@ -171,5 +171,13 @@ Task DeleteRelationAsync(TSource source, TDirection d Task ActionRunningAddAsync(ActionRunningModel actionRunning); + Task ActionRunningAddAsync(ActionRunningModel actionRunning, CancellationToken token); + Task InstanceRunningAddAsync(InstanceRunningModel instanceRunning); + + Task InstanceRunningAddAsync(InstanceRunningModel instanceRunning, CancellationToken token); + + Task HistoricalActionPlanAddAsync(HistoricalActionPlanModel historicalActionPlan); + + Task HistoricalActionPlanAddAsync(HistoricalActionPlanModel historicalActionPlan, CancellationToken token); } \ No newline at end of file diff --git a/src/Common/Services/RedisInterfaceClientService.cs b/src/Common/Services/RedisInterfaceClientService.cs index 811fbcaf..7984e110 100644 --- a/src/Common/Services/RedisInterfaceClientService.cs +++ b/src/Common/Services/RedisInterfaceClientService.cs @@ -561,5 +561,31 @@ public async Task InstanceRunningAddAsync(InstanceRunningModel instanceRun throw; } } + + public async Task HistoricalActionPlanAddAsync(HistoricalActionPlanModel historicalActionPlan) + { + return await HistoricalActionPlanAddAsync(historicalActionPlan, CancellationToken.None); + } + + public async Task HistoricalActionPlanAddAsync(HistoricalActionPlanModel historicalActionPlan, CancellationToken token) + { + if (historicalActionPlan is null) + throw new ArgumentNullException(nameof(historicalActionPlan)); + + string url = $"/api/v1/Action/historicalPlan"; + try + { + var result = await _httpClient.PostAsJsonAsync(url, historicalActionPlan); + //var resp = result. + var response = await result.Content.ReadAsStringAsync(); + _logger.LogInformation("httpResponse: {0}", response); + return result.IsSuccessStatusCode; + } + catch (Exception ex) + { + _logger.LogError(ex, "There was en error while creating action plan: {plan}", historicalActionPlan); + throw; + } + } } } \ No newline at end of file diff --git a/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs b/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs index 0ad7cf26..b47b352b 100644 --- a/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs +++ b/src/DataAccess/ExtensionMethods/DataAccessExtensionMethods.cs @@ -48,6 +48,7 @@ public static IServiceCollection RegisterRepositories(this IServiceCollection se services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); return services; } diff --git a/src/DataAccess/Repositories/Abstract/IHistoricalActionPlanRepository.cs b/src/DataAccess/Repositories/Abstract/IHistoricalActionPlanRepository.cs new file mode 100644 index 00000000..b8338099 --- /dev/null +++ b/src/DataAccess/Repositories/Abstract/IHistoricalActionPlanRepository.cs @@ -0,0 +1,10 @@ +using Middleware.Models.Domain; + +namespace Middleware.DataAccess.Repositories.Abstract; + +public interface IHistoricalActionPlanRepository : IBaseRepository, IRelationRepository +{ + Task> GetRobotActionPlans(Guid robotId); + + Task> GetRobotReplanActionPlans(Guid robotId); +} \ No newline at end of file diff --git a/src/DataAccess/Repositories/Redis/RedisHistoricalActionPlanRepository.cs b/src/DataAccess/Repositories/Redis/RedisHistoricalActionPlanRepository.cs new file mode 100644 index 00000000..ebf47dac --- /dev/null +++ b/src/DataAccess/Repositories/Redis/RedisHistoricalActionPlanRepository.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.Text.Json; +using Microsoft.Extensions.Logging; +using Middleware.Common.Enums; +using Middleware.DataAccess.Repositories.Abstract; +using Middleware.Models.Domain; +using Middleware.Models.Dto; +using Middleware.Models.Enums; +using NReJSON; +using Redis.OM.Contracts; +using RedisGraphDotNet.Client; +using StackExchange.Redis; +using ILogger = Serilog.ILogger; + + +namespace Middleware.DataAccess.Repositories +{ + public class RedisHistoricalActionPlanRepository : RedisRepository, IHistoricalActionPlanRepository + { + /// + /// Default constructor + /// + /// + /// + /// + public RedisHistoricalActionPlanRepository(IRedisConnectionProvider provider, IRedisGraphClient redisGraph, + ILogger logger) : base(provider, redisGraph, true, logger) + { + } + + /// + /// Retrieves all actionPlanModels associated with an specific robot Id. + /// + /// List of ActionPlanModel + public async Task> GetRobotActionPlans(Guid robotId) + { + var guidStr = robotId.ToString(); + var actionPlans = await FindQuery(dto => dto.RobotId == guidStr).ToListAsync(); + var planModels = actionPlans.Select(ToTModel).ToList(); + return planModels; + } + + /// + /// Retrieves all actionPlanModels associated with an specific robot Id that have a replan set to true. + /// + /// List of HistoricalActionPlanModel + public async Task> GetRobotReplanActionPlans(Guid robotId) + { + var guidStr = robotId.ToString(); + var actionPlans = await FindQuery(dto => dto.RobotId == guidStr && dto.IsReplan == true).ToListAsync(); + var planModels = actionPlans.Select(ToTModel).ToList(); + return planModels; + } + } +} \ No newline at end of file diff --git a/src/Models/Domain/HistoricalActionPlanModel.cs b/src/Models/Domain/HistoricalActionPlanModel.cs new file mode 100644 index 00000000..1c4001aa --- /dev/null +++ b/src/Models/Domain/HistoricalActionPlanModel.cs @@ -0,0 +1,67 @@ +using Middleware.Models.Dto; +using System.Text.Json.Serialization; + +namespace Middleware.Models.Domain; + +public sealed class HistoricalActionPlanModel : BaseModel +{ + [JsonPropertyName("Id")] //atuomatically generated plan id by middleware + public override Guid Id { get; set; } + + [JsonPropertyName("TaskId")] //TaskID + public Guid TaskId { get; set; } + + [JsonPropertyName("Name")] // Name of task + public override string Name { get; set; } + + /// + /// Status of the whole plan + /// + [JsonPropertyName("Status")] + public string Status { get; set; } + + [JsonPropertyName("IsReplan")] //Status of whole plan + public bool IsReplan { get; set; } + + [JsonPropertyName("PreviousPlanId")] //If replan, this field must be set to the id of the previous created HistoricalActionPlan. + public Guid PreviousPlanId { get; set; } + + [JsonPropertyName("LastStatusChange")] + public DateTime LastStatusChange { get; set; } // AL 2022-05-10: Not sure we need this one or how to use it. + + [JsonPropertyName("ActionSequence")] + public List ActionSequence { get; set; } + + [JsonPropertyName("RobotId")] + public Guid RobotId { get; set; } + + [JsonPropertyName("TaskStartedAt")] + public DateTime TaskStartedAt { get; set; } + + + public void SetStatus(string status) + { + if (Status is null) + TaskStartedAt = DateTime.UtcNow; + + Status = status; + LastStatusChange = DateTime.UtcNow; + } + public override Dto.Dto ToDto() + { + var domain = this; + return new HistoricalActionPlanDto() + { + Id = domain.Id.ToString(), + TaskId = domain.TaskId.ToString(), + Name = domain.Name, + Status = domain.Status, + IsReplan = domain.IsReplan, + LastStatusChange = domain.LastStatusChange == default ? DateTimeOffset.Now : domain.LastStatusChange, + ActionSequence = domain.ActionSequence, + RobotId = domain.RobotId.ToString(), + TaskStartedAt = domain.TaskStartedAt == default ? DateTimeOffset.Now : domain.TaskStartedAt, + PreviousPlanId = domain.PreviousPlanId.ToString() + }; ; + } +} \ No newline at end of file diff --git a/src/Models/Dto/HistoricalActionPlanDto.cs b/src/Models/Dto/HistoricalActionPlanDto.cs new file mode 100644 index 00000000..3f2f282f --- /dev/null +++ b/src/Models/Dto/HistoricalActionPlanDto.cs @@ -0,0 +1,50 @@ +using Middleware.Models.Domain; +using Redis.OM.Modeling; + +namespace Middleware.Models.Dto; + +[Document(IndexName = "historicalActionPlan-idx", StorageType = StorageType.Json, Prefixes = new[] { HistoricalActionPlanDto.Prefix })] +public class HistoricalActionPlanDto : Dto +{ + public const string Prefix = "HistoricalActionPlan"; + [Indexed] + [RedisIdField] + public override string Id { get; set; } + + [Indexed] + public string? TaskId { get; set; } + [Indexed] + public string Name { get; set; } + [Indexed] + public string? Status { get; set; } + [Indexed] + public bool IsReplan { get; set; } + [Indexed(Sortable = true)] + public DateTimeOffset LastStatusChange { get; set; } + [Indexed] + public List ActionSequence { get; set; } + [Indexed] + public string RobotId { get; set; } + [Indexed(Sortable = true)] + public DateTimeOffset TaskStartedAt { get; set; } + [Indexed] + public string PreviousPlanId { get; set; } + +public override BaseModel ToModel() + { + var dto = this; + return new HistoricalActionPlanModel() + { + Id = Guid.Parse(dto.Id!.Replace(Prefix, "")), + TaskId = Guid.Parse(dto.TaskId), + Name = dto.Name, + Status = dto.Status, + IsReplan = dto.IsReplan, + LastStatusChange = dto.LastStatusChange.DateTime, + ActionSequence = dto.ActionSequence, + RobotId = Guid.Parse(dto.RobotId), + TaskStartedAt = dto.TaskStartedAt.DateTime, + PreviousPlanId = Guid.Parse(dto.PreviousPlanId) + }; + } +} \ No newline at end of file diff --git a/src/Orchestrator/Deployment/DeploymentService.cs b/src/Orchestrator/Deployment/DeploymentService.cs index 2c0f04fc..3c9a89c1 100644 --- a/src/Orchestrator/Deployment/DeploymentService.cs +++ b/src/Orchestrator/Deployment/DeploymentService.cs @@ -10,6 +10,7 @@ using Middleware.Models.Domain; using Middleware.Orchestrator.Exceptions; using Middleware.Orchestrator.Models; +using System.Threading.Tasks; namespace Middleware.Orchestrator.Deployment; @@ -144,7 +145,7 @@ private async Task SaveActionSequence(TaskModel task, Guid robotId) ActionRunningTemp.Name = action.Name; ActionRunningTemp.Tags = action.Tags; ActionRunningTemp.Order = action.Order; - ActionRunningTemp.Placement = action.Placement; + ActionRunningTemp.Placement = action.Placement.Replace("-Edge","").Replace("-Cloud",""); // Trim to proper edge or cloud ActionRunningTemp.PlacementType = action.PlacementType; ActionRunningTemp.ActionPriority = action.ActionPriority; ActionRunningTemp.ActionStatus = ActionStatusEnum.Running.ToString(); @@ -337,6 +338,45 @@ public V1Service CreateService(string serviceImageName, K8SServiceKindEnum kind, /// public async Task DeletePlanAsync(ActionPlanModel actionPlan) { + // Delete OWNS relationship between robot and actionPlan + RobotModel robot = await _redisInterfaceClient.RobotGetByIdAsync(actionPlan.RobotId); + await _redisInterfaceClient.DeleteRelationAsync(robot, actionPlan, "OWNS"); + + // Create historical action plan + HistoricalActionPlanModel historicalActionPlan = new HistoricalActionPlanModel(); + historicalActionPlan.IsReplan = actionPlan.IsReplan; + historicalActionPlan.Name = actionPlan.Name; + historicalActionPlan.RobotId = actionPlan.RobotId; + historicalActionPlan.LastStatusChange = actionPlan.LastStatusChange; + historicalActionPlan.TaskId = actionPlan.TaskId; + historicalActionPlan.Status = actionPlan.Status; + historicalActionPlan.TaskStartedAt = actionPlan.TaskStartedAt; + historicalActionPlan.ActionSequence = actionPlan.ActionSequence.Select(x => new ActionRunningModel() + { + Name = x.Name, + ActionParentId = x.Id, + ActionPlanId = actionPlan.Id, + Tags = x.Tags, + Order = x.Order, + Placement = x.Placement, + PlacementType = x.PlacementType, + ActionPriority = x.ActionPriority, + ActionStatus = x.ActionStatus, + Services = x.Services.Select(y => new InstanceRunningModel() + { + Name = y.Name, + ServiceInstanceId = y.ServiceInstanceId, + ServiceType = y.ServiceType, + ServiceUrl = y.ServiceUrl, + ServiceStatus = y.ServiceStatus, + + }).ToList() + + }).ToList(); + + // Publish to redis the historical action plan + await _redisInterfaceClient.HistoricalActionPlanAddAsync(historicalActionPlan); + bool retVal = true; try { @@ -363,7 +403,8 @@ public async Task DeletePlanAsync(ActionPlanModel actionPlan) _logger.LogError(ex, "The deletion of the Action Plan has failed!"); retVal = false; } - //actionPlan.SetStatus("inactive"); + + //Delete actionPlan return retVal; } @@ -402,7 +443,7 @@ private async Task DeleteInstance(IKubernetes k8sClient, InstanceModel ins // ignored } } - + // TODO: add the removing of the relationships and the bubbles. return retVal; } diff --git a/src/RedisInterface/Controllers/ActionController.cs b/src/RedisInterface/Controllers/ActionController.cs index 8a48562e..bf819bdc 100644 --- a/src/RedisInterface/Controllers/ActionController.cs +++ b/src/RedisInterface/Controllers/ActionController.cs @@ -15,13 +15,15 @@ public class ActionController : ControllerBase private readonly IActionPlanRepository _actionPlanRepository; private readonly ILogger _logger; private readonly IActionService _actionService; + private readonly IHistoricalActionPlanRepository _historicalActionPlanRepository; - public ActionController(IActionRepository actionRepository, IActionPlanRepository actionPlanRepository, ILogger logger, IActionService actionService) + public ActionController(IActionRepository actionRepository, IActionPlanRepository actionPlanRepository, ILogger logger, IActionService actionService, IHistoricalActionPlanRepository historicalActionPlanRepository) { _actionRepository = actionRepository ?? throw new ArgumentNullException(nameof(actionRepository)); _actionPlanRepository = actionPlanRepository ?? throw new ArgumentNullException(nameof(actionPlanRepository)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _actionService = actionService; + _historicalActionPlanRepository = historicalActionPlanRepository; } /// @@ -465,7 +467,7 @@ public async Task GetActionPlanByRobotIdAsync(Guid robotId) return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } } - #endregion + /// /// Get latest action plan given robot Id. @@ -522,6 +524,255 @@ public async Task GetLatestActionPlanByRobotIdAsync(Guid robotId) return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } } + #endregion + + #region HistoricalActionPlan + + /// + /// Retrieves all HistoricalActionPlans + /// + /// + [HttpGet] + [Route("historicalPlan", Name = "HistoricalActionPlanGetAll")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetAllHistoricalActionPlansAsync() + { + try + { + var plans = await _historicalActionPlanRepository.GetAllAsync(); + if (plans == null || plans.Any() == false) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No plans have been found.")); + } + return Ok(plans); + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occurred:"); + int statusCode = (int)HttpStatusCode.InternalServerError; + return StatusCode(statusCode, new ApiResponse(statusCode, ex.Message)); + } + } + + /// + /// Retrieves an HistoricalActionPlan by id + /// + /// + /// + [HttpGet] + [Route("historicalPlan/{id:guid}", Name = "HistoricalActionPlanGetById")] + [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetHistoricalActionPlanByIdAsync(Guid id) + { + try + { + var plan = await _historicalActionPlanRepository.GetByIdAsync(id); + if (plan == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Specified plan was not found.")); + } + return Ok(plan); + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occurred:"); + int statusCode = (int)HttpStatusCode.InternalServerError; + return StatusCode(statusCode, new ApiResponse(statusCode, ex.Message)); + } + } + + /// + /// Creates new HistoricalActionPlan + /// + /// + /// + [HttpPost] + [Route("historicalPlan", Name = "HistoricalActionPlanAdd")] + [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task AddHistoricalActionPlanAsync(HistoricalActionPlanModel historicalActionPlan) + { + try + { + if (historicalActionPlan == null) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Historical plan cannot be null")); + } + var plan = await _historicalActionPlanRepository.AddAsync(historicalActionPlan, () => historicalActionPlan.Id); + if (plan == null) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "The specified historical plan has not been added.")); + } + return Ok(plan); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Delete HistoricalActionPlan by id + /// + /// + /// + [HttpDelete] + [Route("HistoricalPlan/{id}", Name = "HistoricalActionPlanDelete")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task DeleteHistoricalActionPlanAsync(Guid id) + { + try + { + var deleted = await _historicalActionPlanRepository.DeleteByIdAsync(id); + if (deleted == false) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified historical plan has not been found.")); + } + return Ok(); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Patches an existing ActionPlan by id + /// + /// + /// + /// + [HttpPut] + [Route("plan/{id:guid}", Name = "ActionPlanPatch")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task PatchHistoricalActionPlanAsync(Guid id, [FromBody] HistoricalActionPlanModel historicalActionPlan) + { + try + { + if (historicalActionPlan == null || id == Guid.Empty) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Id or updated object has not been specified")); + } + var deleted = await _historicalActionPlanRepository.DeleteByIdAsync(id); + if (deleted == false) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified plan has not been found.")); + } + + var updatedPlan = await _historicalActionPlanRepository.AddAsync(historicalActionPlan, () => id); + return Ok(updatedPlan); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Get historical action plan given robot Id. + /// + /// List + [HttpGet] + [Route("historicalPlan/robot/{robotId}", Name = "GetHistoricalActionPlanByRobotIdAsync")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetHistoricalActionPlanByRobotIdAsync(Guid robotId) + { + try + { + if (robotId == Guid.Empty) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Robot id has not been specified")); + } + // Get list of actionPlans from specific robotId. + List historicalActionPlans = await _historicalActionPlanRepository.GetRobotActionPlans(robotId); + if (historicalActionPlans == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); + } + return Ok(historicalActionPlans); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Get latest historical action plan given robot Id. + /// + /// List + [HttpGet] + [Route("Historicalplan/robot/{robotId}/latest", Name = "GetLatestHistoricalActionPlanByRobotIdAsync")] + [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetLatestHistoricalActionPlanByRobotIdAsync(Guid robotId) + { + try + { + if (robotId == Guid.Empty) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Robot id has not been specified")); + } + // Get list of actionPlans from specific robotId. + List actionPlans = await _historicalActionPlanRepository.GetRobotActionPlans(robotId); + + //Get the newest task of robot. + Dictionary tempDic = new Dictionary(); + Dictionary OrderedTempDic = new Dictionary(); + + // Complete tempDic + foreach (HistoricalActionPlanModel plan in actionPlans) + { + DateTime d; + DateTime.TryParseExact(plan.Status, "ggyyyy$dd-MMM (dddd)", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out d); + tempDic.Add(plan, d); + } + + // Order a new dictionary + foreach (KeyValuePair pair in tempDic.OrderByDescending(p => p.Value)) + { + OrderedTempDic.Add(pair.Key, pair.Value); + } + + // Get last item which is the latest plan. + HistoricalActionPlanModel last = OrderedTempDic.Keys.First(); + + if (actionPlans == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); + } + return Ok(last); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + #endregion } } diff --git a/src/RedisInterface/Services/DashboardService.cs b/src/RedisInterface/Services/DashboardService.cs index 3d41124a..905d4a42 100644 --- a/src/RedisInterface/Services/DashboardService.cs +++ b/src/RedisInterface/Services/DashboardService.cs @@ -19,6 +19,7 @@ public class DashboardService : IDashboardService private readonly ICloudRepository _cloudRepository; private readonly IInstanceRepository _instanceRepository; private readonly IActionRepository _actionRepository; + private readonly IHistoricalActionPlanRepository _historicalActionPlanRepository; public DashboardService(IRobotRepository robotRepository, @@ -27,7 +28,8 @@ public DashboardService(IRobotRepository robotRepository, IEdgeRepository edgeRepository, ICloudRepository cloudRepository, IInstanceRepository instanceRepository, - IActionRepository actionRepository) + IActionRepository actionRepository, + IHistoricalActionPlanRepository historicalActionPlanRepository) { _instanceRepository = instanceRepository; _cloudRepository = cloudRepository; @@ -36,6 +38,7 @@ public DashboardService(IRobotRepository robotRepository, _taskRepository = taskRepository; _robotRepository = robotRepository; _actionRepository = actionRepository; + _historicalActionPlanRepository = historicalActionPlanRepository; } /// @@ -123,7 +126,7 @@ public async Task, int>> GetLocationsStatusLi public async Task, int>> GetRobotStatusListAsync(PaginationFilter filter) { var robots = await _robotRepository.GetAllAsync(); - var actionPlans = await _actionPlanRepository.GetAllAsync(); + var actionPlans = await _historicalActionPlanRepository.GetAllAsync(); var tasks = await _taskRepository.GetAllAsync(); var responses = new List(); From 91d96b2b9fbe6194b1de1d8de66e0f497920d00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Wed, 8 Mar 2023 12:58:54 +0100 Subject: [PATCH 16/31] Fix routing problems with RabbitMQ config --- src/Common/Helpers/QueueHelpers.cs | 36 +++++++++++++++++++ .../ServiceCollectionExtensions.cs | 21 ++++++----- src/ResourcePlanner/ResourcePlanner.cs | 7 ++-- .../ServiceCollectionExtensions.cs | 8 +---- src/TaskPlanner/Services/ActionPlanner.cs | 8 ++++- 5 files changed, 57 insertions(+), 23 deletions(-) create mode 100644 src/Common/Helpers/QueueHelpers.cs diff --git a/src/Common/Helpers/QueueHelpers.cs b/src/Common/Helpers/QueueHelpers.cs new file mode 100644 index 00000000..4a45f0ae --- /dev/null +++ b/src/Common/Helpers/QueueHelpers.cs @@ -0,0 +1,36 @@ +namespace Middleware.Common.Helpers; + +/// +/// Class responsible for handling common +/// +public static class QueueHelpers +{ + /// + /// Constructs the routing key based on the deployment properties + /// + /// + /// + /// + public static string ConstructRoutingKey(string instanceName, string instanceType) + { + if (instanceName == null) throw new ArgumentNullException(nameof(instanceName)); + if (instanceType == null) throw new ArgumentNullException(nameof(instanceType)); + + return $"{instanceName}-{instanceType}"; + } + + /// + /// Constructs the deployment queue name for this specific Middleware instance + /// + /// + /// + /// + /// + public static string ConstructDeploymentQueueName(string organization, string instanceName) + { + if (organization == null) throw new ArgumentNullException(nameof(organization)); + if (instanceName == null) throw new ArgumentNullException(nameof(instanceName)); + + return $"{organization}-{instanceName}-deployments"; + } +} \ No newline at end of file diff --git a/src/Orchestrator/ExtensionMethods/ServiceCollectionExtensions.cs b/src/Orchestrator/ExtensionMethods/ServiceCollectionExtensions.cs index e997631c..3e1624cb 100644 --- a/src/Orchestrator/ExtensionMethods/ServiceCollectionExtensions.cs +++ b/src/Orchestrator/ExtensionMethods/ServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using MassTransit; using Middleware.Common.Config; +using Middleware.Common.Helpers; using Middleware.Common.MessageContracts; using Middleware.Orchestrator.Handlers; using RabbitMQ.Client; @@ -18,25 +19,23 @@ public static IServiceCollection RegisterRabbitMqConsumers(this IServiceCollecti x.AddConsumer(); x.UsingRabbitMq((busRegistrationContext, mqBusFactoryConfigurator) => { - //mqBusFactoryConfigurator.SetKebabCaseEndpointNameFormatter(); - mqBusFactoryConfigurator.ExchangeType = "direct"; - mqBusFactoryConfigurator.Durable = true; mqBusFactoryConfigurator.Host(mqConfig.Address, "/", hostConfig => { hostConfig.Username(mqConfig.User); hostConfig.Password(mqConfig.Pass); }); - mqBusFactoryConfigurator.ReceiveEndpoint("deployments", ec => - { - ec.ConfigureConsumeTopology = false; - ec.PrefetchCount = 1; - ec.Bind(nameof(DeployPlanMessage), b => + mqBusFactoryConfigurator.ReceiveEndpoint( + QueueHelpers.ConstructDeploymentQueueName(mwConfig.Organization, mwConfig.InstanceName), + ec => { - b.ExchangeType = ExchangeType.Direct; - b.RoutingKey = $"{mwConfig.InstanceName}-{mwConfig.InstanceType}"; + ec.ConfigureConsumeTopology = false; + ec.Bind(nameof(DeployPlanMessage), b => + { + b.ExchangeType = ExchangeType.Direct; + b.RoutingKey = QueueHelpers.ConstructRoutingKey(mwConfig.InstanceName, mwConfig.InstanceType); + }); }); - }); mqBusFactoryConfigurator.ConfigureEndpoints(busRegistrationContext); }); diff --git a/src/ResourcePlanner/ResourcePlanner.cs b/src/ResourcePlanner/ResourcePlanner.cs index f50a401b..b6029c94 100644 --- a/src/ResourcePlanner/ResourcePlanner.cs +++ b/src/ResourcePlanner/ResourcePlanner.cs @@ -69,12 +69,11 @@ public async Task Plan(TaskModel taskModel, RobotModel robot) instance = reusedInstance; } // add instance to actions - action.Services.Add(instance); + action.Services.Add(instance); } - //Choose placement based on policy - // TODO: deploy only in the local MW - action.Placement = $"{_mwConfig.InstanceName}-{_mwConfig.InstanceType}"; //"local";//await InferResource(action, robot,false, new()); + action.Placement = _mwConfig.InstanceName; + action.PlacementType = _mwConfig.InstanceType; } return taskModel; diff --git a/src/TaskPlanner/ExtensionMethods/ServiceCollectionExtensions.cs b/src/TaskPlanner/ExtensionMethods/ServiceCollectionExtensions.cs index 58a066c5..7b352756 100644 --- a/src/TaskPlanner/ExtensionMethods/ServiceCollectionExtensions.cs +++ b/src/TaskPlanner/ExtensionMethods/ServiceCollectionExtensions.cs @@ -17,9 +17,6 @@ public static IServiceCollection RegisterRabbitMqPublishers(this IServiceCollect { x.UsingRabbitMq((busRegistrationContext, mqBusFactoryConfigurator) => { - //mqBusFactoryConfigurator.SetKebabCaseEndpointNameFormatter(); - mqBusFactoryConfigurator.ExchangeType = "direct"; - mqBusFactoryConfigurator.Durable = true; mqBusFactoryConfigurator.Host(mqConfig.Address, "/", hostConfig => { hostConfig.Username(mqConfig.User); @@ -30,10 +27,7 @@ public static IServiceCollection RegisterRabbitMqPublishers(this IServiceCollect x.UseRoutingKeyFormatter(t => t.Message.DeploymentLocation); }); mqBusFactoryConfigurator.Message(x => x.SetEntityName(nameof(DeployPlanMessage))); - mqBusFactoryConfigurator.Publish(x => - { - x.ExchangeType = ExchangeType.Direct; - }); + mqBusFactoryConfigurator.Publish(x => { x.ExchangeType = ExchangeType.Direct; }); mqBusFactoryConfigurator.ConfigureEndpoints(busRegistrationContext); }); diff --git a/src/TaskPlanner/Services/ActionPlanner.cs b/src/TaskPlanner/Services/ActionPlanner.cs index 9509e09f..35e31bf6 100644 --- a/src/TaskPlanner/Services/ActionPlanner.cs +++ b/src/TaskPlanner/Services/ActionPlanner.cs @@ -1,3 +1,4 @@ +using Middleware.Common.Helpers; using Middleware.Models.Domain; using Middleware.TaskPlanner.Exceptions; using KeyValuePair = Middleware.Models.Domain.KeyValuePair; @@ -589,7 +590,12 @@ public async Task> ReInferActionSequence public async Task PublishPlanAsync(TaskModel task, RobotModel robot) { - var location = task.ActionSequence.Select(a => a.Placement).Distinct().First(); + var action = task.ActionSequence!.FirstOrDefault(); + + if (action == null) + return; + + var location = QueueHelpers.ConstructRoutingKey(action.Placement, action.PlacementType); var message = new DeployPlanMessage() { Task = task, From feb29555d1791a5921e366d723930c4a8fe3fb9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Wed, 8 Mar 2023 14:29:05 +0100 Subject: [PATCH 17/31] Corrected consumer declaration; added logging --- .../ExtensionMethods/ServiceCollectionExtensions.cs | 4 +++- .../Handlers/DeployPlan/DeployPlanConsumer.cs | 12 +++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Orchestrator/ExtensionMethods/ServiceCollectionExtensions.cs b/src/Orchestrator/ExtensionMethods/ServiceCollectionExtensions.cs index 3e1624cb..c0ca948f 100644 --- a/src/Orchestrator/ExtensionMethods/ServiceCollectionExtensions.cs +++ b/src/Orchestrator/ExtensionMethods/ServiceCollectionExtensions.cs @@ -13,6 +13,7 @@ public static class ServiceCollectionExtensions public static IServiceCollection RegisterRabbitMqConsumers(this IServiceCollection services, RabbitMqConfig mqConfig, MiddlewareConfig mwConfig) { + var routingKey = QueueHelpers.ConstructRoutingKey(mwConfig.InstanceName, mwConfig.InstanceType); services.AddMassTransit(x => { services.AddScoped(); @@ -33,8 +34,9 @@ public static IServiceCollection RegisterRabbitMqConsumers(this IServiceCollecti ec.Bind(nameof(DeployPlanMessage), b => { b.ExchangeType = ExchangeType.Direct; - b.RoutingKey = QueueHelpers.ConstructRoutingKey(mwConfig.InstanceName, mwConfig.InstanceType); + b.RoutingKey = routingKey; }); + ec.ConfigureConsumer(busRegistrationContext); }); mqBusFactoryConfigurator.ConfigureEndpoints(busRegistrationContext); diff --git a/src/Orchestrator/Handlers/DeployPlan/DeployPlanConsumer.cs b/src/Orchestrator/Handlers/DeployPlan/DeployPlanConsumer.cs index b0cb63a9..d0f663bb 100644 --- a/src/Orchestrator/Handlers/DeployPlan/DeployPlanConsumer.cs +++ b/src/Orchestrator/Handlers/DeployPlan/DeployPlanConsumer.cs @@ -1,24 +1,30 @@ using MassTransit; +using Middleware.Common.Config; using Middleware.Common.MessageContracts; using Middleware.Orchestrator.Deployment; -using Serilog; + namespace Middleware.Orchestrator.Handlers; public class DeployPlanConsumer : IConsumer { private readonly IDeploymentService _deploymentService; - private readonly Serilog.ILogger _logger; + private readonly ILogger _logger; + private readonly IConfiguration _cfg; - public DeployPlanConsumer(IDeploymentService deploymentService, Serilog.ILogger logger) + public DeployPlanConsumer(IDeploymentService deploymentService, ILogger logger, IConfiguration cfg) { _deploymentService = deploymentService; _logger = logger; + _cfg = cfg; } public async Task Consume(ConsumeContext ctx) { + _logger.LogInformation("Started processing DeployPlanMessage"); + var mwconfig = _cfg.GetSection(MiddlewareConfig.ConfigName).Get(); var plan = ctx.Message; + _logger.LogDebug("Location {0}-{1} received message request addressed to {2}", mwconfig.InstanceName, mwconfig.InstanceType, plan.DeploymentLocation); var _ = await _deploymentService.DeployAsync(plan.Task, plan.RobotId); } } \ No newline at end of file From d4969a241fd6fc5561a45f48d95928b9869b77e9 Mon Sep 17 00:00:00 2001 From: adrianLIrobotics Date: Wed, 8 Mar 2023 15:14:00 +0100 Subject: [PATCH 18/31] Created HistoricalActionPlanModel in DeletePlanAsync --- src/Common/Enums/ActionStatusEnum.cs | 1 + .../Abstract/IRedisInterfaceClientService.cs | 9 +++ .../Services/RedisInterfaceClientService.cs | 61 +++++++++++++++++++ src/DataConverter/Program.cs | 6 +- .../Domain/HistoricalActionPlanModel.cs | 4 ++ src/Models/Dto/HistoricalActionPlanDto.cs | 5 ++ .../Deployment/DeploymentService.cs | 37 ++++++++++- 7 files changed, 119 insertions(+), 4 deletions(-) diff --git a/src/Common/Enums/ActionStatusEnum.cs b/src/Common/Enums/ActionStatusEnum.cs index 4cf8ecd8..ceb71c72 100644 --- a/src/Common/Enums/ActionStatusEnum.cs +++ b/src/Common/Enums/ActionStatusEnum.cs @@ -4,6 +4,7 @@ public enum ActionStatusEnum { Unknown, Running, + Finished, Idle, Off } diff --git a/src/Common/Services/Abstract/IRedisInterfaceClientService.cs b/src/Common/Services/Abstract/IRedisInterfaceClientService.cs index 8ea424d2..c935f825 100644 --- a/src/Common/Services/Abstract/IRedisInterfaceClientService.cs +++ b/src/Common/Services/Abstract/IRedisInterfaceClientService.cs @@ -180,4 +180,13 @@ Task DeleteRelationAsync(TSource source, TDirection d Task HistoricalActionPlanAddAsync(HistoricalActionPlanModel historicalActionPlan); Task HistoricalActionPlanAddAsync(HistoricalActionPlanModel historicalActionPlan, CancellationToken token); + + Task ActionRunningGetByIdAsync(Guid id); + + Task ActionRunningGetByIdAsync(Guid id, CancellationToken token); + + Task InstanceRunningGetByIdAsync(Guid id); + + Task InstanceRunningGetByIdAsync(Guid id, CancellationToken token); + } \ No newline at end of file diff --git a/src/Common/Services/RedisInterfaceClientService.cs b/src/Common/Services/RedisInterfaceClientService.cs index 7984e110..d2d98163 100644 --- a/src/Common/Services/RedisInterfaceClientService.cs +++ b/src/Common/Services/RedisInterfaceClientService.cs @@ -538,6 +538,37 @@ public async Task ActionRunningAddAsync(ActionRunningModel actionRunning, } } + public async Task ActionRunningGetByIdAsync(Guid id) + { + return await ActionRunningGetByIdAsync(id, CancellationToken.None); + } + + public async Task ActionRunningGetByIdAsync(Guid id, CancellationToken token) + { + if (id == default) + throw new ArgumentNullException(nameof(id)); + + string url = $"/api/v1/actionRunning/{id}"; + try + { + var result = await _httpClient.GetAsync(url, token); + + if (result.IsSuccessStatusCode == false) + { + throw new InvalidOperationException(); + } + + var body = await result.Content.ReadAsStringAsync(token); + var action = JsonConvert.DeserializeObject(body); + return action; + } + catch (Exception ex) + { + _logger.LogError(ex, "There was en error while calling for the action plan with id: {id}", id); + throw; + } + } + public async Task InstanceRunningAddAsync(InstanceRunningModel instanceRunning) { return await InstanceRunningAddAsync(instanceRunning, CancellationToken.None); @@ -587,5 +618,35 @@ public async Task HistoricalActionPlanAddAsync(HistoricalActionPlanModel h throw; } } + public async Task InstanceRunningGetByIdAsync(Guid id) + { + return await InstanceRunningGetByIdAsync(id, CancellationToken.None); + } + + public async Task InstanceRunningGetByIdAsync(Guid id, CancellationToken token) + { + if (id == default) + throw new ArgumentNullException(nameof(id)); + + string url = $"/api/v1/instanceRunning/{id}"; + try + { + var result = await _httpClient.GetAsync(url, token); + + if (result.IsSuccessStatusCode == false) + { + throw new InvalidOperationException(); + } + + var body = await result.Content.ReadAsStringAsync(token); + var action = JsonConvert.DeserializeObject(body); + return action; + } + catch (Exception ex) + { + _logger.LogError(ex, "There was en error while calling for the action plan with id: {id}", id); + throw; + } + } } } \ No newline at end of file diff --git a/src/DataConverter/Program.cs b/src/DataConverter/Program.cs index 6f734362..40213506 100644 --- a/src/DataConverter/Program.cs +++ b/src/DataConverter/Program.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging.Abstractions; using Middleware.Common.Config; using Middleware.DataAccess.Repositories; +using Middleware.DataAccess.Repositories.Abstract; using Middleware.DataAccess.Repositories.Redis; using Middleware.Models.Domain; using Middleware.RedisInterface.Responses; @@ -188,11 +189,12 @@ #endregion + #region relations Console.WriteLine("Writing Relations..."); - +var historicalActionPlanRepository = new RedisHistoricalActionPlanRepository(clusterConnectionProvider, redisGraphClient, Serilog.Log.Logger); var dashboardService = new DashboardService(robotRepository, taskRepository, actionPlanRepository, edgeRepository, - cloudRepository, instanceRepository, actionRepository); + cloudRepository, instanceRepository, actionRepository, historicalActionPlanRepository); var relations = await dashboardService.GetAllRelationModelsAsync(); diff --git a/src/Models/Domain/HistoricalActionPlanModel.cs b/src/Models/Domain/HistoricalActionPlanModel.cs index 1c4001aa..4135be8a 100644 --- a/src/Models/Domain/HistoricalActionPlanModel.cs +++ b/src/Models/Domain/HistoricalActionPlanModel.cs @@ -38,6 +38,9 @@ public sealed class HistoricalActionPlanModel : BaseModel [JsonPropertyName("TaskStartedAt")] public DateTime TaskStartedAt { get; set; } + [JsonPropertyName("CreationTime")] + public DateTime CreationTime { get; set; } + public void SetStatus(string status) { @@ -61,6 +64,7 @@ public override Dto.Dto ToDto() ActionSequence = domain.ActionSequence, RobotId = domain.RobotId.ToString(), TaskStartedAt = domain.TaskStartedAt == default ? DateTimeOffset.Now : domain.TaskStartedAt, + CreationTime = domain.CreationTime == default ? DateTimeOffset.Now : domain.TaskStartedAt, PreviousPlanId = domain.PreviousPlanId.ToString() }; ; } diff --git a/src/Models/Dto/HistoricalActionPlanDto.cs b/src/Models/Dto/HistoricalActionPlanDto.cs index 3f2f282f..f56cdf81 100644 --- a/src/Models/Dto/HistoricalActionPlanDto.cs +++ b/src/Models/Dto/HistoricalActionPlanDto.cs @@ -27,6 +27,10 @@ public class HistoricalActionPlanDto : Dto public string RobotId { get; set; } [Indexed(Sortable = true)] public DateTimeOffset TaskStartedAt { get; set; } + [Indexed(Sortable = true)] + public DateTimeOffset CreationTime { get; set; } + + [Indexed] public string PreviousPlanId { get; set; } @@ -44,6 +48,7 @@ public override BaseModel ToModel() ActionSequence = dto.ActionSequence, RobotId = Guid.Parse(dto.RobotId), TaskStartedAt = dto.TaskStartedAt.DateTime, + CreationTime = dto.CreationTime.DateTime, PreviousPlanId = Guid.Parse(dto.PreviousPlanId) }; } diff --git a/src/Orchestrator/Deployment/DeploymentService.cs b/src/Orchestrator/Deployment/DeploymentService.cs index 3c9a89c1..5f175caf 100644 --- a/src/Orchestrator/Deployment/DeploymentService.cs +++ b/src/Orchestrator/Deployment/DeploymentService.cs @@ -10,6 +10,7 @@ using Middleware.Models.Domain; using Middleware.Orchestrator.Exceptions; using Middleware.Orchestrator.Models; +using System; using System.Threading.Tasks; namespace Middleware.Orchestrator.Deployment; @@ -127,6 +128,7 @@ public async Task DeployAsync(TaskModel task, Guid robotId) /// private async Task SaveActionSequence(TaskModel task, Guid robotId) { + //List actionsRunning = new List(); var actionPlan = new ActionPlanModel(task.ActionPlanId, task.Id, task.Name, task.ActionSequence!, robotId); actionPlan.SetStatus("active"); RobotModel robot = await _redisInterfaceClient.RobotGetByIdAsync(robotId); @@ -157,6 +159,7 @@ private async Task SaveActionSequence(TaskModel task, Guid robotId) }).ToList(); ActionRunningTemp.MinimumRam = action.MinimumRam; ActionRunningTemp.MinimumNumCores = action.MinimumNumCores; + //actionsRunning.Add(ActionRunningTemp); //Add the runningAction to the actionPlang await _redisInterfaceClient.ActionRunningAddAsync(ActionRunningTemp); await _redisInterfaceClient.AddRelationAsync(actionPlan, ActionRunningTemp, "CONSISTS_OF"); @@ -347,10 +350,39 @@ public async Task DeletePlanAsync(ActionPlanModel actionPlan) historicalActionPlan.IsReplan = actionPlan.IsReplan; historicalActionPlan.Name = actionPlan.Name; historicalActionPlan.RobotId = actionPlan.RobotId; - historicalActionPlan.LastStatusChange = actionPlan.LastStatusChange; historicalActionPlan.TaskId = actionPlan.TaskId; - historicalActionPlan.Status = actionPlan.Status; + historicalActionPlan.Status = ActionStatusEnum.Finished.ToString(); historicalActionPlan.TaskStartedAt = actionPlan.TaskStartedAt; + + var images = await _redisInterfaceClient.GetRelationAsync(actionPlan, "CONSISTS_OF"); + foreach (RelationModel relation in images) + { + ActionRunningModel runningAction = await _redisInterfaceClient.ActionRunningGetByIdAsync(relation.PointsTo.Id); + var runningIntancesImages = await _redisInterfaceClient.GetRelationAsync(runningAction, "CONSISTS_OF"); + foreach (RelationModel instanceRelation in runningIntancesImages) + { + InstanceRunningModel runningInstance = await _redisInterfaceClient.InstanceRunningGetByIdAsync(instanceRelation.PointsTo.Id); + runningAction.Services.Add(runningInstance); + } + + historicalActionPlan.ActionSequence.Add(runningAction); + } + /* + + foreach (RelationModel relation in images) + { + InstanceModel instance = await _redisInterfaceClient.InstanceGetByIdAsync(relation.PointsTo.Id); + + if (CanBeReused(instance) && taskModel.ResourceLock) + { + var reusedInstance = await GetInstanceToReuse(instance, orchestratorApiClient); + if (reusedInstance is not null) + instance = reusedInstance; + } + // add instance to actions + action.Services.Add(instance); + } + historicalActionPlan.ActionSequence = actionPlan.ActionSequence.Select(x => new ActionRunningModel() { Name = x.Name, @@ -373,6 +405,7 @@ public async Task DeletePlanAsync(ActionPlanModel actionPlan) }).ToList() }).ToList(); + */ // Publish to redis the historical action plan await _redisInterfaceClient.HistoricalActionPlanAddAsync(historicalActionPlan); From d082ea7d504dddb6e52b89a6bed9f9fc5f0694a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Wed, 8 Mar 2023 15:18:07 +0100 Subject: [PATCH 19/31] Update orchestrator.yaml --- k8s/orchestrator/orchestrator.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/k8s/orchestrator/orchestrator.yaml b/k8s/orchestrator/orchestrator.yaml index 3476a9ce..55540401 100644 --- a/k8s/orchestrator/orchestrator.yaml +++ b/k8s/orchestrator/orchestrator.yaml @@ -35,18 +35,18 @@ spec: - name: awsecr-cred containers: - name: orchestrator-api - image: 394603622351.dkr.ecr.eu-west-1.amazonaws.com/orchestrator-api:v0.2.1-rc7 + image: 394603622351.dkr.ecr.eu-west-1.amazonaws.com/orchestrator-api:v0.2.2 imagePullPolicy: Always resources: {} env: - name: AWS_IMAGE_REGISTRY value: 394603622351.dkr.ecr.eu-west-1.amazonaws.com # to be replaced with real value - name: REDIS_INTERFACE_API_SERVICE_HOST - value: redis-interface-api.middleware.cluster.local # to be replaced with real value + value: redis-interface-api # to be replaced with real value - name: REDIS_INTERFACE_API_SERVICE_PORT value: "80" - name: Middleware__Organization - value: 5G-ERA # to be replaced with real value + value: 5G-ERA-DEV # to be replaced with real value - name: Middleware__InstanceName value: MiddlewareBED # to be replaced with real value - name: Middleware__InstanceType From b72aa87a00062e61b16d2bb437ba3cf2b73a6dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Wed, 8 Mar 2023 15:39:26 +0100 Subject: [PATCH 20/31] Fix checking status of deployed actions --- src/Orchestrator/Jobs/UpdateStatusJob.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Orchestrator/Jobs/UpdateStatusJob.cs b/src/Orchestrator/Jobs/UpdateStatusJob.cs index f31368d0..830adb30 100644 --- a/src/Orchestrator/Jobs/UpdateStatusJob.cs +++ b/src/Orchestrator/Jobs/UpdateStatusJob.cs @@ -87,7 +87,7 @@ protected override async Task ExecuteJobAsync(IJobExecutionContext context) //check if all instances are down for at least half an hour, then terminate foreach (var action in seq.ActionSequence) { - if (action.Placement != $"{_middlewareConfig.InstanceName}-{_middlewareConfig.InstanceType}") + if (action.Placement != _middlewareConfig.InstanceName || action.PlacementType != _middlewareConfig.InstanceType) continue; foreach (var instance in action.Services) From d31cf9e8f9a7ce7ac3f07802bfaf37ac622cb9d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Thu, 9 Mar 2023 14:55:47 +0100 Subject: [PATCH 21/31] Correct conflicting controller paths and names --- src/RedisInterface/Controllers/ActionController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RedisInterface/Controllers/ActionController.cs b/src/RedisInterface/Controllers/ActionController.cs index bf819bdc..e5813240 100644 --- a/src/RedisInterface/Controllers/ActionController.cs +++ b/src/RedisInterface/Controllers/ActionController.cs @@ -654,7 +654,7 @@ public async Task DeleteHistoricalActionPlanAsync(Guid id) /// /// [HttpPut] - [Route("plan/{id:guid}", Name = "ActionPlanPatch")] + [Route("historicalPlan/{id:guid}", Name = "HistoricalActionPlanPatch")] [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] @@ -722,7 +722,7 @@ public async Task GetHistoricalActionPlanByRobotIdAsync(Guid robo /// /// List [HttpGet] - [Route("Historicalplan/robot/{robotId}/latest", Name = "GetLatestHistoricalActionPlanByRobotIdAsync")] + [Route("HistoricalPlan/robot/{robotId}/latest", Name = "GetLatestHistoricalActionPlanByRobotIdAsync")] [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] From c814dfcc93964162555907d5284a4f06debfa408 Mon Sep 17 00:00:00 2001 From: adrianLIrobotics Date: Fri, 10 Mar 2023 10:05:34 +0100 Subject: [PATCH 22/31] Put actionRunning inside the actionController --- .../Controllers/ActionController.cs | 276 ++++++++++++++++- .../Controllers/ActionRunningController.cs | 277 ------------------ .../Controllers/InstanceRunningController.cs | 12 +- 3 files changed, 274 insertions(+), 291 deletions(-) delete mode 100644 src/RedisInterface/Controllers/ActionRunningController.cs diff --git a/src/RedisInterface/Controllers/ActionController.cs b/src/RedisInterface/Controllers/ActionController.cs index bf819bdc..d698db47 100644 --- a/src/RedisInterface/Controllers/ActionController.cs +++ b/src/RedisInterface/Controllers/ActionController.cs @@ -16,14 +16,16 @@ public class ActionController : ControllerBase private readonly ILogger _logger; private readonly IActionService _actionService; private readonly IHistoricalActionPlanRepository _historicalActionPlanRepository; + private readonly IActionRunningRepository _actionRunningRepository; - public ActionController(IActionRepository actionRepository, IActionPlanRepository actionPlanRepository, ILogger logger, IActionService actionService, IHistoricalActionPlanRepository historicalActionPlanRepository) + public ActionController(IActionRepository actionRepository, IActionPlanRepository actionPlanRepository, ILogger logger, IActionService actionService, IHistoricalActionPlanRepository historicalActionPlanRepository, IActionRunningRepository actionRunningRepository) { _actionRepository = actionRepository ?? throw new ArgumentNullException(nameof(actionRepository)); _actionPlanRepository = actionPlanRepository ?? throw new ArgumentNullException(nameof(actionPlanRepository)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _actionService = actionService; _historicalActionPlanRepository = historicalActionPlanRepository; + _actionRunningRepository = actionRunningRepository; } /// @@ -533,7 +535,7 @@ public async Task GetLatestActionPlanByRobotIdAsync(Guid robotId) /// /// [HttpGet] - [Route("historicalPlan", Name = "HistoricalActionPlanGetAll")] + [Route("plan/historical", Name = "HistoricalActionPlanGetAll")] [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] @@ -562,7 +564,7 @@ public async Task GetAllHistoricalActionPlansAsync() /// /// [HttpGet] - [Route("historicalPlan/{id:guid}", Name = "HistoricalActionPlanGetById")] + [Route("plan/historical/{id:guid}", Name = "HistoricalActionPlanGetById")] [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] @@ -591,7 +593,7 @@ public async Task GetHistoricalActionPlanByIdAsync(Guid id) /// /// [HttpPost] - [Route("historicalPlan", Name = "HistoricalActionPlanAdd")] + [Route("plan/historical", Name = "HistoricalActionPlanAdd")] [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] @@ -624,7 +626,7 @@ public async Task AddHistoricalActionPlanAsync(HistoricalActionPl /// /// [HttpDelete] - [Route("HistoricalPlan/{id}", Name = "HistoricalActionPlanDelete")] + [Route("plan/historical/{id}", Name = "HistoricalActionPlanDelete")] [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] @@ -648,13 +650,13 @@ public async Task DeleteHistoricalActionPlanAsync(Guid id) } /// - /// Patches an existing ActionPlan by id + /// Patches an existing HistoricalActionPlan by id /// /// /// /// [HttpPut] - [Route("plan/{id:guid}", Name = "ActionPlanPatch")] + [Route("plan/historical/{id:guid}", Name = "HistoricalActionPlanPatch")] [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] @@ -689,7 +691,7 @@ public async Task PatchHistoricalActionPlanAsync(Guid id, [FromBo /// /// List [HttpGet] - [Route("historicalPlan/robot/{robotId}", Name = "GetHistoricalActionPlanByRobotIdAsync")] + [Route("plan/historical/robot/{robotId}", Name = "GetHistoricalActionPlanByRobotIdAsync")] [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] @@ -722,7 +724,7 @@ public async Task GetHistoricalActionPlanByRobotIdAsync(Guid robo /// /// List [HttpGet] - [Route("Historicalplan/robot/{robotId}/latest", Name = "GetLatestHistoricalActionPlanByRobotIdAsync")] + [Route("plan/historical/robot/{robotId}/latest", Name = "GetLatestHistoricalActionPlanByRobotIdAsync")] [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] @@ -774,5 +776,261 @@ public async Task GetLatestHistoricalActionPlanByRobotIdAsync(Gui #endregion + #region ActionRunning + + /// + /// Get all the ActionRunningModels entities + /// + /// the list of ActionModel entities + [HttpGet] + [Route("running", Name = "ActionRunningGetAll")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task>> GetAllActionRunningAsync() + { + try + { + + List models = await _actionRunningRepository.GetAllAsync(); + if (models.Any() == false) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No actions were found.")); + } + return Ok(models); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + //New end points for depends_on property for actions. + + /// + /// Get an ActionRunningModel entity by id + /// + /// + /// the ActionRunningModel entity for the specified id + [HttpGet] + [Route("running/{id:guid}", Name = "ActionRunningGetById")] + [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetActionRunningByIdAsync(Guid id) + { + try + { + ActionRunningModel model = await _actionRunningRepository.GetByIdAsync(id); + if (model == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, $"Action with id: '{id}' was not found.")); + } + return Ok(model); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Add a new ActionRunningModel entity + /// + /// + /// the newly created ActionModel entity + [HttpPost] + [Route("running", Name = "ActionRunningAdd")] + [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task> AddActionRunningAsync([FromBody] ActionRunningModel model) + { + if (model == null) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Parameters were not specified.")); + } + try + { + model = await _actionRunningRepository.AddAsync(model); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + return Ok(model); + } + + /// + /// Partially update an existing ActionRunningModel entity + /// + /// + /// + /// the modified ActionModel entity + [HttpPatch] + [Route("running/{id:guid}", Name = "ActionRunningPatch")] + [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task PatchActionRunningAsync([FromBody] ActionRunningModel patch, [FromRoute] Guid id) + { + try + { + ActionRunningModel model = await _actionRunningRepository.PatchActionAsync(id, patch); + if (model == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object to be updated was not found.")); + } + return Ok(model); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Delete an ActionRunningModel entity for the given id + /// + /// + /// no return + [HttpDelete] + [Route("running/{id}", Name = "ActionRunningDelete")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task DeleteActionRunningByIdAsync(Guid id) + { + try + { + var deleted = await _actionRunningRepository.DeleteByIdAsync(id); + if (deleted == false) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified Action has not been found.")); + } + return Ok(); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Creates a new relation between two models + /// + /// + /// + [HttpPost] + [Route("running/AddRelation", Name = "ActionRunningAddRelation")] + [ProducesResponseType(typeof(RelationModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task> AddActionRunningRelationAsync([FromBody] RelationModel model) + { + if (model == null) + { + return BadRequest("Parameters were not specified."); + } + try + { + bool isValid = await _actionRunningRepository.AddRelationAsync(model); + if (!isValid) + { + return StatusCode((int)HttpStatusCode.InternalServerError, new ApiResponse((int)HttpStatusCode.InternalServerError, "The relation was not created")); + } + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + return Ok(model); + } + + /// + /// Retrieves a single relation by name + /// + /// + /// + /// + [HttpGet] + [Route("running/relation/{name}", Name = "ActionRunningGetRelationByName")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetActionRunningRelationAsync(Guid id, string name) //Guid of node and name of relationship + { + if (string.IsNullOrWhiteSpace(name)) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); + } + + if (id == Guid.Empty) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); + } + try + { + var relations = await _actionRunningRepository.GetRelation(id, name); + if (!relations.Any()) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found.")); + } + return Ok(relations); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + /// + /// Retrieves two relations by their names + /// + /// + /// + /// + /// + [HttpGet] + [Route("running/relations/{firstName}/{secondName}", Name = "ActionRunningGetRelationsByName")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetActionRunningRelationsAsync(Guid id, string firstName, string secondName) + { + try + { + List relationNames = new List() { firstName, secondName }; + var relations = await _actionRunningRepository.GetRelations(id, relationNames); + if (!relations.Any()) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found")); + } + return Ok(relations); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } + + #endregion + } } diff --git a/src/RedisInterface/Controllers/ActionRunningController.cs b/src/RedisInterface/Controllers/ActionRunningController.cs deleted file mode 100644 index 85d1f756..00000000 --- a/src/RedisInterface/Controllers/ActionRunningController.cs +++ /dev/null @@ -1,277 +0,0 @@ -using System.Net; -using Microsoft.AspNetCore.Mvc; -using Middleware.Common.Responses; -using Middleware.DataAccess.Repositories.Abstract; -using Middleware.Models.Domain; -using Middleware.RedisInterface.Services.Abstract; - -namespace Middleware.RedisInterface.Controllers -{ - [Route("api/v1/[controller]")] - [ApiController] - public class ActionRunningController : ControllerBase - { - private readonly IActionRunningRepository _actionRunningRepository; - private readonly ILogger _logger; - - - public ActionRunningController(IActionRunningRepository actionRunningRepository, ILogger logger) - { - _actionRunningRepository = actionRunningRepository ?? throw new ArgumentNullException(nameof(actionRunningRepository)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - - } - - /// - /// Get all the ActionRunningModels entities - /// - /// the list of ActionModel entities - [HttpGet(Name = "ActionRunningGetAll")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task>> GetAllAsync() - { - try - { - - List models = await _actionRunningRepository.GetAllAsync(); - if (models.Any() == false) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No actions were found.")); - } - return Ok(models); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - } - - //New end points for depends_on property for actions. - - /// - /// Get an ActionRunningModel entity by id - /// - /// - /// the ActionRunningModel entity for the specified id - [HttpGet] - [Route("{id}", Name = "ActionRunningGetById")] - [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetByIdAsync(Guid id) - { - try - { - ActionRunningModel model = await _actionRunningRepository.GetByIdAsync(id); - if (model == null) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, $"Action with id: '{id}' was not found.")); - } - return Ok(model); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - } - - /// - /// Add a new ActionRunningModel entity - /// - /// - /// the newly created ActionModel entity - [HttpPost(Name = "ActionRunningAdd")] - [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task> AddAsync([FromBody] ActionRunningModel model) - { - if (model == null) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Parameters were not specified.")); - } - try - { - model = await _actionRunningRepository.AddAsync(model); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - return Ok(model); - } - - /// - /// Partially update an existing ActionRunningModel entity - /// - /// - /// - /// the modified ActionModel entity - [HttpPatch] - [Route("{id}", Name = "ActionRunningPatch")] - [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task PatchActionAsync([FromBody] ActionRunningModel patch, [FromRoute] Guid id) - { - try - { - ActionRunningModel model = await _actionRunningRepository.PatchActionAsync(id, patch); - if (model == null) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object to be updated was not found.")); - } - return Ok(model); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - } - - - /// - /// Delete an ActionRunningModel entity for the given id - /// - /// - /// no return - [HttpDelete] - [Route("{id}", Name = "ActionRunningDelete")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task DeleteByIdAsync(Guid id) - { - try - { - var deleted = await _actionRunningRepository.DeleteByIdAsync(id); - if (deleted == false) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified Action has not been found.")); - } - return Ok(); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - } - - /// - /// Creates a new relation between two models - /// - /// - /// - [HttpPost] - [Route("AddRelation", Name = "ActionRunningAddRelation")] - [ProducesResponseType(typeof(RelationModel), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task> AddRelationAsync([FromBody] RelationModel model) - { - if (model == null) - { - return BadRequest("Parameters were not specified."); - } - try - { - bool isValid = await _actionRunningRepository.AddRelationAsync(model); - if (!isValid) - { - return StatusCode((int)HttpStatusCode.InternalServerError, new ApiResponse((int)HttpStatusCode.InternalServerError, "The relation was not created")); - } - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - return Ok(model); - } - - /// - /// Retrieves a single relation by name - /// - /// - /// - /// - [HttpGet] - [Route("relation/{name}", Name = "ActionRunningGetRelationByName")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetRelationAsync(Guid id, string name) //Guid of node and name of relationship - { - if (string.IsNullOrWhiteSpace(name)) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); - } - - if (id == Guid.Empty) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); - } - try - { - var relations = await _actionRunningRepository.GetRelation(id, name); - if (!relations.Any()) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found.")); - } - return Ok(relations); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - } - - /// - /// Retrieves two relations by their names - /// - /// - /// - /// - /// - [HttpGet] - [Route("relations/{firstName}/{secondName}", Name = "ActionRunningGetRelationsByName")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetRelationsAsync(Guid id, string firstName, string secondName) - { - try - { - List relationNames = new List() { firstName, secondName }; - var relations = await _actionRunningRepository.GetRelations(id, relationNames); - if (!relations.Any()) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found")); - } - return Ok(relations); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - } - - } -} diff --git a/src/RedisInterface/Controllers/InstanceRunningController.cs b/src/RedisInterface/Controllers/InstanceRunningController.cs index d6f5b9d1..14ac9a20 100644 --- a/src/RedisInterface/Controllers/InstanceRunningController.cs +++ b/src/RedisInterface/Controllers/InstanceRunningController.cs @@ -23,7 +23,8 @@ public InstanceRunningController(IInstanceRunningRepository instanceRunningRepos /// Get all the InstanceModel entities /// /// the list of InstanceModel entities - [HttpGet(Name = "InstanceRunningGetAll")] + [HttpGet] + [Route("instance/running", Name = "InstanceRunningGetAll")] [ProducesResponseType(typeof(InstanceRunningModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] @@ -52,7 +53,7 @@ public async Task>> GetAllAsync() /// /// the InstanceModel entity for the specified id [HttpGet] - [Route("{id}", Name = "InstanceRunningGetById")] + [Route("instance/running/{id:guid}", Name = "InstanceRunningGetById")] [ProducesResponseType(typeof(InstanceRunningModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] @@ -80,7 +81,8 @@ public async Task GetByIdAsync(Guid id) /// /// /// the newly created InstanceModel entity - [HttpPost(Name = "InstanceRunningAdd")] + [HttpPost] + [Route("instance/running", Name = "InstanceRunningAdd")] [ProducesResponseType(typeof(InstanceRunningModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] @@ -116,7 +118,7 @@ public async Task> AddAsync([FromBody] Instan /// /// the modified InstanceModel entity [HttpPatch] - [Route("{id}", Name = "InstanceRunningPatch")] + [Route("instance/running/{id:guid}", Name = "InstanceRunningPatch")] [ProducesResponseType(typeof(InstanceRunningModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] @@ -145,7 +147,7 @@ public async Task PatchInstanceAsync([FromBody] InstanceRunningMo /// /// no return [HttpDelete] - [Route("{id}", Name = "InstanceRunningDelete")] + [Route("instance/running/{id}", Name = "InstanceRunningDelete")] [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] From 8771da098da6e61891889563a5a5c841cae78ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Fri, 10 Mar 2023 11:20:36 +0100 Subject: [PATCH 23/31] Update docker-compose.override.yml --- docker-compose.override.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker-compose.override.yml b/docker-compose.override.yml index d0c67462..1d40b8ee 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -4,7 +4,7 @@ services: redisinterface: environment: - ASPNETCORE_ENVIRONMENT=Development - - ASPNETCORE_URLS=https://+:443;http://+:80 + - ASPNETCORE_URLS=https://+:443;http://+:80 - ASPNETCORE_HTTPS_PORT=7091 #- ASPNETCORE_Kestrel__Certificates__Default__Password=ros #- ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx @@ -13,7 +13,7 @@ services: - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - Middleware__Organization=5G-ERA-DEV - - Middleware__InstanceName=MiddlewareBEDAL + - Middleware__InstanceName=Middleware-BB - Middleware__InstanceType=Edge ports: - "5091:80" @@ -36,7 +36,7 @@ services: - REDIS_HOSTNAME=${REDIS_HOSTNAME} - REDIS_PORT=${REDIS_PORT} - Middleware__Organization=5G-ERA-DEV - - Middleware__InstanceName=MiddlewareBEDAL + - Middleware__InstanceName=Middleware-BB - Middleware__InstanceType=Edge ports: @@ -57,7 +57,7 @@ services: - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - Middleware__Organization=5G-ERA-DEV - - Middleware__InstanceName=MiddlewareBEDAL + - Middleware__InstanceName=Middleware-BB - Middleware__InstanceType=Edge ports: - "5143:80" @@ -75,7 +75,7 @@ services: - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - Middleware__Organization=5G-ERA-DEV - - Middleware__InstanceName=MiddlewareBEDAL + - Middleware__InstanceName=MiddlewareBEDAL - Middleware__InstanceType=Edge ports: - "5263:80" From 9d90e152163858d05623880bf3f33439325d4f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Fri, 10 Mar 2023 12:08:17 +0100 Subject: [PATCH 24/31] Fixed adding new objects to graph --- src/DataAccess/Repositories/RedisRepository.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/DataAccess/Repositories/RedisRepository.cs b/src/DataAccess/Repositories/RedisRepository.cs index a8ed6914..770e47d2 100644 --- a/src/DataAccess/Repositories/RedisRepository.cs +++ b/src/DataAccess/Repositories/RedisRepository.cs @@ -67,6 +67,12 @@ public async Task AddAsync(TModel model, Func guidProvider) { return null!; } + + if (IsWritableToGraph) + { + var graphModel = new GraphEntityModel(model.Id, model.Name, _entityName); + await AddGraphAsync(graphModel); + } return ToTModel(dto); } private async Task DeleteFromGraph(Guid id) From 9cf4bd800d98683c4d9d795f23b58341551230b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Fri, 10 Mar 2023 12:08:43 +0100 Subject: [PATCH 25/31] fix: json within a json payload --- src/Common/Services/RedisInterfaceClientService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Common/Services/RedisInterfaceClientService.cs b/src/Common/Services/RedisInterfaceClientService.cs index a34193f1..2cf2f3ac 100644 --- a/src/Common/Services/RedisInterfaceClientService.cs +++ b/src/Common/Services/RedisInterfaceClientService.cs @@ -33,9 +33,9 @@ public async Task AddRelationAsync(TSource source, TD var relation = CreateRelation(source, direction, name); string url = $"/api/v1/{source.GetType().GetModelName()}/AddRelation"; - - var result = await _httpClient.PostAsJsonAsync(url, JsonConvert.SerializeObject(relation)); - + + var result = await _httpClient.PostAsJsonAsync(url, relation); + return result.IsSuccessStatusCode; } catch (Exception ex) From 1d2a25d06397f25dc72be2f51a8cb81804124324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Fri, 10 Mar 2023 12:26:33 +0100 Subject: [PATCH 26/31] Fix: disallow adding multiple same objects to the graph --- src/DataAccess/Repositories/RedisRepository.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/DataAccess/Repositories/RedisRepository.cs b/src/DataAccess/Repositories/RedisRepository.cs index 770e47d2..a332e61a 100644 --- a/src/DataAccess/Repositories/RedisRepository.cs +++ b/src/DataAccess/Repositories/RedisRepository.cs @@ -249,7 +249,10 @@ public virtual async Task>> GetAllRela public virtual async Task AddGraphAsync(GraphEntityModel model) { model.Type = _entityName; - + if (await ObjectExistsOnGraph(model)) + { + return true; + } string query = "CREATE (x: " + model.Type + " {ID: '" + model.Id + "', Type: '" + model.Type + "', Name: '" + model.Name + "'})"; ResultSet resultSet = await RedisGraph.Query(GraphName, query); @@ -334,5 +337,12 @@ protected TModel ToTModel(TDto dto) { return dto.ToModel() as TModel ?? throw new MappingException(typeof(TDto), typeof(TModel)); } + + private async Task ObjectExistsOnGraph(GraphEntityModel graphModel) + { + var query = "match (x:" + _entityName + " {ID: '" + graphModel.Id + "'}) return x"; + ResultSet resultSet = await RedisGraph.Query(GraphName, query); + return resultSet != null && resultSet.Results.Count > 0; + } } } From 6f91e793709b144ef1c8e6a7ff9f5683d4c8c30f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Fri, 14 Apr 2023 11:01:00 +0200 Subject: [PATCH 27/31] Temp fix compilation errors after merge --- .../Abstract/IActionPlanRepository.cs | 2 +- src/Models/Domain/ActionRunningModel.cs | 8 +- src/Models/Domain/TaskModel.cs | 8 +- src/Models/Dto/ActionRunningDto.cs | 4 +- .../Deployment/DeploymentService.cs | 57 +- .../IRedisInterfaceClient.cs | 10 - .../RedisInterfaceClient.cs | 50 + .../Controllers/ActionController.cs | 1010 +++++------------ .../Controllers/InstanceController.cs | 2 +- 9 files changed, 379 insertions(+), 772 deletions(-) diff --git a/src/DataAccess/Repositories/Abstract/IActionPlanRepository.cs b/src/DataAccess/Repositories/Abstract/IActionPlanRepository.cs index 13922c2f..ef67abe8 100644 --- a/src/DataAccess/Repositories/Abstract/IActionPlanRepository.cs +++ b/src/DataAccess/Repositories/Abstract/IActionPlanRepository.cs @@ -4,5 +4,5 @@ namespace Middleware.DataAccess.Repositories.Abstract; public interface IActionPlanRepository : IBaseRepository, IRelationRepository { - Task> GetRobotActionPlans(Guid robotId); + Task?> GetRobotActionPlans(Guid robotId); } \ No newline at end of file diff --git a/src/Models/Domain/ActionRunningModel.cs b/src/Models/Domain/ActionRunningModel.cs index 82e8b47c..34b57f70 100644 --- a/src/Models/Domain/ActionRunningModel.cs +++ b/src/Models/Domain/ActionRunningModel.cs @@ -31,10 +31,10 @@ public class ActionRunningModel : BaseModel public int Order { get; set; } [JsonPropertyName("Placement")] - public string? Placement { get; set; } + public string Placement { get; set; } = default!; [JsonPropertyName("PlacementType")] - public string? PlacementType { get; set; } // Either edge or cloud. + public string PlacementType { get; set; } = default!; [JsonPropertyName("ActionPriority")] public string? ActionPriority { get; set; } @@ -47,10 +47,10 @@ public class ActionRunningModel : BaseModel public List? Services { get; set; } [JsonPropertyName("MinimumRam")] - public int MinimumRam { get; set; } + public long? MinimumRam { get; set; } [JsonPropertyName("MinimumNumCores")] - public int MinimumNumCores { get; set; } + public int? MinimumNumCores { get; set; } public override Dto.Dto ToDto() { diff --git a/src/Models/Domain/TaskModel.cs b/src/Models/Domain/TaskModel.cs index dc664a2d..db581b53 100644 --- a/src/Models/Domain/TaskModel.cs +++ b/src/Models/Domain/TaskModel.cs @@ -6,10 +6,10 @@ namespace Middleware.Models.Domain public class TaskModel : BaseModel { [JsonPropertyName("Id")] - public override Guid Id { get; set; } + public override Guid Id { get; set; } = Guid.NewGuid(); [JsonPropertyName("Name")] - public override string Name { get; set; } + public override string Name { get; set; } = default!; /// /// //True: Dont change actions in action sequence replan @@ -54,11 +54,11 @@ public class TaskModel : BaseModel [JsonPropertyName("ActionSequence")] //[JsonIgnore] - public List? ActionSequence { get; set; } + public List ActionSequence { get; set; } = new(); [JsonPropertyName("Tags")] //TODO: define allows tags //[JsonIgnore] - public List Tags { get; set; } + public List? Tags { get; set; } public TaskModel() diff --git a/src/Models/Dto/ActionRunningDto.cs b/src/Models/Dto/ActionRunningDto.cs index 8b4bc6fc..6beaab0e 100644 --- a/src/Models/Dto/ActionRunningDto.cs +++ b/src/Models/Dto/ActionRunningDto.cs @@ -18,9 +18,9 @@ public class ActionRunningDto : Dto [Indexed] public string Name { get; init; } = default!; [Indexed] - public List Tags { get; init; } = new(); + public List? Tags { get; init; } = new(); [Indexed] - public string ActionPriority { get; init; } = default!; + public string? ActionPriority { get; init; } = default!; public HardwareRequirements HardwareRequirements { get; init; } = new(); diff --git a/src/Orchestrator/Deployment/DeploymentService.cs b/src/Orchestrator/Deployment/DeploymentService.cs index 5e6091c2..80984489 100644 --- a/src/Orchestrator/Deployment/DeploymentService.cs +++ b/src/Orchestrator/Deployment/DeploymentService.cs @@ -134,7 +134,9 @@ private async Task SaveActionSequence(TaskModel task, Guid robotId) //List actionsRunning = new List(); var actionPlan = new ActionPlanModel(task.ActionPlanId, task.Id, task.Name, task.ActionSequence!, robotId); actionPlan.SetStatus("active"); - RobotModel robot = await _redisInterfaceClient.RobotGetByIdAsync(robotId); + var robotResponse = await _redisInterfaceClient.RobotGetByIdAsync(robotId); + + var robot = robotResponse.ToRobot(); //Add the actionPlan to redis var result = await _redisInterfaceClient.ActionPlanAddAsync(actionPlan); @@ -144,17 +146,18 @@ private async Task SaveActionSequence(TaskModel task, Guid robotId) //Add the actionRunning to redis foreach (ActionModel action in task.ActionSequence) { - ActionRunningModel ActionRunningTemp = new ActionRunningModel(); - ActionRunningTemp.ActionPlanId = actionPlan.Id; - ActionRunningTemp.ActionParentId = action.Id; - ActionRunningTemp.Name = action.Name; - ActionRunningTemp.Tags = action.Tags; - ActionRunningTemp.Order = action.Order; - ActionRunningTemp.Placement = action.Placement.Replace("-Edge", "").Replace("-Cloud", ""); // Trim to proper edge or cloud - ActionRunningTemp.PlacementType = action.PlacementType; - ActionRunningTemp.ActionPriority = action.ActionPriority; - ActionRunningTemp.ActionStatus = ActionStatusEnum.Running.ToString(); - ActionRunningTemp.Services = action.Services.Select(x => new InstanceRunningModel() + ActionRunningModel actionRunningTemp = new ActionRunningModel(); + actionRunningTemp.ActionPlanId = actionPlan.Id; + actionRunningTemp.ActionParentId = action.Id; + actionRunningTemp.Name = action.Name; + actionRunningTemp.Tags = action.Tags; + actionRunningTemp.Order = action.Order; + actionRunningTemp.Placement = + action.Placement!.Replace("-Edge", "").Replace("-Cloud", ""); // Trim to proper edge or cloud + actionRunningTemp.PlacementType = action.PlacementType!; + actionRunningTemp.ActionPriority = action.ActionPriority; + actionRunningTemp.ActionStatus = ActionStatusEnum.Running.ToString(); + actionRunningTemp.Services = action.Services!.Select(x => new InstanceRunningModel() { Name = x.Name, ServiceInstanceId = x.ServiceInstanceId, @@ -163,32 +166,31 @@ private async Task SaveActionSequence(TaskModel task, Guid robotId) ServiceStatus = x.ServiceStatus, DeployedTime = DateTime.Now }).ToList(); - ActionRunningTemp.MinimumRam = action.MinimumRam; - ActionRunningTemp.MinimumNumCores = action.MinimumNumCores; + actionRunningTemp.MinimumRam = action.MinimumRam; + actionRunningTemp.MinimumNumCores = action.MinimumNumCores; //actionsRunning.Add(ActionRunningTemp); //Add the runningAction to the actionPlang - await _redisInterfaceClient.ActionRunningAddAsync(ActionRunningTemp); - await _redisInterfaceClient.AddRelationAsync(actionPlan, ActionRunningTemp, "CONSISTS_OF"); + await _redisInterfaceClient.ActionRunningAddAsync(actionRunningTemp); + await _redisInterfaceClient.AddRelationAsync(actionPlan, actionRunningTemp, "CONSISTS_OF"); //Add the InstanceRunning to redis - foreach (InstanceRunningModel runningInstance in ActionRunningTemp.Services) + foreach (InstanceRunningModel runningInstance in actionRunningTemp.Services) { await _redisInterfaceClient.InstanceRunningAddAsync(runningInstance); - await _redisInterfaceClient.AddRelationAsync(ActionRunningTemp, runningInstance, "CONSISTS_OF"); + await _redisInterfaceClient.AddRelationAsync(actionRunningTemp, runningInstance, "CONSISTS_OF"); if (action.PlacementType == "CLOUD") { - CloudModel cloud = await _redisInterfaceClient.GetCloudByNameAsync(action.Placement); + CloudModel cloud = (await _redisInterfaceClient.GetCloudByNameAsync(action.Placement)).ToCloud(); await _redisInterfaceClient.AddRelationAsync(runningInstance, cloud, "LOCATED_AT"); } + if (action.PlacementType == "EDGE") { - EdgeModel edge = await _redisInterfaceClient.GetEdgeByNameAsync(action.Placement); + EdgeModel edge = (await _redisInterfaceClient.GetEdgeByNameAsync(action.Placement)).ToEdge(); await _redisInterfaceClient.AddRelationAsync(runningInstance, edge, "LOCATED_AT"); } - } - } return result; @@ -349,7 +351,9 @@ public V1Service CreateService(string serviceImageName, K8SServiceKindEnum kind, public async Task DeletePlanAsync(ActionPlanModel actionPlan) { // Delete OWNS relationship between robot and actionPlan - RobotModel robot = await _redisInterfaceClient.RobotGetByIdAsync(actionPlan.RobotId); + var robotResponse = await _redisInterfaceClient.RobotGetByIdAsync(actionPlan.RobotId); + var robot = robotResponse.ToRobot(); + await _redisInterfaceClient.DeleteRelationAsync(robot, actionPlan, "OWNS"); // Create historical action plan @@ -364,11 +368,13 @@ public async Task DeletePlanAsync(ActionPlanModel actionPlan) var images = await _redisInterfaceClient.GetRelationAsync(actionPlan, "CONSISTS_OF"); foreach (RelationModel relation in images) { - ActionRunningModel runningAction = await _redisInterfaceClient.ActionRunningGetByIdAsync(relation.PointsTo.Id); + ActionRunningModel runningAction = + await _redisInterfaceClient.ActionRunningGetByIdAsync(relation.PointsTo.Id); var runningIntancesImages = await _redisInterfaceClient.GetRelationAsync(runningAction, "CONSISTS_OF"); foreach (RelationModel instanceRelation in runningIntancesImages) { - InstanceRunningModel runningInstance = await _redisInterfaceClient.InstanceRunningGetByIdAsync(instanceRelation.PointsTo.Id); + InstanceRunningModel runningInstance = + await _redisInterfaceClient.InstanceRunningGetByIdAsync(instanceRelation.PointsTo.Id); runningAction.Services.Add(runningInstance); } @@ -560,7 +566,6 @@ private async Task DeleteInstance(IKubernetes kubeClient, Guid instanceId) } /// - /// public V1Deployment CreateStartupDeployment(string name, string tag) { var mwConfig = _configuration.GetSection(MiddlewareConfig.ConfigName).Get(); diff --git a/src/RedisInterface.Sdk/IRedisInterfaceClient.cs b/src/RedisInterface.Sdk/IRedisInterfaceClient.cs index 87729110..6b50e0e6 100644 --- a/src/RedisInterface.Sdk/IRedisInterfaceClient.cs +++ b/src/RedisInterface.Sdk/IRedisInterfaceClient.cs @@ -112,29 +112,19 @@ Task DeleteRelationAsync(TSource source, TDirection d /// Task ContainerImageGetForInstanceAsync(Guid id); - Task> ContainerImageGetForInstanceAsync(Guid id); - Task> ContainerImageGetForInstanceAsync(Guid id, CancellationToken token); - Task ActionRunningAddAsync(ActionRunningModel actionRunning); - Task ActionRunningAddAsync(ActionRunningModel actionRunning, CancellationToken token); - Task InstanceRunningAddAsync(InstanceRunningModel instanceRunning); - Task InstanceRunningAddAsync(InstanceRunningModel instanceRunning, CancellationToken token); Task HistoricalActionPlanAddAsync(HistoricalActionPlanModel historicalActionPlan); - Task HistoricalActionPlanAddAsync(HistoricalActionPlanModel historicalActionPlan, CancellationToken token); Task ActionRunningGetByIdAsync(Guid id); - Task ActionRunningGetByIdAsync(Guid id, CancellationToken token); Task InstanceRunningGetByIdAsync(Guid id); - Task InstanceRunningGetByIdAsync(Guid id, CancellationToken token); - /// /// Get Active Global policies in the Middleware /// diff --git a/src/RedisInterface.Sdk/RedisInterfaceClient.cs b/src/RedisInterface.Sdk/RedisInterfaceClient.cs index 5166c5ff..a9c8ad84 100644 --- a/src/RedisInterface.Sdk/RedisInterfaceClient.cs +++ b/src/RedisInterface.Sdk/RedisInterfaceClient.cs @@ -236,6 +236,56 @@ public async Task ActionPlanDeleteAsync(Guid id, CancellationToken token) return await ContainerImageGetForInstanceAsync(id, CancellationToken.None); } + public async Task ActionRunningAddAsync(ActionRunningModel actionRunning) + { + throw new NotImplementedException(); + } + + public async Task ActionRunningAddAsync(ActionRunningModel actionRunning, CancellationToken token) + { + throw new NotImplementedException(); + } + + public async Task InstanceRunningAddAsync(InstanceRunningModel instanceRunning) + { + throw new NotImplementedException(); + } + + public async Task InstanceRunningAddAsync(InstanceRunningModel instanceRunning, CancellationToken token) + { + throw new NotImplementedException(); + } + + public async Task HistoricalActionPlanAddAsync(HistoricalActionPlanModel historicalActionPlan) + { + throw new NotImplementedException(); + } + + public async Task HistoricalActionPlanAddAsync(HistoricalActionPlanModel historicalActionPlan, CancellationToken token) + { + throw new NotImplementedException(); + } + + public async Task ActionRunningGetByIdAsync(Guid id) + { + throw new NotImplementedException(); + } + + public async Task ActionRunningGetByIdAsync(Guid id, CancellationToken token) + { + throw new NotImplementedException(); + } + + public async Task InstanceRunningGetByIdAsync(Guid id) + { + throw new NotImplementedException(); + } + + public async Task InstanceRunningGetByIdAsync(Guid id, CancellationToken token) + { + throw new NotImplementedException(); + } + public async Task PolicyGetActiveAsync() { var result = await _api.PolicyGetActive(); diff --git a/src/RedisInterface/Controllers/ActionController.cs b/src/RedisInterface/Controllers/ActionController.cs index 0cd7f48a..e04cc3e1 100644 --- a/src/RedisInterface/Controllers/ActionController.cs +++ b/src/RedisInterface/Controllers/ActionController.cs @@ -21,20 +21,13 @@ public class ActionController : ControllerBase private readonly IActionPlanRepository _actionPlanRepository; private readonly ILogger _logger; private readonly IActionService _actionService; - - public ActionController(IActionRepository actionRepository, - IActionPlanRepository actionPlanRepository, - ILogger logger, - IActionService actionService) - { - private readonly IActionRepository _actionRepository; - private readonly IActionPlanRepository _actionPlanRepository; - private readonly ILogger _logger; - private readonly IActionService _actionService; private readonly IHistoricalActionPlanRepository _historicalActionPlanRepository; private readonly IActionRunningRepository _actionRunningRepository; - public ActionController(IActionRepository actionRepository, IActionPlanRepository actionPlanRepository, ILogger logger, IActionService actionService, IHistoricalActionPlanRepository historicalActionPlanRepository, IActionRunningRepository actionRunningRepository) + public ActionController(IActionRepository actionRepository, IActionPlanRepository actionPlanRepository, + ILogger logger, IActionService actionService, + IHistoricalActionPlanRepository historicalActionPlanRepository, + IActionRunningRepository actionRunningRepository) { _actionRepository = actionRepository ?? throw new ArgumentNullException(nameof(actionRepository)); _actionPlanRepository = actionPlanRepository ?? throw new ArgumentNullException(nameof(actionPlanRepository)); @@ -44,256 +37,11 @@ public ActionController(IActionRepository actionRepository, IActionPlanRepositor _actionRunningRepository = actionRunningRepository; } - /// - /// Get all the ActionModel entities - /// - /// the list of ActionModel entities - [HttpGet(Name = "ActionGetAll")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task>> GetAllAsync() - { - try - { - - List models = await _actionRepository.GetAllAsync(); - if (models.Any() == false) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No actions were found.")); - } - return Ok(models); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - } - - //New end points for depends_on property for actions. - - /// - /// Get an ActionModel entity by id - /// - /// - /// the ActionModel entity for the specified id - [HttpGet] - [Route("{id}", Name = "ActionGetById")] - [ProducesResponseType(typeof(ActionModel), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetByIdAsync(Guid id) - { - try - { - ActionModel model = await _actionService.GetByIdAsync(id); - if (model == null) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, $"Action with id: '{id}' was not found.")); - } - return Ok(model); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - } - - /// - /// Add a new ActionModel entity - /// - /// - /// the newly created ActionModel entity - [HttpPost(Name = "ActionAdd")] - [ProducesResponseType(typeof(ActionModel), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task> AddAsync([FromBody] ActionModel model) - { - if (model == null) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Parameters were not specified.")); - } - try - { - model = await _actionService.AddAsync(model); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - return Ok(model); - } - - /// - /// Partially update an existing ActionModel entity - /// - /// - /// - /// the modified ActionModel entity - [HttpPatch] - [Route("{id}", Name = "ActionPatch")] - [ProducesResponseType(typeof(ActionModel), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task PatchActionAsync([FromBody] ActionModel patch, [FromRoute] Guid id) - { - try - { - ActionModel model = await _actionRepository.PatchActionAsync(id, patch); - if (model == null) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object to be updated was not found.")); - } - return Ok(model); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - } - - - /// - /// Delete an ActionModel entity for the given id - /// - /// - /// no return - [HttpDelete] - [Route("{id}", Name = "ActionDelete")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task DeleteByIdAsync(Guid id) - { - try - { - var deleted = await _actionRepository.DeleteByIdAsync(id); - if (deleted == false) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified Action has not been found.")); - } - return Ok(); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - } - - /// - /// Creates a new relation between two models - /// - /// - /// - [HttpPost] - [Route("AddRelation", Name = "ActionAddRelation")] - [ProducesResponseType(typeof(RelationModel), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task> AddRelationAsync([FromBody] RelationModel model) - { - if (model == null) - { - return BadRequest("Parameters were not specified."); - } - try - { - bool isValid = await _actionRepository.AddRelationAsync(model); - if (!isValid) - { - return StatusCode((int)HttpStatusCode.InternalServerError, new ApiResponse((int)HttpStatusCode.InternalServerError, "The relation was not created")); - } - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - return Ok(model); - } - /// - /// Retrieves a single relation by name - /// - /// - /// - /// - [HttpGet] - [Route("relation/{name}", Name = "ActionGetRelationByName")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetRelationAsync(Guid id, string name) //Guid of node and name of relationship - { - if (string.IsNullOrWhiteSpace(name)) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); - } + - if (id == Guid.Empty) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); - } - try - { - var relations = await _actionRepository.GetRelation(id, name); - if (!relations.Any()) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found.")); - } - return Ok(relations); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - } - /// - /// Retrieves two relations by their names - /// - /// - /// - /// - /// - [HttpGet] - [Route("relations/{firstName}/{secondName}", Name = "ActionGetRelationsByName")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetRelationsAsync(Guid id, string firstName, string secondName) - { - try - { - List relationNames = new List() { firstName, secondName }; - var relations = await _actionRepository.GetRelations(id, relationNames); - if (!relations.Any()) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found")); - } - return Ok(relations); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - } + #region ActionPlan @@ -315,6 +63,7 @@ public async Task GetAllActionPlansAsync() { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No plans have been found.")); } + return Ok(plans); } catch (Exception ex) @@ -344,6 +93,7 @@ public async Task GetActionPlanByIdAsync(Guid id) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Specified plan was not found.")); } + return Ok(plan); } catch (Exception ex) @@ -372,11 +122,14 @@ public async Task AddActionPlanAsync(ActionPlanModel actionPlan) { return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Plan cannot be null")); } + var plan = await _actionPlanRepository.AddAsync(actionPlan, () => actionPlan.Id); if (plan == null) { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "The specified plan has not been added.")); + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, + "The specified plan has not been added.")); } + return Ok(plan); } catch (Exception ex) @@ -404,8 +157,10 @@ public async Task DeleteActionPlanAsync(Guid id) var deleted = await _actionPlanRepository.DeleteByIdAsync(id); if (deleted == false) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified plan has not been found.")); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, + "The specified plan has not been found.")); } + return Ok(); } catch (Exception ex) @@ -434,12 +189,15 @@ public async Task PatchActionPlanAsync(Guid id, [FromBody] Action { if (actionPlan == null || id == Guid.Empty) { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Id or updated object has not been specified")); + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, + "Id or updated object has not been specified")); } + var deleted = await _actionPlanRepository.DeleteByIdAsync(id); if (deleted == false) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified plan has not been found.")); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, + "The specified plan has not been found.")); } var updatedPlan = await _actionPlanRepository.AddAsync(actionPlan, () => id); @@ -452,6 +210,7 @@ public async Task PatchActionPlanAsync(Guid id, [FromBody] Action return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } } + /// /// Get action plan given robot Id. /// @@ -469,12 +228,14 @@ public async Task GetActionPlanByRobotIdAsync(Guid robotId) { return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Robot id has not been specified")); } + // Get list of actionPlans from specific robotId. List actionPlans = await _actionPlanRepository.GetRobotActionPlans(robotId); if (actionPlans == null) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); } + //List activePoliciesRecords = actionPlans.Select(p => new ActionPlanModel(p.Id, p.Name, p.Description)).ToList(); return Ok(actionPlans); } @@ -504,9 +265,14 @@ public async Task GetLatestActionPlanByRobotIdAsync(Guid robotId) { return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Robot id has not been specified")); } + // Get list of actionPlans from specific robotId. List actionPlans = await _actionPlanRepository.GetRobotActionPlans(robotId); + if (actionPlans == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); + } //Get the newest task of robot. Dictionary tempDic = new Dictionary(); Dictionary OrderedTempDic = new Dictionary(); @@ -515,7 +281,8 @@ public async Task GetLatestActionPlanByRobotIdAsync(Guid robotId) foreach (ActionPlanModel plan in actionPlans) { DateTime d; - DateTime.TryParseExact(plan.Status, "ggyyyy$dd-MMM (dddd)", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out d); + DateTime.TryParseExact(plan.Status, "ggyyyy$dd-MMM (dddd)", + System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out d); tempDic.Add(plan, d); } @@ -528,10 +295,8 @@ public async Task GetLatestActionPlanByRobotIdAsync(Guid robotId) // Get last item which is the latest plan. ActionPlanModel last = OrderedTempDic.Keys.First(); - if (actionPlans == null) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); - } + + //List activePoliciesRecords = actionPlans.Select(p => new ActionPlanModel(p.Id, p.Name, p.Description)).ToList(); return Ok(last); } @@ -542,6 +307,7 @@ public async Task GetLatestActionPlanByRobotIdAsync(Guid robotId) return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } } + #endregion #region HistoricalActionPlan @@ -564,6 +330,7 @@ public async Task GetAllHistoricalActionPlansAsync() { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No plans have been found.")); } + return Ok(plans); } catch (Exception ex) @@ -593,6 +360,7 @@ public async Task GetHistoricalActionPlanByIdAsync(Guid id) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Specified plan was not found.")); } + return Ok(plan); } catch (Exception ex) @@ -621,11 +389,15 @@ public async Task AddHistoricalActionPlanAsync(HistoricalActionPl { return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Historical plan cannot be null")); } - var plan = await _historicalActionPlanRepository.AddAsync(historicalActionPlan, () => historicalActionPlan.Id); + + var plan = await _historicalActionPlanRepository.AddAsync(historicalActionPlan, + () => historicalActionPlan.Id); if (plan == null) { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "The specified historical plan has not been added.")); + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, + "The specified historical plan has not been added.")); } + return Ok(plan); } catch (Exception ex) @@ -653,8 +425,10 @@ public async Task DeleteHistoricalActionPlanAsync(Guid id) var deleted = await _historicalActionPlanRepository.DeleteByIdAsync(id); if (deleted == false) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified historical plan has not been found.")); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, + "The specified historical plan has not been found.")); } + return Ok(); } catch (Exception ex) @@ -677,18 +451,22 @@ public async Task DeleteHistoricalActionPlanAsync(Guid id) [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task PatchHistoricalActionPlanAsync(Guid id, [FromBody] HistoricalActionPlanModel historicalActionPlan) + public async Task PatchHistoricalActionPlanAsync(Guid id, + [FromBody] HistoricalActionPlanModel historicalActionPlan) { try { if (historicalActionPlan == null || id == Guid.Empty) { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Id or updated object has not been specified")); + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, + "Id or updated object has not been specified")); } + var deleted = await _historicalActionPlanRepository.DeleteByIdAsync(id); if (deleted == false) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified plan has not been found.")); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, + "The specified plan has not been found.")); } var updatedPlan = await _historicalActionPlanRepository.AddAsync(historicalActionPlan, () => id); @@ -719,12 +497,15 @@ public async Task GetHistoricalActionPlanByRobotIdAsync(Guid robo { return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Robot id has not been specified")); } + // Get list of actionPlans from specific robotId. - List historicalActionPlans = await _historicalActionPlanRepository.GetRobotActionPlans(robotId); + List historicalActionPlans = + await _historicalActionPlanRepository.GetRobotActionPlans(robotId); if (historicalActionPlans == null) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); } + return Ok(historicalActionPlans); } catch (Exception ex) @@ -752,18 +533,23 @@ public async Task GetLatestHistoricalActionPlanByRobotIdAsync(Gui { return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Robot id has not been specified")); } + // Get list of actionPlans from specific robotId. - List actionPlans = await _historicalActionPlanRepository.GetRobotActionPlans(robotId); + List actionPlans = + await _historicalActionPlanRepository.GetRobotActionPlans(robotId); //Get the newest task of robot. - Dictionary tempDic = new Dictionary(); - Dictionary OrderedTempDic = new Dictionary(); + Dictionary tempDic = + new Dictionary(); + Dictionary OrderedTempDic = + new Dictionary(); // Complete tempDic foreach (HistoricalActionPlanModel plan in actionPlans) { DateTime d; - DateTime.TryParseExact(plan.Status, "ggyyyy$dd-MMM (dddd)", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out d); + DateTime.TryParseExact(plan.Status, "ggyyyy$dd-MMM (dddd)", + System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out d); tempDic.Add(plan, d); } @@ -780,6 +566,7 @@ public async Task GetLatestHistoricalActionPlanByRobotIdAsync(Gui { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); } + return Ok(last); } catch (Exception ex) @@ -807,12 +594,12 @@ public async Task>> GetAllActionRunningAsy { try { - List models = await _actionRunningRepository.GetAllAsync(); if (models.Any() == false) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No actions were found.")); } + return Ok(models); } catch (Exception ex) @@ -842,8 +629,10 @@ public async Task GetActionRunningByIdAsync(Guid id) ActionRunningModel model = await _actionRunningRepository.GetByIdAsync(id); if (model == null) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, $"Action with id: '{id}' was not found.")); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, + $"Action with id: '{id}' was not found.")); } + return Ok(model); } catch (Exception ex) @@ -870,6 +659,7 @@ public async Task> AddActionRunningAsync([FromB { return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Parameters were not specified.")); } + try { model = await _actionRunningRepository.AddAsync(model); @@ -880,6 +670,7 @@ public async Task> AddActionRunningAsync([FromB _logger.LogError(ex, "An error occurred:"); return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } + return Ok(model); } @@ -903,6 +694,7 @@ public async Task PatchActionRunningAsync([FromBody] ActionRunnin { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object to be updated was not found.")); } + return Ok(model); } catch (Exception ex) @@ -930,8 +722,10 @@ public async Task DeleteActionRunningByIdAsync(Guid id) var deleted = await _actionRunningRepository.DeleteByIdAsync(id); if (deleted == false) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified Action has not been found.")); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, + "The specified Action has not been found.")); } + return Ok(); } catch (Exception ex) @@ -958,12 +752,14 @@ public async Task> AddActionRunningRelationAsync([Fr { return BadRequest("Parameters were not specified."); } + try { bool isValid = await _actionRunningRepository.AddRelationAsync(model); if (!isValid) { - return StatusCode((int)HttpStatusCode.InternalServerError, new ApiResponse((int)HttpStatusCode.InternalServerError, "The relation was not created")); + return StatusCode((int)HttpStatusCode.InternalServerError, + new ApiResponse((int)HttpStatusCode.InternalServerError, "The relation was not created")); } } catch (Exception ex) @@ -972,6 +768,7 @@ public async Task> AddActionRunningRelationAsync([Fr _logger.LogError(ex, "An error occurred:"); return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } + return Ok(model); } @@ -986,7 +783,8 @@ public async Task> AddActionRunningRelationAsync([Fr [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetActionRunningRelationAsync(Guid id, string name) //Guid of node and name of relationship + public async Task + GetActionRunningRelationAsync(Guid id, string name) //Guid of node and name of relationship { if (string.IsNullOrWhiteSpace(name)) { @@ -997,6 +795,7 @@ public async Task GetActionRunningRelationAsync(Guid id, string n { return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); } + try { var relations = await _actionRunningRepository.GetRelation(id, name); @@ -1004,6 +803,7 @@ public async Task GetActionRunningRelationAsync(Guid id, string n { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found.")); } + return Ok(relations); } catch (Exception ex) @@ -1036,6 +836,7 @@ public async Task GetActionRunningRelationsAsync(Guid id, string { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found")); } + return Ok(relations); } catch (Exception ex) @@ -1048,501 +849,262 @@ public async Task GetActionRunningRelationsAsync(Guid id, string #endregion -} - -/// -/// Get all the actions -/// -/// the list of ActionModel entities -[HttpGet(Name = "ActionGetAll")] -[ProducesResponseType(typeof(GetActionsResponse), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task GetAllAsync() -{ - try + + /// + /// Get all the actions + /// + /// the list of ActionModel entities + [HttpGet(Name = "ActionGetAll")] + [ProducesResponseType(typeof(GetActionsResponse), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetAllAsync() { - List models = await _actionRepository.GetAllAsync(); - if (models.Any() == false) + try { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No actions were found.")); + List models = await _actionRepository.GetAllAsync(); + if (models.Any() == false) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No actions were found.")); + } + + return Ok(models.ToActionsResponse()); } - return Ok(models.ToActionsResponse()); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } -} - -/// -/// Get an action entity by id -/// -/// -/// the ActionModel entity for the specified id -[HttpGet] -[Route("{id}", Name = "ActionGetById")] -[ProducesResponseType(typeof(ActionResponse), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task GetByIdAsync(Guid id) -{ - try - { - ActionModel model = await _actionService.GetByIdAsync(id); - if (model == null) + catch (Exception ex) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, $"Action with id: '{id}' was not found.")); + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } - return Ok(model.ToActionResponse()); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } -} - -/// -/// Add a new action entity -/// -/// -/// the newly created ActionModel entity -[HttpPost(Name = "ActionAdd")] -[ProducesResponseType(typeof(ActionResponse), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task AddAsync([FromBody] ActionRequest request) -{ - try - { - var action = await _actionService.AddAsync(request.ToAction()); - return Ok(action.ToActionResponse()); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } -} - -/// -/// Update an existing action entity -/// -/// the modified ActionModel entity -[HttpPut] -[Route("{id}", Name = "ActionUpdate")] -[ProducesResponseType(typeof(ActionResponse), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task UpdateActionAsync([FromMultiSource] UpdateActionRequest request) -{ - try - { - var existingAction = await _actionService.GetByIdAsync(request.Id); - if (existingAction is null) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object to be updated was not found.")); - } - - var action = request.ToAction(); - await _actionService.UpdateAsync(action); - var response = action.ToActionResponse(); - return Ok(response); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } -} - -/// -/// Delete an action entity with the given id -/// -/// -/// no return -[HttpDelete] -[Route("{id}", Name = "ActionDelete")] -[ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task DeleteByIdAsync(Guid id) -{ - try + /// + /// Get an action entity by id + /// + /// + /// the ActionModel entity for the specified id + [HttpGet] + [Route("{id}", Name = "ActionGetById")] + [ProducesResponseType(typeof(ActionResponse), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetByIdAsync(Guid id) { - var exists = await _actionService.GetByIdAsync(id); - if (exists is null) + try { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified Action has not been found.")); + ActionModel model = await _actionService.GetByIdAsync(id); + if (model == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, + $"Action with id: '{id}' was not found.")); + } + + return Ok(model.ToActionResponse()); } - await _actionService.DeleteAsync(id); - return Ok(); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } -} - -/// -/// Creates a new relation between two models -/// -/// -/// -[HttpPost] -[Route("AddRelation", Name = "ActionAddRelation")] -[ProducesResponseType(typeof(RelationModel), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task> AddRelationAsync([FromBody] RelationModel model) -{ - if (model == null) - { - return BadRequest("Parameters were not specified."); - } - try - { - bool isValid = await _actionRepository.AddRelationAsync(model); - if (!isValid) + catch (Exception ex) { - return StatusCode((int)HttpStatusCode.InternalServerError, new ApiResponse((int)HttpStatusCode.InternalServerError, "The relation was not created")); + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - return Ok(model); -} - -/// -/// Retrieves a single relation by name -/// -/// -/// -/// -[HttpGet] -[Route("relation/{name}", Name = "ActionGetRelationByName")] -[ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task GetRelationAsync(Guid id, string name) //Guid of node and name of relationship -{ - if (string.IsNullOrWhiteSpace(name)) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); - } - if (id == Guid.Empty) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); - } - try + /// + /// Add a new action entity + /// + /// + /// the newly created ActionModel entity + [HttpPost(Name = "ActionAdd")] + [ProducesResponseType(typeof(ActionResponse), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task AddAsync([FromBody] ActionRequest request) { - var relations = await _actionRepository.GetRelation(id, name); - if (!relations.Any()) + try { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found.")); + var action = await _actionService.AddAsync(request.ToAction()); + return Ok(action.ToActionResponse()); } - return Ok(relations); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } -} - -/// -/// Retrieves two relations by their names -/// -/// -/// -/// -/// -[HttpGet] -[Route("relations/{firstName}/{secondName}", Name = "ActionGetRelationsByName")] -[ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task GetRelationsAsync(Guid id, string firstName, string secondName) -{ - try - { - List relationNames = new List() { firstName, secondName }; - var relations = await _actionRepository.GetRelations(id, relationNames); - if (!relations.Any()) + catch (Exception ex) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found")); + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } - return Ok(relations); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } -} - -#region ActionPlan - -/// -/// Retrieves all ActionPlans -/// -/// -[HttpGet] -[Route("plan", Name = "ActionPlanGetAll")] -[ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task GetAllActionPlansAsync() -{ - try + + /// + /// Update an existing action entity + /// + /// the modified ActionModel entity + [HttpPut] + [Route("{id}", Name = "ActionUpdate")] + [ProducesResponseType(typeof(ActionResponse), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task UpdateActionAsync([FromMultiSource] UpdateActionRequest request) { - var plans = await _actionPlanRepository.GetAllAsync(); - if (plans == null || plans.Any() == false) + try { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No plans have been found.")); + var existingAction = await _actionService.GetByIdAsync(request.Id); + if (existingAction is null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object to be updated was not found.")); + } + + var action = request.ToAction(); + await _actionService.UpdateAsync(action); + + var response = action.ToActionResponse(); + return Ok(response); } - return Ok(plans); - } - catch (Exception ex) - { - _logger.LogError(ex, "An error occurred:"); - int statusCode = (int)HttpStatusCode.InternalServerError; - return StatusCode(statusCode, new ApiResponse(statusCode, ex.Message)); - } -} - -/// -/// Retrieves an ActionPlan by id -/// -/// -/// -[HttpGet] -[Route("plan/{id:guid}", Name = "ActionPlanGetById")] -[ProducesResponseType(typeof(ActionPlanModel), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task GetActionPlanByIdAsync(Guid id) -{ - try - { - var plan = await _actionPlanRepository.GetByIdAsync(id); - if (plan == null) + catch (Exception ex) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Specified plan was not found.")); + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } - return Ok(plan); } - catch (Exception ex) - { - _logger.LogError(ex, "An error occurred:"); - int statusCode = (int)HttpStatusCode.InternalServerError; - return StatusCode(statusCode, new ApiResponse(statusCode, ex.Message)); - } -} - -/// -/// Creates new ActionPlan -/// -/// -/// -[HttpPost] -[Route("plan", Name = "ActionPlanAdd")] -[ProducesResponseType(typeof(ActionPlanModel), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task AddActionPlanAsync(ActionPlanModel actionPlan) -{ - try + + /// + /// Delete an action entity with the given id + /// + /// + /// no return + [HttpDelete] + [Route("{id}", Name = "ActionDelete")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task DeleteByIdAsync(Guid id) { - if (actionPlan == null) + try { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Plan cannot be null")); + var exists = await _actionService.GetByIdAsync(id); + if (exists is null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, + "The specified Action has not been found.")); + } + + await _actionService.DeleteAsync(id); + return Ok(); } - var plan = await _actionPlanRepository.AddAsync(actionPlan, () => actionPlan.Id); - if (plan == null) + catch (Exception ex) { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "The specified plan has not been added.")); + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } - return Ok(plan); } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } -} - -/// -/// Delete ActionPlan by id -/// -/// -/// -[HttpDelete] -[Route("plan/{id}", Name = "ActionPlanDelete")] -[ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task DeleteActionPlanAsync(Guid id) -{ - try + + /// + /// Creates a new relation between two models + /// + /// + /// + [HttpPost] + [Route("AddRelation", Name = "ActionAddRelation")] + [ProducesResponseType(typeof(RelationModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task> AddRelationAsync([FromBody] RelationModel model) { - var deleted = await _actionPlanRepository.DeleteByIdAsync(id); - if (deleted == false) + if (model == null) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified plan has not been found.")); + return BadRequest("Parameters were not specified."); } - return Ok(); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } -} - -/// -/// Patches an existing ActionPlan by id -/// -/// -/// -/// -[HttpPut] -[Route("plan/{id:guid}", Name = "ActionPlanPatch")] -[ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task PatchActionPlanAsync(Guid id, [FromBody] ActionPlanModel actionPlan) -{ - try - { - if (actionPlan == null || id == Guid.Empty) + + try { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Id or updated object has not been specified")); + bool isValid = await _actionRepository.AddRelationAsync(model); + if (!isValid) + { + return StatusCode((int)HttpStatusCode.InternalServerError, + new ApiResponse((int)HttpStatusCode.InternalServerError, "The relation was not created")); + } } - var deleted = await _actionPlanRepository.DeleteByIdAsync(id); - if (deleted == false) + catch (Exception ex) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified plan has not been found.")); + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } - var updatedPlan = await _actionPlanRepository.AddAsync(actionPlan, () => id); - return Ok(updatedPlan); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + return Ok(model); } -} -/// -/// Get action plan given robot Id. -/// -/// List -[HttpGet] -[Route("plan/robot/{robotId}", Name = "GetActionPlanByRobotIdAsync")] -[ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task GetActionPlanByRobotIdAsync(Guid robotId) -{ - try + + /// + /// Retrieves a single relation by name + /// + /// + /// + /// + [HttpGet] + [Route("relation/{name}", Name = "ActionGetRelationByName")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetRelationAsync(Guid id, string name) //Guid of node and name of relationship { - if (robotId == Guid.Empty) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Robot id has not been specified")); - } - // Get list of actionPlans from specific robotId. - List actionPlans = await _actionPlanRepository.GetRobotActionPlans(robotId); - if (actionPlans == null) + if (string.IsNullOrWhiteSpace(name)) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); } - //List activePoliciesRecords = actionPlans.Select(p => new ActionPlanModel(p.Id, p.Name, p.Description)).ToList(); - return Ok(actionPlans); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } -} -#endregion - -/// -/// Get latest action plan given robot Id. -/// -/// List -[HttpGet] -[Route("plan/robot/{robotId}/latest", Name = "GetLatestActionPlanByRobotIdAsync")] -[ProducesResponseType(typeof(ActionPlanModel), (int)HttpStatusCode.OK)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] -[ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] -public async Task GetLatestActionPlanByRobotIdAsync(Guid robotId) -{ - try - { - if (robotId == Guid.Empty) + + if (id == Guid.Empty) { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Robot id has not been specified")); + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); } - // Get list of actionPlans from specific robotId. - List actionPlans = await _actionPlanRepository.GetRobotActionPlans(robotId); - - //Get the newest task of robot. - Dictionary tempDic = new Dictionary(); - Dictionary OrderedTempDic = new Dictionary(); - // Complete tempDic - foreach (ActionPlanModel plan in actionPlans) + try { - DateTime d; - DateTime.TryParseExact(plan.Status, "ggyyyy$dd-MMM (dddd)", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out d); - tempDic.Add(plan, d); - } + var relations = await _actionRepository.GetRelation(id, name); + if (!relations.Any()) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found.")); + } - // Order a new dictionary - foreach (KeyValuePair pair in tempDic.OrderByDescending(p => p.Value)) + return Ok(relations); + } + catch (Exception ex) { - OrderedTempDic.Add(pair.Key, pair.Value); + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } + } - // Get last item which is the latest plan. - ActionPlanModel last = OrderedTempDic.Keys.First(); + /// + /// Retrieves two relations by their names + /// + /// + /// + /// + /// + [HttpGet] + [Route("relations/{firstName}/{secondName}", Name = "ActionGetRelationsByName")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task GetRelationsAsync(Guid id, string firstName, string secondName) + { + try + { + List relationNames = new List() { firstName, secondName }; + var relations = await _actionRepository.GetRelations(id, relationNames); + if (!relations.Any()) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found")); + } - if (actionPlans == null) + return Ok(relations); + } + catch (Exception ex) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } - //List activePoliciesRecords = actionPlans.Select(p => new ActionPlanModel(p.Id, p.Name, p.Description)).ToList(); - return Ok(last); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } -} - } \ No newline at end of file diff --git a/src/RedisInterface/Controllers/InstanceController.cs b/src/RedisInterface/Controllers/InstanceController.cs index ec2012f3..87cbc02c 100644 --- a/src/RedisInterface/Controllers/InstanceController.cs +++ b/src/RedisInterface/Controllers/InstanceController.cs @@ -362,7 +362,7 @@ public async Task FindAlternativeInstance(Guid id) [ProducesResponseType(typeof(InstanceRunningModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task>> GetAllInstanceRunningAsync() + public async Task>> GetAllInstanceRunningAsync() { try { From 7104cfab1236b658bd6f6417528e7c945b6eadfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Fri, 14 Apr 2023 11:16:48 +0200 Subject: [PATCH 28/31] Use LocationTypeEnum instead of string --- .../Mappings/ApiContractToDomainMapper.cs | 1 + src/CentralApi/Services/LocationService.cs | 1 + .../Validation/RegisterRequestValidator.cs | 1 + src/Common/Enums/ActionStatusEnum.cs | 19 +++++++++---------- src/Common/Enums/LocationType.cs | 7 ------- .../Abstract/IActionRunningRepository.cs | 2 +- .../Redis/RedisActionRepository.cs | 2 +- .../Redis/RedisActionRunningRepository.cs | 6 +++--- src/Models/Domain/ActionModel.cs | 3 ++- src/Models/Domain/ActionRunningModel.cs | 3 ++- src/Models/Enums/LocationType.cs | 8 ++++++++ .../Deployment/DeploymentService.cs | 9 +++++---- src/Orchestrator/Jobs/UpdateStatusJob.cs | 3 ++- src/ResourcePlanner/ResourcePlanner.cs | 3 ++- src/TaskPlanner/Services/PublishingService.cs | 2 +- .../Services/LocationServiceTests.cs | 1 + 16 files changed, 40 insertions(+), 31 deletions(-) delete mode 100644 src/Common/Enums/LocationType.cs create mode 100644 src/Models/Enums/LocationType.cs diff --git a/src/CentralApi/Mappings/ApiContractToDomainMapper.cs b/src/CentralApi/Mappings/ApiContractToDomainMapper.cs index 61d06024..20028cf3 100644 --- a/src/CentralApi/Mappings/ApiContractToDomainMapper.cs +++ b/src/CentralApi/Mappings/ApiContractToDomainMapper.cs @@ -2,6 +2,7 @@ using Middleware.CentralApi.Domain; using Middleware.Common.Enums; using Middleware.Models.Domain; +using Middleware.Models.Enums; namespace Middleware.CentralApi.Mappings; diff --git a/src/CentralApi/Services/LocationService.cs b/src/CentralApi/Services/LocationService.cs index 2ace3817..0ddd492b 100644 --- a/src/CentralApi/Services/LocationService.cs +++ b/src/CentralApi/Services/LocationService.cs @@ -8,6 +8,7 @@ using OneOf.Types; using Middleware.CentralApi.Mappings; using Middleware.Common.Enums; +using Middleware.Models.Enums; namespace Middleware.CentralApi.Services; diff --git a/src/CentralApi/Validation/RegisterRequestValidator.cs b/src/CentralApi/Validation/RegisterRequestValidator.cs index 34334547..50bf3bf6 100644 --- a/src/CentralApi/Validation/RegisterRequestValidator.cs +++ b/src/CentralApi/Validation/RegisterRequestValidator.cs @@ -1,6 +1,7 @@ using FluentValidation; using Middleware.CentralApi.Contracts.Requests; using Middleware.Common.Enums; +using Middleware.Models.Enums; namespace Middleware.CentralApi.Validation; diff --git a/src/Common/Enums/ActionStatusEnum.cs b/src/Common/Enums/ActionStatusEnum.cs index ceb71c72..992da500 100644 --- a/src/Common/Enums/ActionStatusEnum.cs +++ b/src/Common/Enums/ActionStatusEnum.cs @@ -1,11 +1,10 @@ -namespace Middleware.Common.Enums +namespace Middleware.Common.Enums; + +public enum ActionStatusEnum { - public enum ActionStatusEnum - { - Unknown, - Running, - Finished, - Idle, - Off - } -} + Unknown, + Running, + Finished, + Idle, + Off +} \ No newline at end of file diff --git a/src/Common/Enums/LocationType.cs b/src/Common/Enums/LocationType.cs deleted file mode 100644 index 66790abe..00000000 --- a/src/Common/Enums/LocationType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Middleware.Common.Enums; - -public enum LocationType -{ - Edge, - Cloud -} \ No newline at end of file diff --git a/src/DataAccess/Repositories/Abstract/IActionRunningRepository.cs b/src/DataAccess/Repositories/Abstract/IActionRunningRepository.cs index 7f48001c..264328a8 100644 --- a/src/DataAccess/Repositories/Abstract/IActionRunningRepository.cs +++ b/src/DataAccess/Repositories/Abstract/IActionRunningRepository.cs @@ -4,6 +4,6 @@ namespace Middleware.DataAccess.Repositories.Abstract { public interface IActionRunningRepository: IBaseRepository, IRelationRepository { - Task PatchActionAsync(Guid id, ActionRunningModel patch); + Task PatchActionAsync(Guid id, ActionRunningModel patch); } } diff --git a/src/DataAccess/Repositories/Redis/RedisActionRepository.cs b/src/DataAccess/Repositories/Redis/RedisActionRepository.cs index 3cab4ab7..d045557b 100644 --- a/src/DataAccess/Repositories/Redis/RedisActionRepository.cs +++ b/src/DataAccess/Repositories/Redis/RedisActionRepository.cs @@ -37,7 +37,7 @@ public async Task PatchActionAsync(Guid id, ActionModel patch) { currentModel.Placement = patch.Placement; } - if (!string.IsNullOrEmpty(patch.PlacementType)) + if (patch.PlacementType is not null) { currentModel.PlacementType = patch.PlacementType; } diff --git a/src/DataAccess/Repositories/Redis/RedisActionRunningRepository.cs b/src/DataAccess/Repositories/Redis/RedisActionRunningRepository.cs index 3daeef48..bb2f95e9 100644 --- a/src/DataAccess/Repositories/Redis/RedisActionRunningRepository.cs +++ b/src/DataAccess/Repositories/Redis/RedisActionRunningRepository.cs @@ -1,7 +1,7 @@ using Middleware.DataAccess.Repositories.Abstract; using Middleware.Models.Domain; using Middleware.Models.Dto; -using Redis.OM; +using Middleware.Models.Enums; using Redis.OM.Contracts; using RedisGraphDotNet.Client; using Serilog; @@ -15,7 +15,7 @@ public RedisActionRunningRepository(IRedisConnectionProvider provider, IRedisGra } - public async Task PatchActionAsync(Guid id, ActionRunningModel patch) + public async Task PatchActionAsync(Guid id, ActionRunningModel patch) { ActionRunningModel? currentModel = await GetByIdAsync(id); if (currentModel == null) @@ -38,7 +38,7 @@ public async Task PatchActionAsync(Guid id, ActionRunningMod { currentModel.Placement = patch.Placement; } - if (!string.IsNullOrEmpty(patch.PlacementType)) + if (patch.PlacementType != LocationType.Unspecified) { currentModel.PlacementType = patch.PlacementType; } diff --git a/src/Models/Domain/ActionModel.cs b/src/Models/Domain/ActionModel.cs index 8a1200e1..57d8fef6 100644 --- a/src/Models/Domain/ActionModel.cs +++ b/src/Models/Domain/ActionModel.cs @@ -1,6 +1,7 @@ using System.Text.Json.Serialization; using Middleware.Models.Dto; using Middleware.Models.Dto.Hardware; +using Middleware.Models.Enums; namespace Middleware.Models.Domain; @@ -23,7 +24,7 @@ public class ActionModel : BaseModel public string? Placement { get; set; } [JsonPropertyName("PlacementType")] - public string? PlacementType { get; set; } // Either edge or cloud. + public LocationType? PlacementType { get; set; } [JsonPropertyName("ActionPriority")] public string? ActionPriority { get; set; } diff --git a/src/Models/Domain/ActionRunningModel.cs b/src/Models/Domain/ActionRunningModel.cs index 34b57f70..a912fdcf 100644 --- a/src/Models/Domain/ActionRunningModel.cs +++ b/src/Models/Domain/ActionRunningModel.cs @@ -6,6 +6,7 @@ using System.Text; using System.Text.Json.Serialization; using System.Threading.Tasks; +using Middleware.Models.Enums; namespace Middleware.Models.Domain; @@ -34,7 +35,7 @@ public class ActionRunningModel : BaseModel public string Placement { get; set; } = default!; [JsonPropertyName("PlacementType")] - public string PlacementType { get; set; } = default!; + public LocationType PlacementType { get; set; } [JsonPropertyName("ActionPriority")] public string? ActionPriority { get; set; } diff --git a/src/Models/Enums/LocationType.cs b/src/Models/Enums/LocationType.cs new file mode 100644 index 00000000..0ce012ef --- /dev/null +++ b/src/Models/Enums/LocationType.cs @@ -0,0 +1,8 @@ +namespace Middleware.Models.Enums; + +public enum LocationType +{ + Unspecified = 0, + Edge = 1, + Cloud =2 +} \ No newline at end of file diff --git a/src/Orchestrator/Deployment/DeploymentService.cs b/src/Orchestrator/Deployment/DeploymentService.cs index 80984489..2f5256cc 100644 --- a/src/Orchestrator/Deployment/DeploymentService.cs +++ b/src/Orchestrator/Deployment/DeploymentService.cs @@ -7,6 +7,7 @@ using Middleware.Common.ExtensionMethods; using Middleware.Common.Responses; using Middleware.Models.Domain; +using Middleware.Models.Enums; using Middleware.Orchestrator.Exceptions; using Middleware.Orchestrator.Models; using Middleware.RedisInterface.Contracts.Mappings; @@ -154,7 +155,7 @@ private async Task SaveActionSequence(TaskModel task, Guid robotId) actionRunningTemp.Order = action.Order; actionRunningTemp.Placement = action.Placement!.Replace("-Edge", "").Replace("-Cloud", ""); // Trim to proper edge or cloud - actionRunningTemp.PlacementType = action.PlacementType!; + actionRunningTemp.PlacementType = action.PlacementType!.Value; actionRunningTemp.ActionPriority = action.ActionPriority; actionRunningTemp.ActionStatus = ActionStatusEnum.Running.ToString(); actionRunningTemp.Services = action.Services!.Select(x => new InstanceRunningModel() @@ -179,13 +180,13 @@ private async Task SaveActionSequence(TaskModel task, Guid robotId) await _redisInterfaceClient.InstanceRunningAddAsync(runningInstance); await _redisInterfaceClient.AddRelationAsync(actionRunningTemp, runningInstance, "CONSISTS_OF"); - if (action.PlacementType == "CLOUD") + if (action.PlacementType == LocationType.Cloud) { CloudModel cloud = (await _redisInterfaceClient.GetCloudByNameAsync(action.Placement)).ToCloud(); await _redisInterfaceClient.AddRelationAsync(runningInstance, cloud, "LOCATED_AT"); } - if (action.PlacementType == "EDGE") + if (action.PlacementType == LocationType.Edge) { EdgeModel edge = (await _redisInterfaceClient.GetEdgeByNameAsync(action.Placement)).ToEdge(); await _redisInterfaceClient.AddRelationAsync(runningInstance, edge, "LOCATED_AT"); @@ -519,7 +520,7 @@ public async Task DeployActionAsync(Guid actionPlanId, Guid actionId) } action.Placement = _mwConfig.Value.InstanceName; - action.PlacementType = _mwConfig.Value.InstanceType; + action.PlacementType = Enum.Parse(_mwConfig.Value.InstanceType); _logger.LogDebug("Saving updated ActionPlan"); await _redisInterfaceClient.ActionPlanAddAsync(actionPlan); diff --git a/src/Orchestrator/Jobs/UpdateStatusJob.cs b/src/Orchestrator/Jobs/UpdateStatusJob.cs index aeb21814..3fa2f074 100644 --- a/src/Orchestrator/Jobs/UpdateStatusJob.cs +++ b/src/Orchestrator/Jobs/UpdateStatusJob.cs @@ -6,6 +6,7 @@ using Middleware.Common.Enums; using Middleware.Common.ExtensionMethods; using Middleware.Models.Domain; +using Middleware.Models.Enums; using Middleware.Orchestrator.Deployment; using Middleware.RedisInterface.Sdk; using Quartz; @@ -87,7 +88,7 @@ protected override async Task ExecuteJobAsync(IJobExecutionContext context) //check if all instances are down for at least half an hour, then terminate foreach (var action in seq.ActionSequence) { - if (action.Placement != _middlewareConfig.InstanceName || action.PlacementType != _middlewareConfig.InstanceType) + if (action.Placement != _middlewareConfig.InstanceName || action.PlacementType != Enum.Parse(_middlewareConfig.InstanceType)) continue; foreach (var instance in action.Services) diff --git a/src/ResourcePlanner/ResourcePlanner.cs b/src/ResourcePlanner/ResourcePlanner.cs index 126bcca6..ca49bda9 100644 --- a/src/ResourcePlanner/ResourcePlanner.cs +++ b/src/ResourcePlanner/ResourcePlanner.cs @@ -7,6 +7,7 @@ using Middleware.ResourcePlanner.ApiReference; using KeyValuePair = Middleware.Models.Domain.KeyValuePair; using Middleware.Common.Config; +using Middleware.Models.Enums; using Middleware.RedisInterface.Contracts.Mappings; using Middleware.RedisInterface.Contracts.Responses; using Middleware.RedisInterface.Sdk; @@ -76,7 +77,7 @@ public async Task Plan(TaskModel taskModel, RobotModel robot) } action.Placement = _mwConfig.InstanceName; - action.PlacementType = _mwConfig.InstanceType; + action.PlacementType = Enum.Parse(_mwConfig.InstanceType); } return taskModel; diff --git a/src/TaskPlanner/Services/PublishingService.cs b/src/TaskPlanner/Services/PublishingService.cs index 3d977620..b5d6670f 100644 --- a/src/TaskPlanner/Services/PublishingService.cs +++ b/src/TaskPlanner/Services/PublishingService.cs @@ -37,7 +37,7 @@ public async Task PublishPlanAsync(TaskModel task, RobotModel robot) if (action == null) return; - var location = QueueHelpers.ConstructRoutingKey(action.Placement, action.PlacementType); + var location = QueueHelpers.ConstructRoutingKey(action.Placement, action.PlacementType.ToString()); var message = new DeployPlanMessage() { Task = task, diff --git a/test/CentralApi.Tests.Unit/Services/LocationServiceTests.cs b/test/CentralApi.Tests.Unit/Services/LocationServiceTests.cs index e8f42ea7..0c12fc47 100644 --- a/test/CentralApi.Tests.Unit/Services/LocationServiceTests.cs +++ b/test/CentralApi.Tests.Unit/Services/LocationServiceTests.cs @@ -5,6 +5,7 @@ using Middleware.Common.Enums; using Middleware.DataAccess.Repositories.Abstract; using Middleware.Models.Domain; +using Middleware.Models.Enums; using NSubstitute; using OneOf.Types; From 761105696394c1f995397f19473f2b33915fa97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Fri, 14 Apr 2023 12:05:35 +0200 Subject: [PATCH 29/31] Correct nullability warnings in some classes --- src/Models/Domain/ActionPlanModel.cs | 15 ++++++++------- src/Models/Domain/SensorModel.cs | 11 +++++++---- src/Models/Dto/Ros/Sensor.cs | 8 ++++---- src/Models/Dto/TaskDto.cs | 22 +++++++++++----------- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/Models/Domain/ActionPlanModel.cs b/src/Models/Domain/ActionPlanModel.cs index 5dc3ddb9..6bbad177 100644 --- a/src/Models/Domain/ActionPlanModel.cs +++ b/src/Models/Domain/ActionPlanModel.cs @@ -5,20 +5,20 @@ namespace Middleware.Models.Domain; public sealed class ActionPlanModel : BaseModel { - [JsonPropertyName("Id")] //atuomatically generated plan id by middleware + [JsonPropertyName("Id")] public override Guid Id { get; set; } - [JsonPropertyName("TaskId")] //TaskID + [JsonPropertyName("TaskId")] public Guid TaskId { get; set; } - [JsonPropertyName("Name")] // Name of task - public override string Name { get; set; } + [JsonPropertyName("Name")] + public override string Name { get; set; } = default!; /// /// Status of the whole plan /// [JsonPropertyName("Status")] - public string Status { get; set; } + public string? Status { get; set; } [JsonPropertyName("IsReplan")] //Status of whole plan public bool IsReplan { get; set; } @@ -27,7 +27,7 @@ public sealed class ActionPlanModel : BaseModel public DateTime LastStatusChange { get; set; } // AL 2022-05-10: Not sure we need this one or how to use it. [JsonPropertyName("ActionSequence")] - public List ActionSequence { get; set; } + public List ActionSequence { get; set; } = new(); [JsonPropertyName("RobotId")] public Guid RobotId { get; set; } @@ -56,6 +56,7 @@ public void SetStatus(string status) Status = status; LastStatusChange = DateTime.UtcNow; } + public override Dto.Dto ToDto() { var domain = this; @@ -70,6 +71,6 @@ public override Dto.Dto ToDto() ActionSequence = domain.ActionSequence, RobotId = domain.RobotId.ToString(), TaskStartedAt = domain.TaskStartedAt == default ? DateTimeOffset.Now : domain.TaskStartedAt, - }; ; + }; } } \ No newline at end of file diff --git a/src/Models/Domain/SensorModel.cs b/src/Models/Domain/SensorModel.cs index 067dab34..ee613650 100644 --- a/src/Models/Domain/SensorModel.cs +++ b/src/Models/Domain/SensorModel.cs @@ -4,11 +4,14 @@ namespace Middleware.Models.Domain { public class SensorModel { - public string Name { get; set; } - public string Type { get; set; } + public string Name { get; set; } = default!; + public string Type { get; set; } = default!; // public string SensorLocation { get; set; } - public string Description { get; set; } - public List Nodes { get; set; } //A sensor can publish multiple topics + public string? Description { get; set; } + /// + /// List of the topics published by a sensor + /// + public List Nodes { get; set; } = new(); public int Number { get; set; } diff --git a/src/Models/Dto/Ros/Sensor.cs b/src/Models/Dto/Ros/Sensor.cs index 97997ea8..d669cbc1 100644 --- a/src/Models/Dto/Ros/Sensor.cs +++ b/src/Models/Dto/Ros/Sensor.cs @@ -4,10 +4,10 @@ namespace Middleware.Models.Dto.Ros; public class Sensor { - public string? Name { get; set; } - public string? Type { get; set; } - public string? Description { get; set; } - public List Nodes { get; set; } = new(); + public string Name { get; init; } = default!; + public string Type { get; init; } = default!; + public string? Description { get; init; } + public List Nodes { get; init; } = new(); public int Number { get; set; } diff --git a/src/Models/Dto/TaskDto.cs b/src/Models/Dto/TaskDto.cs index 37a4a9eb..161e2757 100644 --- a/src/Models/Dto/TaskDto.cs +++ b/src/Models/Dto/TaskDto.cs @@ -6,27 +6,27 @@ namespace Middleware.Models.Dto; [Document(IndexName = "task-idx", StorageType = StorageType.Json, Prefixes = new[] { TaskDto.Prefix })] public class TaskDto : Dto { - public const string Prefix = "Task"; + private const string Prefix = "Task"; [Indexed] [RedisIdField] - public override string Id { get; set; } + public override string Id { get; set; } = default!; [Indexed] - public string? Name { get; set; } - - + public string Name { get; set; } = default!; + [Indexed(Sortable = true)] public int TaskPriority { get; set; } - - + + /// + /// Is the result of the task always the same + /// [Indexed(Sortable = true)] - public bool DeterministicTask { get; set; } // The result is always the same if true. - + public bool DeterministicTask { get; set; } + /*[Indexed] public List ActionSequence { get; set; }*/ [Indexed] - public List Tags { get; set; } - + public List? Tags { get; set; } public override BaseModel ToModel() { From e4f3d757d4b84eab39532d1ede0f7d75029daa47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Fri, 14 Apr 2023 15:32:11 +0200 Subject: [PATCH 30/31] Added ActionPlan responses --- .../Repositories/Abstract/IBaseRepository.cs | 2 +- src/DataConverter/Program.cs | 2 +- src/Models/Domain/ActionModel.cs | 24 +- src/Models/Domain/ActionRunningModel.cs | 35 +- src/Models/Domain/InstanceRunningModel.cs | 10 +- src/Models/Dto/ActionRunningDto.cs | 28 +- src/Models/Dto/TaskDto.cs | 12 +- .../Deployment/DeploymentService.cs | 4 +- .../Mappings/ApiContractToDomainMapper.cs | 15 + .../Mappings/DomainToApiContractMapper.cs | 87 ++ .../Requests/ActionPlanRequest.cs | 20 + .../Requests/InstanceRunningRequest.cs | 18 + .../Requests/RunningActionRequest.cs | 26 + .../Responses/ActionPlanResponse.cs | 20 + .../Responses/GetActionPlansResponse.cs | 6 + .../Responses/InstanceRunningResponse.cs | 18 + .../Responses/RunningActionResponse.cs | 28 + .../Controllers/ActionController.cs | 749 +++++++++--------- .../Services/ActionRunningService.cs | 1 - 19 files changed, 684 insertions(+), 421 deletions(-) create mode 100644 src/RedisInterface.Contracts/Requests/ActionPlanRequest.cs create mode 100644 src/RedisInterface.Contracts/Requests/InstanceRunningRequest.cs create mode 100644 src/RedisInterface.Contracts/Requests/RunningActionRequest.cs create mode 100644 src/RedisInterface.Contracts/Responses/ActionPlanResponse.cs create mode 100644 src/RedisInterface.Contracts/Responses/GetActionPlansResponse.cs create mode 100644 src/RedisInterface.Contracts/Responses/InstanceRunningResponse.cs create mode 100644 src/RedisInterface.Contracts/Responses/RunningActionResponse.cs diff --git a/src/DataAccess/Repositories/Abstract/IBaseRepository.cs b/src/DataAccess/Repositories/Abstract/IBaseRepository.cs index ba4c19cc..f4d3d8ed 100644 --- a/src/DataAccess/Repositories/Abstract/IBaseRepository.cs +++ b/src/DataAccess/Repositories/Abstract/IBaseRepository.cs @@ -28,7 +28,7 @@ public interface IBaseRepository where T : class /// Get all objects of the specified type from the data store /// /// - Task> GetAllAsync(); + Task?> GetAllAsync(); /// /// Delete object from the data store by its id diff --git a/src/DataConverter/Program.cs b/src/DataConverter/Program.cs index f4c7e7ec..669a2205 100644 --- a/src/DataConverter/Program.cs +++ b/src/DataConverter/Program.cs @@ -183,7 +183,7 @@ { actionPlan.LastStatusChange = DateTime.Now; actionPlan.TaskStartedAt = DateTime.Now.AddDays(-1); - actionPlan.ActionSequence.ForEach(a=>a.Services.ForEach(i=>i.OnboardedTime = DateTime.Today.AddDays(-10))); + //actionPlan.ActionSequence.ForEach(a=>a.Services.ForEach(i=>i.OnboardedTime = DateTime.Today.AddDays(-10))); await redisActionPlanRepository.AddAsync(actionPlan); } diff --git a/src/Models/Domain/ActionModel.cs b/src/Models/Domain/ActionModel.cs index 57d8fef6..29e91c1c 100644 --- a/src/Models/Domain/ActionModel.cs +++ b/src/Models/Domain/ActionModel.cs @@ -11,8 +11,7 @@ public class ActionModel : BaseModel public override Guid Id { get; set; } = Guid.NewGuid(); [JsonPropertyName("Name")] - public override string Name { get; set; } - + public override string Name { get; set; } = default!; [JsonPropertyName("Tags")] public List? Tags { get; set; } @@ -55,7 +54,26 @@ public override Dto.Dto ToDto() MinimumRam = domain.MinimumRam, MinimumNumCores = domain.MinimumNumCores }, - Tags = domain.Tags + Tags = domain.Tags ?? new List() + }; + } + + public ActionRunningModel ToActionRunningModel(Guid actionPlanId) + { + var that = this; + return new ActionRunningModel() + { + ActionPriority = that.ActionPriority, + ActionId = that.Id, + ActionPlanId = actionPlanId, + Name = that.Name, + ActionStatus = that.ActionStatus, + Placement = that.Placement!, + PlacementType = that.PlacementType!.Value, + Order = that.Order, + MinimumRam = that.MinimumRam, + MinimumNumCores = that.MinimumNumCores, + Tags = that.Tags }; } } \ No newline at end of file diff --git a/src/Models/Domain/ActionRunningModel.cs b/src/Models/Domain/ActionRunningModel.cs index a912fdcf..c798dbda 100644 --- a/src/Models/Domain/ActionRunningModel.cs +++ b/src/Models/Domain/ActionRunningModel.cs @@ -1,29 +1,36 @@ -using Middleware.Models.Dto.Hardware; +using System.Runtime.InteropServices; +using Middleware.Models.Dto.Hardware; using Middleware.Models.Dto; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Text.Json.Serialization; -using System.Threading.Tasks; using Middleware.Models.Enums; namespace Middleware.Models.Domain; public class ActionRunningModel : BaseModel { + /// + /// Unique identifier of a running action + /// [JsonPropertyName("Id")] - public override Guid Id { get; set; } + public override Guid Id { get; set; } = Guid.NewGuid(); - [JsonPropertyName("ActionParentId")] - public Guid ActionParentId { get; set; } // This is the Id of normal Actio from which the running action is based. + /// + /// Identifier of the action definition + /// + [JsonPropertyName("ActionId")] + public Guid ActionId { get; set; } + /// + /// Identifier of the ActionPlan the running action is associated with + /// [JsonPropertyName("ActionPlanId")] public Guid ActionPlanId { get; set; } + /// + /// Name of the running action + /// [JsonPropertyName("Name")] - public override string Name { get; set; } - + public override string Name { get; set; } = default!; [JsonPropertyName("Tags")] public List? Tags { get; set; } @@ -61,14 +68,16 @@ public override Dto.Dto ToDto() Id = domain.Id.ToString(), ActionPriority = domain.ActionPriority, Name = domain.Name, - ActionParentId = domain.ActionParentId.ToString(), + ActionId = domain.ActionId.ToString(), ActionPlanId = domain.ActionPlanId.ToString(), HardwareRequirements = new HardwareRequirements() { MinimumRam = domain.MinimumRam, MinimumNumCores = domain.MinimumNumCores }, - Tags = domain.Tags + Tags = domain.Tags, + Placement = domain.Placement, + PlacementType = domain.PlacementType.ToString() }; } } diff --git a/src/Models/Domain/InstanceRunningModel.cs b/src/Models/Domain/InstanceRunningModel.cs index 58ef4fd6..41d02122 100644 --- a/src/Models/Domain/InstanceRunningModel.cs +++ b/src/Models/Domain/InstanceRunningModel.cs @@ -1,7 +1,5 @@ using System.Text.Json.Serialization; using Middleware.Models.Dto; -using Middleware.Models.Dto.Hardware; -using Middleware.Models.Enums; namespace Middleware.Models.Domain; @@ -12,20 +10,20 @@ public class InstanceRunningModel : BaseModel public override Guid Id { get; set; } [JsonPropertyName("Name")] - public override string Name { get; set; } + public override string Name { get; set; } = default!; [JsonPropertyName("ServiceInstanceId")] public Guid ServiceInstanceId { get; set; } [JsonPropertyName("ServiceType")] - public string ServiceType { get; set; } + public string ServiceType { get; set; } = default!; [JsonPropertyName("ServiceUrl")] - public string ServiceUrl { get; set; } + public string ServiceUrl { get; set; } = default!; [JsonPropertyName("ServiceStatus")] - public string ServiceStatus { get; set; } //updated every 10 sec + public string ServiceStatus { get; set; } = default!; //updated every 10 sec [JsonPropertyName("DeployedTime")] public DateTime DeployedTime { get; set; } // Compulsory field diff --git a/src/Models/Dto/ActionRunningDto.cs b/src/Models/Dto/ActionRunningDto.cs index 6beaab0e..b22e982d 100644 --- a/src/Models/Dto/ActionRunningDto.cs +++ b/src/Models/Dto/ActionRunningDto.cs @@ -1,18 +1,23 @@ using Middleware.Models.Domain; using Middleware.Models.Dto.Hardware; +using Middleware.Models.Enums; using Redis.OM.Modeling; namespace Middleware.Models.Dto; -[Document(IndexName = "actionRunning-idx", StorageType = StorageType.Json, Prefixes = new[] { ActionRunningDto.Prefix })] +[Document(IndexName = "actionRunning-idx", StorageType = StorageType.Json, + Prefixes = new[] { ActionRunningDto.Prefix })] public class ActionRunningDto : Dto { public const string Prefix = "ActionRunning"; [Indexed] [RedisIdField] public override string Id { get; set; } = default!; + /// + /// This is the ID in which the actionRunning is based from the normal Action + /// [Indexed] - public string ActionParentId { get; init; } = default!; // This is the ID in which the actionRunning us based from the normal Action + public string ActionId { get; init; } = default!; [Indexed] public string ActionPlanId { get; init; } = default!; [Indexed] @@ -20,8 +25,11 @@ public class ActionRunningDto : Dto [Indexed] public List? Tags { get; init; } = new(); [Indexed] - public string? ActionPriority { get; init; } = default!; - + public string? ActionPriority { get; init; } + [Indexed] + public string Placement { get; init; } = default!; + [Indexed] + public string PlacementType { get; init; } = default!; public HardwareRequirements HardwareRequirements { get; init; } = new(); public override BaseModel ToModel() @@ -29,14 +37,18 @@ public override BaseModel ToModel() var dto = this; return new ActionRunningModel() { - Id = Guid.Parse(dto.Id.Replace(Prefix, "")), - ActionParentId = Guid.Parse(dto.ActionParentId.Replace(Prefix, "")), - ActionPlanId = Guid.Parse(dto.ActionParentId.Replace(Prefix, "")), + Id = Guid.Parse(dto.Id), + ActionId = Guid.Parse(dto.ActionId), + ActionPlanId = Guid.Parse(dto.ActionPlanId), Name = dto.Name, Tags = dto.Tags, MinimumRam = dto.HardwareRequirements.MinimumRam, MinimumNumCores = dto.HardwareRequirements.MinimumNumCores, - ActionPriority = dto.ActionPriority + ActionPriority = dto.ActionPriority, + Placement = dto.Placement, + PlacementType = Enum.IsDefined(typeof(LocationType), PlacementType) + ? Enum.Parse(dto.PlacementType) + : LocationType.Unspecified }; } } \ No newline at end of file diff --git a/src/Models/Dto/TaskDto.cs b/src/Models/Dto/TaskDto.cs index 161e2757..9535577a 100644 --- a/src/Models/Dto/TaskDto.cs +++ b/src/Models/Dto/TaskDto.cs @@ -12,28 +12,26 @@ public class TaskDto : Dto public override string Id { get; set; } = default!; [Indexed] - public string Name { get; set; } = default!; + public string Name { get; init; } = default!; [Indexed(Sortable = true)] - public int TaskPriority { get; set; } + public int TaskPriority { get; init; } /// /// Is the result of the task always the same /// [Indexed(Sortable = true)] - public bool DeterministicTask { get; set; } + public bool DeterministicTask { get; init; } - /*[Indexed] - public List ActionSequence { get; set; }*/ [Indexed] - public List? Tags { get; set; } + public List? Tags { get; init; } public override BaseModel ToModel() { var dto = this; return new TaskModel() { - Id = Guid.Parse(dto.Id!.Replace(Prefix, "")), + Id = Guid.Parse(dto.Id), Name = dto.Name, TaskPriority = dto.TaskPriority, DeterministicTask = dto.DeterministicTask, diff --git a/src/Orchestrator/Deployment/DeploymentService.cs b/src/Orchestrator/Deployment/DeploymentService.cs index 2f5256cc..9cf3320d 100644 --- a/src/Orchestrator/Deployment/DeploymentService.cs +++ b/src/Orchestrator/Deployment/DeploymentService.cs @@ -149,7 +149,7 @@ private async Task SaveActionSequence(TaskModel task, Guid robotId) { ActionRunningModel actionRunningTemp = new ActionRunningModel(); actionRunningTemp.ActionPlanId = actionPlan.Id; - actionRunningTemp.ActionParentId = action.Id; + actionRunningTemp.ActionId = action.Id; actionRunningTemp.Name = action.Name; actionRunningTemp.Tags = action.Tags; actionRunningTemp.Order = action.Order; @@ -400,7 +400,7 @@ public async Task DeletePlanAsync(ActionPlanModel actionPlan) historicalActionPlan.ActionSequence = actionPlan.ActionSequence.Select(x => new ActionRunningModel() { Name = x.Name, - ActionParentId = x.Id, + ActionId = x.Id, ActionPlanId = actionPlan.Id, Tags = x.Tags, Order = x.Order, diff --git a/src/RedisInterface.Contracts/Mappings/ApiContractToDomainMapper.cs b/src/RedisInterface.Contracts/Mappings/ApiContractToDomainMapper.cs index a2ef868c..bb2ac58f 100644 --- a/src/RedisInterface.Contracts/Mappings/ApiContractToDomainMapper.cs +++ b/src/RedisInterface.Contracts/Mappings/ApiContractToDomainMapper.cs @@ -346,4 +346,19 @@ public static TaskModel ToTask(this TaskResponse x) Tags = x.Tags?.ToList() }; } + + // public static ActionPlanModel ToActionPlan(this ActionPlanRequest x) + // { + // return new ActionPlanModel() + // { + // Id = x.Id, + // Name = x.Name, + // RobotId = x.RobotId, + // TaskId = x.TaskId, + // IsReplan = x.IsReplan, + // TaskStartedAt = x.TaskStartedAt, + // Status = x.Status, + // ActionSequence = x.ActionSequence.t + // } + // } } \ No newline at end of file diff --git a/src/RedisInterface.Contracts/Mappings/DomainToApiContractMapper.cs b/src/RedisInterface.Contracts/Mappings/DomainToApiContractMapper.cs index 1cc1dff5..624bd5fa 100644 --- a/src/RedisInterface.Contracts/Mappings/DomainToApiContractMapper.cs +++ b/src/RedisInterface.Contracts/Mappings/DomainToApiContractMapper.cs @@ -121,6 +121,7 @@ public static InstanceResponse ToInstanceResponse(this InstanceModel x) OnboardedTime = x.OnboardedTime }; } + public static GetInstancesResponse ToInstancesResponse(this IEnumerable instances) { return new GetInstancesResponse() @@ -185,6 +186,7 @@ public static RobotResponse ToRobotResponse(this RobotModel x) OnboardedTime = x.OnboardedTime }; } + public static GetRobotsResponse ToRobotsResponse(this IEnumerable robots) { return new GetRobotsResponse() @@ -212,4 +214,89 @@ public static GetTasksResponse ToTasksResponse(this IEnumerable tasks Tasks = tasks.Select(x => x.ToTaskResponse()) }; } + + public static IEnumerable ToInstanceRunningList( + this IEnumerable instances) + { + return instances.Select(x => new InstanceRunningResponse() + { + Id = x.Id, + ServiceInstanceId = x.ServiceInstanceId, + Name = x.Name, + ServiceType = x.ServiceType, + ServiceStatus = x.ServiceStatus, + ServiceUrl = x.ServiceUrl, + DeployedTime = x.DeployedTime + }); + } + + public static IEnumerable ToRunningActionResponseList( + this IEnumerable actions) + { + return actions.Select(x => new RunningActionResponse() + { + Id = x.Id, + ActionId = x.ActionId, + ActionPlanId = x.ActionPlanId, + Name = x.Name, + ActionPriority = x.ActionPriority, + ActionStatus = x.ActionStatus, + Placement = x.Placement, + PlacementType = x.PlacementType.ToString(), + Order = x.Order, + Services = x.Services.ToInstanceRunningList(), + Tags = x.Tags + }); + } + + public static RunningActionResponse ToRunningActionResponse(this ActionRunningModel x) + { + return new RunningActionResponse() + { + Id = x.Id, + ActionId = x.ActionId, + ActionPlanId = x.ActionPlanId, + Name = x.Name, + ActionPriority = x.ActionPriority, + ActionStatus = x.ActionStatus, + Placement = x.Placement, + PlacementType = x.PlacementType.ToString(), + Order = x.Order, + Services = x.Services.ToInstanceRunningList(), + Tags = x.Tags + }; + } + + public static GetActionPlansResponse ToActionPlansResponse(this IEnumerable plans) + { + return new GetActionPlansResponse() + { + ActionPlans = plans.Select(x => new ActionPlanResponse() + { + Id = x.Id, + Name = x.Name, + RobotId = x.RobotId, + TaskId = x.TaskId, + Status = x.Status, + IsReplan = x.IsReplan, + TaskStartedAt = x.TaskStartedAt, + ActionSequence = x.ActionSequence.Select(a => a.ToActionRunningModel(x.Id)) + .ToRunningActionResponseList() + }) + }; + } + public static ActionPlanResponse ToActionPlanResponse(this ActionPlanModel x) + { + return new ActionPlanResponse() + { + Id = x.Id, + Name = x.Name, + RobotId = x.RobotId, + TaskId = x.TaskId, + Status = x.Status, + IsReplan = x.IsReplan, + TaskStartedAt = x.TaskStartedAt, + ActionSequence = x.ActionSequence.Select(a => a.ToActionRunningModel(x.Id)).ToRunningActionResponseList() + }; + } } \ No newline at end of file diff --git a/src/RedisInterface.Contracts/Requests/ActionPlanRequest.cs b/src/RedisInterface.Contracts/Requests/ActionPlanRequest.cs new file mode 100644 index 00000000..87d2634e --- /dev/null +++ b/src/RedisInterface.Contracts/Requests/ActionPlanRequest.cs @@ -0,0 +1,20 @@ +namespace Middleware.RedisInterface.Contracts.Requests; + +public class ActionPlanRequest +{ + public Guid Id { get; set; } + + public Guid TaskId { get; set; } + + public string Name { get; set; } = default!; + + public string Status { get; set; } + + public bool IsReplan { get; set; } + + public IEnumerable ActionSequence { get; set; } = Enumerable.Empty(); + + public Guid RobotId { get; set; } + + public DateTime TaskStartedAt { get; set; } +} \ No newline at end of file diff --git a/src/RedisInterface.Contracts/Requests/InstanceRunningRequest.cs b/src/RedisInterface.Contracts/Requests/InstanceRunningRequest.cs new file mode 100644 index 00000000..fe092d97 --- /dev/null +++ b/src/RedisInterface.Contracts/Requests/InstanceRunningRequest.cs @@ -0,0 +1,18 @@ +namespace Middleware.RedisInterface.Contracts.Requests; + +public class InstanceRunningRequest +{ + public Guid Id { get; set; } + + public string Name { get; set; } + + public Guid ServiceInstanceId { get; set; } + + public string ServiceType { get; set; } + + public string ServiceUrl { get; set; } + + public string ServiceStatus { get; set; } + + public DateTime DeployedTime { get; set; } +} \ No newline at end of file diff --git a/src/RedisInterface.Contracts/Requests/RunningActionRequest.cs b/src/RedisInterface.Contracts/Requests/RunningActionRequest.cs new file mode 100644 index 00000000..0ec239e7 --- /dev/null +++ b/src/RedisInterface.Contracts/Requests/RunningActionRequest.cs @@ -0,0 +1,26 @@ +namespace Middleware.RedisInterface.Contracts.Requests; + +public class RunningActionRequest +{ + public Guid Id { get; set; } + + public Guid ActionId { get; set; } + + public Guid ActionPlanId { get; set; } + + public string Name { get; set; } = default!; + + public List? Tags { get; set; } + + public int Order { get; set; } + + public string Placement { get; set; } = default!; + + public string PlacementType { get; set; } + + public string ActionPriority { get; set; } + + public string ActionStatus { get; set; } + + public IEnumerable Services { get; set; } +} \ No newline at end of file diff --git a/src/RedisInterface.Contracts/Responses/ActionPlanResponse.cs b/src/RedisInterface.Contracts/Responses/ActionPlanResponse.cs new file mode 100644 index 00000000..1b167840 --- /dev/null +++ b/src/RedisInterface.Contracts/Responses/ActionPlanResponse.cs @@ -0,0 +1,20 @@ +namespace Middleware.RedisInterface.Contracts.Responses; + +public class ActionPlanResponse +{ + public Guid Id { get; set; } + + public Guid TaskId { get; set; } + + public string Name { get; set; } = default!; + + public string Status { get; set; } + + public bool IsReplan { get; set; } + + public IEnumerable ActionSequence { get; set; } = Enumerable.Empty(); + + public Guid RobotId { get; set; } + + public DateTime TaskStartedAt { get; set; } +} \ No newline at end of file diff --git a/src/RedisInterface.Contracts/Responses/GetActionPlansResponse.cs b/src/RedisInterface.Contracts/Responses/GetActionPlansResponse.cs new file mode 100644 index 00000000..04c5ad2d --- /dev/null +++ b/src/RedisInterface.Contracts/Responses/GetActionPlansResponse.cs @@ -0,0 +1,6 @@ +namespace Middleware.RedisInterface.Contracts.Responses; + +public class GetActionPlansResponse +{ + public IEnumerable ActionPlans { get; set; } = Enumerable.Empty(); +} \ No newline at end of file diff --git a/src/RedisInterface.Contracts/Responses/InstanceRunningResponse.cs b/src/RedisInterface.Contracts/Responses/InstanceRunningResponse.cs new file mode 100644 index 00000000..bde1d757 --- /dev/null +++ b/src/RedisInterface.Contracts/Responses/InstanceRunningResponse.cs @@ -0,0 +1,18 @@ +namespace Middleware.RedisInterface.Contracts.Responses; + +public class InstanceRunningResponse +{ + public Guid Id { get; set; } + + public string Name { get; set; } + + public Guid ServiceInstanceId { get; set; } + + public string ServiceType { get; set; } + + public string ServiceUrl { get; set; } + + public string ServiceStatus { get; set; } + + public DateTime DeployedTime { get; set; } +} \ No newline at end of file diff --git a/src/RedisInterface.Contracts/Responses/RunningActionResponse.cs b/src/RedisInterface.Contracts/Responses/RunningActionResponse.cs new file mode 100644 index 00000000..78ed099c --- /dev/null +++ b/src/RedisInterface.Contracts/Responses/RunningActionResponse.cs @@ -0,0 +1,28 @@ +namespace Middleware.RedisInterface.Contracts.Responses; + +public class RunningActionResponse +{ + + public Guid Id { get; set; } + + public Guid ActionId { get; set; } + + public Guid ActionPlanId { get; set; } + + public string Name { get; set; } = default!; + + public List? Tags { get; set; } + + public int Order { get; set; } + + public string Placement { get; set; } = default!; + + public string PlacementType { get; set; } + + public string ActionPriority { get; set; } + + public string ActionStatus { get; set; } + + public IEnumerable Services { get; set; } + +} \ No newline at end of file diff --git a/src/RedisInterface/Controllers/ActionController.cs b/src/RedisInterface/Controllers/ActionController.cs index e04cc3e1..5146ffa2 100644 --- a/src/RedisInterface/Controllers/ActionController.cs +++ b/src/RedisInterface/Controllers/ActionController.cs @@ -38,99 +38,115 @@ public ActionController(IActionRepository actionRepository, IActionPlanRepositor } - - - - - - #region ActionPlan + #region Action /// - /// Retrieves all ActionPlans + /// Get all the actions /// - /// - [HttpGet] - [Route("plan", Name = "ActionPlanGetAll")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + /// the list of ActionModel entities + [HttpGet(Name = "ActionGetAll")] + [ProducesResponseType(typeof(GetActionsResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetAllActionPlansAsync() + public async Task GetAllAsync() { try { - var plans = await _actionPlanRepository.GetAllAsync(); - if (plans == null || plans.Any() == false) + List models = await _actionRepository.GetAllAsync(); + if (models != null && models.Any() == false) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No plans have been found.")); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No actions were found.")); } - return Ok(plans); + return Ok(models.ToActionsResponse()); } catch (Exception ex) { - _logger.LogError(ex, "An error occurred:"); int statusCode = (int)HttpStatusCode.InternalServerError; - return StatusCode(statusCode, new ApiResponse(statusCode, ex.Message)); + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } } /// - /// Retrieves an ActionPlan by id + /// Get an action entity by id /// /// - /// + /// the ActionModel entity for the specified id [HttpGet] - [Route("plan/{id:guid}", Name = "ActionPlanGetById")] - [ProducesResponseType(typeof(ActionPlanModel), (int)HttpStatusCode.OK)] + [Route("{id}", Name = "ActionGetById")] + [ProducesResponseType(typeof(ActionResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetActionPlanByIdAsync(Guid id) + public async Task GetByIdAsync(Guid id) { try { - var plan = await _actionPlanRepository.GetByIdAsync(id); - if (plan == null) + ActionModel model = await _actionService.GetByIdAsync(id); + if (model == null) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Specified plan was not found.")); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, + $"Action with id: '{id}' was not found.")); } - return Ok(plan); + return Ok(model.ToActionResponse()); } catch (Exception ex) { - _logger.LogError(ex, "An error occurred:"); int statusCode = (int)HttpStatusCode.InternalServerError; - return StatusCode(statusCode, new ApiResponse(statusCode, ex.Message)); + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } } /// - /// Creates new ActionPlan + /// Add a new action entity /// - /// - /// - [HttpPost] - [Route("plan", Name = "ActionPlanAdd")] - [ProducesResponseType(typeof(ActionPlanModel), (int)HttpStatusCode.OK)] + /// + /// the newly created ActionModel entity + [HttpPost(Name = "ActionAdd")] + [ProducesResponseType(typeof(ActionResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task AddActionPlanAsync(ActionPlanModel actionPlan) + public async Task AddAsync([FromBody] ActionRequest request) { try { - if (actionPlan == null) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Plan cannot be null")); - } + var action = await _actionService.AddAsync(request.ToAction()); + return Ok(action.ToActionResponse()); + } + catch (Exception ex) + { + int statusCode = (int)HttpStatusCode.InternalServerError; + _logger.LogError(ex, "An error occurred:"); + return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + } + } - var plan = await _actionPlanRepository.AddAsync(actionPlan, () => actionPlan.Id); - if (plan == null) + /// + /// Update an existing action entity + /// + /// the modified ActionModel entity + [HttpPut] + [Route("{id}", Name = "ActionUpdate")] + [ProducesResponseType(typeof(ActionResponse), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] + public async Task UpdateActionAsync([FromMultiSource] UpdateActionRequest request) + { + try + { + var existingAction = await _actionService.GetByIdAsync(request.Id); + if (existingAction is null) { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, - "The specified plan has not been added.")); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object to be updated was not found.")); } - return Ok(plan); + var action = request.ToAction(); + await _actionService.UpdateAsync(action); + + var response = action.ToActionResponse(); + return Ok(response); } catch (Exception ex) { @@ -141,26 +157,27 @@ public async Task AddActionPlanAsync(ActionPlanModel actionPlan) } /// - /// Delete ActionPlan by id + /// Delete an action entity with the given id /// /// - /// + /// no return [HttpDelete] - [Route("plan/{id}", Name = "ActionPlanDelete")] + [Route("{id}", Name = "ActionDelete")] [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task DeleteActionPlanAsync(Guid id) + public async Task DeleteByIdAsync(Guid id) { try { - var deleted = await _actionPlanRepository.DeleteByIdAsync(id); - if (deleted == false) + var exists = await _actionService.GetByIdAsync(id); + if (exists is null) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, - "The specified plan has not been found.")); + "The specified Action has not been found.")); } + await _actionService.DeleteAsync(id); return Ok(); } catch (Exception ex) @@ -172,36 +189,30 @@ public async Task DeleteActionPlanAsync(Guid id) } /// - /// Patches an existing ActionPlan by id + /// Creates a new relation between two models /// - /// - /// + /// /// - [HttpPut] - [Route("plan/{id:guid}", Name = "ActionPlanPatch")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] + [HttpPost] + [Route("AddRelation", Name = "ActionAddRelation")] + [ProducesResponseType(typeof(RelationModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task PatchActionPlanAsync(Guid id, [FromBody] ActionPlanModel actionPlan) + public async Task> AddRelationAsync([FromBody] RelationModel model) { - try + if (model == null) { - if (actionPlan == null || id == Guid.Empty) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, - "Id or updated object has not been specified")); - } + return BadRequest("Parameters were not specified."); + } - var deleted = await _actionPlanRepository.DeleteByIdAsync(id); - if (deleted == false) + try + { + bool isValid = await _actionRepository.AddRelationAsync(model); + if (!isValid) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, - "The specified plan has not been found.")); + return StatusCode((int)HttpStatusCode.InternalServerError, + new ApiResponse((int)HttpStatusCode.InternalServerError, "The relation was not created")); } - - var updatedPlan = await _actionPlanRepository.AddAsync(actionPlan, () => id); - return Ok(updatedPlan); } catch (Exception ex) { @@ -209,35 +220,42 @@ public async Task PatchActionPlanAsync(Guid id, [FromBody] Action _logger.LogError(ex, "An error occurred:"); return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } + + return Ok(model); } /// - /// Get action plan given robot Id. + /// Retrieves a single relation by name /// - /// List + /// + /// + /// [HttpGet] - [Route("plan/robot/{robotId}", Name = "GetActionPlanByRobotIdAsync")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [Route("relation/{name}", Name = "ActionGetRelationByName")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetActionPlanByRobotIdAsync(Guid robotId) + public async Task GetRelationAsync(Guid id, string name) //Guid of node and name of relationship { - try + if (string.IsNullOrWhiteSpace(name)) { - if (robotId == Guid.Empty) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Robot id has not been specified")); - } + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); + } - // Get list of actionPlans from specific robotId. - List actionPlans = await _actionPlanRepository.GetRobotActionPlans(robotId); - if (actionPlans == null) + if (id == Guid.Empty) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); + } + + try + { + var relations = await _actionRepository.GetRelation(id, name); + if (!relations.Any()) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found.")); } - //List activePoliciesRecords = actionPlans.Select(p => new ActionPlanModel(p.Id, p.Name, p.Description)).ToList(); - return Ok(actionPlans); + return Ok(relations); } catch (Exception ex) { @@ -247,58 +265,30 @@ public async Task GetActionPlanByRobotIdAsync(Guid robotId) } } - /// - /// Get latest action plan given robot Id. + /// Retrieves two relations by their names /// - /// List + /// + /// + /// + /// [HttpGet] - [Route("plan/robot/{robotId}/latest", Name = "GetLatestActionPlanByRobotIdAsync")] - [ProducesResponseType(typeof(ActionPlanModel), (int)HttpStatusCode.OK)] + [Route("relations/{firstName}/{secondName}", Name = "ActionGetRelationsByName")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetLatestActionPlanByRobotIdAsync(Guid robotId) + public async Task GetRelationsAsync(Guid id, string firstName, string secondName) { try { - if (robotId == Guid.Empty) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Robot id has not been specified")); - } - - // Get list of actionPlans from specific robotId. - List actionPlans = await _actionPlanRepository.GetRobotActionPlans(robotId); - - if (actionPlans == null) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); - } - //Get the newest task of robot. - Dictionary tempDic = new Dictionary(); - Dictionary OrderedTempDic = new Dictionary(); - - // Complete tempDic - foreach (ActionPlanModel plan in actionPlans) - { - DateTime d; - DateTime.TryParseExact(plan.Status, "ggyyyy$dd-MMM (dddd)", - System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out d); - tempDic.Add(plan, d); - } - - // Order a new dictionary - foreach (KeyValuePair pair in tempDic.OrderByDescending(p => p.Value)) + List relationNames = new List() { firstName, secondName }; + var relations = await _actionRepository.GetRelations(id, relationNames); + if (!relations.Any()) { - OrderedTempDic.Add(pair.Key, pair.Value); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found")); } - // Get last item which is the latest plan. - ActionPlanModel last = OrderedTempDic.Keys.First(); - - - - //List activePoliciesRecords = actionPlans.Select(p => new ActionPlanModel(p.Id, p.Name, p.Description)).ToList(); - return Ok(last); + return Ok(relations); } catch (Exception ex) { @@ -310,28 +300,29 @@ public async Task GetLatestActionPlanByRobotIdAsync(Guid robotId) #endregion - #region HistoricalActionPlan + + #region ActionPlan /// - /// Retrieves all HistoricalActionPlans + /// Retrieves all ActionPlans /// /// [HttpGet] - [Route("plan/historical", Name = "HistoricalActionPlanGetAll")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [Route("plan", Name = "ActionPlanGetAll")] + [ProducesResponseType(typeof(GetActionPlansResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetAllHistoricalActionPlansAsync() + public async Task GetAllActionPlansAsync() { try { - var plans = await _historicalActionPlanRepository.GetAllAsync(); + var plans = await _actionPlanRepository.GetAllAsync(); if (plans == null || plans.Any() == false) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No plans have been found.")); } - return Ok(plans); + return Ok(plans.ToActionPlansResponse()); } catch (Exception ex) { @@ -342,26 +333,26 @@ public async Task GetAllHistoricalActionPlansAsync() } /// - /// Retrieves an HistoricalActionPlan by id + /// Retrieves an ActionPlan by id /// /// /// [HttpGet] - [Route("plan/historical/{id:guid}", Name = "HistoricalActionPlanGetById")] - [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] + [Route("plan/{id:guid}", Name = "ActionPlanGetById")] + [ProducesResponseType(typeof(ActionPlanResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetHistoricalActionPlanByIdAsync(Guid id) + public async Task GetActionPlanByIdAsync(Guid id) { try { - var plan = await _historicalActionPlanRepository.GetByIdAsync(id); + var plan = await _actionPlanRepository.GetByIdAsync(id); if (plan == null) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Specified plan was not found.")); } - return Ok(plan); + return Ok(plan.ToActionPlanResponse()); } catch (Exception ex) { @@ -372,33 +363,27 @@ public async Task GetHistoricalActionPlanByIdAsync(Guid id) } /// - /// Creates new HistoricalActionPlan + /// Creates new ActionPlan /// - /// + /// /// [HttpPost] - [Route("plan/historical", Name = "HistoricalActionPlanAdd")] - [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] + [Route("plan", Name = "ActionPlanAdd")] + [ProducesResponseType(typeof(ActionPlanResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task AddHistoricalActionPlanAsync(HistoricalActionPlanModel historicalActionPlan) + public async Task AddActionPlanAsync(ActionPlanModel actionPlan) { try { - if (historicalActionPlan == null) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Historical plan cannot be null")); - } - - var plan = await _historicalActionPlanRepository.AddAsync(historicalActionPlan, - () => historicalActionPlan.Id); - if (plan == null) + if (actionPlan == null) { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, - "The specified historical plan has not been added.")); + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Plan cannot be null")); } - return Ok(plan); + var plan = await _actionPlanRepository.AddAsync(actionPlan, () => actionPlan.Id); + + return Ok(plan.ToActionPlanResponse()); } catch (Exception ex) { @@ -409,68 +394,67 @@ public async Task AddHistoricalActionPlanAsync(HistoricalActionPl } /// - /// Delete HistoricalActionPlan by id + /// Delete ActionPlan by id /// /// /// [HttpDelete] - [Route("plan/historical/{id}", Name = "HistoricalActionPlanDelete")] + [Route("plan/{id}", Name = "ActionPlanDelete")] [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task DeleteHistoricalActionPlanAsync(Guid id) + public async Task DeleteActionPlanAsync(Guid id) { try { - var deleted = await _historicalActionPlanRepository.DeleteByIdAsync(id); + var deleted = await _actionPlanRepository.DeleteByIdAsync(id); if (deleted == false) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, - "The specified historical plan has not been found.")); + "The specified plan has not been found.")); } return Ok(); } catch (Exception ex) { - int statusCode = (int)HttpStatusCode.InternalServerError; + var statusCode = (int)HttpStatusCode.InternalServerError; _logger.LogError(ex, "An error occurred:"); return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } } /// - /// Patches an existing HistoricalActionPlan by id + /// Patches an existing ActionPlan by id /// /// - /// + /// /// [HttpPut] - [Route("plan/historical/{id:guid}", Name = "HistoricalActionPlanPatch")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [Route("plan/{id:guid}", Name = "ActionPlanPatch")] + [ProducesResponseType(typeof(ActionPlanResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task PatchHistoricalActionPlanAsync(Guid id, - [FromBody] HistoricalActionPlanModel historicalActionPlan) + public async Task PatchActionPlanAsync(Guid id, [FromBody] ActionPlanModel actionPlan) { try { - if (historicalActionPlan == null || id == Guid.Empty) + if (actionPlan == null || id == Guid.Empty) { return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Id or updated object has not been specified")); } - var deleted = await _historicalActionPlanRepository.DeleteByIdAsync(id); + var deleted = await _actionPlanRepository.DeleteByIdAsync(id); if (deleted == false) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified plan has not been found.")); } - var updatedPlan = await _historicalActionPlanRepository.AddAsync(historicalActionPlan, () => id); - return Ok(updatedPlan); + var updatedPlan = await _actionPlanRepository.AddAsync(actionPlan, () => id); + return Ok(updatedPlan.ToActionPlanResponse()); } catch (Exception ex) { @@ -481,15 +465,14 @@ public async Task PatchHistoricalActionPlanAsync(Guid id, } /// - /// Get historical action plan given robot Id. + /// Get action plan given robot Id. /// - /// List [HttpGet] - [Route("plan/historical/robot/{robotId}", Name = "GetHistoricalActionPlanByRobotIdAsync")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [Route("plan/robot/{robotId}", Name = "GetActionPlanByRobotIdAsync")] + [ProducesResponseType(typeof(GetActionPlansResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetHistoricalActionPlanByRobotIdAsync(Guid robotId) + public async Task GetActionPlanByRobotIdAsync(Guid robotId) { try { @@ -499,14 +482,14 @@ public async Task GetHistoricalActionPlanByRobotIdAsync(Guid robo } // Get list of actionPlans from specific robotId. - List historicalActionPlans = - await _historicalActionPlanRepository.GetRobotActionPlans(robotId); - if (historicalActionPlans == null) + List actionPlans = await _actionPlanRepository.GetRobotActionPlans(robotId); + if (actionPlans == null) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); } - return Ok(historicalActionPlans); + //List activePoliciesRecords = actionPlans.Select(p => new ActionPlanModel(p.Id, p.Name, p.Description)).ToList(); + return Ok(actionPlans.ToActionPlansResponse()); } catch (Exception ex) { @@ -516,16 +499,16 @@ public async Task GetHistoricalActionPlanByRobotIdAsync(Guid robo } } + /// - /// Get latest historical action plan given robot Id. + /// Get latest action plan given robot Id. /// - /// List [HttpGet] - [Route("plan/historical/robot/{robotId}/latest", Name = "GetLatestHistoricalActionPlanByRobotIdAsync")] - [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] + [Route("plan/robot/{robotId}/latest", Name = "GetLatestActionPlanByRobotIdAsync")] + [ProducesResponseType(typeof(ActionPlanResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetLatestHistoricalActionPlanByRobotIdAsync(Guid robotId) + public async Task GetLatestActionPlanByRobotIdAsync(Guid robotId) { try { @@ -535,43 +518,41 @@ public async Task GetLatestHistoricalActionPlanByRobotIdAsync(Gui } // Get list of actionPlans from specific robotId. - List actionPlans = - await _historicalActionPlanRepository.GetRobotActionPlans(robotId); + var actionPlans = await _actionPlanRepository.GetRobotActionPlans(robotId); + + if (actionPlans == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); + } //Get the newest task of robot. - Dictionary tempDic = - new Dictionary(); - Dictionary OrderedTempDic = - new Dictionary(); + var tempDic = new Dictionary(); + var orderedTempDic = new Dictionary(); // Complete tempDic - foreach (HistoricalActionPlanModel plan in actionPlans) + foreach (var plan in actionPlans) { - DateTime d; DateTime.TryParseExact(plan.Status, "ggyyyy$dd-MMM (dddd)", - System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out d); + System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out var d); tempDic.Add(plan, d); } // Order a new dictionary - foreach (KeyValuePair pair in tempDic.OrderByDescending(p => p.Value)) + foreach (var pair in tempDic.OrderByDescending(p => p.Value)) { - OrderedTempDic.Add(pair.Key, pair.Value); + orderedTempDic.Add(pair.Key, pair.Value); } // Get last item which is the latest plan. - HistoricalActionPlanModel last = OrderedTempDic.Keys.First(); + var last = orderedTempDic.Keys.First(); - if (actionPlans == null) - { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); - } - return Ok(last); + //List activePoliciesRecords = actionPlans.Select(p => new ActionPlanModel(p.Id, p.Name, p.Description)).ToList(); + return Ok(last.ToActionPlanResponse()); } catch (Exception ex) { - int statusCode = (int)HttpStatusCode.InternalServerError; + var statusCode = (int)HttpStatusCode.InternalServerError; _logger.LogError(ex, "An error occurred:"); return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } @@ -579,123 +560,95 @@ public async Task GetLatestHistoricalActionPlanByRobotIdAsync(Gui #endregion - #region ActionRunning + #region HistoricalActionPlan /// - /// Get all the ActionRunningModels entities + /// Retrieves all HistoricalActionPlans /// - /// the list of ActionModel entities + /// [HttpGet] - [Route("running", Name = "ActionRunningGetAll")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [Route("plan/historical", Name = "HistoricalActionPlanGetAll")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task>> GetAllActionRunningAsync() + public async Task GetAllHistoricalActionPlansAsync() { try { - List models = await _actionRunningRepository.GetAllAsync(); - if (models.Any() == false) + var plans = await _historicalActionPlanRepository.GetAllAsync(); + if (plans == null || plans.Any() == false) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No actions were found.")); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No plans have been found.")); } - return Ok(models); + return Ok(plans); } catch (Exception ex) { - int statusCode = (int)HttpStatusCode.InternalServerError; _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + int statusCode = (int)HttpStatusCode.InternalServerError; + return StatusCode(statusCode, new ApiResponse(statusCode, ex.Message)); } } - //New end points for depends_on property for actions. - /// - /// Get an ActionRunningModel entity by id + /// Retrieves an HistoricalActionPlan by id /// /// - /// the ActionRunningModel entity for the specified id + /// [HttpGet] - [Route("running/{id:guid}", Name = "ActionRunningGetById")] - [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] + [Route("plan/historical/{id:guid}", Name = "HistoricalActionPlanGetById")] + [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetActionRunningByIdAsync(Guid id) + public async Task GetHistoricalActionPlanByIdAsync(Guid id) { try { - ActionRunningModel model = await _actionRunningRepository.GetByIdAsync(id); - if (model == null) + var plan = await _historicalActionPlanRepository.GetByIdAsync(id); + if (plan == null) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, - $"Action with id: '{id}' was not found.")); + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Specified plan was not found.")); } - return Ok(model); + return Ok(plan); } catch (Exception ex) { - int statusCode = (int)HttpStatusCode.InternalServerError; _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); + int statusCode = (int)HttpStatusCode.InternalServerError; + return StatusCode(statusCode, new ApiResponse(statusCode, ex.Message)); } } /// - /// Add a new ActionRunningModel entity + /// Creates new HistoricalActionPlan /// - /// - /// the newly created ActionModel entity + /// + /// [HttpPost] - [Route("running", Name = "ActionRunningAdd")] - [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] + [Route("plan/historical", Name = "HistoricalActionPlanAdd")] + [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task> AddActionRunningAsync([FromBody] ActionRunningModel model) + public async Task AddHistoricalActionPlanAsync(HistoricalActionPlanModel historicalActionPlan) { - if (model == null) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Parameters were not specified.")); - } - try { - model = await _actionRunningRepository.AddAsync(model); - } - catch (Exception ex) - { - int statusCode = (int)HttpStatusCode.InternalServerError; - _logger.LogError(ex, "An error occurred:"); - return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); - } - - return Ok(model); - } + if (historicalActionPlan == null) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Historical plan cannot be null")); + } - /// - /// Partially update an existing ActionRunningModel entity - /// - /// - /// - /// the modified ActionModel entity - [HttpPatch] - [Route("running/{id:guid}", Name = "ActionRunningPatch")] - [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] - [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task PatchActionRunningAsync([FromBody] ActionRunningModel patch, [FromRoute] Guid id) - { - try - { - ActionRunningModel model = await _actionRunningRepository.PatchActionAsync(id, patch); - if (model == null) + var plan = await _historicalActionPlanRepository.AddAsync(historicalActionPlan, + () => historicalActionPlan.Id); + if (plan == null) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object to be updated was not found.")); + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, + "The specified historical plan has not been added.")); } - return Ok(model); + return Ok(plan); } catch (Exception ex) { @@ -706,24 +659,24 @@ public async Task PatchActionRunningAsync([FromBody] ActionRunnin } /// - /// Delete an ActionRunningModel entity for the given id + /// Delete HistoricalActionPlan by id /// /// - /// no return + /// [HttpDelete] - [Route("running/{id}", Name = "ActionRunningDelete")] + [Route("plan/historical/{id}", Name = "HistoricalActionPlanDelete")] [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task DeleteActionRunningByIdAsync(Guid id) + public async Task DeleteHistoricalActionPlanAsync(Guid id) { try { - var deleted = await _actionRunningRepository.DeleteByIdAsync(id); + var deleted = await _historicalActionPlanRepository.DeleteByIdAsync(id); if (deleted == false) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, - "The specified Action has not been found.")); + "The specified historical plan has not been found.")); } return Ok(); @@ -737,30 +690,37 @@ public async Task DeleteActionRunningByIdAsync(Guid id) } /// - /// Creates a new relation between two models + /// Patches an existing HistoricalActionPlan by id /// - /// + /// + /// /// - [HttpPost] - [Route("running/AddRelation", Name = "ActionRunningAddRelation")] - [ProducesResponseType(typeof(RelationModel), (int)HttpStatusCode.OK)] + [HttpPut] + [Route("plan/historical/{id:guid}", Name = "HistoricalActionPlanPatch")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task> AddActionRunningRelationAsync([FromBody] RelationModel model) + public async Task PatchHistoricalActionPlanAsync(Guid id, + [FromBody] HistoricalActionPlanModel historicalActionPlan) { - if (model == null) - { - return BadRequest("Parameters were not specified."); - } - try { - bool isValid = await _actionRunningRepository.AddRelationAsync(model); - if (!isValid) + if (historicalActionPlan == null || id == Guid.Empty) { - return StatusCode((int)HttpStatusCode.InternalServerError, - new ApiResponse((int)HttpStatusCode.InternalServerError, "The relation was not created")); + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, + "Id or updated object has not been specified")); + } + + var deleted = await _historicalActionPlanRepository.DeleteByIdAsync(id); + if (deleted == false) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, + "The specified plan has not been found.")); } + + var updatedPlan = await _historicalActionPlanRepository.AddAsync(historicalActionPlan, () => id); + return Ok(updatedPlan); } catch (Exception ex) { @@ -768,43 +728,35 @@ public async Task> AddActionRunningRelationAsync([Fr _logger.LogError(ex, "An error occurred:"); return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } - - return Ok(model); } /// - /// Retrieves a single relation by name + /// Get historical action plan given robot Id. /// - /// - /// - /// + /// List [HttpGet] - [Route("running/relation/{name}", Name = "ActionRunningGetRelationByName")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [Route("plan/historical/robot/{robotId}", Name = "GetHistoricalActionPlanByRobotIdAsync")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task - GetActionRunningRelationAsync(Guid id, string name) //Guid of node and name of relationship + public async Task GetHistoricalActionPlanByRobotIdAsync(Guid robotId) { - if (string.IsNullOrWhiteSpace(name)) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); - } - - if (id == Guid.Empty) - { - return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Relation name not specified")); - } - try { - var relations = await _actionRunningRepository.GetRelation(id, name); - if (!relations.Any()) + if (robotId == Guid.Empty) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found.")); + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Robot id has not been specified")); } - return Ok(relations); + // Get list of actionPlans from specific robotId. + List historicalActionPlans = + await _historicalActionPlanRepository.GetRobotActionPlans(robotId); + if (historicalActionPlans == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); + } + + return Ok(historicalActionPlans); } catch (Exception ex) { @@ -815,29 +767,57 @@ public async Task } /// - /// Retrieves two relations by their names + /// Get latest historical action plan given robot Id. /// - /// - /// - /// - /// + /// List [HttpGet] - [Route("running/relations/{firstName}/{secondName}", Name = "ActionRunningGetRelationsByName")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [Route("plan/historical/robot/{robotId}/latest", Name = "GetLatestHistoricalActionPlanByRobotIdAsync")] + [ProducesResponseType(typeof(HistoricalActionPlanModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetActionRunningRelationsAsync(Guid id, string firstName, string secondName) + public async Task GetLatestHistoricalActionPlanByRobotIdAsync(Guid robotId) { try { - List relationNames = new List() { firstName, secondName }; - var relations = await _actionRunningRepository.GetRelations(id, relationNames); - if (!relations.Any()) + if (robotId == Guid.Empty) { - return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found")); + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Robot id has not been specified")); } - return Ok(relations); + // Get list of actionPlans from specific robotId. + List actionPlans = + await _historicalActionPlanRepository.GetRobotActionPlans(robotId); + + //Get the newest task of robot. + Dictionary tempDic = + new Dictionary(); + Dictionary OrderedTempDic = + new Dictionary(); + + // Complete tempDic + foreach (HistoricalActionPlanModel plan in actionPlans) + { + DateTime d; + DateTime.TryParseExact(plan.Status, "ggyyyy$dd-MMM (dddd)", + System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out d); + tempDic.Add(plan, d); + } + + // Order a new dictionary + foreach (KeyValuePair pair in tempDic.OrderByDescending(p => p.Value)) + { + OrderedTempDic.Add(pair.Key, pair.Value); + } + + // Get last item which is the latest plan. + HistoricalActionPlanModel last = OrderedTempDic.Keys.First(); + + if (actionPlans == null) + { + return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object was not found.")); + } + + return Ok(last); } catch (Exception ex) { @@ -849,26 +829,28 @@ public async Task GetActionRunningRelationsAsync(Guid id, string #endregion + #region ActionRunning /// - /// Get all the actions + /// Get all the ActionRunningModels entities /// /// the list of ActionModel entities - [HttpGet(Name = "ActionGetAll")] - [ProducesResponseType(typeof(GetActionsResponse), (int)HttpStatusCode.OK)] + [HttpGet] + [Route("running", Name = "ActionRunningGetAll")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetAllAsync() + public async Task>> GetAllActionRunningAsync() { try { - List models = await _actionRepository.GetAllAsync(); + List models = await _actionRunningRepository.GetAllAsync(); if (models.Any() == false) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No actions were found.")); } - return Ok(models.ToActionsResponse()); + return Ok(models); } catch (Exception ex) { @@ -878,28 +860,30 @@ public async Task GetAllAsync() } } + //New end points for depends_on property for actions. + /// - /// Get an action entity by id + /// Get an ActionRunningModel entity by id /// /// - /// the ActionModel entity for the specified id + /// the ActionRunningModel entity for the specified id [HttpGet] - [Route("{id}", Name = "ActionGetById")] - [ProducesResponseType(typeof(ActionResponse), (int)HttpStatusCode.OK)] + [Route("running/{id:guid}", Name = "ActionRunningGetById")] + [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetByIdAsync(Guid id) + public async Task GetActionRunningByIdAsync(Guid id) { try { - ActionModel model = await _actionService.GetByIdAsync(id); + ActionRunningModel model = await _actionRunningRepository.GetByIdAsync(id); if (model == null) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, $"Action with id: '{id}' was not found.")); } - return Ok(model.ToActionResponse()); + return Ok(model); } catch (Exception ex) { @@ -910,20 +894,25 @@ public async Task GetByIdAsync(Guid id) } /// - /// Add a new action entity + /// Add a new ActionRunningModel entity /// - /// + /// /// the newly created ActionModel entity - [HttpPost(Name = "ActionAdd")] - [ProducesResponseType(typeof(ActionResponse), (int)HttpStatusCode.OK)] + [HttpPost] + [Route("running", Name = "ActionRunningAdd")] + [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task AddAsync([FromBody] ActionRequest request) + public async Task> AddActionRunningAsync([FromBody] ActionRunningModel model) { + if (model == null) + { + return BadRequest(new ApiResponse((int)HttpStatusCode.BadRequest, "Parameters were not specified.")); + } + try { - var action = await _actionService.AddAsync(request.ToAction()); - return Ok(action.ToActionResponse()); + model = await _actionRunningRepository.AddAsync(model); } catch (Exception ex) { @@ -931,32 +920,32 @@ public async Task AddAsync([FromBody] ActionRequest request) _logger.LogError(ex, "An error occurred:"); return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } + + return Ok(model); } /// - /// Update an existing action entity + /// Partially update an existing ActionRunningModel entity /// + /// + /// /// the modified ActionModel entity - [HttpPut] - [Route("{id}", Name = "ActionUpdate")] - [ProducesResponseType(typeof(ActionResponse), (int)HttpStatusCode.OK)] + [HttpPatch] + [Route("running/{id:guid}", Name = "ActionRunningPatch")] + [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task UpdateActionAsync([FromMultiSource] UpdateActionRequest request) + public async Task PatchActionRunningAsync([FromBody] ActionRunningModel patch, [FromRoute] Guid id) { try { - var existingAction = await _actionService.GetByIdAsync(request.Id); - if (existingAction is null) + ActionRunningModel model = await _actionRunningRepository.PatchActionAsync(id, patch); + if (model == null) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object to be updated was not found.")); } - var action = request.ToAction(); - await _actionService.UpdateAsync(action); - - var response = action.ToActionResponse(); - return Ok(response); + return Ok(model); } catch (Exception ex) { @@ -967,27 +956,26 @@ public async Task UpdateActionAsync([FromMultiSource] UpdateActio } /// - /// Delete an action entity with the given id + /// Delete an ActionRunningModel entity for the given id /// /// /// no return [HttpDelete] - [Route("{id}", Name = "ActionDelete")] + [Route("running/{id}", Name = "ActionRunningDelete")] [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task DeleteByIdAsync(Guid id) + public async Task DeleteActionRunningByIdAsync(Guid id) { try { - var exists = await _actionService.GetByIdAsync(id); - if (exists is null) + var deleted = await _actionRunningRepository.DeleteByIdAsync(id); + if (deleted == false) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "The specified Action has not been found.")); } - await _actionService.DeleteAsync(id); return Ok(); } catch (Exception ex) @@ -1004,11 +992,11 @@ public async Task DeleteByIdAsync(Guid id) /// /// [HttpPost] - [Route("AddRelation", Name = "ActionAddRelation")] + [Route("running/AddRelation", Name = "ActionRunningAddRelation")] [ProducesResponseType(typeof(RelationModel), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task> AddRelationAsync([FromBody] RelationModel model) + public async Task> AddActionRunningRelationAsync([FromBody] RelationModel model) { if (model == null) { @@ -1017,7 +1005,7 @@ public async Task> AddRelationAsync([FromBody] Relat try { - bool isValid = await _actionRepository.AddRelationAsync(model); + bool isValid = await _actionRunningRepository.AddRelationAsync(model); if (!isValid) { return StatusCode((int)HttpStatusCode.InternalServerError, @@ -1041,11 +1029,12 @@ public async Task> AddRelationAsync([FromBody] Relat /// /// [HttpGet] - [Route("relation/{name}", Name = "ActionGetRelationByName")] + [Route("running/relation/{name}", Name = "ActionRunningGetRelationByName")] [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetRelationAsync(Guid id, string name) //Guid of node and name of relationship + public async Task + GetActionRunningRelationAsync(Guid id, string name) //Guid of node and name of relationship { if (string.IsNullOrWhiteSpace(name)) { @@ -1059,7 +1048,7 @@ public async Task GetRelationAsync(Guid id, string name) //Guid o try { - var relations = await _actionRepository.GetRelation(id, name); + var relations = await _actionRunningRepository.GetRelation(id, name); if (!relations.Any()) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found.")); @@ -1083,16 +1072,16 @@ public async Task GetRelationAsync(Guid id, string name) //Guid o /// /// [HttpGet] - [Route("relations/{firstName}/{secondName}", Name = "ActionGetRelationsByName")] + [Route("running/relations/{firstName}/{secondName}", Name = "ActionRunningGetRelationsByName")] [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] - public async Task GetRelationsAsync(Guid id, string firstName, string secondName) + public async Task GetActionRunningRelationsAsync(Guid id, string firstName, string secondName) { try { List relationNames = new List() { firstName, secondName }; - var relations = await _actionRepository.GetRelations(id, relationNames); + var relations = await _actionRunningRepository.GetRelations(id, relationNames); if (!relations.Any()) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Relations were not found")); @@ -1107,4 +1096,6 @@ public async Task GetRelationsAsync(Guid id, string firstName, st return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } } + + #endregion } \ No newline at end of file diff --git a/src/RedisInterface/Services/ActionRunningService.cs b/src/RedisInterface/Services/ActionRunningService.cs index fa92a5e8..2d668d9e 100644 --- a/src/RedisInterface/Services/ActionRunningService.cs +++ b/src/RedisInterface/Services/ActionRunningService.cs @@ -1,5 +1,4 @@ using Middleware.DataAccess.Repositories.Abstract; -using Middleware.DataAccess.Repositories.Redis; using Middleware.Models.Domain; using Middleware.RedisInterface.Services.Abstract; From 0b061a4b3c8e08738be31e89010c837354b74125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Bratu=C5=9B?= <34897359+Artonus@users.noreply.github.com> Date: Fri, 14 Apr 2023 15:43:32 +0200 Subject: [PATCH 31/31] Running action responses --- .../Mappings/DomainToApiContractMapper.cs | 23 ++++++++++++++++++- .../Responses/GetRunningActionsResponse.cs | 6 +++++ .../Controllers/ActionController.cs | 18 +++++++-------- 3 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 src/RedisInterface.Contracts/Responses/GetRunningActionsResponse.cs diff --git a/src/RedisInterface.Contracts/Mappings/DomainToApiContractMapper.cs b/src/RedisInterface.Contracts/Mappings/DomainToApiContractMapper.cs index 624bd5fa..96e31995 100644 --- a/src/RedisInterface.Contracts/Mappings/DomainToApiContractMapper.cs +++ b/src/RedisInterface.Contracts/Mappings/DomainToApiContractMapper.cs @@ -262,11 +262,32 @@ public static RunningActionResponse ToRunningActionResponse(this ActionRunningMo Placement = x.Placement, PlacementType = x.PlacementType.ToString(), Order = x.Order, - Services = x.Services.ToInstanceRunningList(), + Services = x.Services?.ToInstanceRunningList() ?? new List(), Tags = x.Tags }; } + public static GetRunningActionsResponse ToRunningActionsResponse(this IEnumerable actions) + { + return new GetRunningActionsResponse() + { + RunningActions = actions.Select(x => new RunningActionResponse() + { + Id = x.Id, + ActionId = x.ActionId, + ActionPlanId = x.ActionPlanId, + Name = x.Name, + ActionPriority = x.ActionPriority, + ActionStatus = x.ActionStatus, + Placement = x.Placement, + PlacementType = x.PlacementType.ToString(), + Order = x.Order, + Services = x.Services?.ToInstanceRunningList() ?? new List(), + Tags = x.Tags + }) + }; + } + public static GetActionPlansResponse ToActionPlansResponse(this IEnumerable plans) { return new GetActionPlansResponse() diff --git a/src/RedisInterface.Contracts/Responses/GetRunningActionsResponse.cs b/src/RedisInterface.Contracts/Responses/GetRunningActionsResponse.cs new file mode 100644 index 00000000..4df92bdb --- /dev/null +++ b/src/RedisInterface.Contracts/Responses/GetRunningActionsResponse.cs @@ -0,0 +1,6 @@ +namespace Middleware.RedisInterface.Contracts.Responses; + +public class GetRunningActionsResponse +{ + public IEnumerable RunningActions { get; set; } = Enumerable.Empty(); +} \ No newline at end of file diff --git a/src/RedisInterface/Controllers/ActionController.cs b/src/RedisInterface/Controllers/ActionController.cs index 5146ffa2..11e1b9b2 100644 --- a/src/RedisInterface/Controllers/ActionController.cs +++ b/src/RedisInterface/Controllers/ActionController.cs @@ -837,7 +837,7 @@ public async Task GetLatestHistoricalActionPlanByRobotIdAsync(Gui /// the list of ActionModel entities [HttpGet] [Route("running", Name = "ActionRunningGetAll")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(GetRunningActionsResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] public async Task>> GetAllActionRunningAsync() @@ -845,12 +845,12 @@ public async Task>> GetAllActionRunningAsy try { List models = await _actionRunningRepository.GetAllAsync(); - if (models.Any() == false) + if (models != null && models.Any() == false) { return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "No actions were found.")); } - return Ok(models); + return Ok(models.ToRunningActionsResponse()); } catch (Exception ex) { @@ -869,7 +869,7 @@ public async Task>> GetAllActionRunningAsy /// the ActionRunningModel entity for the specified id [HttpGet] [Route("running/{id:guid}", Name = "ActionRunningGetById")] - [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(RunningActionResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] public async Task GetActionRunningByIdAsync(Guid id) @@ -883,7 +883,7 @@ public async Task GetActionRunningByIdAsync(Guid id) $"Action with id: '{id}' was not found.")); } - return Ok(model); + return Ok(model.ToRunningActionResponse()); } catch (Exception ex) { @@ -900,7 +900,7 @@ public async Task GetActionRunningByIdAsync(Guid id) /// the newly created ActionModel entity [HttpPost] [Route("running", Name = "ActionRunningAdd")] - [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(RunningActionResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] public async Task> AddActionRunningAsync([FromBody] ActionRunningModel model) @@ -921,7 +921,7 @@ public async Task> AddActionRunningAsync([FromB return StatusCode(statusCode, new ApiResponse(statusCode, $"An error has occurred: {ex.Message}")); } - return Ok(model); + return Ok(model.ToRunningActionResponse()); } /// @@ -932,7 +932,7 @@ public async Task> AddActionRunningAsync([FromB /// the modified ActionModel entity [HttpPatch] [Route("running/{id:guid}", Name = "ActionRunningPatch")] - [ProducesResponseType(typeof(ActionRunningModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(RunningActionResponse), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ApiResponse), (int)HttpStatusCode.InternalServerError)] public async Task PatchActionRunningAsync([FromBody] ActionRunningModel patch, [FromRoute] Guid id) @@ -945,7 +945,7 @@ public async Task PatchActionRunningAsync([FromBody] ActionRunnin return NotFound(new ApiResponse((int)HttpStatusCode.NotFound, "Object to be updated was not found.")); } - return Ok(model); + return Ok(model.ToRunningActionResponse()); } catch (Exception ex) {