Skip to content

Commit 3261f1d

Browse files
committed
Add tenant
1 parent 9db6ff0 commit 3261f1d

File tree

11 files changed

+124
-104
lines changed

11 files changed

+124
-104
lines changed

scripts/mongo-create-index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
db.tf_state.createIndex({ "name": 1 });
2-
db.tf_state_revisions.createIndex({ "name": 1 });
3-
db.tf_state_lock.createIndex({ "name": 1 });
1+
db.tf_state.createIndex({ "tenant": 1, "name": 1 });
2+
//TODO: use tf_state_revisions
3+
db.tf_state_lock.createIndex({ "tenant": 1, "name": 1 });

src/Domain/Models/StateLockModel.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ public class StateLockModel
1414
[JsonPropertyName("ID")]
1515
public string Id { get; set; } = string.Empty;
1616

17+
/// <summary>
18+
///
19+
/// </summary>
20+
public string Tenant { get; set; } = string.Empty;
21+
1722
/// <summary>
1823
/// Name of the Terraform state.
1924
/// </summary>

src/Domain/Models/StateModel.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public class StateModel
1010
[BsonRepresentation(BsonType.ObjectId)]
1111
public string Id { get; set; } = string.Empty;
1212

13+
public string Tenant { get; set; } = string.Empty;
14+
1315
public string Name { get; set; } = string.Empty;
1416

1517
public DateTime CreatedAt { get; set; } = DateTime.MinValue;

src/Domain/Repositories/IStateLockRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Devpro.TerraformBackend.Domain.Repositories;
66

