diff --git a/src/TeamCloud.API/Controllers/DeploymentScopesController.cs b/src/TeamCloud.API/Controllers/DeploymentScopesController.cs index 85ef46f5..66e025c9 100644 --- a/src/TeamCloud.API/Controllers/DeploymentScopesController.cs +++ b/src/TeamCloud.API/Controllers/DeploymentScopesController.cs @@ -95,6 +95,15 @@ public Task Post([FromBody] DeploymentScopeDefinition deploymentS .BadRequest(definitionValidationResult) .ToActionResult(); + var nameExists = await deploymentScopeRepository + .NameExistsAsync(organization.Id, deploymentScopeDefinition.Slug) + .ConfigureAwait(false); + + if (nameExists) + return ErrorResult + .Conflict($"A Deployment Scope with name '{deploymentScopeDefinition.DisplayName}' already exists. Deployment Scope names must be unique. Please try your request again with a unique name.") + .ToActionResult(); + var deploymentScope = new DeploymentScope { Id = Guid.NewGuid().ToString(), diff --git a/src/TeamCloud.API/Controllers/OrganizationsController.cs b/src/TeamCloud.API/Controllers/OrganizationsController.cs index 1e1d0b51..4d4219e5 100644 --- a/src/TeamCloud.API/Controllers/OrganizationsController.cs +++ b/src/TeamCloud.API/Controllers/OrganizationsController.cs @@ -102,16 +102,16 @@ public async Task Post([FromBody] OrganizationDefinition organiza .BadRequest(validationResult) .ToActionResult(); - var organization = await organizationRepository - .GetAsync(UserService.CurrentUserTenant, organizationDefinition.Slug) + var nameExists = await organizationRepository + .NameExistsAsync(UserService.CurrentUserTenant, organizationDefinition.Slug) .ConfigureAwait(false); - if (organization is not null) + if (nameExists) return ErrorResult .Conflict($"The Organication '{organizationDefinition.Slug}' already exists. Please try your request again with a unique Organization Name or Id.") .ToActionResult(); - organization = new Organization + var organization = new Organization { Id = Guid.NewGuid().ToString(), Tenant = UserService.CurrentUserTenant, diff --git a/src/TeamCloud.API/Controllers/ProjectController.cs b/src/TeamCloud.API/Controllers/ProjectController.cs index ffaa3e0d..8d943f80 100644 --- a/src/TeamCloud.API/Controllers/ProjectController.cs +++ b/src/TeamCloud.API/Controllers/ProjectController.cs @@ -131,7 +131,7 @@ public Task Post([FromBody] ProjectDefinition projectDefinition) .ToActionResult(); var nameExists = await projectRepository - .NameExistsAsync(organization.Id, projectDefinition.DisplayName) + .NameExistsAsync(organization.Id, projectDefinition.Slug) .ConfigureAwait(false); if (nameExists) diff --git a/src/TeamCloud.Data/CosmosDb/CosmosDbDeploymentScopeRepository.cs b/src/TeamCloud.Data/CosmosDb/CosmosDbDeploymentScopeRepository.cs index a0804572..63645815 100644 --- a/src/TeamCloud.Data/CosmosDb/CosmosDbDeploymentScopeRepository.cs +++ b/src/TeamCloud.Data/CosmosDb/CosmosDbDeploymentScopeRepository.cs @@ -56,6 +56,14 @@ private void RemoveCachedIds(DeploymentScope deploymentScope) cache.Remove($"{deploymentScope.Organization}_{deploymentScope.Id}"); } + public async Task NameExistsAsync(string organization, string name) + { + var project = await ResolveIdAsync(organization, name) + .ConfigureAwait(false); + + return project is not null; + } + public override async Task AddAsync(DeploymentScope deploymentScope) { if (deploymentScope is null) diff --git a/src/TeamCloud.Data/CosmosDb/CosmosDbOrganizationRepository.cs b/src/TeamCloud.Data/CosmosDb/CosmosDbOrganizationRepository.cs index 3d8e7e47..46f0cb7b 100644 --- a/src/TeamCloud.Data/CosmosDb/CosmosDbOrganizationRepository.cs +++ b/src/TeamCloud.Data/CosmosDb/CosmosDbOrganizationRepository.cs @@ -50,6 +50,14 @@ public async Task ResolveIdAsync(string tenant, string identifier) return id; } + public async Task NameExistsAsync(string organization, string name) + { + var project = await ResolveIdAsync(organization, name) + .ConfigureAwait(false); + + return project is not null; + } + public override async Task AddAsync(Organization organization) { if (organization is null) diff --git a/src/TeamCloud.Data/IDeploymentScopeRepository.cs b/src/TeamCloud.Data/IDeploymentScopeRepository.cs index 27a59945..7736654e 100644 --- a/src/TeamCloud.Data/IDeploymentScopeRepository.cs +++ b/src/TeamCloud.Data/IDeploymentScopeRepository.cs @@ -10,6 +10,9 @@ namespace TeamCloud.Data; public interface IDeploymentScopeRepository : IDocumentRepository { - Task ResolveIdAsync(string tenant, string identifier); + Task NameExistsAsync(string organization, string name); + + Task ResolveIdAsync(string organization, string identifier); + Task GetDefaultAsync(string organization); } diff --git a/src/TeamCloud.Data/IOrganizationRepository.cs b/src/TeamCloud.Data/IOrganizationRepository.cs index 90de60c5..a223b567 100644 --- a/src/TeamCloud.Data/IOrganizationRepository.cs +++ b/src/TeamCloud.Data/IOrganizationRepository.cs @@ -11,6 +11,8 @@ namespace TeamCloud.Data; public interface IOrganizationRepository : IDocumentRepository { + Task NameExistsAsync(string organization, string name); + Task ResolveIdAsync(string tenant, string identifier); IAsyncEnumerable ListAsync(string tenant, IEnumerable identifiers); diff --git a/src/TeamCloud.Data/IProjectTemplateRepository.cs b/src/TeamCloud.Data/IProjectTemplateRepository.cs index b13722d4..fd345a06 100644 --- a/src/TeamCloud.Data/IProjectTemplateRepository.cs +++ b/src/TeamCloud.Data/IProjectTemplateRepository.cs @@ -10,5 +10,7 @@ namespace TeamCloud.Data; public interface IProjectTemplateRepository : IDocumentRepository { + // Task NameExistsAsync(string organization, string name); + Task GetDefaultAsync(string organization); } diff --git a/src/TeamCloud.Model/Data/Component.cs b/src/TeamCloud.Model/Data/Component.cs index 25dafac2..6df5807b 100644 --- a/src/TeamCloud.Model/Data/Component.cs +++ b/src/TeamCloud.Model/Data/Component.cs @@ -50,6 +50,7 @@ public sealed class Component : ContainerDocument, ISoftDelete, IProjectContext, /// /// Gets or sets the component's display name. /// + [UniqueKey] public string DisplayName { get; set; } /// diff --git a/src/TeamCloud.Model/Data/DeploymentScope.cs b/src/TeamCloud.Model/Data/DeploymentScope.cs index f78e53f7..b9714b14 100644 --- a/src/TeamCloud.Model/Data/DeploymentScope.cs +++ b/src/TeamCloud.Model/Data/DeploymentScope.cs @@ -20,6 +20,7 @@ public sealed class DeploymentScope : ContainerDocument, IOrganizationContext, I [JsonProperty(Required = Required.Always)] public string Organization { get; set; } + [UniqueKey] [JsonProperty(Required = Required.Always)] public string DisplayName { get; set; } diff --git a/src/TeamCloud.Model/Data/Organization.cs b/src/TeamCloud.Model/Data/Organization.cs index 69921b7c..d0411b82 100644 --- a/src/TeamCloud.Model/Data/Organization.cs +++ b/src/TeamCloud.Model/Data/Organization.cs @@ -30,6 +30,7 @@ public string Slug set => slug = value; } + [UniqueKey] [JsonProperty(Required = Required.Always)] public string DisplayName { get; set; } diff --git a/src/TeamCloud.Model/Data/ProjectTemplate.cs b/src/TeamCloud.Model/Data/ProjectTemplate.cs index 1dfca8ee..204715e0 100644 --- a/src/TeamCloud.Model/Data/ProjectTemplate.cs +++ b/src/TeamCloud.Model/Data/ProjectTemplate.cs @@ -31,6 +31,7 @@ public string Slug public string Name { get; set; } + [UniqueKey] [JsonProperty(Required = Required.Always)] public string DisplayName { get; set; }