Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .tests/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@

if __name__ == "__main__":
decimal.DefaultContext.prec = 2
Launcher.launch("1.3.1")
Launcher.launch("1.4.0")
2 changes: 2 additions & 0 deletions .tests/tests/access_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ def test_login_permissions_normal(self):
3,
id=president_edit_response.json()["id"],
name="Catégorie",
type="Category",
),
)

Expand Down Expand Up @@ -258,6 +259,7 @@ def test_login_permissions_minimum(self):
3,
id=president_edit_response.json()["id"],
name="Catégorie",
type="Category",
),
)

Expand Down
77 changes: 63 additions & 14 deletions .tests/tests/category_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ def test_category_get_all(self):
category = categories[0]
self.expect(category).to.have.an_item("id").of.type(int)
self.expect(category).to.have.an_item("name").of.type(str)
self.expect(category).to.have.an_item("type").that.is_.one_of(
"Category", "Group"
)

def test_category_get_one(self):
existing_id = self.get("categories").json()[0]["id"]
Expand All @@ -36,6 +39,9 @@ def test_category_get_one(self):
self.expect(category).to.be.a(dict)
self.expect(category).to.have.an_item("id").of.type(int)
self.expect(category).to.have.an_item("name").of.type(str)
self.expect(category).to.have.an_item("type").that.is_.one_of(
"Category", "Group"
)

# Test avec une catégorie inexistante

Expand All @@ -45,27 +51,54 @@ def test_category_get_one(self):
def test_category_create(self):
previous_category_count = len(self.get("categories").json())

valid_category = {"name": "Jus"}
implicit_type = {"name": "Jus"}

with self.audit.watch():
response = self.post("categories", valid_category)
response = self.post("categories", implicit_type)

self.expect(response.status_code).to.be.equal_to(201)
location = self.expect(response.headers).to.have.an_item("Location").value

created_category = response.json()
self.expect(created_category).to.have.an_item("id")
self.expect(created_category["name"]).to.be.equal_to("Jus")
self.expect(created_category["type"]).to.be.equal_to("Category")

response = self.get(location)
self.expect(response.status_code).to.be.equal_to(200)
created_category = response.json()
self.expect(created_category["name"]).to.be.equal_to("Jus")
self.expect(created_category["type"]).to.be.equal_to("Category")

new_category_count = len(self.get("categories").json())
self.expect(new_category_count).to.be.equal_to(previous_category_count + 1)

self.audit.expect_entries(
self.audit.entry("CategoryAdded", id=created_category["id"], name="Jus")
self.audit.entry(
"CategoryAdded", id=created_category["id"], name="Jus", type="Category"
)
)

explicit_type = {"name": "Caca-Cola", "type": "Group"}

with self.audit.watch():
response = self.post("categories", explicit_type)

self.expect(response.status_code).to.be.equal_to(201)
location = self.expect(response.headers).to.have.an_item("Location").value

created_category = response.json()
self.expect(created_category).to.have.an_item("id")
self.expect(created_category["name"]).to.be.equal_to("Caca-Cola")
self.expect(created_category["type"]).to.be.equal_to("Group")

self.audit.expect_entries(
self.audit.entry(
"CategoryAdded",
id=created_category["id"],
name="Caca-Cola",
type="Group",
)
)

# Informations manquantes
Expand All @@ -87,30 +120,44 @@ def test_category_create(self):
)

def test_category_edit(self):
valid_category = self.get("categories").json()[-1]
valid_category.update(Name="Jus")
category_id = valid_category["id"]
valid_category = {"name": "Jus", "type": "Category"}
response = self.post("categories", valid_category)
category_id = response.json()["id"]
location = response.headers["Location"]

valid_category.update(name="Jus 2")

with self.audit.watch():
response = self.put(f"categories/{category_id}", valid_category)
response = self.put(location, valid_category)
self.expect(response.status_code).to.be.equal_to(200)

edited_category = self.get(f"categories/{category_id}").json()
self.expect(edited_category["name"]).to.be.equal_to("Jus")
edited_category = self.get(location).json()
self.expect(edited_category["name"]).to.be.equal_to("Jus 2")

self.audit.expect_entries(
self.audit.entry("CategoryModified", id=category_id, name="Jus")
self.audit.entry(
"CategoryModified", id=category_id, name="Jus 2", type="Category"
)
)

# category qui n'existe pas
# Changement de type

valid_category.update(type="Group")
response = self.put(location, valid_category)
self.expect(response.status_code).to.be.equal_to(200)

edited_category = self.get(location).json()
self.expect(edited_category["type"]).to.be.equal_to("Category")

# Catégorie qui n'existe pas

response = self.put("categories/12345", valid_category)
self.expect(response.status_code).to.be.equal_to(404)

# Informations manquantes