77
public interface IStateLockRepository
88
{
9-
Task<StateLockModel> FindOneAsync(string name);
9+
Task<StateLockModel> FindOneAsync(string tenant, string name);
1010

1111
Task<StateLockModel> CreateAsync(StateLockModel input);
1212

src/Domain/Repositories/IStateRepository.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ namespace Devpro.TerraformBackend.Domain.Repositories;
44

55
public interface IStateRepository
66
{
7-
Task<string> FindOneAsync(string name);
7+
Task<string> FindOneAsync(string tenant, string name);
88

9-
Task CreateAsync(string name, string jsonInput);
9+
Task CreateAsync(string tenant, string name, string jsonInput);
1010

11-
Task<bool> DeleteAsync(string name);
11+
Task<bool> DeleteAsync(string tenant, string name);
1212
}
Lines changed: 36 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,46 @@
1-
using System.Collections.Generic;
2-
using System.Threading.Tasks;
1+
using System.Threading.Tasks;
32
using Devpro.Common.MongoDb;
43
using Devpro.TerraformBackend.Domain.Models;
54
using Devpro.TerraformBackend.Domain.Repositories;
65
using Microsoft.Extensions.Logging;
76
using MongoDB.Driver;
87

9-
namespace Devpro.TerraformBackend.Infrastructure.MongoDb.Repositories
8+
namespace Devpro.TerraformBackend.Infrastructure.MongoDb.Repositories;
9+
10+
public class StateLockRepository : RepositoryBase, IStateLockRepository
1011
{
11-
public class StateLockRepository : RepositoryBase, IStateLockRepository
12+
private readonly IMongoCollection<StateLockModel> _modelCollection;
13+
14+
public StateLockRepository(IMongoClientFactory mongoClientFactory, ILogger<StateLockRepository> logger, MongoDbConfiguration configuration)
15+
: base(mongoClientFactory, logger, configuration)
16+
{
17+
_modelCollection = GetCollection<StateLockModel>();
18+
}
19+
20+
protected override string CollectionName => "tf_state_lock";
21+
22+
public async Task<StateLockModel> FindOneAsync(string tenant, string name)
23+
{
24+
return await _modelCollection.Find(x => x.Name == name).FirstOrDefaultAsync();
25+
}
26+
27+
//public async Task<List<StateLockModel>> FindAllAsync()
28+
//{
29+
// //var documents = await _bsonCollection.Find(new BsonDocument()).ToListAsync();
30+
// //return documents.Select(x => BsonSerializer.Deserialize<StateLockModel>(x)).ToList();
31+
32+
// return await _modelCollection.Find(_ => true).ToListAsync();
33+
//}
34+
35+
public async Task<StateLockModel> CreateAsync(StateLockModel input)
36+
{
37+
await _modelCollection.InsertOneAsync(input);
38+
return input;
39+
}
40+
41+
public async Task<bool> DeleteAsync(StateLockModel input)
1242
{
13-
//private readonly IMongoCollection<BsonDocument> _bsonCollection;
14-
private readonly IMongoCollection<StateLockModel> _modelCollection;
15-
16-
public StateLockRepository(IMongoClientFactory mongoClientFactory, ILogger<StateLockRepository> logger, MongoDbConfiguration configuration)
17-
: base(mongoClientFactory, logger, configuration)
18-
{
19-
//_bsonCollection = GetCollection<BsonDocument>();
20-
_modelCollection = GetCollection<StateLockModel>();
21-
}
22-
23-
protected override string CollectionName => "tf_state_lock";
24-
25-
public async Task<StateLockModel> FindOneAsync(string name)
26-
{
27-
return await _modelCollection.Find(x => x.Name == name).FirstOrDefaultAsync();
28-
}
29-
30-
public async Task<List<StateLockModel>> FindAllAsync()
31-
{
32-
//var documents = await _bsonCollection.Find(new BsonDocument()).ToListAsync();
33-
//return documents.Select(x => BsonSerializer.Deserialize<StateLockModel>(x)).ToList();
34-
35-
return await _modelCollection.Find(_ => true).ToListAsync();
36-
}
37-
38-
public async Task<StateLockModel> CreateAsync(StateLockModel input)
39-
{
40-
await _modelCollection.InsertOneAsync(input);
41-
return input;
42-
}
43-
44-
public async Task<bool> DeleteAsync(StateLockModel input)
45-
{
46-
var result = await _modelCollection.DeleteOneAsync(x => x.Id == input.Id && x.Name == input.Name);
47-
return result.DeletedCount > 0;
48-
}
43+
var result = await _modelCollection.DeleteOneAsync(x => x.Tenant == input.Tenant && x.Name == input.Name && x.Id == input.Id);
44+
return result.DeletedCount > 0;
4945
}
5046
}
Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,42 @@
11
using System;
22
using System.Threading.Tasks;
33
using Devpro.Common.MongoDb;
4+
using Devpro.TerraformBackend.Domain.Models;
45
using Devpro.TerraformBackend.Domain.Repositories;
56
using Microsoft.Extensions.Logging;
67
using MongoDB.Bson;
78
using MongoDB.Driver;
89

910
namespace Devpro.TerraformBackend.Infrastructure.MongoDb.Repositories;
1011

11-
public class StateRepository(IMongoClientFactory mongoClientFactory, ILogger<StateRepository> logger, MongoDbConfiguration configuration)
12-
: RepositoryBase(mongoClientFactory, logger, configuration), IStateRepository
12+
public class StateRepository : RepositoryBase, IStateRepository
1313
{
14+
private readonly IMongoCollection<BsonDocument> _bsonCollection;
15+
16+
public StateRepository(IMongoClientFactory mongoClientFactory, ILogger<StateLockRepository> logger, MongoDbConfiguration configuration)
17+
: base(mongoClientFactory, logger, configuration)
18+
{
19+
_bsonCollection = GetCollection<BsonDocument>();
20+
}
21+
1422
protected override string CollectionName => "tf_state";
1523

16-
public async Task CreateAsync(string name, string jsonInput)
24+
public async Task CreateAsync(string tenant, string name, string jsonInput)
1725
{
18-
//TODO: makes it the latest value
1926
var document = new BsonDocument
2027
{
2128
["_id"] = new BsonObjectId(ObjectId.GenerateNewId()),
29+
["tenant"] = tenant,
2230
["name"] = name,
2331
["createdAt"] = new BsonDateTime(DateTime.UtcNow), // stored as ToString("yyyy-MM-ddTHH:mm:ss.fff+00:00")
2432
["value"] = BsonDocument.Parse(jsonInput)
2533
};
26-
var collection = GetCollection<BsonDocument>();
27-
await collection.InsertOneAsync(document);
34+
await _bsonCollection.InsertOneAsync(document);
2835
}
2936

30-
public async Task<string> FindOneAsync(string name)
37+
public async Task<string> FindOneAsync(string tenant, string name)
3138
{
32-
var collection = GetCollection<BsonDocument>();
33-
var document = await collection.Find(new BsonDocument("name", name))
39+
var document = await _bsonCollection.Find(GetFilter(tenant, name))
3440
.Sort(Builders<BsonDocument>.Sort.Descending("createdAt"))
3541
.FirstOrDefaultAsync();
3642
if (document == null)
@@ -41,10 +47,18 @@ public async Task<string> FindOneAsync(string name)
4147
return document["value"].ToJson();
4248
}
4349

44-
public async Task<bool> DeleteAsync(string name)
50+
public async Task<bool> DeleteAsync(string tenant, string name)
4551
{
46-
var collection = GetCollection<BsonDocument>();
47-
var deleteResult = await collection.DeleteOneAsync(new BsonDocument("name", name));
52+
var deleteResult = await _bsonCollection.DeleteOneAsync(GetFilter(tenant, name));
4853
return deleteResult.DeletedCount > 0;
4954
}
55+
56+
private static BsonDocument GetFilter(string tenant, string name)
57+
{
58+
return new BsonDocument
59+
{
60+
{ "tenant", tenant },
61+
{ "name", name }
62+
};
63+
}
5064
}
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
using Devpro.Common.MongoDb;
22

3-
namespace Devpro.TerraformBackend.WebApi
3+
namespace Devpro.TerraformBackend.WebApi;
4+
5+
public class ApplicationConfiguration(IConfigurationRoot configurationRoot)
6+
: WebApiConfiguration(configurationRoot)
47
{
5-
public class ApplicationConfiguration(IConfigurationRoot configurationRoot) : WebApiConfiguration(configurationRoot)
6-
{
7-
public MongoDbConfiguration MongoDbConfiguration =>
8-
new()
9-
{
10-
ConnectionString = ConfigurationRoot.GetConnectionString(TryGetSection("MongoDb:ConnectionStringName")?.Get<string>() ?? string.Empty) ?? string.Empty,
11-
DatabaseName = TryGetSection("MongoDb:DatabaseName").Get<string>() ?? string.Empty
12-
};
13-
}
8+
public MongoDbConfiguration MongoDbConfiguration =>
9+
new()
10+
{
11+
ConnectionString = ConfigurationRoot.GetConnectionString(TryGetSection("MongoDb:ConnectionStringName")?.Get<string>() ?? string.Empty) ?? string.Empty,
12+
DatabaseName = TryGetSection("MongoDb:DatabaseName").Get<string>() ?? string.Empty
13+
};
1414
}

src/WebApi/Controllers/StateController.cs

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,23 @@ namespace Devpro.TerraformBackend.WebApi.Controllers;
88

99
[Authorize]
1010
[ApiController]
11-
[Route("state")]
11+
[Route("{tenant}/state")]
1212
public class StateController(IStateRepository stateRepository, IStateLockRepository stateLockRepository) : ControllerBase
1313
{
1414
/// <summary>
1515
/// Get Terraform state value.
16-
/// GET /state/:name?ID=:lockId
16+
/// GET /:tenant/state/:name?ID=:lockId
1717
/// </summary>
18+
/// <param name="tenant"></param>
1819
/// <param name="name">The name of the Terraform state</param>
1920
/// <returns>Raw string</returns>
2021
[HttpGet("{name:regex([[a-zA-Z]]+)}", Name = "GetState")]
2122
[Produces("text/plain")]
2223
[ProducesResponseType(200)]
2324
[ProducesResponseType(204)]
24-
public async Task<IActionResult> FindOne(string name)
25+
public async Task<IActionResult> FindOne(string tenant, string name)
2526
{
26-
var state = await stateRepository.FindOneAsync(name);
27+
var state = await stateRepository.FindOneAsync(tenant, name);
2728
if (string.IsNullOrEmpty(state))
2829
{
2930
return NoContent();
@@ -34,7 +35,7 @@ public async Task<IActionResult> FindOne(string name)
3435

3536
/// <summary>
3637
/// Get Terraform state value.
37-
/// POST /state/:name?ID=:lockId
38+
/// POST /:tenant/state/:name?ID=:lockId
3839
/// </summary>
3940
/// <param name="name">The name of the Terraform state</param>
4041
/// <param name="lockId">Terraform state lock ID</param>
@@ -44,17 +45,17 @@ public async Task<IActionResult> FindOne(string name)
4445
[ProducesResponseType(200)]
4546
[ProducesResponseType(409)]
4647
[ProducesResponseType(423)]
47-
public async Task<IActionResult> Create(string name, [FromBody] object input, [FromQuery(Name = "ID")] string? lockId = "")
48+
public async Task<IActionResult> Create(string tenant, string name, [FromBody] object input, [FromQuery(Name = "ID")] string? lockId = "")
4849
{
49-
if (await CheckLock(name, lockId) is { } lockResult) return lockResult;
50+
if (await CheckLock(tenant, name, lockId) is { } lockResult) return lockResult;
5051

5152
var jsonInput = JsonSerializer.Serialize(input);
52-
await stateRepository.CreateAsync(name, jsonInput);
53+
await stateRepository.CreateAsync(tenant, name, jsonInput);
5354
return Ok();
5455
}
5556

5657
/// <summary>
57-
/// DELETE /state/:name?ID=:lockId
58+
/// DELETE /:tenant/state/:name?ID=:lockId
5859
/// </summary>
5960
/// <param name="name"></param>
6061
/// <param name="lockId">Terraform state lock ID</param>
@@ -63,16 +64,16 @@ public async Task<IActionResult> Create(string name, [FromBody] object input, [F
6364
[ProducesResponseType(200)]
6465
[ProducesResponseType(409)]
6566
[ProducesResponseType(423)]
66-
public async Task<IActionResult> Delete(string name, [FromQuery(Name = "ID")] string? lockId = "")
67+
public async Task<IActionResult> Delete(string tenant, string name, [FromQuery(Name = "ID")] string? lockId = "")
6768
{
68-
if (await CheckLock(name, lockId) is { } lockResult) return lockResult;
69+
if (await CheckLock(tenant, name, lockId) is { } lockResult) return lockResult;
6970

70-
await stateRepository.DeleteAsync(name);
71+
await stateRepository.DeleteAsync(tenant, name);
7172
return Ok();
7273
}
7374

7475
/// <summary>
75-
/// POST /state/:name/lock
76+
/// POST /:tenant/state/:name/lock
7677
/// </summary>
7778
/// <param name="name"></param>
7879
/// <param name="input"></param>
@@ -83,17 +84,18 @@ public async Task<IActionResult> Delete(string name, [FromQuery(Name = "ID")] st
8384
[ProducesResponseType(200)]
8485
[ProducesResponseType(409)]
8586
[ProducesResponseType(423)]
86-
public async Task<IActionResult> Lock(string name, StateLockModel input)
87+
public async Task<IActionResult> Lock(string tenant, string name, StateLockModel input)
8788
{
88-
if (await CheckLock(name, input.Id) is { } lockResult) return lockResult;
89+
if (await CheckLock(tenant, name, input.Id) is { } lockResult) return lockResult;
8990

91+
input.Tenant = tenant;
9092
input.Name = name;
9193
var entry = await stateLockRepository.CreateAsync(input);
9294
return Ok(entry);
9395
}
9496

9597
/// <summary>
96-
/// DELETE /state/:name/lock
98+
/// DELETE /:tenant/state/:name/lock
9799
/// </summary>
98100
/// <param name="name"></param>
99101
/// <param name="input"></param>
@@ -102,16 +104,17 @@ public async Task<IActionResult> Lock(string name, StateLockModel input)
102104
[ProducesResponseType(200)]
103105
[Consumes("application/json", "text/json")]
104106
[Produces("application/json")]
105-
public async Task<IActionResult> Unlock(string name, [FromBody] StateLockModel input)
107+
public async Task<IActionResult> Unlock(string tenant, string name, [FromBody] StateLockModel input)
106108
{
109+
input.Tenant = tenant;
107110
input.Name = name;
108111
await stateLockRepository.DeleteAsync(input);
109112
return Ok();
110113
}
111114

112-
private async Task<ObjectResult?> CheckLock(string name, string? lockId = "")
115+
private async Task<ObjectResult?> CheckLock(string tenant, string name, string? lockId = "")
113116
{
114-
var existingLock = await stateLockRepository.FindOneAsync(name);
117+
var existingLock = await stateLockRepository.FindOneAsync(tenant, name);
115118
if (existingLock != null)
116119
{
117120
if (string.IsNullOrEmpty(lockId))

src/WebApi/Program.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
// creates the builder
1+
// creates the web application builder and adds services to the container
22
var builder = WebApplication.CreateBuilder(args);
33
var configuration = new ApplicationConfiguration(builder.Configuration);
4-
5-
// adds services to the container
64
builder.Services.AddControllers(x => x.InputFormatters.Insert(0, new RawRequestBodyFormatter()));
75
builder.Services.AddInfrastructure(configuration);
86
builder.Services.AddEndpointsApiExplorer();
@@ -11,7 +9,7 @@
119
builder.Services.AddHealthChecks();
1210
builder.Services.AddInvalidModelStateLog();
1311

14-
// create the application and configures the HTTP request pipeline
12+
// creates the application and configures the HTTP request pipeline
1513
var app = builder.Build();
1614
app.UseSwagger(configuration);
1715
app.UseHttpsRedirection(configuration);

0 commit comments

Comments
 (0)