invalid_category = {}
response = self.put(f"categories/{category_id}", invalid_category)
response = self.put(location, invalid_category)
self.expect(response.status_code).to.be.equal_to(400)
self.expect(response.json()).to.have.an_item("code").that._is.equal_to(
"InvalidResource"
Expand All @@ -119,7 +166,7 @@ def test_category_edit(self):
# Informations non valides

invalid_category = {"name": ""}
response = self.put(f"categories/{category_id}", invalid_category)
response = self.put(location, invalid_category)
self.expect(response.status_code).to.be.equal_to(400)
self.expect(response.json()).to.have.an_item("code").that._is.equal_to(
"InvalidResource"
Expand All @@ -138,7 +185,9 @@ def test_category_delete(self):
self.expect(response.status_code).to.be.equal_to(200)

self.audit.expect_entries(
self.audit.entry("CategoryDeleted", id=category_id, name="Jus")
self.audit.entry(
"CategoryDeleted", id=category_id, name="Jus", type="Category"
)
)

# La catégorie n'existe plus
Expand Down
2 changes: 1 addition & 1 deletion Core/Data/IBasicDao.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public interface IBasicDao<TKey, TItem>
public TItem Update(TKey key, TItem item);

/// <summary>
/// Supprimme un item.
/// Supprime un item.
/// </summary>
/// <param name="key">La clé de l'item à supprimer.</param>
public void Delete(TKey key);
Expand Down
3 changes: 2 additions & 1 deletion Core/Exceptions/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ public enum ErrorCode
ServiceUnavailable,
FailedPrecondition,
AccessMethodNotAllowed,
DisabledApplication
DisabledApplication,
MissingIdentification
}
8 changes: 8 additions & 0 deletions Core/Exceptions/MissingIdentificationException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using GalliumPlus.Core.Security;

namespace GalliumPlus.Core.Exceptions;

public class MissingIdentificationException(string message) : UnauthorisedAccessException(message)
{
public override ErrorCode ErrorCode => ErrorCode.MissingIdentification;
}
2 changes: 1 addition & 1 deletion Core/Exceptions/PermissionDeniedException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace GalliumPlus.Core.Exceptions;
/// Erreur indiquant que l'utilisateur n'a pas les permissions suffisantes
/// pour effectuer une action.
/// </summary>
public class PermissionDeniedException : GalliumException
public class PermissionDeniedException : UnauthorisedAccessException
{
private readonly Permission required;

Expand Down
16 changes: 16 additions & 0 deletions Core/Exceptions/UnauthorisedAccessException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace GalliumPlus.Core.Exceptions;

/// <summary>
/// Exception de base indiquant qu'une action ne peut pas être effectuée,
/// car le niveau d'autorisation est insuffisant (identification ou permissions manquantes par exemple).
/// </summary>
public abstract class UnauthorisedAccessException : GalliumException
{
protected UnauthorisedAccessException()
{
}

protected UnauthorisedAccessException(string message) : base(message)
{
}
}
1 change: 1 addition & 0 deletions Core/Logs/Builders/AuditLogEntryBuilder.Category.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ protected override void AddDetails()
{
this.Root.details.Add("id", category.Id);
this.Root.details.Add("name", category.Name);
this.Root.details.Add("type", category.Type.ToString());
}
}
}
7 changes: 7 additions & 0 deletions Core/Logs/Builders/AuditLogEntryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ public AuditLogEntryBuilder By(Client app, User? user = null)
return this;
}

public AuditLogEntryBuilder By(Session session)
{
this.clientId = session.Client.Id;
this.userId = session.User?.Iuid;
return this;
}

public IGenericEntryBuilder Category(Category category) => new CategoryEntryBuilder(this, category);

public IClientEntryBuilder Client(Client client) => new ClientEntryBuilder(this, client);
Expand Down
25 changes: 19 additions & 6 deletions Core/Stocks/Category.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
namespace GalliumPlus.Core.Stocks;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
using KiwiQuery.Mapped;

namespace GalliumPlus.Core.Stocks;

/// <summary>
/// Une catégorie de produits.
/// </summary>
public class Category
{
private int id;
private string name;
[PrimaryKey(AutoIncrement = true)]
private readonly int id;
private readonly string name;
private readonly CategoryType type;

/// <summary>
/// L'identifiant de la catégorie.
Expand All @@ -16,18 +22,25 @@ public class Category
/// <summary>
/// Le nom affiché de la catégorie.
/// </summary>
[Required]
public string Name => this.name;

/// <summary>
/// Le type de catégorie (classique ou groupe).
/// </summary>
[EnumDataType(typeof(CategoryType))]
public CategoryType Type => this.type;

/// <summary>
/// Crée une catégorie.
/// </summary>
/// <param name="id">L'identifiant de la catégorie.</param>
/// <param name="name">Le nom affiché de la catégorie.</param>
public Category(int id, string name)
/// <param name="type">Le type de catégorie (classique ou groupe).</param>
public Category(int id, string name, CategoryType type = CategoryType.Category)
{
this.id = id;
this.name = name;
this.type = type;
}

public Category WithId(int id) => new(id, this.name);
}
17 changes: 17 additions & 0 deletions Core/Stocks/CategoryType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace GalliumPlus.Core.Stocks;

/// <summary>
/// Les différents types de catégories d'articles.
/// </summary>
public enum CategoryType
{
/// <summary>
/// Une catégorie classique.
/// </summary>
Category = 1,

/// <summary>
/// Un regroupement.
/// </summary>
Group = 2
}
Loading