From 833295177ae682a409b4d7201bac3cc5a14d1b73 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 20 Oct 2024 23:35:11 +0100 Subject: [PATCH 001/303] Added Database Response --- .../Responses/Database.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Responses/Database.cs diff --git a/src/PinguApps.Appwrite.Shared/Responses/Database.cs b/src/PinguApps.Appwrite.Shared/Responses/Database.cs new file mode 100644 index 00000000..91e56cfe --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/Database.cs @@ -0,0 +1,20 @@ +using System; +using System.Text.Json.Serialization; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite Database object +/// +/// Database ID +/// Database Name +/// Database creation date in ISO 8601 format +/// Database update date in ISO 8601 format +/// If database is enabled. Can be 'enabled' or 'disabled'. When disabled, the database is inaccessible to users, but remains accessible to Server SDKs using API keys +public record Database( + [property: JsonPropertyName("$id")] string Id, + [property: JsonPropertyName("name")] string Name, + [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt")] DateTime UpdatedAt, + [property: JsonPropertyName("enabled")] bool Enabled +); From 431bd54a058a597b96e0803b2930775dbe40bedc Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 20 Oct 2024 23:43:31 +0100 Subject: [PATCH 002/303] added tests for Database Response --- .../Responses/DatabaseTests.cs | 42 +++++++++++++++++++ .../TestConstants.cs | 10 +++++ 2 files changed, 52 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/DatabaseTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DatabaseTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DatabaseTests.cs new file mode 100644 index 00000000..b8db1201 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DatabaseTests.cs @@ -0,0 +1,42 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class DatabaseTests +{ + [Fact] + public void Constructor_AssignsPropertiesCorrectly() + { + // Arrange + var id = "5e5ea5c16897e"; + var name = "My Database"; + var createdAt = DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(); + var updatedAt = DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(); + var enabled = false; + + // Act + var database = new Database(id, name, createdAt, updatedAt, enabled); + + // Assert + Assert.Equal(id, database.Id); + Assert.Equal(name, database.Name); + Assert.Equal(createdAt, database.CreatedAt); + Assert.Equal(updatedAt, database.UpdatedAt); + Assert.Equal(enabled, database.Enabled); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + // Act + var database = JsonSerializer.Deserialize(TestConstants.DatabaseResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + // Assert + Assert.NotNull(database); + Assert.Equal("5e5ea5c16897e", database.Id); + Assert.Equal("My Database", database.Name); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), database.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), database.UpdatedAt.ToUniversalTime()); + Assert.False(database.Enabled); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index 2ffef9d0..7a7e5235 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -406,4 +406,14 @@ public static class TestConstants ] } """; + + public const string DatabaseResponse = """ + { + "$id": "5e5ea5c16897e", + "name": "My Database", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "enabled": false + } + """; } From 4e4d5a4434a7e7190947da44ba7448db90eca59d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 20 Oct 2024 23:45:40 +0100 Subject: [PATCH 003/303] Added databases list response --- .../Responses/DatabasesList.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Responses/DatabasesList.cs diff --git a/src/PinguApps.Appwrite.Shared/Responses/DatabasesList.cs b/src/PinguApps.Appwrite.Shared/Responses/DatabasesList.cs new file mode 100644 index 00000000..ed18fca5 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/DatabasesList.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite Databases List object +/// +/// Total number of databases documents that matched your query +/// List of databases. Can be one of: +public record DatabasesList( + [property: JsonPropertyName("total")] int Total, + [property: JsonPropertyName("databases")] IReadOnlyList Databases +); From 8521d7e7030230e9342eede3ee0ac41725502993 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 20 Oct 2024 23:47:55 +0100 Subject: [PATCH 004/303] added tests for databases list --- .../Responses/DatabasesListTests.cs | 43 +++++++++++++++++++ .../TestConstants.cs | 15 +++++++ 2 files changed, 58 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/DatabasesListTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DatabasesListTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DatabasesListTests.cs new file mode 100644 index 00000000..837ecd1a --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DatabasesListTests.cs @@ -0,0 +1,43 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class DatabasesListTests +{ + [Fact] + public void Constructor_AssignsPropertiesCorrectly() + { + // Arrange + var total = 5; + var databases = new List + { + new("5e5ea5c16897e", "My Database", DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), false) + }; + + // Act + var databasesList = new DatabasesList(total, databases); + + // Assert + Assert.Equal(total, databasesList.Total); + Assert.Equal(databases, databasesList.Databases); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + // Act + var databasesList = JsonSerializer.Deserialize(TestConstants.DatabasesListResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + // Assert + Assert.NotNull(databasesList); + Assert.Equal(5, databasesList.Total); + Assert.Single(databasesList.Databases); + + var database = databasesList.Databases[0]; + Assert.Equal("5e5ea5c16897e", database.Id); + Assert.Equal("My Database", database.Name); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), database.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), database.UpdatedAt.ToUniversalTime()); + Assert.False(database.Enabled); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index 7a7e5235..f6f2872d 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -416,4 +416,19 @@ public static class TestConstants "enabled": false } """; + + public const string DatabasesListResponse = """ + { + "total": 5, + "databases": [ + { + "$id": "5e5ea5c16897e", + "name": "My Database", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "enabled": false + } + ] + } + """; } From 4946b9bceb7b04aee1f798fc4d528c06c50b9ad1 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Mon, 21 Oct 2024 02:00:32 +0100 Subject: [PATCH 005/303] Added all attributes, Think I have JSON serialization for them complete and working --- .../Converters/AttributeJsonConverter.cs | 49 +++++++++++++++++ .../Converters/AttributeListJsonConverter.cs | 49 +++++++++++++++++ .../Enums/AttributeStatus.cs | 39 ++++++++++++++ .../Enums/OnDelete.cs | 27 ++++++++++ .../Enums/RelationType.cs | 33 ++++++++++++ .../Enums/RelationshipSide.cs | 21 ++++++++ .../Responses/Attribute.cs | 27 ++++++++++ .../Responses/AttributeBoolean.cs | 30 +++++++++++ .../Responses/AttributeDatetime.cs | 33 ++++++++++++ .../Responses/AttributeEmail.cs | 32 +++++++++++ .../Responses/AttributeEnum.cs | 35 ++++++++++++ .../Responses/AttributeFloat.cs | 34 ++++++++++++ .../Responses/AttributeInteger.cs | 34 ++++++++++++ .../Responses/AttributeIp.cs | 32 +++++++++++ .../Responses/AttributeRelationship.cs | 40 ++++++++++++++ .../Responses/AttributeString.cs | 32 +++++++++++ .../Responses/AttributeUrl.cs | 32 +++++++++++ .../Responses/AttributesList.cs | 9 ++++ .../Responses/AttributesListTests.cs | 53 +++++++++++++++++++ .../TestConstants.cs | 19 +++++++ 20 files changed, 660 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Converters/AttributeJsonConverter.cs create mode 100644 src/PinguApps.Appwrite.Shared/Converters/AttributeListJsonConverter.cs create mode 100644 src/PinguApps.Appwrite.Shared/Enums/AttributeStatus.cs create mode 100644 src/PinguApps.Appwrite.Shared/Enums/OnDelete.cs create mode 100644 src/PinguApps.Appwrite.Shared/Enums/RelationType.cs create mode 100644 src/PinguApps.Appwrite.Shared/Enums/RelationshipSide.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/Attribute.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/AttributeBoolean.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/AttributeDatetime.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/AttributeEmail.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/AttributeEnum.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/AttributeIp.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/AttributeString.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/AttributeUrl.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/AttributesList.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributesListTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/AttributeJsonConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/AttributeJsonConverter.cs new file mode 100644 index 00000000..8845814b --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/AttributeJsonConverter.cs @@ -0,0 +1,49 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Responses; +using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; + +namespace PinguApps.Appwrite.Shared.Converters; +public class AttributeJsonConverter : JsonConverter +{ + public override Attribute? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var jsonDoc = JsonDocument.ParseValue(ref reader); + var jsonObject = jsonDoc.RootElement; + + if (!jsonObject.TryGetProperty("type", out var typeProperty)) + { + throw new JsonException("Missing `Type` property"); + } + + var type = typeProperty.GetString(); + jsonObject.TryGetProperty("format", out var formatProperty); + var format = formatProperty.ValueKind == JsonValueKind.String ? formatProperty.GetString() : null; + + var derivedType = type switch + { + "boolean" => typeof(AttributeBoolean), + "integer" => typeof(AttributeInteger), + "double" => typeof(AttributeFloat), + "datetime" => typeof(AttributeDatetime), + "string" => format switch + { + "email" => typeof(AttributeEmail), + "url" => typeof(AttributeUrl), + "ip" => typeof(AttributeIp), + "enum" => typeof(AttributeEnum), + null or "" => jsonObject.TryGetProperty("relatedCollection", out _) ? typeof(AttributeRelationship) : typeof(AttributeString), + _ => throw new JsonException($"Unknown format: {format}") + }, + _ => throw new JsonException($"Unknown type: {type}") + }; + + return (Attribute?)JsonSerializer.Deserialize(jsonObject.GetRawText(), derivedType, options); + } + + public override void Write(Utf8JsonWriter writer, Attribute value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, value.GetType(), options); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Converters/AttributeListJsonConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/AttributeListJsonConverter.cs new file mode 100644 index 00000000..8661fd31 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/AttributeListJsonConverter.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; + +namespace PinguApps.Appwrite.Shared.Converters; +public class AttributeListJsonConverter : JsonConverter> +{ + private readonly AttributeJsonConverter _attributeConverter = new(); + + public override IReadOnlyList? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var attributes = new List(); + + if (reader.TokenType != JsonTokenType.StartArray) + { + throw new JsonException("Expected start of array"); + } + + reader.Read(); + + while (reader.TokenType != JsonTokenType.EndArray) + { + var attribute = _attributeConverter.Read(ref reader, typeof(Attribute), options); + + if (attribute is not null) + { + attributes.Add(attribute); + } + + reader.Read(); + } + + return attributes; + } + + public override void Write(Utf8JsonWriter writer, IReadOnlyList value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + + foreach (var attribute in value) + { + _attributeConverter.Write(writer, attribute, options); + } + + writer.WriteEndArray(); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Enums/AttributeStatus.cs b/src/PinguApps.Appwrite.Shared/Enums/AttributeStatus.cs new file mode 100644 index 00000000..38c789e9 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Enums/AttributeStatus.cs @@ -0,0 +1,39 @@ +using System.Runtime.Serialization; + +namespace PinguApps.Appwrite.Shared.Enums; + +/// +/// An Appwrite Status of an Attribute +/// +public enum AttributeStatus +{ + /// + /// Available + /// + [EnumMember(Value = "available")] + Available, + + /// + /// Processing + /// + [EnumMember(Value = "processing")] + Processing, + + /// + /// Deleting + /// + [EnumMember(Value = "deleting")] + Deleting, + + /// + /// Stuck + /// + [EnumMember(Value = "stuck")] + Stuck, + + /// + /// Failed + /// + [EnumMember(Value = "failed")] + Failed +} diff --git a/src/PinguApps.Appwrite.Shared/Enums/OnDelete.cs b/src/PinguApps.Appwrite.Shared/Enums/OnDelete.cs new file mode 100644 index 00000000..33786ae4 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Enums/OnDelete.cs @@ -0,0 +1,27 @@ +using System.Runtime.Serialization; + +namespace PinguApps.Appwrite.Shared.Enums; + +/// +/// An Appwrite OnDelete enum for Relationship Attributes +/// +public enum OnDelete +{ + /// + /// Restrict + /// + [EnumMember(Value = "restrict")] + Restrict, + + /// + /// Cascade + /// + [EnumMember(Value = "cascade")] + Cascade, + + /// + /// Set null + /// + [EnumMember(Value = "setNull")] + SetNull +} diff --git a/src/PinguApps.Appwrite.Shared/Enums/RelationType.cs b/src/PinguApps.Appwrite.Shared/Enums/RelationType.cs new file mode 100644 index 00000000..1273820b --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Enums/RelationType.cs @@ -0,0 +1,33 @@ +using System.Runtime.Serialization; + +namespace PinguApps.Appwrite.Shared.Enums; + +/// +/// An Appwrite rerlation type for relationship attributes +/// +public enum RelationType +{ + /// + /// One to one + /// + [EnumMember(Value = "oneToOne")] + OneToOne, + + /// + /// One to many + /// + [EnumMember(Value = "oneToMany")] + OneToMany, + + /// + /// Many to one + /// + [EnumMember(Value = "manyToOne")] + ManyToOne, + + /// + /// Many to many + /// + [EnumMember(Value = "manyToMany")] + ManyToMany +} diff --git a/src/PinguApps.Appwrite.Shared/Enums/RelationshipSide.cs b/src/PinguApps.Appwrite.Shared/Enums/RelationshipSide.cs new file mode 100644 index 00000000..7654aeaa --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Enums/RelationshipSide.cs @@ -0,0 +1,21 @@ +using System.Runtime.Serialization; + +namespace PinguApps.Appwrite.Shared.Enums; + +/// +/// An Appwrite Side enum for Relationship Attributes +/// +public enum RelationshipSide +{ + /// + /// Parent + /// + [EnumMember(Value = "parent")] + Parent, + + /// + /// Child + /// + [EnumMember(Value = "child")] + Child +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs new file mode 100644 index 00000000..0edbf4b0 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs @@ -0,0 +1,27 @@ +using System; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite base Attribute object +/// +/// Attribute Key +/// Attribute type +/// Attribute status +/// Error message. Displays error generated on failure of creating or deleting an attribute +/// Is attribute required? +/// Is attribute an array? +/// Attribute creation date in ISO 8601 format +/// Attribute update date in ISO 8601 format +public abstract record Attribute( + [property: JsonPropertyName("key")] string Key, + [property: JsonPropertyName("type")] string Type, + [property: JsonPropertyName("status"), JsonConverter(typeof(JsonStringEnumConverter))] AttributeStatus Status, + [property: JsonPropertyName("error")] string? Error, + [property: JsonPropertyName("required")] bool Required, + [property: JsonPropertyName("array")] bool Array, + [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt")] DateTime UpdatedAt +); diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeBoolean.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeBoolean.cs new file mode 100644 index 00000000..a26c3167 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeBoolean.cs @@ -0,0 +1,30 @@ +using System; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite AttributeBoolean object +/// +/// Attribute Key +/// Attribute type +/// Attribute status +/// Error message. Displays error generated on failure of creating or deleting an attribute +/// Is attribute required? +/// Is attribute an array? +/// Attribute creation date in ISO 8601 format +/// Attribute update date in ISO 8601 format +/// Default value for attribute when not provided. Cannot be set when attribute is required +public record AttributeBoolean( + string Key, + string Type, + AttributeStatus Status, + string? Error, + bool Required, + bool Array, + DateTime CreatedAt, + DateTime UpdatedAt, + + [property: JsonPropertyName("default")] bool? Default +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeDatetime.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeDatetime.cs new file mode 100644 index 00000000..d0425079 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeDatetime.cs @@ -0,0 +1,33 @@ +using System; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite AttributeDatetime object +/// +/// Attribute Key +/// Attribute type +/// Attribute status +/// Error message. Displays error generated on failure of creating or deleting an attribute +/// Is attribute required? +/// Is attribute an array? +/// Attribute creation date in ISO 8601 format +/// Attribute update date in ISO 8601 format +/// ISO 8601 format +/// Default value for attribute when not provided. Only null is optional +public record AttributeDatetime( + string Key, + string Type, + AttributeStatus Status, + string? Error, + bool Required, + bool Array, + DateTime CreatedAt, + DateTime UpdatedAt, + + [property: JsonPropertyName("format")] string Format, + [property: JsonPropertyName("default"), JsonConverter(typeof(NullableDateTimeConverter))] DateTime? Default +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeEmail.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeEmail.cs new file mode 100644 index 00000000..eaf933f8 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeEmail.cs @@ -0,0 +1,32 @@ +using System; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite AttributeEmail object +/// +/// Attribute Key +/// Attribute type +/// Attribute status +/// Error message. Displays error generated on failure of creating or deleting an attribute +/// Is attribute required? +/// Is attribute an array? +/// Attribute creation date in ISO 8601 format +/// Attribute update date in ISO 8601 format +/// String format +/// Default value for attribute when not provided. Cannot be set when attribute is required +public record AttributeEmail( + string Key, + string Type, + AttributeStatus Status, + string? Error, + bool Required, + bool Array, + DateTime CreatedAt, + DateTime UpdatedAt, + + [property: JsonPropertyName("format")] string Format, + [property: JsonPropertyName("default")] string? Default +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeEnum.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeEnum.cs new file mode 100644 index 00000000..ccdbf7d9 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeEnum.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite AttributeEnum object +/// +/// Attribute Key +/// Attribute type +/// Attribute status +/// Error message. Displays error generated on failure of creating or deleting an attribute +/// Is attribute required? +/// Is attribute an array? +/// Attribute creation date in ISO 8601 format +/// Attribute update date in ISO 8601 format +/// Array of elements in enumerated type +/// String format +/// Default value for attribute when not provided. Cannot be set when attribute is required +public record AttributeEnum( + string Key, + string Type, + AttributeStatus Status, + string? Error, + bool Required, + bool Array, + DateTime CreatedAt, + DateTime UpdatedAt, + + [property: JsonPropertyName("elements")] IReadOnlyList Elements, + [property: JsonPropertyName("format")] string Format, + [property: JsonPropertyName("default")] string? Default +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs new file mode 100644 index 00000000..9dd21ab8 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs @@ -0,0 +1,34 @@ +using System; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite AttributeFloat object +/// +/// Attribute Key +/// Attribute type +/// Attribute status +/// Error message. Displays error generated on failure of creating or deleting an attribute +/// Is attribute required? +/// Is attribute an array? +/// Attribute creation date in ISO 8601 format +/// Attribute update date in ISO 8601 format +/// Minimum value to enforce for new documents +/// Maximum value to enforce for new documents +/// Default value for attribute when not provided. Cannot be set when attribute is required +public record AttributeFloat( + string Key, + string Type, + AttributeStatus Status, + string? Error, + bool Required, + bool Array, + DateTime CreatedAt, + DateTime UpdatedAt, + + [property: JsonPropertyName("min")] float? Min, + [property: JsonPropertyName("max")] float? Max, + [property: JsonPropertyName("default")] float? Default +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs new file mode 100644 index 00000000..239c0c3e --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs @@ -0,0 +1,34 @@ +using System; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite AttributeInteger object +/// +/// Attribute Key +/// Attribute type +/// Attribute status +/// Error message. Displays error generated on failure of creating or deleting an attribute +/// Is attribute required? +/// Is attribute an array? +/// Attribute creation date in ISO 8601 format +/// Attribute update date in ISO 8601 format +/// Minimum value to enforce for new documents +/// Maximum value to enforce for new documents +/// Default value for attribute when not provided. Cannot be set when attribute is required +public record AttributeInteger( + string Key, + string Type, + AttributeStatus Status, + string? Error, + bool Required, + bool Array, + DateTime CreatedAt, + DateTime UpdatedAt, + + [property: JsonPropertyName("min")] int? Min, + [property: JsonPropertyName("max")] int? Max, + [property: JsonPropertyName("default")] int? Default +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeIp.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeIp.cs new file mode 100644 index 00000000..1e895522 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeIp.cs @@ -0,0 +1,32 @@ +using System; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite AttributeIp object +/// +/// Attribute Key +/// Attribute type +/// Attribute status +/// Error message. Displays error generated on failure of creating or deleting an attribute +/// Is attribute required? +/// Is attribute an array? +/// Attribute creation date in ISO 8601 format +/// Attribute update date in ISO 8601 format +/// String format +/// Default value for attribute when not provided. Cannot be set when attribute is required +public record AttributeIp( + string Key, + string Type, + AttributeStatus Status, + string? Error, + bool Required, + bool Array, + DateTime CreatedAt, + DateTime UpdatedAt, + + [property: JsonPropertyName("format")] string Format, + [property: JsonPropertyName("default")] string? Default +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs new file mode 100644 index 00000000..aa4684f9 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs @@ -0,0 +1,40 @@ +using System; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite AttributeRelationship object +/// +/// Attribute Key +/// Attribute type +/// Attribute status +/// Error message. Displays error generated on failure of creating or deleting an attribute +/// Is attribute required? +/// Is attribute an array? +/// Attribute creation date in ISO 8601 format +/// Attribute update date in ISO 8601 format +/// The ID of the related collection +/// The type of the relationship +/// Is the relationship two-way? +/// The key of the two-way relationship +/// How deleting the parent document will propagate to child documents +/// Whether this is the parent or child side of the relationship +public record AttributeRelationship( + string Key, + string Type, + AttributeStatus Status, + string? Error, + bool Required, + bool Array, + DateTime CreatedAt, + DateTime UpdatedAt, + + [property: JsonPropertyName("relatedCollection")] string RelatedCollection, + [property: JsonPropertyName("relationType"), JsonConverter(typeof(JsonStringEnumConverter))] RelationType RelationType, + [property: JsonPropertyName("twoWay")] bool TwoWay, + [property: JsonPropertyName("twoWayKey")] string TwoWayKey, + [property: JsonPropertyName("onDelete"), JsonConverter(typeof(JsonStringEnumConverter))] OnDelete OnDelete, + [property: JsonPropertyName("side"), JsonConverter(typeof(JsonStringEnumConverter))] RelationshipSide Side +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeString.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeString.cs new file mode 100644 index 00000000..47e2b662 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeString.cs @@ -0,0 +1,32 @@ +using System; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite AttributeString object +/// +/// Attribute Key +/// Attribute type +/// Attribute status +/// Error message. Displays error generated on failure of creating or deleting an attribute +/// Is attribute required? +/// Is attribute an array? +/// Attribute creation date in ISO 8601 format +/// Attribute update date in ISO 8601 format +/// Attribute size +/// Default value for attribute when not provided. Cannot be set when attribute is required +public record AttributeString( + string Key, + string Type, + AttributeStatus Status, + string? Error, + bool Required, + bool Array, + DateTime CreatedAt, + DateTime UpdatedAt, + + [property: JsonPropertyName("size")] int Size, + [property: JsonPropertyName("default")] string? Default +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeUrl.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeUrl.cs new file mode 100644 index 00000000..5fd46c4c --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeUrl.cs @@ -0,0 +1,32 @@ +using System; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite AttributeUrl object +/// +/// Attribute Key +/// Attribute type +/// Attribute status +/// Error message. Displays error generated on failure of creating or deleting an attribute +/// Is attribute required? +/// Is attribute an array? +/// Attribute creation date in ISO 8601 format +/// Attribute update date in ISO 8601 format +/// String format +/// Default value for attribute when not provided. Cannot be set when attribute is required +public record AttributeUrl( + string Key, + string Type, + AttributeStatus Status, + string? Error, + bool Required, + bool Array, + DateTime CreatedAt, + DateTime UpdatedAt, + + [property: JsonPropertyName("format")] string Format, + [property: JsonPropertyName("default")] string? Default +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributesList.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributesList.cs new file mode 100644 index 00000000..ce621dd6 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributesList.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; + +namespace PinguApps.Appwrite.Shared.Responses; +public record AttributesList( + [property: JsonPropertyName("total")] int Total, + [property: JsonPropertyName("attributes"), JsonConverter(typeof(AttributeListJsonConverter))] IReadOnlyList Attributes +); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributesListTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributesListTests.cs new file mode 100644 index 00000000..abad91d7 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributesListTests.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class AttributesListTests +{ + [Fact] + public void Constructor_AssignsPropertiesCorrectly() + { + // Arrange + var total = 5; + var attributes = new List + { + new AttributeBoolean("isEnabled", "boolean", AttributeStatus.Available, "string", true, + false, DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), + DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), false) + }; + + // Act + var attributesList = new AttributesList(total, attributes); + + // Assert + Assert.Equal(total, attributesList.Total); + Assert.Equal(attributes, attributesList.Attributes); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + // Act + var attributesList = JsonSerializer.Deserialize(TestConstants.AttributesListResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + // Assert + Assert.NotNull(attributesList); + Assert.Equal(5, attributesList.Total); + Assert.Single(attributesList.Attributes); + + var attribute = attributesList.Attributes[0]; + Assert.IsType(attribute); + var attributeBoolean = (AttributeBoolean)attribute; + Assert.Equal("isEnabled", attributeBoolean.Key); + Assert.Equal("boolean", attributeBoolean.Type); + Assert.Equal(AttributeStatus.Available, attributeBoolean.Status); + Assert.Equal("string", attributeBoolean.Error); + Assert.True(attributeBoolean.Required); + Assert.False(attributeBoolean.Array); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attributeBoolean.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attributeBoolean.UpdatedAt.ToUniversalTime()); + Assert.False(attributeBoolean.Default); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index f6f2872d..482bd683 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -431,4 +431,23 @@ public static class TestConstants ] } """; + + public const string AttributesListResponse = """ + { + "total": 5, + "attributes": [ + { + "key": "isEnabled", + "type": "boolean", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "default": false + } + ] + } + """; } From 1391c69f7f2d8d641cfc7034eabe43ada06a3d18 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 22 Oct 2024 23:00:44 +0100 Subject: [PATCH 006/303] Implemented visitor pattern for attributes --- src/PinguApps.Appwrite.Playground/App.cs | 56 +++++++++++-------- .../Responses/Attribute.cs | 18 +++++- .../Responses/AttributeBoolean.cs | 16 +++++- .../Responses/AttributeDatetime.cs | 16 +++++- .../Responses/AttributeEmail.cs | 16 +++++- .../Responses/AttributeEnum.cs | 16 +++++- .../Responses/AttributeFloat.cs | 16 +++++- .../Responses/AttributeInteger.cs | 16 +++++- .../Responses/AttributeIp.cs | 16 +++++- .../Responses/AttributeRelationship.cs | 16 +++++- .../Responses/AttributeString.cs | 16 +++++- .../Responses/AttributeUrl.cs | 16 +++++- .../Responses/Interfaces/IAttributeVisitor.cs | 14 +++++ .../Interfaces/IAttributeVisitorGeneric.cs | 14 +++++ 14 files changed, 228 insertions(+), 34 deletions(-) create mode 100644 src/PinguApps.Appwrite.Shared/Responses/Interfaces/IAttributeVisitor.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/Interfaces/IAttributeVisitorGeneric.cs diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 1d229a3d..2f286131 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -1,5 +1,7 @@ using Microsoft.Extensions.Configuration; -using PinguApps.Appwrite.Shared.Requests.Teams; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Responses.Interfaces; +using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; namespace PinguApps.Appwrite.Playground; internal class App @@ -17,31 +19,39 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - _client.SetSession(_session); - - var request = new UpdatePreferencesRequest() + var attributes = new List { - TeamId = "67142b78001c379958cb", - Preferences = new Dictionary() - { - {"key", "value"} - } + new AttributeBoolean("a", "boolean", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null), + new AttributeDatetime("b", "datetime", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "datetime", null), + new AttributeEmail("c", "string", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "email", null), + new AttributeEnum("d", "string", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, ["some", "elements"], "enum", null), + new AttributeFloat("e", "float", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null, null, null), + new AttributeInteger("f", "integer", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null, null, null), + new AttributeIp("g", "string", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "ip", null), + new AttributeRelationship("h", "string", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "collection", Shared.Enums.RelationType.OneToMany, false, "a string", Shared.Enums.OnDelete.Restrict, Shared.Enums.RelationshipSide.Parent), + new AttributeString("i", "string", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, 128, null), + new AttributeUrl("j", "string", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "url", null) }; - //var clientResponse = await _client.Teams.UpdatePreferences(request); - - //Console.WriteLine(clientResponse.Result.Match( - // result => result.ToString(), - // appwriteError => appwriteError.Message, - // internalError => internalError.Message)); - - Console.WriteLine("############################################################################"); - - var serverResponse = await _server.Teams.UpdatePreferences(request); + var visitor = new AttributeVisitor(); - Console.WriteLine(serverResponse.Result.Match( - result => result.ToString(), - appwriteError => appwriteError.Message, - internalError => internalError.Message)); + foreach (var attribute in attributes) + { + attribute.Accept(visitor); + } } } + +internal class AttributeVisitor : IAttributeVisitor +{ + public void Visit(AttributeBoolean attribute) => Console.WriteLine($"Boolean attribute Key: {attribute.Key}"); + public void Visit(AttributeInteger attribute) => Console.WriteLine($"Integer attribute Key: {attribute.Key}"); + public void Visit(AttributeFloat attribute) => Console.WriteLine($"Float attribute Key: {attribute.Key}"); + public void Visit(AttributeString attribute) => Console.WriteLine($"String attribute Key: {attribute.Key}"); + public void Visit(AttributeEmail attribute) => Console.WriteLine($"Email attribute Key: {attribute.Key}"); + public void Visit(AttributeUrl attribute) => Console.WriteLine($"Url attribute Key: {attribute.Key}"); + public void Visit(AttributeIp attribute) => Console.WriteLine($"Ip attribute Key: {attribute.Key}"); + public void Visit(AttributeDatetime attribute) => Console.WriteLine($"Datetime attribute Key: {attribute.Key}"); + public void Visit(AttributeEnum attribute) => Console.WriteLine($"Enum attribute Key: {attribute.Key}"); + public void Visit(AttributeRelationship attribute) => Console.WriteLine($"Relationship attribute Key: {attribute.Key}"); +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs index 0edbf4b0..b67f65f4 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses.Interfaces; namespace PinguApps.Appwrite.Shared.Responses; @@ -24,4 +25,19 @@ public abstract record Attribute( [property: JsonPropertyName("array")] bool Array, [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, [property: JsonPropertyName("$updatedAt")] DateTime UpdatedAt -); +) +{ + /// + /// Useful for iterating a mixed list of Attributes using the Visitor pattern + /// + /// Your own implementation of + public abstract void Accept(IAttributeVisitor visitor); + + /// + /// Useful for iterating a mixed list of Attributes using the Visitor pattern + /// + /// The return type + /// Your own implementation of + /// T + public abstract T Accept(IAttributeVisitor visitor); +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeBoolean.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeBoolean.cs index a26c3167..94b5f720 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeBoolean.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeBoolean.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses.Interfaces; namespace PinguApps.Appwrite.Shared.Responses; @@ -27,4 +28,17 @@ public record AttributeBoolean( DateTime UpdatedAt, [property: JsonPropertyName("default")] bool? Default -) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) +{ + /// + public override void Accept(IAttributeVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override T Accept(IAttributeVisitor visitor) + { + return visitor.Visit(this); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeDatetime.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeDatetime.cs index d0425079..03bcd709 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeDatetime.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeDatetime.cs @@ -2,6 +2,7 @@ using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Converters; using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses.Interfaces; namespace PinguApps.Appwrite.Shared.Responses; @@ -30,4 +31,17 @@ public record AttributeDatetime( [property: JsonPropertyName("format")] string Format, [property: JsonPropertyName("default"), JsonConverter(typeof(NullableDateTimeConverter))] DateTime? Default -) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) +{ + /// + public override void Accept(IAttributeVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override T Accept(IAttributeVisitor visitor) + { + return visitor.Visit(this); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeEmail.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeEmail.cs index eaf933f8..bb9854f2 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeEmail.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeEmail.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses.Interfaces; namespace PinguApps.Appwrite.Shared.Responses; @@ -29,4 +30,17 @@ public record AttributeEmail( [property: JsonPropertyName("format")] string Format, [property: JsonPropertyName("default")] string? Default -) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) +{ + /// + public override void Accept(IAttributeVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override T Accept(IAttributeVisitor visitor) + { + return visitor.Visit(this); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeEnum.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeEnum.cs index ccdbf7d9..2ebfe6f7 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeEnum.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeEnum.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses.Interfaces; namespace PinguApps.Appwrite.Shared.Responses; @@ -32,4 +33,17 @@ public record AttributeEnum( [property: JsonPropertyName("elements")] IReadOnlyList Elements, [property: JsonPropertyName("format")] string Format, [property: JsonPropertyName("default")] string? Default -) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) +{ + /// + public override void Accept(IAttributeVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override T Accept(IAttributeVisitor visitor) + { + return visitor.Visit(this); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs index 9dd21ab8..421de5cc 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses.Interfaces; namespace PinguApps.Appwrite.Shared.Responses; @@ -31,4 +32,17 @@ public record AttributeFloat( [property: JsonPropertyName("min")] float? Min, [property: JsonPropertyName("max")] float? Max, [property: JsonPropertyName("default")] float? Default -) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) +{ + /// + public override void Accept(IAttributeVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override T Accept(IAttributeVisitor visitor) + { + return visitor.Visit(this); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs index 239c0c3e..e9c630ee 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses.Interfaces; namespace PinguApps.Appwrite.Shared.Responses; @@ -31,4 +32,17 @@ public record AttributeInteger( [property: JsonPropertyName("min")] int? Min, [property: JsonPropertyName("max")] int? Max, [property: JsonPropertyName("default")] int? Default -) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) +{ + /// + public override void Accept(IAttributeVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override T Accept(IAttributeVisitor visitor) + { + return visitor.Visit(this); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeIp.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeIp.cs index 1e895522..1ad3b4a1 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeIp.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeIp.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses.Interfaces; namespace PinguApps.Appwrite.Shared.Responses; @@ -29,4 +30,17 @@ public record AttributeIp( [property: JsonPropertyName("format")] string Format, [property: JsonPropertyName("default")] string? Default -) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) +{ + /// + public override void Accept(IAttributeVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override T Accept(IAttributeVisitor visitor) + { + return visitor.Visit(this); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs index aa4684f9..4d3c5b7f 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses.Interfaces; namespace PinguApps.Appwrite.Shared.Responses; @@ -37,4 +38,17 @@ public record AttributeRelationship( [property: JsonPropertyName("twoWayKey")] string TwoWayKey, [property: JsonPropertyName("onDelete"), JsonConverter(typeof(JsonStringEnumConverter))] OnDelete OnDelete, [property: JsonPropertyName("side"), JsonConverter(typeof(JsonStringEnumConverter))] RelationshipSide Side -) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) +{ + /// + public override void Accept(IAttributeVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override T Accept(IAttributeVisitor visitor) + { + return visitor.Visit(this); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeString.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeString.cs index 47e2b662..11aad8bd 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeString.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeString.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses.Interfaces; namespace PinguApps.Appwrite.Shared.Responses; @@ -29,4 +30,17 @@ public record AttributeString( [property: JsonPropertyName("size")] int Size, [property: JsonPropertyName("default")] string? Default -) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) +{ + /// + public override void Accept(IAttributeVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override T Accept(IAttributeVisitor visitor) + { + return visitor.Visit(this); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeUrl.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeUrl.cs index 5fd46c4c..7b4bafb1 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeUrl.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeUrl.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses.Interfaces; namespace PinguApps.Appwrite.Shared.Responses; @@ -29,4 +30,17 @@ public record AttributeUrl( [property: JsonPropertyName("format")] string Format, [property: JsonPropertyName("default")] string? Default -) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt); +) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) +{ + /// + public override void Accept(IAttributeVisitor visitor) + { + visitor.Visit(this); + } + + /// + public override T Accept(IAttributeVisitor visitor) + { + return visitor.Visit(this); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/Interfaces/IAttributeVisitor.cs b/src/PinguApps.Appwrite.Shared/Responses/Interfaces/IAttributeVisitor.cs new file mode 100644 index 00000000..dfa3c86a --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/Interfaces/IAttributeVisitor.cs @@ -0,0 +1,14 @@ +namespace PinguApps.Appwrite.Shared.Responses.Interfaces; +public interface IAttributeVisitor +{ + void Visit(AttributeBoolean attribute); + void Visit(AttributeInteger attribute); + void Visit(AttributeFloat attribute); + void Visit(AttributeString attribute); + void Visit(AttributeEmail attribute); + void Visit(AttributeUrl attribute); + void Visit(AttributeIp attribute); + void Visit(AttributeDatetime attribute); + void Visit(AttributeEnum attribute); + void Visit(AttributeRelationship attribute); +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/Interfaces/IAttributeVisitorGeneric.cs b/src/PinguApps.Appwrite.Shared/Responses/Interfaces/IAttributeVisitorGeneric.cs new file mode 100644 index 00000000..da55110f --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/Interfaces/IAttributeVisitorGeneric.cs @@ -0,0 +1,14 @@ +namespace PinguApps.Appwrite.Shared.Responses.Interfaces; +public interface IAttributeVisitor +{ + T Visit(AttributeBoolean attribute); + T Visit(AttributeInteger attribute); + T Visit(AttributeFloat attribute); + T Visit(AttributeString attribute); + T Visit(AttributeEmail attribute); + T Visit(AttributeUrl attribute); + T Visit(AttributeIp attribute); + T Visit(AttributeDatetime attribute); + T Visit(AttributeEnum attribute); + T Visit(AttributeRelationship attribute); +} From f7d0a4a60388a655c3cc71112d60e63e2b421f0c Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 22 Oct 2024 23:53:32 +0100 Subject: [PATCH 007/303] Added tests for attribute json converter, plus fixed a serialisation issue --- src/PinguApps.Appwrite.Playground/App.cs | 17 ++- .../Converters/CamelCaseEnumConverter.cs | 10 ++ .../Account/Create2faChallengeRequest.cs | 3 +- .../Requests/Users/CreateUserTargetRequest.cs | 3 +- .../Responses/Attribute.cs | 3 +- .../Responses/AttributeRelationship.cs | 7 +- .../Responses/Target.cs | 3 +- .../Converters/AttributeJsonConverterTests.cs | 142 ++++++++++++++++++ 8 files changed, 180 insertions(+), 8 deletions(-) create mode 100644 src/PinguApps.Appwrite.Shared/Converters/CamelCaseEnumConverter.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeJsonConverterTests.cs diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 2f286131..a5deb2dd 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -1,4 +1,7 @@ -using Microsoft.Extensions.Configuration; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.Extensions.Configuration; +using PinguApps.Appwrite.Shared.Converters; using PinguApps.Appwrite.Shared.Responses; using PinguApps.Appwrite.Shared.Responses.Interfaces; using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; @@ -39,6 +42,18 @@ public async Task Run(string[] args) { attribute.Accept(visitor); } + + var options = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + options.Converters.Add(new IgnoreSdkExcludedPropertiesConverterFactory()); + + var json = JsonSerializer.Serialize(attributes[0]); + + Console.WriteLine(json); } } diff --git a/src/PinguApps.Appwrite.Shared/Converters/CamelCaseEnumConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/CamelCaseEnumConverter.cs new file mode 100644 index 00000000..03fe804b --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/CamelCaseEnumConverter.cs @@ -0,0 +1,10 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace PinguApps.Appwrite.Shared.Converters; +public class CamelCaseEnumConverter : JsonStringEnumConverter +{ + public CamelCaseEnumConverter() : base(JsonNamingPolicy.CamelCase, false) + { + } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Account/Create2faChallengeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Account/Create2faChallengeRequest.cs index 78cdc679..43ac1d85 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Account/Create2faChallengeRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Account/Create2faChallengeRequest.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Requests.Account.Validators; @@ -18,6 +19,6 @@ public class Create2faChallengeRequest : BaseRequest /// [JsonPropertyName("factor")] - [JsonConverter(typeof(JsonStringEnumConverter))] + [JsonConverter(typeof(CamelCaseEnumConverter))] public SecondFactor Factor { get; set; } } diff --git a/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserTargetRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserTargetRequest.cs index b4e48df0..1c277bc8 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserTargetRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Users/CreateUserTargetRequest.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Requests.Users.Validators; using PinguApps.Appwrite.Shared.Utils; @@ -20,7 +21,7 @@ public class CreateUserTargetRequest : UserIdBaseRequest, , /// [JsonPropertyName("providerType")] - [JsonConverter(typeof(JsonStringEnumConverter))] + [JsonConverter(typeof(CamelCaseEnumConverter))] public TargetProviderType ProviderType { get; set; } /// diff --git a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs index b67f65f4..5c6ed07a 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Responses.Interfaces; @@ -19,7 +20,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public abstract record Attribute( [property: JsonPropertyName("key")] string Key, [property: JsonPropertyName("type")] string Type, - [property: JsonPropertyName("status"), JsonConverter(typeof(JsonStringEnumConverter))] AttributeStatus Status, + [property: JsonPropertyName("status"), JsonConverter(typeof(CamelCaseEnumConverter))] AttributeStatus Status, [property: JsonPropertyName("error")] string? Error, [property: JsonPropertyName("required")] bool Required, [property: JsonPropertyName("array")] bool Array, diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs index 4d3c5b7f..8bd188a7 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Responses.Interfaces; @@ -33,11 +34,11 @@ public record AttributeRelationship( DateTime UpdatedAt, [property: JsonPropertyName("relatedCollection")] string RelatedCollection, - [property: JsonPropertyName("relationType"), JsonConverter(typeof(JsonStringEnumConverter))] RelationType RelationType, + [property: JsonPropertyName("relationType"), JsonConverter(typeof(CamelCaseEnumConverter))] RelationType RelationType, [property: JsonPropertyName("twoWay")] bool TwoWay, [property: JsonPropertyName("twoWayKey")] string TwoWayKey, - [property: JsonPropertyName("onDelete"), JsonConverter(typeof(JsonStringEnumConverter))] OnDelete OnDelete, - [property: JsonPropertyName("side"), JsonConverter(typeof(JsonStringEnumConverter))] RelationshipSide Side + [property: JsonPropertyName("onDelete"), JsonConverter(typeof(CamelCaseEnumConverter))] OnDelete OnDelete, + [property: JsonPropertyName("side"), JsonConverter(typeof(CamelCaseEnumConverter))] RelationshipSide Side ) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) { /// diff --git a/src/PinguApps.Appwrite.Shared/Responses/Target.cs b/src/PinguApps.Appwrite.Shared/Responses/Target.cs index 05200ac5..68552bc5 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Target.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Target.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; using PinguApps.Appwrite.Shared.Enums; namespace PinguApps.Appwrite.Shared.Responses; @@ -21,6 +22,6 @@ public record Target( [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("userId")] string UserId, [property: JsonPropertyName("providerId")] string? ProviderId, - [property: JsonPropertyName("providerType"), JsonConverter(typeof(JsonStringEnumConverter))] TargetProviderType ProviderType, + [property: JsonPropertyName("providerType"), JsonConverter(typeof(CamelCaseEnumConverter))] TargetProviderType ProviderType, [property: JsonPropertyName("identifier")] string Identifier ); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeJsonConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeJsonConverterTests.cs new file mode 100644 index 00000000..5ac6b73b --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeJsonConverterTests.cs @@ -0,0 +1,142 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class AttributeJsonConverterTests +{ + private readonly JsonSerializerOptions _options; + + public AttributeJsonConverterTests() + { + _options = new JsonSerializerOptions + { + Converters = { new AttributeJsonConverter() } + }; + } + + [Fact] + public void Read_ShouldDeserializeBooleanAttribute() + { + var json = "{\"type\":\"boolean\"}"; + var attribute = JsonSerializer.Deserialize(json, _options); + Assert.IsType(attribute); + } + + [Fact] + public void Read_ShouldDeserializeIntegerAttribute() + { + var json = "{\"type\":\"integer\"}"; + var attribute = JsonSerializer.Deserialize(json, _options); + Assert.IsType(attribute); + } + + [Fact] + public void Read_ShouldDeserializeDoubleAttribute() + { + var json = "{\"type\":\"double\"}"; + var attribute = JsonSerializer.Deserialize(json, _options); + Assert.IsType(attribute); + } + + [Fact] + public void Read_ShouldDeserializeDatetimeAttribute() + { + var json = "{\"type\":\"datetime\"}"; + var attribute = JsonSerializer.Deserialize(json, _options); + Assert.IsType(attribute); + } + + [Fact] + public void Read_ShouldDeserializeStringAttribute() + { + var json = "{\"type\":\"string\"}"; + var attribute = JsonSerializer.Deserialize(json, _options); + Assert.IsType(attribute); + } + + [Fact] + public void Read_ShouldDeserializeEmailAttribute() + { + var json = "{\"type\":\"string\", \"format\":\"email\"}"; + var attribute = JsonSerializer.Deserialize(json, _options); + Assert.IsType(attribute); + } + + [Fact] + public void Read_ShouldDeserializeUrlAttribute() + { + var json = "{\"type\":\"string\", \"format\":\"url\"}"; + var attribute = JsonSerializer.Deserialize(json, _options); + Assert.IsType(attribute); + } + + [Fact] + public void Read_ShouldDeserializeIpAttribute() + { + var json = "{\"type\":\"string\", \"format\":\"ip\"}"; + var attribute = JsonSerializer.Deserialize(json, _options); + Assert.IsType(attribute); + } + + [Fact] + public void Read_ShouldDeserializeEnumAttribute() + { + var json = "{\"type\":\"string\", \"format\":\"enum\"}"; + var attribute = JsonSerializer.Deserialize(json, _options); + Assert.IsType(attribute); + } + + [Fact] + public void Read_ShouldDeserializeRelationshipAttribute() + { + var json = "{\"type\":\"string\", \"relatedCollection\":\"someCollection\"}"; + var attribute = JsonSerializer.Deserialize(json, _options); + Assert.IsType(attribute); + } + + [Fact] + public void Read_ShouldThrowJsonExceptionForUnknownType() + { + var json = "{\"type\":\"unknown\"}"; + Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); + } + + [Fact] + public void Read_ShouldThrowJsonExceptionForUnknownFormat() + { + var json = "{\"type\":\"string\", \"format\":\"unknown\"}"; + Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); + } + + [Fact] + public void Read_ShouldThrowJsonExceptionForMissingType() + { + var json = "{}"; + Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); + } + + [Fact] + public void Write_ShouldSerializeAttribute() + { + var attribute = new AttributeBoolean("a", "boolean", AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null); + var converter = new AttributeJsonConverter(); + + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + converter.Write(writer, attribute, _options); + writer.Flush(); + + var json = System.Text.Encoding.UTF8.GetString(stream.ToArray()); + Assert.Contains("\"type\":\"boolean\"", json); + Assert.Contains("\"key\":\"a\"", json); + Assert.Contains("\"status\":\"available\"", json); + Assert.Contains("\"required\":false", json); + Assert.Contains("\"array\":false", json); + Assert.Contains($"\"$createdAt\":\"{attribute.CreatedAt:O}\"", json); + Assert.Contains($"\"$updatedAt\":\"{attribute.UpdatedAt:O}\"", json); + } +} From 1868abe14627c44fcb81c2685b71dfc8e46aef69 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 23 Oct 2024 00:11:42 +0100 Subject: [PATCH 008/303] added tests for attribute list converter --- .../AttributeListJsonConverterTests.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeListJsonConverterTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeListJsonConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeListJsonConverterTests.cs new file mode 100644 index 00000000..66d4fbd0 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeListJsonConverterTests.cs @@ -0,0 +1,64 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class AttributeListJsonConverterTests +{ + private readonly JsonSerializerOptions _options; + + public AttributeListJsonConverterTests() + { + _options = new JsonSerializerOptions + { + Converters = { new AttributeListJsonConverter() } + }; + } + + [Fact] + public void Read_ShouldDeserializeAttributeList() + { + var json = "[{\"type\":\"boolean\"}, {\"type\":\"integer\"}]"; + var attributes = JsonSerializer.Deserialize>(json, _options); + + Assert.NotNull(attributes); + Assert.Equal(2, attributes.Count); + Assert.IsType(attributes[0]); + Assert.IsType(attributes[1]); + } + + [Fact] + public void Read_ShouldThrowJsonExceptionForInvalidStartToken() + { + var json = "{\"type\":\"boolean\"}"; + var exception = Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); + Assert.Equal("Expected start of array", exception.Message); + } + + [Fact] + public void Write_ShouldSerializeAttributeList() + { + var attributes = new List + { + new AttributeBoolean("a", "boolean", AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null), + new AttributeInteger("b", "integer", AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null, null, null) + }; + + var json = JsonSerializer.Serialize((IReadOnlyList)attributes, _options); + + Assert.Contains("\"type\":\"boolean\"", json); + Assert.Contains("\"type\":\"integer\"", json); + } + + [Fact] + public void Write_ShouldHandleEmptyList() + { + var attributes = new List(); + + var json = JsonSerializer.Serialize((IReadOnlyList)attributes, _options); + + Assert.Equal("[]", json); + } +} From ca3fd7862f3a3ae354d768f79583e1a32b18c90d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 23 Oct 2024 01:14:22 +0100 Subject: [PATCH 009/303] added tests for AttributeBolean, and fixed some datetime handling --- .../MultiFormatDateTimeConverter.cs | 2 +- .../Converters/NullableDateTimeConverter.cs | 13 +-- .../Responses/Attribute.cs | 4 +- .../Responses/Database.cs | 5 +- .../Responses/IdentityModel.cs | 7 +- .../Responses/LogModel.cs | 3 +- .../Responses/Membership.cs | 6 +- .../Responses/MfaChallenge.cs | 5 +- .../Responses/Session.cs | 4 +- .../Responses/Target.cs | 4 +- .../Responses/Team.cs | 5 +- .../Responses/Token.cs | 5 +- .../Responses/User.cs | 6 +- .../Converters/AttributeJsonConverterTests.cs | 8 +- .../MultiFormatDateTimeConverterTests.cs | 13 ++- .../NullableDateTimeConverterTests.cs | 12 ++- .../Responses/AttributeBooleanTests.cs | 92 +++++++++++++++++++ .../TestConstants.cs | 14 +++ 18 files changed, 163 insertions(+), 45 deletions(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeBooleanTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/MultiFormatDateTimeConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/MultiFormatDateTimeConverter.cs index 83f2cfca..5f20e0af 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/MultiFormatDateTimeConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/MultiFormatDateTimeConverter.cs @@ -11,7 +11,7 @@ namespace PinguApps.Appwrite.Shared.Converters; public class MultiFormatDateTimeConverter : JsonConverter { private readonly string[] _formats = [ - "yyyy-MM-ddTHH:mm:ss.fffK", + "yyyy-MM-ddTHH:mm:ss.fffzzz", "yyyy-MM-dd HH:mm:ss.fff" ]; diff --git a/src/PinguApps.Appwrite.Shared/Converters/NullableDateTimeConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/NullableDateTimeConverter.cs index 2719aeca..86df73fa 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/NullableDateTimeConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/NullableDateTimeConverter.cs @@ -3,8 +3,10 @@ using System.Text.Json.Serialization; namespace PinguApps.Appwrite.Shared.Converters; -internal class NullableDateTimeConverter : JsonConverter +public class NullableDateTimeConverter : JsonConverter { + private readonly MultiFormatDateTimeConverter _dateTimeConverter = new(); + public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.String) @@ -15,12 +17,7 @@ internal class NullableDateTimeConverter : JsonConverter return null; } - if (DateTime.TryParse(stringValue, out var dateTime)) - { - return dateTime; - } - - throw new JsonException($"Unable to parse '{stringValue}' to DateTime."); + return _dateTimeConverter.Read(ref reader, typeof(DateTime), options); } throw new JsonException("Unexpected token type."); @@ -30,7 +27,7 @@ public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerialize { if (value.HasValue) { - writer.WriteStringValue(value.Value.ToString("o")); + _dateTimeConverter.Write(writer, value.Value, options); } } } diff --git a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs index 5c6ed07a..51d56823 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs @@ -24,8 +24,8 @@ public abstract record Attribute( [property: JsonPropertyName("error")] string? Error, [property: JsonPropertyName("required")] bool Required, [property: JsonPropertyName("array")] bool Array, - [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, - [property: JsonPropertyName("$updatedAt")] DateTime UpdatedAt + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt ) { /// diff --git a/src/PinguApps.Appwrite.Shared/Responses/Database.cs b/src/PinguApps.Appwrite.Shared/Responses/Database.cs index 91e56cfe..8a8dc752 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Database.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Database.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; namespace PinguApps.Appwrite.Shared.Responses; @@ -14,7 +15,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public record Database( [property: JsonPropertyName("$id")] string Id, [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, - [property: JsonPropertyName("$updatedAt")] DateTime UpdatedAt, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, [property: JsonPropertyName("enabled")] bool Enabled ); diff --git a/src/PinguApps.Appwrite.Shared/Responses/IdentityModel.cs b/src/PinguApps.Appwrite.Shared/Responses/IdentityModel.cs index e39ad3f3..21a89cf8 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/IdentityModel.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/IdentityModel.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; namespace PinguApps.Appwrite.Shared.Responses; @@ -18,13 +19,13 @@ namespace PinguApps.Appwrite.Shared.Responses; /// Identity Provider Refresh Token public record IdentityModel( [property: JsonPropertyName("$id")] string Id, - [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, - [property: JsonPropertyName("$updatedAt")] DateTime UpdatedAt, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, [property: JsonPropertyName("userId")] string UserId, [property: JsonPropertyName("provider")] string Provider, [property: JsonPropertyName("providerUid")] string ProviderUid, [property: JsonPropertyName("providerEmail")] string ProviderEmail, [property: JsonPropertyName("providerAccessToken")] string ProviderAccessToken, - [property: JsonPropertyName("providerAccessTokenExpiry")] DateTime ProviderAccessTokenExpiry, + [property: JsonPropertyName("providerAccessTokenExpiry"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime ProviderAccessTokenExpiry, [property: JsonPropertyName("providerRefreshToken")] string ProviderRefreshToken ); diff --git a/src/PinguApps.Appwrite.Shared/Responses/LogModel.cs b/src/PinguApps.Appwrite.Shared/Responses/LogModel.cs index 1de05284..8102ac5e 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/LogModel.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/LogModel.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; namespace PinguApps.Appwrite.Shared.Responses; @@ -34,7 +35,7 @@ public record LogModel( [property: JsonPropertyName("userName")] string UserName, [property: JsonPropertyName("mode")] string Mode, [property: JsonPropertyName("ip")] string Ip, - [property: JsonPropertyName("time")] DateTime Time, + [property: JsonPropertyName("time"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime Time, [property: JsonPropertyName("osCode")] string OsCode, [property: JsonPropertyName("osName")] string OsName, [property: JsonPropertyName("osVersion")] string OsVersion, diff --git a/src/PinguApps.Appwrite.Shared/Responses/Membership.cs b/src/PinguApps.Appwrite.Shared/Responses/Membership.cs index 196d61d6..5d531410 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Membership.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Membership.cs @@ -23,14 +23,14 @@ namespace PinguApps.Appwrite.Shared.Responses; /// User list of roles public record Membership( [property: JsonPropertyName("$id")] string Id, - [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, - [property: JsonPropertyName("$updatedAt")] DateTime UpdatedAt, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, [property: JsonPropertyName("userId")] string UserId, [property: JsonPropertyName("userName")] string UserName, [property: JsonPropertyName("userEmail")] string UserEmail, [property: JsonPropertyName("teamId")] string TeamId, [property: JsonPropertyName("teamName")] string TeamName, - [property: JsonPropertyName("invited")] DateTime Invited, + [property: JsonPropertyName("invited"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime Invited, [property: JsonPropertyName("joined"), JsonConverter(typeof(NullableDateTimeConverter))] DateTime? Joined, [property: JsonPropertyName("confirm")] bool Confirm, [property: JsonPropertyName("mfa")] bool Mfa, diff --git a/src/PinguApps.Appwrite.Shared/Responses/MfaChallenge.cs b/src/PinguApps.Appwrite.Shared/Responses/MfaChallenge.cs index 0faf14c4..be5264ba 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/MfaChallenge.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/MfaChallenge.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; namespace PinguApps.Appwrite.Shared.Responses; @@ -12,7 +13,7 @@ namespace PinguApps.Appwrite.Shared.Responses; /// Token expiration date in ISO 8601 format public record MfaChallenge( [property: JsonPropertyName("$id")] string Id, - [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, [property: JsonPropertyName("userId")] string UserId, - [property: JsonPropertyName("expire")] DateTime Expire + [property: JsonPropertyName("expire"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime Expire ); diff --git a/src/PinguApps.Appwrite.Shared/Responses/Session.cs b/src/PinguApps.Appwrite.Shared/Responses/Session.cs index d6375563..e607a9a3 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Session.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Session.cs @@ -39,8 +39,8 @@ namespace PinguApps.Appwrite.Shared.Responses; /// Most recent date in ISO 8601 format when the session successfully passed MFA challenge public record Session( [property: JsonPropertyName("$id")] string Id, - [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, - [property: JsonPropertyName("$updatedAt")] DateTime UpdatedAt, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, [property: JsonPropertyName("userId")] string UserId, [property: JsonPropertyName("expire"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime ExpiresAt, [property: JsonPropertyName("provider")] string Provider, diff --git a/src/PinguApps.Appwrite.Shared/Responses/Target.cs b/src/PinguApps.Appwrite.Shared/Responses/Target.cs index 68552bc5..a5847a68 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Target.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Target.cs @@ -17,8 +17,8 @@ namespace PinguApps.Appwrite.Shared.Responses; /// The target identifier public record Target( [property: JsonPropertyName("$id")] string Id, - [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, - [property: JsonPropertyName("$updatedAt")] DateTime UpdatedAt, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("userId")] string UserId, [property: JsonPropertyName("providerId")] string? ProviderId, diff --git a/src/PinguApps.Appwrite.Shared/Responses/Team.cs b/src/PinguApps.Appwrite.Shared/Responses/Team.cs index f67644e9..3bbc6e97 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Team.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Team.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; namespace PinguApps.Appwrite.Shared.Responses; @@ -15,8 +16,8 @@ namespace PinguApps.Appwrite.Shared.Responses; /// Team preferences as a key-value object public record Team( [property: JsonPropertyName("$id")] string Id, - [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, - [property: JsonPropertyName("$updatedAt")] DateTime UpdatedAt, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("total")] int Total, [property: JsonPropertyName("prefs")] IReadOnlyDictionary Prefs diff --git a/src/PinguApps.Appwrite.Shared/Responses/Token.cs b/src/PinguApps.Appwrite.Shared/Responses/Token.cs index e1f9cf38..465231a0 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Token.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Token.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; namespace PinguApps.Appwrite.Shared.Responses; @@ -14,9 +15,9 @@ namespace PinguApps.Appwrite.Shared.Responses; /// Security phrase of a token. Empty if security phrase was not requested when creating a token. It includes randomly generated phrase which is also sent in the external resource such as email public record Token( [property: JsonPropertyName("$id")] string Id, - [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, [property: JsonPropertyName("userId")] string UserId, [property: JsonPropertyName("secret")] string Secret, - [property: JsonPropertyName("expire")] DateTime ExpiresAt, + [property: JsonPropertyName("expire"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime ExpiresAt, [property: JsonPropertyName("phrase")] string Phrase ); diff --git a/src/PinguApps.Appwrite.Shared/Responses/User.cs b/src/PinguApps.Appwrite.Shared/Responses/User.cs index 6aa2d82d..97ebbc6c 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/User.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/User.cs @@ -30,13 +30,13 @@ namespace PinguApps.Appwrite.Shared.Responses; /// Most recent access date in ISO 8601 format. This attribute is only updated again after 24 hours public record User( [property: JsonPropertyName("$id")] string Id, - [property: JsonPropertyName("$createdAt")] DateTime CreatedAt, - [property: JsonPropertyName("$updatedAt")] DateTime UpdatedAt, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("password")] string? Password, [property: JsonPropertyName("hash")] string? Hash, [property: JsonPropertyName("hashOptions")] HashOptions? HashOptions, - [property: JsonPropertyName("registration")] DateTime Registration, + [property: JsonPropertyName("registration"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime Registration, [property: JsonPropertyName("status")] bool Status, [property: JsonPropertyName("labels")] IReadOnlyList Labels, [property: JsonPropertyName("passwordUpdate"), JsonConverter(typeof(NullableDateTimeConverter))] DateTime? PasswordUpdate, diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeJsonConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeJsonConverterTests.cs index 5ac6b73b..02411725 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeJsonConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeJsonConverterTests.cs @@ -13,6 +13,7 @@ public AttributeJsonConverterTests() { _options = new JsonSerializerOptions { + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, Converters = { new AttributeJsonConverter() } }; } @@ -121,7 +122,8 @@ public void Read_ShouldThrowJsonExceptionForMissingType() [Fact] public void Write_ShouldSerializeAttribute() { - var attribute = new AttributeBoolean("a", "boolean", AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null); + var fixedDate = new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc); + var attribute = new AttributeBoolean("a", "boolean", AttributeStatus.Available, null, false, false, fixedDate, fixedDate, null); var converter = new AttributeJsonConverter(); using var stream = new MemoryStream(); @@ -136,7 +138,7 @@ public void Write_ShouldSerializeAttribute() Assert.Contains("\"status\":\"available\"", json); Assert.Contains("\"required\":false", json); Assert.Contains("\"array\":false", json); - Assert.Contains($"\"$createdAt\":\"{attribute.CreatedAt:O}\"", json); - Assert.Contains($"\"$updatedAt\":\"{attribute.UpdatedAt:O}\"", json); + Assert.Contains($"\"$createdAt\":\"{fixedDate:yyyy-MM-ddTHH:mm:ss.fff}", json); + Assert.Contains($"\"$updatedAt\":\"{fixedDate:yyyy-MM-ddTHH:mm:ss.fff}", json); } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/MultiFormatDateTimeConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/MultiFormatDateTimeConverterTests.cs index 382eb00e..cef2db5e 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/MultiFormatDateTimeConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/MultiFormatDateTimeConverterTests.cs @@ -9,14 +9,17 @@ public class MultiFormatDateTimeConverterTests public MultiFormatDateTimeConverterTests() { - _options = new JsonSerializerOptions(); + _options = new JsonSerializerOptions() + { + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; _options.Converters.Add(new MultiFormatDateTimeConverter()); } [Fact] public void Read_ValidDateStringWithTimeZone_ReturnsDateTime() { - var json = "\"2023-01-01T00:00:00.000Z\""; + var json = "\"2023-01-01T00:00:00.000+00:00\""; var result = JsonSerializer.Deserialize(json, _options); // Convert both to UTC to compare @@ -53,7 +56,7 @@ public void Write_ValidDateTime_WritesExpectedString() { var dateTime = new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc); var json = JsonSerializer.Serialize(dateTime, _options); - Assert.Equal("\"2023-01-01T00:00:00.000Z\"", json); + Assert.Equal("\"2023-01-01T00:00:00.000+00:00\"", json); } public class MultiFormatDateTimeObject @@ -66,7 +69,7 @@ public class MultiFormatDateTimeObject [Fact] public void Read_ValidDateStringInObject_ReturnsDateTime() { - var json = "{\"x\": \"2023-01-01T00:00:00.000Z\"}"; + var json = "{\"x\": \"2023-01-01T00:00:00.000+00:00\"}"; var result = JsonSerializer.Deserialize(json, _options); Assert.NotNull(result); @@ -83,6 +86,6 @@ public void Write_ValidDateTimeInObject_WritesExpectedString() { var obj = new MultiFormatDateTimeObject { X = new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc) }; var json = JsonSerializer.Serialize(obj, _options); - Assert.Equal("{\"x\":\"2023-01-01T00:00:00.000Z\"}", json); + Assert.Equal("{\"x\":\"2023-01-01T00:00:00.000+00:00\"}", json); } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/NullableDateTimeConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/NullableDateTimeConverterTests.cs index 2c0fb17a..f050759f 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/NullableDateTimeConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/NullableDateTimeConverterTests.cs @@ -10,14 +10,18 @@ public class NullableDateTimeConverterTests public NullableDateTimeConverterTests() { - _options = new JsonSerializerOptions(); + _options = new JsonSerializerOptions() + { + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + _options.Converters.Add(new NullableDateTimeConverter()); } [Fact] public void Read_ValidDateString_ReturnsDateTime() { - var json = "\"2023-01-01T00:00:00\""; + var json = "\"2023-01-01T00:00:00.000+00:00\""; var result = JsonSerializer.Deserialize(json, _options); Assert.NotNull(result); Assert.Equal(new DateTime(2023, 1, 1), result.Value); @@ -72,9 +76,9 @@ public void Read_UnexpectedTokenType_ThrowsJsonException() [Fact] public void Write_NonNullDateTime_WritesExpectedString() { - var dateTime = new DateTime(2023, 1, 1); + var dateTime = new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc); var json = JsonSerializer.Serialize(dateTime, _options); - Assert.Equal("\"2023-01-01T00:00:00.0000000\"", json); + Assert.Equal("\"2023-01-01T00:00:00.000+00:00\"", json); } [Fact] diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeBooleanTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeBooleanTests.cs new file mode 100644 index 00000000..85ad9cec --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeBooleanTests.cs @@ -0,0 +1,92 @@ +using System.Text.Json; +using Moq; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Responses.Interfaces; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class AttributeBooleanTests +{ + [Fact] + public void Default_ShouldBeSerialized() + { + var attribute = new AttributeBoolean( + "a", + "boolean", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + true + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"default\":true", json); + } + + [Fact] + public void Accept_ShouldInvokeVisitor() + { + var attribute = new AttributeBoolean( + "a", + "boolean", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + true + ); + + var visitorMock = new Mock(); + attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + } + + [Fact] + public void AcceptT_ShouldInvokeVisitor() + { + var attribute = new AttributeBoolean( + "a", + "boolean", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + true + ); + + var visitorMock = new Mock>(); + visitorMock.Setup(v => v.Visit(attribute)).Returns("Visited"); + + var result = attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + Assert.Equal("Visited", result); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + var json = TestConstants.AttributeBooleanResponse; + + var attribute = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.NotNull(attribute); + Assert.Equal("isEnabled", attribute.Key); + Assert.Equal("boolean", attribute.Type); + Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal("string", attribute.Error); + Assert.True(attribute.Required); + Assert.False(attribute.Array); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.UpdatedAt.ToUniversalTime()); + Assert.False(attribute.Default); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index 482bd683..fa40fa57 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -450,4 +450,18 @@ public static class TestConstants ] } """; + + public const string AttributeBooleanResponse = """ + { + "key": "isEnabled", + "type": "boolean", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "default": false + } + """; } From bdc1adb8f96f3f19aa0e9b7734e0baa01907af9e Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 23 Oct 2024 01:20:29 +0100 Subject: [PATCH 010/303] added tests for Attribute Datetime --- .../Responses/AttributeDatetimeTests.cs | 116 ++++++++++++++++++ .../TestConstants.cs | 15 +++ 2 files changed, 131 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeDatetimeTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeDatetimeTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeDatetimeTests.cs new file mode 100644 index 00000000..3e758925 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeDatetimeTests.cs @@ -0,0 +1,116 @@ +using System.Text.Json; +using Moq; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Responses.Interfaces; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class AttributeDatetimeTests +{ + [Fact] + public void Default_ShouldBeSerialized() + { + var attribute = new AttributeDatetime( + "a", + "datetime", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "datetime", + DateTime.UtcNow + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"default\":\"", json); + } + + [Fact] + public void Format_ShouldBeSerialized() + { + var attribute = new AttributeDatetime( + "a", + "datetime", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "datetime", + DateTime.UtcNow + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"format\":\"datetime\"", json); + } + + [Fact] + public void Accept_ShouldInvokeVisitor() + { + var attribute = new AttributeDatetime( + "a", + "datetime", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "datetime", + DateTime.UtcNow + ); + + var visitorMock = new Mock(); + attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + } + + [Fact] + public void AcceptT_ShouldInvokeVisitor() + { + var attribute = new AttributeDatetime( + "a", + "datetime", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "datetime", + DateTime.UtcNow + ); + + var visitorMock = new Mock>(); + visitorMock.Setup(v => v.Visit(attribute)).Returns("Visited"); + + var result = attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + Assert.Equal("Visited", result); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + var json = TestConstants.AttributeDatetimeResponse; + + var attribute = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.NotNull(attribute); + Assert.Equal("birthDay", attribute.Key); + Assert.Equal("datetime", attribute.Type); + Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal("string", attribute.Error); + Assert.True(attribute.Required); + Assert.False(attribute.Array); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.UpdatedAt.ToUniversalTime()); + Assert.Equal("datetime", attribute.Format); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.Default?.ToUniversalTime()); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index fa40fa57..ddf5331b 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -464,4 +464,19 @@ public static class TestConstants "default": false } """; + + public const string AttributeDatetimeResponse = """ + { + "key": "birthDay", + "type": "datetime", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "format": "datetime", + "default": "2020-10-15T06:38:00.000+00:00" + } + """; } From 792975ff5661bb33412205f672f07591592f3b77 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 23 Oct 2024 01:30:30 +0100 Subject: [PATCH 011/303] Added tests for Attribute Email --- .../Responses/AttributeEmailTests.cs | 116 ++++++++++++++++++ .../TestConstants.cs | 15 +++ 2 files changed, 131 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEmailTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEmailTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEmailTests.cs new file mode 100644 index 00000000..40aba6c4 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEmailTests.cs @@ -0,0 +1,116 @@ +using System.Text.Json; +using Moq; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Responses.Interfaces; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class AttributeEmailTests +{ + [Fact] + public void Default_ShouldBeSerialized() + { + var attribute = new AttributeEmail( + "a", + "email", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "email", + "default@example.com" + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"default\":\"default@example.com\"", json); + } + + [Fact] + public void Format_ShouldBeSerialized() + { + var attribute = new AttributeEmail( + "a", + "email", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "email", + "default@example.com" + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"format\":\"email\"", json); + } + + [Fact] + public void Accept_ShouldInvokeVisitor() + { + var attribute = new AttributeEmail( + "a", + "email", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "email", + "default@example.com" + ); + + var visitorMock = new Mock(); + attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + } + + [Fact] + public void AcceptT_ShouldInvokeVisitor() + { + var attribute = new AttributeEmail( + "a", + "email", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "email", + "default@example.com" + ); + + var visitorMock = new Mock>(); + visitorMock.Setup(v => v.Visit(attribute)).Returns("Visited"); + + var result = attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + Assert.Equal("Visited", result); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + var json = TestConstants.AttributeEmailResponse; + + var attribute = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.NotNull(attribute); + Assert.Equal("userEmail", attribute.Key); + Assert.Equal("string", attribute.Type); + Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal("string", attribute.Error); + Assert.True(attribute.Required); + Assert.False(attribute.Array); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.UpdatedAt.ToUniversalTime()); + Assert.Equal("email", attribute.Format); + Assert.Equal("default@example.com", attribute.Default); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index ddf5331b..cebf9fd3 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -479,4 +479,19 @@ public static class TestConstants "default": "2020-10-15T06:38:00.000+00:00" } """; + + public const string AttributeEmailResponse = """ + { + "key": "userEmail", + "type": "string", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "format": "email", + "default": "default@example.com" + } + """; } From 92371d8a8918737481828807844428ff66dbc449 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 23 Oct 2024 01:40:20 +0100 Subject: [PATCH 012/303] Added tests for Attribute enum and float --- .../Responses/AttributeEnumTests.cs | 142 ++++++++++++++++++ .../Responses/AttributeFloatTests.cs | 142 ++++++++++++++++++ .../TestConstants.cs | 34 +++++ 3 files changed, 318 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEnumTests.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeFloatTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEnumTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEnumTests.cs new file mode 100644 index 00000000..c5903cb9 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEnumTests.cs @@ -0,0 +1,142 @@ +using System.Text.Json; +using Moq; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Responses.Interfaces; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class AttributeEnumTests +{ + [Fact] + public void Default_ShouldBeSerialized() + { + var attribute = new AttributeEnum( + "a", + "string", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + ["element1", "element2"], + "enum", + "element1" + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"default\":\"element1\"", json); + } + + [Fact] + public void Elements_ShouldBeSerialized() + { + var attribute = new AttributeEnum( + "a", + "string", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + ["element1", "element2"], + "enum", + "element1" + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"elements\":[\"element1\",\"element2\"]", json); + } + + [Fact] + public void Format_ShouldBeSerialized() + { + var attribute = new AttributeEnum( + "a", + "string", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + ["element1", "element2"], + "enum", + "element1" + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"format\":\"enum\"", json); + } + + [Fact] + public void Accept_ShouldInvokeVisitor() + { + var attribute = new AttributeEnum( + "a", + "string", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + ["element1", "element2"], + "enum", + "element1" + ); + + var visitorMock = new Mock(); + attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + } + + [Fact] + public void AcceptT_ShouldInvokeVisitor() + { + var attribute = new AttributeEnum( + "a", + "string", + AttributeStatus.Available, + null, + false, + false, + DateTime.UtcNow, + DateTime.UtcNow, + ["element1", "element2"], + "enum", + "element1" + ); + + var visitorMock = new Mock>(); + visitorMock.Setup(v => v.Visit(attribute)).Returns("Visited"); + + var result = attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + Assert.Equal("Visited", result); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + var json = TestConstants.AttributeEnumResponse; + + var attribute = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.NotNull(attribute); + Assert.Equal("status", attribute.Key); + Assert.Equal("string", attribute.Type); + Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal("string", attribute.Error); + Assert.True(attribute.Required); + Assert.False(attribute.Array); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.UpdatedAt.ToUniversalTime()); + Assert.Equal(new List { "element" }, attribute.Elements); + Assert.Equal("enum", attribute.Format); + Assert.Equal("element", attribute.Default); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeFloatTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeFloatTests.cs new file mode 100644 index 00000000..2744c226 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeFloatTests.cs @@ -0,0 +1,142 @@ +using System.Text.Json; +using Moq; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Responses.Interfaces; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class AttributeFloatTests +{ + [Fact] + public void Default_ShouldBeSerialized() + { + var attribute = new AttributeFloat( + "percentageCompleted", + "double", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 1.5f, + 10.5f, + 2.5f + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"default\":2.5", json); + } + + [Fact] + public void Min_ShouldBeSerialized() + { + var attribute = new AttributeFloat( + "percentageCompleted", + "double", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 1.5f, + 10.5f, + 2.5f + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"min\":1.5", json); + } + + [Fact] + public void Max_ShouldBeSerialized() + { + var attribute = new AttributeFloat( + "percentageCompleted", + "double", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 1.5f, + 10.5f, + 2.5f + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"max\":10.5", json); + } + + [Fact] + public void Accept_ShouldInvokeVisitor() + { + var attribute = new AttributeFloat( + "percentageCompleted", + "double", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 1.5f, + 10.5f, + 2.5f + ); + + var visitorMock = new Mock(); + attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + } + + [Fact] + public void AcceptT_ShouldInvokeVisitor() + { + var attribute = new AttributeFloat( + "percentageCompleted", + "double", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 1.5f, + 10.5f, + 2.5f + ); + + var visitorMock = new Mock>(); + visitorMock.Setup(v => v.Visit(attribute)).Returns("Visited"); + + var result = attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + Assert.Equal("Visited", result); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + var json = TestConstants.AttributeFloatResponse; + + var attribute = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.NotNull(attribute); + Assert.Equal("percentageCompleted", attribute.Key); + Assert.Equal("double", attribute.Type); + Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal("string", attribute.Error); + Assert.True(attribute.Required); + Assert.False(attribute.Array); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.UpdatedAt.ToUniversalTime()); + Assert.Equal(1.5f, attribute.Min); + Assert.Equal(10.5f, attribute.Max); + Assert.Equal(2.5f, attribute.Default); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index cebf9fd3..75544f50 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -494,4 +494,38 @@ public static class TestConstants "default": "default@example.com" } """; + + public const string AttributeEnumResponse = """ + { + "key": "status", + "type": "string", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "elements": [ + "element" + ], + "format": "enum", + "default": "element" + } + """; + + public const string AttributeFloatResponse = """ + { + "key": "percentageCompleted", + "type": "double", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "min": 1.5, + "max": 10.5, + "default": 2.5 + } + """; } From cede97ff1704ca92e440bcaadadaea442cf70138 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 23 Oct 2024 01:50:27 +0100 Subject: [PATCH 013/303] Added tests for Attribute Integer --- .../Responses/AttributeIntegerTests.cs | 142 ++++++++++++++++++ .../TestConstants.cs | 16 ++ 2 files changed, 158 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIntegerTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIntegerTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIntegerTests.cs new file mode 100644 index 00000000..93a74aec --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIntegerTests.cs @@ -0,0 +1,142 @@ +using System.Text.Json; +using Moq; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Responses.Interfaces; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class AttributeIntegerTests +{ + [Fact] + public void Default_ShouldBeSerialized() + { + var attribute = new AttributeInteger( + "count", + "integer", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 1, + 10, + 10 + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"default\":10", json); + } + + [Fact] + public void Min_ShouldBeSerialized() + { + var attribute = new AttributeInteger( + "count", + "integer", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 1, + 10, + 10 + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"min\":1", json); + } + + [Fact] + public void Max_ShouldBeSerialized() + { + var attribute = new AttributeInteger( + "count", + "integer", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 1, + 10, + 10 + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"max\":10", json); + } + + [Fact] + public void Accept_ShouldInvokeVisitor() + { + var attribute = new AttributeInteger( + "count", + "integer", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 1, + 10, + 10 + ); + + var visitorMock = new Mock(); + attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + } + + [Fact] + public void AcceptT_ShouldInvokeVisitor() + { + var attribute = new AttributeInteger( + "count", + "integer", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 1, + 10, + 10 + ); + + var visitorMock = new Mock>(); + visitorMock.Setup(v => v.Visit(attribute)).Returns("Visited"); + + var result = attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + Assert.Equal("Visited", result); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + var json = TestConstants.AttributeIntegerResponse; + + var attribute = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.NotNull(attribute); + Assert.Equal("count", attribute.Key); + Assert.Equal("integer", attribute.Type); + Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal("string", attribute.Error); + Assert.True(attribute.Required); + Assert.False(attribute.Array); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.UpdatedAt.ToUniversalTime()); + Assert.Equal(1, attribute.Min); + Assert.Equal(10, attribute.Max); + Assert.Equal(10, attribute.Default); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index 75544f50..54e16a9c 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -528,4 +528,20 @@ public static class TestConstants "default": 2.5 } """; + + public const string AttributeIntegerResponse = """ + { + "key": "count", + "type": "integer", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "min": 1, + "max": 10, + "default": 10 + } + """; } From e1d71426001d4df6938fca6c7c107b1db6282bf3 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 23 Oct 2024 02:02:01 +0100 Subject: [PATCH 014/303] Added tests for Attribute Ip --- .../Responses/AttributeIpTests.cs | 116 ++++++++++++++++++ .../TestConstants.cs | 15 +++ 2 files changed, 131 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIpTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIpTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIpTests.cs new file mode 100644 index 00000000..945bde10 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIpTests.cs @@ -0,0 +1,116 @@ +using System.Text.Json; +using Moq; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Responses.Interfaces; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class AttributeIpTests +{ + [Fact] + public void Default_ShouldBeSerialized() + { + var attribute = new AttributeIp( + "ipAddress", + "string", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "ip", + "192.0.2.0" + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"default\":\"192.0.2.0\"", json); + } + + [Fact] + public void Format_ShouldBeSerialized() + { + var attribute = new AttributeIp( + "ipAddress", + "string", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "ip", + "192.0.2.0" + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"format\":\"ip\"", json); + } + + [Fact] + public void Accept_ShouldInvokeVisitor() + { + var attribute = new AttributeIp( + "ipAddress", + "string", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "ip", + "192.0.2.0" + ); + + var visitorMock = new Mock(); + attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + } + + [Fact] + public void AcceptT_ShouldInvokeVisitor() + { + var attribute = new AttributeIp( + "ipAddress", + "string", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "ip", + "192.0.2.0" + ); + + var visitorMock = new Mock>(); + visitorMock.Setup(v => v.Visit(attribute)).Returns("Visited"); + + var result = attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + Assert.Equal("Visited", result); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + var json = TestConstants.AttributeIpResponse; + + var attribute = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.NotNull(attribute); + Assert.Equal("ipAddress", attribute.Key); + Assert.Equal("string", attribute.Type); + Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal("string", attribute.Error); + Assert.True(attribute.Required); + Assert.False(attribute.Array); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.UpdatedAt.ToUniversalTime()); + Assert.Equal("ip", attribute.Format); + Assert.Equal("192.0.2.0", attribute.Default); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index 54e16a9c..85d45c71 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -544,4 +544,19 @@ public static class TestConstants "default": 10 } """; + + public const string AttributeIpResponse = """ + { + "key": "ipAddress", + "type": "string", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "format": "ip", + "default": "192.0.2.0" + } + """; } From a76af2570bc2b2981c5141a4276cd2f2fcf46355 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 23 Oct 2024 02:12:39 +0100 Subject: [PATCH 015/303] Added tests for Attribute Relationship --- .../Responses/AttributeRelationshipTests.cs | 232 ++++++++++++++++++ .../TestConstants.cs | 19 ++ 2 files changed, 251 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeRelationshipTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeRelationshipTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeRelationshipTests.cs new file mode 100644 index 00000000..3dcc1d08 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeRelationshipTests.cs @@ -0,0 +1,232 @@ +using System.Text.Json; +using Moq; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Responses.Interfaces; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class AttributeRelationshipTests +{ + [Fact] + public void RelatedCollection_ShouldBeSerialized() + { + var attribute = new AttributeRelationship( + "fullName", + "string", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "collection", + RelationType.OneToOne, + false, + "string", + OnDelete.Restrict, + RelationshipSide.Parent + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"relatedCollection\":\"collection\"", json); + } + + [Fact] + public void RelationType_ShouldBeSerialized() + { + var attribute = new AttributeRelationship( + "fullName", + "string", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "collection", + RelationType.OneToOne, + false, + "string", + OnDelete.Restrict, + RelationshipSide.Parent + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"relationType\":\"oneToOne\"", json); + } + + [Fact] + public void TwoWay_ShouldBeSerialized() + { + var attribute = new AttributeRelationship( + "fullName", + "string", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "collection", + RelationType.OneToOne, + false, + "string", + OnDelete.Restrict, + RelationshipSide.Parent + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"twoWay\":false", json); + } + + [Fact] + public void TwoWayKey_ShouldBeSerialized() + { + var attribute = new AttributeRelationship( + "fullName", + "string", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "collection", + RelationType.OneToOne, + false, + "string", + OnDelete.Restrict, + RelationshipSide.Parent + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"twoWayKey\":\"string\"", json); + } + + [Fact] + public void OnDelete_ShouldBeSerialized() + { + var attribute = new AttributeRelationship( + "fullName", + "string", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "collection", + RelationType.OneToOne, + false, + "string", + OnDelete.Restrict, + RelationshipSide.Parent + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"onDelete\":\"restrict\"", json); + } + + [Fact] + public void Side_ShouldBeSerialized() + { + var attribute = new AttributeRelationship( + "fullName", + "string", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "collection", + RelationType.OneToOne, + false, + "string", + OnDelete.Restrict, + RelationshipSide.Parent + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"side\":\"parent\"", json); + } + + [Fact] + public void Accept_ShouldInvokeVisitor() + { + var attribute = new AttributeRelationship( + "fullName", + "string", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "collection", + RelationType.OneToOne, + false, + "string", + OnDelete.Restrict, + RelationshipSide.Parent + ); + + var visitorMock = new Mock(); + attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + } + + [Fact] + public void AcceptT_ShouldInvokeVisitor() + { + var attribute = new AttributeRelationship( + "fullName", + "string", + AttributeStatus.Available, + null, + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "collection", + RelationType.OneToOne, + false, + "string", + OnDelete.Restrict, + RelationshipSide.Parent + ); + + var visitorMock = new Mock>(); + visitorMock.Setup(v => v.Visit(attribute)).Returns("Visited"); + + var result = attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + Assert.Equal("Visited", result); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + var json = TestConstants.AttributeRelationshipResponse; + + var attribute = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.NotNull(attribute); + Assert.Equal("fullName", attribute.Key); + Assert.Equal("string", attribute.Type); + Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal("string", attribute.Error); + Assert.True(attribute.Required); + Assert.False(attribute.Array); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.UpdatedAt.ToUniversalTime()); + Assert.Equal("collection", attribute.RelatedCollection); + Assert.Equal(RelationType.ManyToOne, attribute.RelationType); + Assert.False(attribute.TwoWay); + Assert.Equal("string", attribute.TwoWayKey); + Assert.Equal(OnDelete.Cascade, attribute.OnDelete); + Assert.Equal(RelationshipSide.Child, attribute.Side); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index 85d45c71..dcacb17a 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -559,4 +559,23 @@ public static class TestConstants "default": "192.0.2.0" } """; + + public const string AttributeRelationshipResponse = """ + { + "key": "fullName", + "type": "string", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "relatedCollection": "collection", + "relationType": "manyToOne", + "twoWay": false, + "twoWayKey": "string", + "onDelete": "cascade", + "side": "child" + } + """; } From 41fdbb2cf6cba4767de40769b4d23f357dd4c681 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 23 Oct 2024 02:22:52 +0100 Subject: [PATCH 016/303] Added tests for Attribute string and url --- .../Responses/AttributeStringTests.cs | 116 ++++++++++++++++++ .../Responses/AttributeUrlTests.cs | 114 +++++++++++++++++ .../TestConstants.cs | 30 +++++ 3 files changed, 260 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeStringTests.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeUrlTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeStringTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeStringTests.cs new file mode 100644 index 00000000..e4806ab4 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeStringTests.cs @@ -0,0 +1,116 @@ +using System.Text.Json; +using Moq; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Responses.Interfaces; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class AttributeStringTests +{ + [Fact] + public void Size_ShouldBeSerialized() + { + var attribute = new AttributeString( + "fullName", + "string", + AttributeStatus.Available, + "string", + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 128, + "default" + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"size\":128", json); + } + + [Fact] + public void Default_ShouldBeSerialized() + { + var attribute = new AttributeString( + "fullName", + "string", + AttributeStatus.Available, + "string", + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 128, + "default" + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"default\":\"default\"", json); + } + + [Fact] + public void Accept_ShouldInvokeVisitor() + { + var attribute = new AttributeString( + "fullName", + "string", + AttributeStatus.Available, + "string", + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 128, + "default" + ); + + var visitorMock = new Mock(); + attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + } + + [Fact] + public void AcceptT_ShouldInvokeVisitor() + { + var attribute = new AttributeString( + "fullName", + "string", + AttributeStatus.Available, + "string", + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + 128, + "default" + ); + + var visitorMock = new Mock>(); + visitorMock.Setup(v => v.Visit(attribute)).Returns("Visited"); + + var result = attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + Assert.Equal("Visited", result); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + var json = TestConstants.AttributeStringResponse; + + var attribute = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.NotNull(attribute); + Assert.Equal("fullName", attribute.Key); + Assert.Equal("string", attribute.Type); + Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal("string", attribute.Error); + Assert.True(attribute.Required); + Assert.False(attribute.Array); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.UpdatedAt.ToUniversalTime()); + Assert.Equal(128, attribute.Size); + Assert.Equal("default", attribute.Default); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeUrlTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeUrlTests.cs new file mode 100644 index 00000000..67931c33 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeUrlTests.cs @@ -0,0 +1,114 @@ +using System.Text.Json; +using Moq; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Responses.Interfaces; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class AttributeUrlTests +{ + [Fact] + public void Format_ShouldBeSerialized() + { + var attribute = new AttributeUrl( + "githubUrl", + "string", + AttributeStatus.Available, + "string", + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "url", + "http://example.com" + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"format\":\"url\"", json); + } + + [Fact] + public void Default_ShouldBeSerialized() + { + var attribute = new AttributeUrl( + "githubUrl", + "string", + AttributeStatus.Available, + "string", + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "url", + "http://example.com" + ); + + var json = JsonSerializer.Serialize(attribute); + Assert.Contains("\"default\":\"http://example.com\"", json); + } + + [Fact] + public void Accept_ShouldInvokeVisitor() + { + var attribute = new AttributeUrl( + "githubUrl", + "string", + AttributeStatus.Available, + "string", + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "url", + "http://example.com" + ); + + var visitorMock = new Mock(); + attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + } + + [Fact] + public void AcceptT_ShouldInvokeVisitor() + { + var attribute = new AttributeUrl( + "githubUrl", + "string", + AttributeStatus.Available, + "string", + true, + false, + DateTime.UtcNow, + DateTime.UtcNow, + "url", + "http://example.com" + ); + + var visitorMock = new Mock>(); + visitorMock.Setup(v => v.Visit(attribute)).Returns("Visited"); + + var result = attribute.Accept(visitorMock.Object); + + visitorMock.Verify(v => v.Visit(attribute), Times.Once); + Assert.Equal("Visited", result); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + var attribute = JsonSerializer.Deserialize(TestConstants.AttributeUrlResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.NotNull(attribute); + Assert.Equal("githubUrl", attribute.Key); + Assert.Equal("string", attribute.Type); + Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal("string", attribute.Error); + Assert.True(attribute.Required); + Assert.False(attribute.Array); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), attribute.UpdatedAt.ToUniversalTime()); + Assert.Equal("url", attribute.Format); + Assert.Equal("http://example.com", attribute.Default); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index dcacb17a..b6a68327 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -578,4 +578,34 @@ public static class TestConstants "side": "child" } """; + + public const string AttributeStringResponse = """ + { + "key": "fullName", + "type": "string", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "size": 128, + "default": "default" + } + """; + + public const string AttributeUrlResponse = """ + { + "key": "githubUrl", + "type": "string", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "format": "url", + "default": "http://example.com" + } + """; } From d32ed44a1d61c966a2b9daed4bf576a4ba2bc8c2 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 24 Oct 2024 18:16:37 +0100 Subject: [PATCH 017/303] Added Collection and Index, and renamed AttributeStatus to DatabaseElementStatus --- src/PinguApps.Appwrite.Playground/App.cs | 26 +++++++++------ ...buteStatus.cs => DatabaseElementStatus.cs} | 2 +- .../Enums/IndexType.cs | 20 ++++++++++++ .../Responses/Attribute.cs | 2 +- .../Responses/AttributeBoolean.cs | 2 +- .../Responses/AttributeDatetime.cs | 2 +- .../Responses/AttributeEmail.cs | 2 +- .../Responses/AttributeEnum.cs | 2 +- .../Responses/AttributeFloat.cs | 2 +- .../Responses/AttributeInteger.cs | 2 +- .../Responses/AttributeIp.cs | 2 +- .../Responses/AttributeRelationship.cs | 2 +- .../Responses/AttributeString.cs | 2 +- .../Responses/AttributeUrl.cs | 2 +- .../Responses/Collection.cs | 32 +++++++++++++++++++ .../Responses/Index.cs | 29 +++++++++++++++++ .../Converters/AttributeJsonConverterTests.cs | 2 +- .../AttributeListJsonConverterTests.cs | 4 +-- .../Responses/AttributeBooleanTests.cs | 8 ++--- .../Responses/AttributeDatetimeTests.cs | 10 +++--- .../Responses/AttributeEmailTests.cs | 10 +++--- .../Responses/AttributeEnumTests.cs | 12 +++---- .../Responses/AttributeFloatTests.cs | 12 +++---- .../Responses/AttributeIntegerTests.cs | 12 +++---- .../Responses/AttributeIpTests.cs | 10 +++--- .../Responses/AttributeRelationshipTests.cs | 18 +++++------ .../Responses/AttributeStringTests.cs | 10 +++--- .../Responses/AttributeUrlTests.cs | 10 +++--- .../Responses/AttributesListTests.cs | 4 +-- 29 files changed, 170 insertions(+), 83 deletions(-) rename src/PinguApps.Appwrite.Shared/Enums/{AttributeStatus.cs => DatabaseElementStatus.cs} (95%) create mode 100644 src/PinguApps.Appwrite.Shared/Enums/IndexType.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/Collection.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/Index.cs diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index a5deb2dd..02b8ceff 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -24,16 +24,16 @@ public async Task Run(string[] args) { var attributes = new List { - new AttributeBoolean("a", "boolean", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null), - new AttributeDatetime("b", "datetime", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "datetime", null), - new AttributeEmail("c", "string", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "email", null), - new AttributeEnum("d", "string", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, ["some", "elements"], "enum", null), - new AttributeFloat("e", "float", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null, null, null), - new AttributeInteger("f", "integer", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null, null, null), - new AttributeIp("g", "string", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "ip", null), - new AttributeRelationship("h", "string", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "collection", Shared.Enums.RelationType.OneToMany, false, "a string", Shared.Enums.OnDelete.Restrict, Shared.Enums.RelationshipSide.Parent), - new AttributeString("i", "string", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, 128, null), - new AttributeUrl("j", "string", Shared.Enums.AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "url", null) + new AttributeBoolean("a", "boolean", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null), + new AttributeDatetime("b", "datetime", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "datetime", null), + new AttributeEmail("c", "string", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "email", null), + new AttributeEnum("d", "string", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, ["some", "elements"], "enum", null), + new AttributeFloat("e", "float", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null, null, null), + new AttributeInteger("f", "integer", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null, null, null), + new AttributeIp("g", "string", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "ip", null), + new AttributeRelationship("h", "string", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "collection", Shared.Enums.RelationType.OneToMany, false, "a string", Shared.Enums.OnDelete.Restrict, Shared.Enums.RelationshipSide.Parent), + new AttributeString("i", "string", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, 128, null), + new AttributeUrl("j", "string", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "url", null) }; var visitor = new AttributeVisitor(); @@ -54,6 +54,12 @@ public async Task Run(string[] args) var json = JsonSerializer.Serialize(attributes[0]); Console.WriteLine(json); + + var x = new Shared.Responses.Index("Key", Shared.Enums.IndexType.Fulltext); + + var j = JsonSerializer.Serialize(x, options); + + Console.WriteLine(j); } } diff --git a/src/PinguApps.Appwrite.Shared/Enums/AttributeStatus.cs b/src/PinguApps.Appwrite.Shared/Enums/DatabaseElementStatus.cs similarity index 95% rename from src/PinguApps.Appwrite.Shared/Enums/AttributeStatus.cs rename to src/PinguApps.Appwrite.Shared/Enums/DatabaseElementStatus.cs index 38c789e9..4833a2ed 100644 --- a/src/PinguApps.Appwrite.Shared/Enums/AttributeStatus.cs +++ b/src/PinguApps.Appwrite.Shared/Enums/DatabaseElementStatus.cs @@ -5,7 +5,7 @@ namespace PinguApps.Appwrite.Shared.Enums; /// /// An Appwrite Status of an Attribute /// -public enum AttributeStatus +public enum DatabaseElementStatus { /// /// Available diff --git a/src/PinguApps.Appwrite.Shared/Enums/IndexType.cs b/src/PinguApps.Appwrite.Shared/Enums/IndexType.cs new file mode 100644 index 00000000..0349b501 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Enums/IndexType.cs @@ -0,0 +1,20 @@ +namespace PinguApps.Appwrite.Shared.Enums; + +/// +/// An Appwrite IndexType enum for Collection Indexes +/// +public enum IndexType +{ + /// + /// Key + /// + Key, + /// + /// Unique + /// + Unique, + /// + /// Fulltext + /// + Fulltext +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs index 51d56823..cc4a44b5 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs @@ -20,7 +20,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public abstract record Attribute( [property: JsonPropertyName("key")] string Key, [property: JsonPropertyName("type")] string Type, - [property: JsonPropertyName("status"), JsonConverter(typeof(CamelCaseEnumConverter))] AttributeStatus Status, + [property: JsonPropertyName("status"), JsonConverter(typeof(CamelCaseEnumConverter))] DatabaseElementStatus Status, [property: JsonPropertyName("error")] string? Error, [property: JsonPropertyName("required")] bool Required, [property: JsonPropertyName("array")] bool Array, diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeBoolean.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeBoolean.cs index 94b5f720..f79d81f2 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeBoolean.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeBoolean.cs @@ -20,7 +20,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public record AttributeBoolean( string Key, string Type, - AttributeStatus Status, + DatabaseElementStatus Status, string? Error, bool Required, bool Array, diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeDatetime.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeDatetime.cs index 03bcd709..12043bac 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeDatetime.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeDatetime.cs @@ -22,7 +22,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public record AttributeDatetime( string Key, string Type, - AttributeStatus Status, + DatabaseElementStatus Status, string? Error, bool Required, bool Array, diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeEmail.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeEmail.cs index bb9854f2..47a97101 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeEmail.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeEmail.cs @@ -21,7 +21,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public record AttributeEmail( string Key, string Type, - AttributeStatus Status, + DatabaseElementStatus Status, string? Error, bool Required, bool Array, diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeEnum.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeEnum.cs index 2ebfe6f7..c5b012da 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeEnum.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeEnum.cs @@ -23,7 +23,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public record AttributeEnum( string Key, string Type, - AttributeStatus Status, + DatabaseElementStatus Status, string? Error, bool Required, bool Array, diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs index 421de5cc..0dbab8e2 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs @@ -22,7 +22,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public record AttributeFloat( string Key, string Type, - AttributeStatus Status, + DatabaseElementStatus Status, string? Error, bool Required, bool Array, diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs index e9c630ee..db324627 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs @@ -22,7 +22,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public record AttributeInteger( string Key, string Type, - AttributeStatus Status, + DatabaseElementStatus Status, string? Error, bool Required, bool Array, diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeIp.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeIp.cs index 1ad3b4a1..3f122235 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeIp.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeIp.cs @@ -21,7 +21,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public record AttributeIp( string Key, string Type, - AttributeStatus Status, + DatabaseElementStatus Status, string? Error, bool Required, bool Array, diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs index 8bd188a7..7b591748 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeRelationship.cs @@ -26,7 +26,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public record AttributeRelationship( string Key, string Type, - AttributeStatus Status, + DatabaseElementStatus Status, string? Error, bool Required, bool Array, diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeString.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeString.cs index 11aad8bd..a352ee25 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeString.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeString.cs @@ -21,7 +21,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public record AttributeString( string Key, string Type, - AttributeStatus Status, + DatabaseElementStatus Status, string? Error, bool Required, bool Array, diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeUrl.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeUrl.cs index 7b4bafb1..60d2d085 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeUrl.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeUrl.cs @@ -21,7 +21,7 @@ namespace PinguApps.Appwrite.Shared.Responses; public record AttributeUrl( string Key, string Type, - AttributeStatus Status, + DatabaseElementStatus Status, string? Error, bool Required, bool Array, diff --git a/src/PinguApps.Appwrite.Shared/Responses/Collection.cs b/src/PinguApps.Appwrite.Shared/Responses/Collection.cs new file mode 100644 index 00000000..47ffda15 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/Collection.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite Collection object +/// +/// Collection ID +/// Collection creation date in ISO 8601 format +/// Collection update date in ISO 8601 format +/// Collection permissions. Learn more about permissions +/// Database ID +/// Collection name +/// Collection enabled. Can be 'enabled' or 'disabled'. When disabled, the collection is inaccessible to users, but remains accessible to Server SDKs using API keys +/// Whether document-level permissions are enabled. Learn more about permissions +/// Collection attributes. Can be one of: , , , , , , , , , +/// Collection indexes +public record Collection( + [property: JsonPropertyName("$id")] string Id, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, + [property: JsonPropertyName("$permissions")] IReadOnlyList Permissions, + [property: JsonPropertyName("databaseId")] string DatabaseId, + [property: JsonPropertyName("name")] string Name, + [property: JsonPropertyName("enabled")] bool Enabled, + [property: JsonPropertyName("documentSecurity")] bool DocumentSecurity, + [property: JsonPropertyName("attributes"), JsonConverter(typeof(AttributeListJsonConverter))] List Attributes, + [property: JsonPropertyName("indexes")] List Indexes +); diff --git a/src/PinguApps.Appwrite.Shared/Responses/Index.cs b/src/PinguApps.Appwrite.Shared/Responses/Index.cs new file mode 100644 index 00000000..166820b6 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/Index.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite Index object +/// +/// Index Key +/// Index Type +/// Index status. Possible values: available, processing, deleting, stuck, or failed +/// Error message. Displays error generated on failure of creating or deleting an index +/// Index attributes +/// Index orders +/// Index creation date in ISO 8601 format +/// Index update date in ISO 8601 format +public record Index( + [property: JsonPropertyName("key")] string Key, + [property: JsonPropertyName("type"), JsonConverter(typeof(CamelCaseEnumConverter))] IndexType Type, + [property: JsonPropertyName("status"), JsonConverter(typeof(CamelCaseEnumConverter))] DatabaseElementStatus Status, + [property: JsonPropertyName("error")] string? Error, + [property: JsonPropertyName("attributes")] IReadOnlyList Attributes, + [property: JsonPropertyName("orders")] IReadOnlyList Orders, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt +); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeJsonConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeJsonConverterTests.cs index 02411725..7f277a19 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeJsonConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeJsonConverterTests.cs @@ -123,7 +123,7 @@ public void Read_ShouldThrowJsonExceptionForMissingType() public void Write_ShouldSerializeAttribute() { var fixedDate = new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc); - var attribute = new AttributeBoolean("a", "boolean", AttributeStatus.Available, null, false, false, fixedDate, fixedDate, null); + var attribute = new AttributeBoolean("a", "boolean", DatabaseElementStatus.Available, null, false, false, fixedDate, fixedDate, null); var converter = new AttributeJsonConverter(); using var stream = new MemoryStream(); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeListJsonConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeListJsonConverterTests.cs index 66d4fbd0..3db420fa 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeListJsonConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AttributeListJsonConverterTests.cs @@ -42,8 +42,8 @@ public void Write_ShouldSerializeAttributeList() { var attributes = new List { - new AttributeBoolean("a", "boolean", AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null), - new AttributeInteger("b", "integer", AttributeStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null, null, null) + new AttributeBoolean("a", "boolean", DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null), + new AttributeInteger("b", "integer", DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null, null, null) }; var json = JsonSerializer.Serialize((IReadOnlyList)attributes, _options); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeBooleanTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeBooleanTests.cs index 85ad9cec..25d2da9d 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeBooleanTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeBooleanTests.cs @@ -13,7 +13,7 @@ public void Default_ShouldBeSerialized() var attribute = new AttributeBoolean( "a", "boolean", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -32,7 +32,7 @@ public void Accept_ShouldInvokeVisitor() var attribute = new AttributeBoolean( "a", "boolean", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -53,7 +53,7 @@ public void AcceptT_ShouldInvokeVisitor() var attribute = new AttributeBoolean( "a", "boolean", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -81,7 +81,7 @@ public void CanBeDeserialized_FromJson() Assert.NotNull(attribute); Assert.Equal("isEnabled", attribute.Key); Assert.Equal("boolean", attribute.Type); - Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal(DatabaseElementStatus.Available, attribute.Status); Assert.Equal("string", attribute.Error); Assert.True(attribute.Required); Assert.False(attribute.Array); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeDatetimeTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeDatetimeTests.cs index 3e758925..7bf4f745 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeDatetimeTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeDatetimeTests.cs @@ -13,7 +13,7 @@ public void Default_ShouldBeSerialized() var attribute = new AttributeDatetime( "a", "datetime", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -33,7 +33,7 @@ public void Format_ShouldBeSerialized() var attribute = new AttributeDatetime( "a", "datetime", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -53,7 +53,7 @@ public void Accept_ShouldInvokeVisitor() var attribute = new AttributeDatetime( "a", "datetime", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -75,7 +75,7 @@ public void AcceptT_ShouldInvokeVisitor() var attribute = new AttributeDatetime( "a", "datetime", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -104,7 +104,7 @@ public void CanBeDeserialized_FromJson() Assert.NotNull(attribute); Assert.Equal("birthDay", attribute.Key); Assert.Equal("datetime", attribute.Type); - Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal(DatabaseElementStatus.Available, attribute.Status); Assert.Equal("string", attribute.Error); Assert.True(attribute.Required); Assert.False(attribute.Array); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEmailTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEmailTests.cs index 40aba6c4..4ce68010 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEmailTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEmailTests.cs @@ -13,7 +13,7 @@ public void Default_ShouldBeSerialized() var attribute = new AttributeEmail( "a", "email", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -33,7 +33,7 @@ public void Format_ShouldBeSerialized() var attribute = new AttributeEmail( "a", "email", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -53,7 +53,7 @@ public void Accept_ShouldInvokeVisitor() var attribute = new AttributeEmail( "a", "email", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -75,7 +75,7 @@ public void AcceptT_ShouldInvokeVisitor() var attribute = new AttributeEmail( "a", "email", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -104,7 +104,7 @@ public void CanBeDeserialized_FromJson() Assert.NotNull(attribute); Assert.Equal("userEmail", attribute.Key); Assert.Equal("string", attribute.Type); - Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal(DatabaseElementStatus.Available, attribute.Status); Assert.Equal("string", attribute.Error); Assert.True(attribute.Required); Assert.False(attribute.Array); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEnumTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEnumTests.cs index c5903cb9..76226390 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEnumTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeEnumTests.cs @@ -13,7 +13,7 @@ public void Default_ShouldBeSerialized() var attribute = new AttributeEnum( "a", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -34,7 +34,7 @@ public void Elements_ShouldBeSerialized() var attribute = new AttributeEnum( "a", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -55,7 +55,7 @@ public void Format_ShouldBeSerialized() var attribute = new AttributeEnum( "a", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -76,7 +76,7 @@ public void Accept_ShouldInvokeVisitor() var attribute = new AttributeEnum( "a", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -99,7 +99,7 @@ public void AcceptT_ShouldInvokeVisitor() var attribute = new AttributeEnum( "a", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, false, false, @@ -129,7 +129,7 @@ public void CanBeDeserialized_FromJson() Assert.NotNull(attribute); Assert.Equal("status", attribute.Key); Assert.Equal("string", attribute.Type); - Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal(DatabaseElementStatus.Available, attribute.Status); Assert.Equal("string", attribute.Error); Assert.True(attribute.Required); Assert.False(attribute.Array); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeFloatTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeFloatTests.cs index 2744c226..d6c8d8d4 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeFloatTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeFloatTests.cs @@ -13,7 +13,7 @@ public void Default_ShouldBeSerialized() var attribute = new AttributeFloat( "percentageCompleted", "double", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -34,7 +34,7 @@ public void Min_ShouldBeSerialized() var attribute = new AttributeFloat( "percentageCompleted", "double", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -55,7 +55,7 @@ public void Max_ShouldBeSerialized() var attribute = new AttributeFloat( "percentageCompleted", "double", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -76,7 +76,7 @@ public void Accept_ShouldInvokeVisitor() var attribute = new AttributeFloat( "percentageCompleted", "double", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -99,7 +99,7 @@ public void AcceptT_ShouldInvokeVisitor() var attribute = new AttributeFloat( "percentageCompleted", "double", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -129,7 +129,7 @@ public void CanBeDeserialized_FromJson() Assert.NotNull(attribute); Assert.Equal("percentageCompleted", attribute.Key); Assert.Equal("double", attribute.Type); - Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal(DatabaseElementStatus.Available, attribute.Status); Assert.Equal("string", attribute.Error); Assert.True(attribute.Required); Assert.False(attribute.Array); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIntegerTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIntegerTests.cs index 93a74aec..f8a9aa50 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIntegerTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIntegerTests.cs @@ -13,7 +13,7 @@ public void Default_ShouldBeSerialized() var attribute = new AttributeInteger( "count", "integer", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -34,7 +34,7 @@ public void Min_ShouldBeSerialized() var attribute = new AttributeInteger( "count", "integer", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -55,7 +55,7 @@ public void Max_ShouldBeSerialized() var attribute = new AttributeInteger( "count", "integer", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -76,7 +76,7 @@ public void Accept_ShouldInvokeVisitor() var attribute = new AttributeInteger( "count", "integer", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -99,7 +99,7 @@ public void AcceptT_ShouldInvokeVisitor() var attribute = new AttributeInteger( "count", "integer", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -129,7 +129,7 @@ public void CanBeDeserialized_FromJson() Assert.NotNull(attribute); Assert.Equal("count", attribute.Key); Assert.Equal("integer", attribute.Type); - Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal(DatabaseElementStatus.Available, attribute.Status); Assert.Equal("string", attribute.Error); Assert.True(attribute.Required); Assert.False(attribute.Array); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIpTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIpTests.cs index 945bde10..3e792dcb 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIpTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeIpTests.cs @@ -13,7 +13,7 @@ public void Default_ShouldBeSerialized() var attribute = new AttributeIp( "ipAddress", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -33,7 +33,7 @@ public void Format_ShouldBeSerialized() var attribute = new AttributeIp( "ipAddress", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -53,7 +53,7 @@ public void Accept_ShouldInvokeVisitor() var attribute = new AttributeIp( "ipAddress", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -75,7 +75,7 @@ public void AcceptT_ShouldInvokeVisitor() var attribute = new AttributeIp( "ipAddress", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -104,7 +104,7 @@ public void CanBeDeserialized_FromJson() Assert.NotNull(attribute); Assert.Equal("ipAddress", attribute.Key); Assert.Equal("string", attribute.Type); - Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal(DatabaseElementStatus.Available, attribute.Status); Assert.Equal("string", attribute.Error); Assert.True(attribute.Required); Assert.False(attribute.Array); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeRelationshipTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeRelationshipTests.cs index 3dcc1d08..948a0a27 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeRelationshipTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeRelationshipTests.cs @@ -13,7 +13,7 @@ public void RelatedCollection_ShouldBeSerialized() var attribute = new AttributeRelationship( "fullName", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -37,7 +37,7 @@ public void RelationType_ShouldBeSerialized() var attribute = new AttributeRelationship( "fullName", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -61,7 +61,7 @@ public void TwoWay_ShouldBeSerialized() var attribute = new AttributeRelationship( "fullName", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -85,7 +85,7 @@ public void TwoWayKey_ShouldBeSerialized() var attribute = new AttributeRelationship( "fullName", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -109,7 +109,7 @@ public void OnDelete_ShouldBeSerialized() var attribute = new AttributeRelationship( "fullName", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -133,7 +133,7 @@ public void Side_ShouldBeSerialized() var attribute = new AttributeRelationship( "fullName", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -157,7 +157,7 @@ public void Accept_ShouldInvokeVisitor() var attribute = new AttributeRelationship( "fullName", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -183,7 +183,7 @@ public void AcceptT_ShouldInvokeVisitor() var attribute = new AttributeRelationship( "fullName", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, null, true, false, @@ -216,7 +216,7 @@ public void CanBeDeserialized_FromJson() Assert.NotNull(attribute); Assert.Equal("fullName", attribute.Key); Assert.Equal("string", attribute.Type); - Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal(DatabaseElementStatus.Available, attribute.Status); Assert.Equal("string", attribute.Error); Assert.True(attribute.Required); Assert.False(attribute.Array); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeStringTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeStringTests.cs index e4806ab4..70fd32bb 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeStringTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeStringTests.cs @@ -13,7 +13,7 @@ public void Size_ShouldBeSerialized() var attribute = new AttributeString( "fullName", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, "string", true, false, @@ -33,7 +33,7 @@ public void Default_ShouldBeSerialized() var attribute = new AttributeString( "fullName", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, "string", true, false, @@ -53,7 +53,7 @@ public void Accept_ShouldInvokeVisitor() var attribute = new AttributeString( "fullName", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, "string", true, false, @@ -75,7 +75,7 @@ public void AcceptT_ShouldInvokeVisitor() var attribute = new AttributeString( "fullName", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, "string", true, false, @@ -104,7 +104,7 @@ public void CanBeDeserialized_FromJson() Assert.NotNull(attribute); Assert.Equal("fullName", attribute.Key); Assert.Equal("string", attribute.Type); - Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal(DatabaseElementStatus.Available, attribute.Status); Assert.Equal("string", attribute.Error); Assert.True(attribute.Required); Assert.False(attribute.Array); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeUrlTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeUrlTests.cs index 67931c33..4bf1ec65 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeUrlTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributeUrlTests.cs @@ -13,7 +13,7 @@ public void Format_ShouldBeSerialized() var attribute = new AttributeUrl( "githubUrl", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, "string", true, false, @@ -33,7 +33,7 @@ public void Default_ShouldBeSerialized() var attribute = new AttributeUrl( "githubUrl", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, "string", true, false, @@ -53,7 +53,7 @@ public void Accept_ShouldInvokeVisitor() var attribute = new AttributeUrl( "githubUrl", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, "string", true, false, @@ -75,7 +75,7 @@ public void AcceptT_ShouldInvokeVisitor() var attribute = new AttributeUrl( "githubUrl", "string", - AttributeStatus.Available, + DatabaseElementStatus.Available, "string", true, false, @@ -102,7 +102,7 @@ public void CanBeDeserialized_FromJson() Assert.NotNull(attribute); Assert.Equal("githubUrl", attribute.Key); Assert.Equal("string", attribute.Type); - Assert.Equal(AttributeStatus.Available, attribute.Status); + Assert.Equal(DatabaseElementStatus.Available, attribute.Status); Assert.Equal("string", attribute.Error); Assert.True(attribute.Required); Assert.False(attribute.Array); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributesListTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributesListTests.cs index abad91d7..5375c07a 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributesListTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/AttributesListTests.cs @@ -13,7 +13,7 @@ public void Constructor_AssignsPropertiesCorrectly() var total = 5; var attributes = new List { - new AttributeBoolean("isEnabled", "boolean", AttributeStatus.Available, "string", true, + new AttributeBoolean("isEnabled", "boolean", DatabaseElementStatus.Available, "string", true, false, DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), false) }; @@ -42,7 +42,7 @@ public void CanBeDeserialized_FromJson() var attributeBoolean = (AttributeBoolean)attribute; Assert.Equal("isEnabled", attributeBoolean.Key); Assert.Equal("boolean", attributeBoolean.Type); - Assert.Equal(AttributeStatus.Available, attributeBoolean.Status); + Assert.Equal(DatabaseElementStatus.Available, attributeBoolean.Status); Assert.Equal("string", attributeBoolean.Error); Assert.True(attributeBoolean.Required); Assert.False(attributeBoolean.Array); From 952d78f88707e06bc59513a516b611b74abfc727 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 24 Oct 2024 18:29:22 +0100 Subject: [PATCH 018/303] Added tests for index response --- src/PinguApps.Appwrite.Playground/App.cs | 6 --- .../Responses/IndexTests.cs | 52 +++++++++++++++++++ .../TestConstants.cs | 13 +++++ 3 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/IndexTests.cs diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 02b8ceff..785b44a0 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -54,12 +54,6 @@ public async Task Run(string[] args) var json = JsonSerializer.Serialize(attributes[0]); Console.WriteLine(json); - - var x = new Shared.Responses.Index("Key", Shared.Enums.IndexType.Fulltext); - - var j = JsonSerializer.Serialize(x, options); - - Console.WriteLine(j); } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/IndexTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/IndexTests.cs new file mode 100644 index 00000000..3a6f8662 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/IndexTests.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Enums; +using Index = PinguApps.Appwrite.Shared.Responses.Index; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class IndexTests +{ + [Fact] + public void Constructor_AssignsPropertiesCorrectly() + { + // Arrange + var key = "index1"; + var type = IndexType.Unique; + var status = DatabaseElementStatus.Available; + var error = "string"; + var attributes = Array.Empty(); + var orders = Array.Empty(); + var createdAt = DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(); + var updatedAt = DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(); + + // Act + var index = new Index(key, type, status, error, attributes, orders, createdAt, updatedAt); + + // Assert + Assert.Equal(key, index.Key); + Assert.Equal(type, index.Type); + Assert.Equal(status, index.Status); + Assert.Equal(error, index.Error); + Assert.Equal(attributes, index.Attributes); + Assert.Equal(orders, index.Orders); + Assert.Equal(createdAt, index.CreatedAt); + Assert.Equal(updatedAt, index.UpdatedAt); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + // Act + var index = JsonSerializer.Deserialize(TestConstants.IndexResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + // Assert + Assert.NotNull(index); + Assert.Equal("index1", index.Key); + Assert.Equal(IndexType.Unique, index.Type); + Assert.Equal(DatabaseElementStatus.Available, index.Status); + Assert.Equal("string", index.Error); + Assert.Empty(index.Attributes); + Assert.Empty(index.Orders); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), index.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), index.UpdatedAt.ToUniversalTime()); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index b6a68327..4066558f 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -608,4 +608,17 @@ public static class TestConstants "default": "http://example.com" } """; + + public const string IndexResponse = """ + { + "key": "index1", + "type": "unique", + "status": "available", + "error": "string", + "attributes": [], + "orders": [], + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00" + } + """; } From dbbe9b69429adf0108183aa8a387d348a26284ec Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 24 Oct 2024 18:47:06 +0100 Subject: [PATCH 019/303] added tests for collection response --- .../Responses/Collection.cs | 4 +- .../Responses/CollectionTests.cs | 66 +++++++++++++++++++ .../TestConstants.cs | 40 +++++++++++ 3 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Responses/Collection.cs b/src/PinguApps.Appwrite.Shared/Responses/Collection.cs index 47ffda15..c32568a5 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Collection.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Collection.cs @@ -27,6 +27,6 @@ public record Collection( [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("enabled")] bool Enabled, [property: JsonPropertyName("documentSecurity")] bool DocumentSecurity, - [property: JsonPropertyName("attributes"), JsonConverter(typeof(AttributeListJsonConverter))] List Attributes, - [property: JsonPropertyName("indexes")] List Indexes + [property: JsonPropertyName("attributes"), JsonConverter(typeof(AttributeListJsonConverter))] IReadOnlyList Attributes, + [property: JsonPropertyName("indexes")] IReadOnlyList Indexes ); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionTests.cs new file mode 100644 index 00000000..4e561750 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionTests.cs @@ -0,0 +1,66 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; +using Index = PinguApps.Appwrite.Shared.Responses.Index; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class CollectionTests +{ + [Fact] + public void Constructor_AssignsPropertiesCorrectly() + { + // Arrange + var id = "5e5ea5c16897e"; + var createdAt = DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(); + var updatedAt = DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(); + var permissions = new List { "read(\"any\")" }; + var databaseId = "5e5ea5c16897e"; + var name = "My Collection"; + var enabled = false; + var documentSecurity = true; + var attributes = new List + { + new AttributeBoolean("isEnabled", "boolean", DatabaseElementStatus.Available, "string", true, false, createdAt, updatedAt, false) + }; + var indexes = new List + { + new Index("index1", IndexType.Unique, DatabaseElementStatus.Available, "string", new List(), new List(), createdAt, updatedAt) + }; + + // Act + var collection = new Collection(id, createdAt, updatedAt, permissions, databaseId, name, enabled, documentSecurity, attributes, indexes); + + // Assert + Assert.Equal(id, collection.Id); + Assert.Equal(createdAt, collection.CreatedAt); + Assert.Equal(updatedAt, collection.UpdatedAt); + Assert.Equal(permissions, collection.Permissions); + Assert.Equal(databaseId, collection.DatabaseId); + Assert.Equal(name, collection.Name); + Assert.Equal(enabled, collection.Enabled); + Assert.Equal(documentSecurity, collection.DocumentSecurity); + Assert.Equal(attributes, collection.Attributes); + Assert.Equal(indexes, collection.Indexes); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + // Act + var collection = JsonSerializer.Deserialize(TestConstants.CollectionResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + // Assert + Assert.NotNull(collection); + Assert.Equal("5e5ea5c16897e", collection.Id); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), collection.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), collection.UpdatedAt.ToUniversalTime()); + Assert.Contains("read(\"any\")", collection.Permissions); + Assert.Equal("5e5ea5c16897e", collection.DatabaseId); + Assert.Equal("My Collection", collection.Name); + Assert.False(collection.Enabled); + Assert.True(collection.DocumentSecurity); + Assert.Single(collection.Attributes); + Assert.Single(collection.Indexes); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index 4066558f..f6895990 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -621,4 +621,44 @@ public static class TestConstants "$updatedAt": "2020-10-15T06:38:00.000+00:00" } """; + + public const string CollectionResponse = """ + { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "$permissions": [ + "read(\"any\")" + ], + "databaseId": "5e5ea5c16897e", + "name": "My Collection", + "enabled": false, + "documentSecurity": true, + "attributes": [ + { + "key": "isEnabled", + "type": "boolean", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "default": false + } + ], + "indexes": [ + { + "key": "index1", + "type": "unique", + "status": "available", + "error": "string", + "attributes": [], + "orders": [], + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00" + } + ] + } + """; } From 1123cb1977ebcd24724e41c441d46596c1cdd90d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 24 Oct 2024 18:50:03 +0100 Subject: [PATCH 020/303] added collections list response --- .../Responses/CollectionsList.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Responses/CollectionsList.cs diff --git a/src/PinguApps.Appwrite.Shared/Responses/CollectionsList.cs b/src/PinguApps.Appwrite.Shared/Responses/CollectionsList.cs new file mode 100644 index 00000000..baf8f1b3 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/CollectionsList.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite CollectionsList object +/// +/// Total number of collections documents that matched your query +/// List of collections +public record CollectionsList( + [property: JsonPropertyName("total")] int Total, + [property: JsonPropertyName("collections")] IReadOnlyList Collections +); From 8d7d697d42e6f733f6876c180cd956ccd04d6e64 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 24 Oct 2024 18:53:44 +0100 Subject: [PATCH 021/303] added tests for collections list response --- .../Responses/CollectionsListTests.cs | 67 +++++++++++++++++++ .../TestConstants.cs | 45 +++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionsListTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionsListTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionsListTests.cs new file mode 100644 index 00000000..00fffa2a --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionsListTests.cs @@ -0,0 +1,67 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; +using Index = PinguApps.Appwrite.Shared.Responses.Index; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class CollectionsListTests +{ + [Fact] + public void Constructor_AssignsPropertiesCorrectly() + { + // Arrange + var total = 5; + var collections = new List + { + new Collection( + "5e5ea5c16897e", + DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), + DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), + new List { "read(\"any\")" }, + "5e5ea5c16897e", + "My Collection", + false, + true, + new List + { + new AttributeBoolean("isEnabled", "boolean", DatabaseElementStatus.Available, "string", true, false, DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), false) + }, + new List + { + new Index("index1", IndexType.Unique, DatabaseElementStatus.Available, "string", new List(), new List(), DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime()) + } + ) + }; + + // Act + var collectionsList = new CollectionsList(total, collections); + + // Assert + Assert.Equal(total, collectionsList.Total); + Assert.Equal(collections, collectionsList.Collections); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + // Act + var collectionsList = JsonSerializer.Deserialize(TestConstants.CollectionsListResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + // Assert + Assert.NotNull(collectionsList); + Assert.Equal(5, collectionsList.Total); + Assert.Single(collectionsList.Collections); + var collection = collectionsList.Collections[0]; + Assert.Equal("5e5ea5c16897e", collection.Id); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), collection.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), collection.UpdatedAt.ToUniversalTime()); + Assert.Contains("read(\"any\")", collection.Permissions); + Assert.Equal("5e5ea5c16897e", collection.DatabaseId); + Assert.Equal("My Collection", collection.Name); + Assert.False(collection.Enabled); + Assert.True(collection.DocumentSecurity); + Assert.Single(collection.Attributes); + Assert.Single(collection.Indexes); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index f6895990..79075e3d 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -661,4 +661,49 @@ public static class TestConstants ] } """; + + public const string CollectionsListResponse = """ + { + "total": 5, + "collections": [ + { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "$permissions": [ + "read(\"any\")" + ], + "databaseId": "5e5ea5c16897e", + "name": "My Collection", + "enabled": false, + "documentSecurity": true, + "attributes": [ + { + "key": "isEnabled", + "type": "boolean", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "default": false + } + ], + "indexes": [ + { + "key": "index1", + "type": "unique", + "status": "available", + "error": "string", + "attributes": [], + "orders": [], + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00" + } + ] + } + ] + } + """; } From 088aec57479fa04852c227fae393705ec3c5cf85 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 24 Oct 2024 18:58:55 +0100 Subject: [PATCH 022/303] added indexes list response --- src/PinguApps.Appwrite.Shared/Responses/IndexesList.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Responses/IndexesList.cs diff --git a/src/PinguApps.Appwrite.Shared/Responses/IndexesList.cs b/src/PinguApps.Appwrite.Shared/Responses/IndexesList.cs new file mode 100644 index 00000000..a683b802 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/IndexesList.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace PinguApps.Appwrite.Shared.Responses; +public record IndexesList( + [property: JsonPropertyName("total")] int Total, + [property: JsonPropertyName("indexes")] IReadOnlyList Indexes +); From 628ca8632aaa0ff6f95b297627c6dbbff5ac160b Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 24 Oct 2024 19:00:50 +0100 Subject: [PATCH 023/303] added tests for indexes list response --- .../Responses/IndexesListTests.cs | 56 +++++++++++++++++++ .../TestConstants.cs | 18 ++++++ 2 files changed, 74 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/IndexesListTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/IndexesListTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/IndexesListTests.cs new file mode 100644 index 00000000..0ff6b44e --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/IndexesListTests.cs @@ -0,0 +1,56 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using Index = PinguApps.Appwrite.Shared.Responses.Index; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class IndexesListTests +{ + [Fact] + public void Constructor_AssignsPropertiesCorrectly() + { + // Arrange + var total = 5; + var indexes = new List + { + new Index( + "index1", + IndexType.Unique, + DatabaseElementStatus.Available, + "string", + new List(), + new List(), + DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), + DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime() + ) + }; + + // Act + var indexesList = new IndexesList(total, indexes); + + // Assert + Assert.Equal(total, indexesList.Total); + Assert.Equal(indexes, indexesList.Indexes); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + // Act + var indexesList = JsonSerializer.Deserialize(TestConstants.IndexesListResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + // Assert + Assert.NotNull(indexesList); + Assert.Equal(5, indexesList.Total); + Assert.Single(indexesList.Indexes); + var index = indexesList.Indexes[0]; + Assert.Equal("index1", index.Key); + Assert.Equal(IndexType.Unique, index.Type); + Assert.Equal(DatabaseElementStatus.Available, index.Status); + Assert.Equal("string", index.Error); + Assert.Empty(index.Attributes); + Assert.Empty(index.Orders); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), index.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), index.UpdatedAt.ToUniversalTime()); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index 79075e3d..1df96d46 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -706,4 +706,22 @@ public static class TestConstants ] } """; + + public const string IndexesListResponse = """ + { + "total": 5, + "indexes": [ + { + "key": "index1", + "type": "unique", + "status": "available", + "error": "string", + "attributes": [], + "orders": [], + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00" + } + ] + } + """; } From 915a9f8e00e858d2cfdeedc4802a8fa6c224d654 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 24 Oct 2024 22:50:26 +0100 Subject: [PATCH 024/303] Added Document response and converter --- .../Converters/DocumentConverter.cs | 288 ++++++++++++++++++ .../Responses/Document.cs | 34 +++ 2 files changed, 322 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/Document.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs new file mode 100644 index 00000000..c628163a --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs @@ -0,0 +1,288 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Shared.Converters; +public class DocumentConverter : JsonConverter +{ + public override Document? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + string? id = null; + string? collectionId = null; + string? databaseId = null; + DateTime? createdAt = null; + DateTime? updatedAt = null; + List? permissions = null; + var data = new Dictionary(); + + var dateTimeConverter = new MultiFormatDateTimeConverter(); + + if (reader.TokenType is not JsonTokenType.StartObject) + { + throw new JsonException("Expected StartObject token"); + } + + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType is not JsonTokenType.PropertyName) + { + throw new JsonException("Expected PropertyName token"); + } + + var propertyName = reader.GetString(); + + if (propertyName is null) + { + throw new NullReferenceException("PropertyName was null"); + } + + reader.Read(); + + switch (propertyName) + { + case "$id": + id = reader.GetString(); + break; + case "$collectionId": + collectionId = reader.GetString(); + break; + case "$databaseId": + databaseId = reader.GetString(); + break; + case "$createdAt": + createdAt = dateTimeConverter.Read(ref reader, typeof(DateTime), options); + break; + case "$updatedAt": + updatedAt = dateTimeConverter.Read(ref reader, typeof(DateTime), options); + break; + case "$permissions": + permissions = JsonSerializer.Deserialize>(ref reader, options); + break; + default: + var value = ReadValue(ref reader, options); + data[propertyName] = value; + break; + } + } + + if (id is null) + { + throw new JsonException("Unable to find a value for Id"); + } + + if (collectionId is null) + { + throw new JsonException("Unable to find a value for CollectionId"); + } + + if (databaseId is null) + { + throw new JsonException("Unable to find a value for DatabaseId"); + } + + if (createdAt is null) + { + throw new JsonException("Unable to find a value for CreatedAt"); + } + + if (updatedAt is null) + { + throw new JsonException("Unable to find a value for UpdatedAt"); + } + + if (permissions is null) + { + throw new JsonException("Unable to find a value for Permissions"); + } + + return new Document(id, collectionId, databaseId, createdAt.Value, updatedAt.Value, permissions, data); + } + + private object? ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var str = reader.GetString(); + + if ((DateTime.TryParse(str, out var dateTime))) + { + return dateTime; + } + return str; + + case JsonTokenType.Number: + if (reader.TryGetInt64(out var longValue)) + { + return longValue; + } + return reader.GetSingle(); + + case JsonTokenType.True: + case JsonTokenType.False: + return reader.GetBoolean(); + + case JsonTokenType.Null: + return null; + + case JsonTokenType.StartArray: + return ReadArray(ref reader, options); + + case JsonTokenType.StartObject: + return ReadObject(ref reader, options); + + default: + throw new JsonException($"Unsupported token type: {reader.TokenType}"); + } + } + + private IReadOnlyCollection ReadArray(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + var list = new List(); + + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndArray) + { + break; + } + + var item = ReadValue(ref reader, options); + list.Add(item); + } + + return list; + } + + private Dictionary ReadObject(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + var dict = new Dictionary(); + + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType is not JsonTokenType.PropertyName) + { + throw new JsonException("Expected PropertyName token"); + } + + var propertyName = reader.GetString(); + + if (propertyName is null) + { + throw new NullReferenceException("PropertyName was null"); + } + + reader.Read(); + + var value = ReadValue(ref reader, options); + + dict[propertyName] = value; + } + + return dict; + } + + public override void Write(Utf8JsonWriter writer, Document value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + // Write known properties + writer.WriteString("$id", value.Id); + writer.WriteString("$collectionId", value.CollectionId); + writer.WriteString("$databaseId", value.DatabaseId); + + // Use MultiFormatDateTimeConverter for DateTime properties + var dateTimeConverter = new MultiFormatDateTimeConverter(); + + writer.WritePropertyName("$createdAt"); + dateTimeConverter.Write(writer, value.CreatedAt, options); + + writer.WritePropertyName("$updatedAt"); + dateTimeConverter.Write(writer, value.UpdatedAt, options); + + writer.WritePropertyName("$permissions"); + JsonSerializer.Serialize(writer, value.Permissions, options); + + // Write dynamic properties from the Data dictionary + foreach (var kvp in value.Data) + { + writer.WritePropertyName(kvp.Key); + WriteValue(writer, kvp.Value, options); + } + + writer.WriteEndObject(); + } + + private void WriteValue(Utf8JsonWriter writer, object? value, JsonSerializerOptions options) + { + // Handle null values + if (value is null) + { + writer.WriteNullValue(); + return; + } + + // Determine the type of the value and write accordingly + switch (value) + { + case string s: + writer.WriteStringValue(s); + break; + case int i: + writer.WriteNumberValue(i); + break; + case long l: + writer.WriteNumberValue(l); + break; + case float f: + writer.WriteNumberValue(f); + break; + case double d: + writer.WriteNumberValue(d); + break; + case decimal dec: + writer.WriteNumberValue(dec); + break; + case bool b: + writer.WriteBooleanValue(b); + break; + case DateTime dt: + var dateTimeConverter = new MultiFormatDateTimeConverter(); + dateTimeConverter.Write(writer, dt, options); + break; + case IReadOnlyList list: + writer.WriteStartArray(); + foreach (var item in list) + { + WriteValue(writer, item, options); + } + writer.WriteEndArray(); + break; + case Dictionary dict: + writer.WriteStartObject(); + foreach (var kvp in dict) + { + writer.WritePropertyName(kvp.Key); + WriteValue(writer, kvp.Value, options); + } + writer.WriteEndObject(); + break; + default: + // Fallback to default serialization + JsonSerializer.Serialize(writer, value, value.GetType(), options); + break; + } + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/Document.cs b/src/PinguApps.Appwrite.Shared/Responses/Document.cs new file mode 100644 index 00000000..9cb82bf0 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/Document.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; + +namespace PinguApps.Appwrite.Shared.Responses; + +[JsonConverter(typeof(DocumentConverter))] +public record Document( + [property: JsonPropertyName("$id")] string Id, + [property: JsonPropertyName("$collectionId")] string CollectionId, + [property: JsonPropertyName("$databaseId")] string DatabaseId, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, + [property: JsonPropertyName("$permissions")] IReadOnlyList Permissions, + [property: JsonExtensionData] Dictionary Data +) +{ + public object? this[string key] => Data.ContainsKey(key) ? Data[key] : null; + + public T GetValue(string key) + { + if (Data.TryGetValue(key, out var value)) + { + if (value is T tValue) + { + return tValue; + } + + throw new InvalidCastException($"Value for '{key}' is not of type {typeof(T)}."); + } + throw new KeyNotFoundException($"Key '{key}' not found."); + } +} From 60d628eb31f91d62b2f970c2fb2cf2cb8145b022 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 25 Oct 2024 00:18:48 +0100 Subject: [PATCH 025/303] Added tests for document --- .../MultiFormatDateTimeConverter.cs | 2 +- .../Responses/DocumentTests.cs | 128 ++++++++++++++++++ .../TestConstants.cs | 23 ++++ 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/MultiFormatDateTimeConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/MultiFormatDateTimeConverter.cs index 5f20e0af..a6e2c04f 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/MultiFormatDateTimeConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/MultiFormatDateTimeConverter.cs @@ -35,6 +35,6 @@ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, Jso public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) { - writer.WriteStringValue(value.ToString(_formats[0])); // Use the first format for serialization + writer.WriteStringValue(value.ToUniversalTime().ToString(_formats[0])); // Use the first format for serialization } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs new file mode 100644 index 00000000..db90aa8a --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs @@ -0,0 +1,128 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class DocumentTests +{ + [Fact] + public void Document_ShouldBeDeserialized_FromJson() + { + var document = JsonSerializer.Deserialize(TestConstants.DocumentResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.NotNull(document); + Assert.Equal("5e5ea5c16897e", document.Id); + Assert.Equal("5e5ea5c15117e", document.CollectionId); + Assert.Equal("5e5ea5c15117e", document.DatabaseId); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.UpdatedAt.ToUniversalTime()); + Assert.Contains("read(\"any\")", document.Permissions); + Assert.Equal("a string prop", document.Data["str"]); + var dt = (DateTime?)document.Data["dt"]; + Assert.NotNull(dt); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), dt.Value.ToUniversalTime()); + Assert.Equal(5L, document.Data["num"]); + var num2 = document.Data["num2"]; + Assert.IsType(num2); + Assert.Equal(6.7, (float)num2, 0.0001); + Assert.Equal(true, document.Data["boo"]); + Assert.Equal(new List { "one", "two", "three" }, document.Data["lis"]); + } + + [Fact] + public void Indexer_ShouldReturnCorrectValue() + { + var data = new Dictionary + { + { "key1", "value1" }, + { "key2", 123 } + }; + + var document = new Document( + "id", + "collectionId", + "databaseId", + DateTime.UtcNow, + DateTime.UtcNow, + new List(), + data + ); + + Assert.Equal("value1", document["key1"]); + Assert.Equal(123, document["key2"]); + Assert.Null(document["key3"]); + } + + [Fact] + public void GetValue_ShouldReturnCorrectValue() + { + var data = new Dictionary + { + { "key1", "value1" }, + { "key2", 123 } + }; + + var document = new Document( + "id", + "collectionId", + "databaseId", + DateTime.UtcNow, + DateTime.UtcNow, + new List(), + data + ); + + Assert.Equal("value1", document.GetValue("key1")); + Assert.Equal(123, document.GetValue("key2")); + } + + [Fact] + public void GetValue_ShouldThrowException_WhenKeyNotFound() + { + var document = new Document( + "id", + "collectionId", + "databaseId", + DateTime.UtcNow, + DateTime.UtcNow, + new List(), + new Dictionary() + ); + + Assert.Throws(() => document.GetValue("key1")); + } + + [Fact] + public void GetValue_ShouldThrowException_WhenInvalidCast() + { + var data = new Dictionary + { + { "key1", "value1" } + }; + + var document = new Document( + "id", + "collectionId", + "databaseId", + DateTime.UtcNow, + DateTime.UtcNow, + new List(), + data + ); + + Assert.Throws(() => document.GetValue("key1")); + } + + [Fact] + public void Document_ShouldContainUnmatchedProperties() + { + var document = JsonSerializer.Deserialize(TestConstants.DocumentResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + Assert.NotNull(document); + Assert.True(document.Data.ContainsKey("str")); + Assert.True(document.Data.ContainsKey("dt")); + Assert.True(document.Data.ContainsKey("num")); + Assert.True(document.Data.ContainsKey("num2")); + Assert.True(document.Data.ContainsKey("boo")); + Assert.True(document.Data.ContainsKey("lis")); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index 1df96d46..e138842d 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -724,4 +724,27 @@ public static class TestConstants ] } """; + + public const string DocumentResponse = """ + { + "$id": "5e5ea5c16897e", + "$collectionId": "5e5ea5c15117e", + "$databaseId": "5e5ea5c15117e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "$permissions": [ + "read(\"any\")" + ], + "str": "a string prop", + "dt": "2020-10-15T06:38:00.000+00:00", + "num": 5, + "num2": 6.7, + "boo": true, + "lis": [ + "one", + "two", + "three" + ] + } + """; } From 6a4646491e76dac8cf605e236769ba9ca58d8945 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 25 Oct 2024 00:19:00 +0100 Subject: [PATCH 026/303] added tests for document converter --- .../Converters/DocumentConverter.cs | 26 +- .../Converters/DocumentConverterTests.cs | 445 ++++++++++++++++++ 2 files changed, 448 insertions(+), 23 deletions(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs index c628163a..17090b6f 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs @@ -31,17 +31,7 @@ public class DocumentConverter : JsonConverter break; } - if (reader.TokenType is not JsonTokenType.PropertyName) - { - throw new JsonException("Expected PropertyName token"); - } - - var propertyName = reader.GetString(); - - if (propertyName is null) - { - throw new NullReferenceException("PropertyName was null"); - } + var propertyName = reader.GetString()!; reader.Read(); @@ -105,7 +95,7 @@ public class DocumentConverter : JsonConverter return new Document(id, collectionId, databaseId, createdAt.Value, updatedAt.Value, permissions, data); } - private object? ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) + internal object? ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) { switch (reader.TokenType) { @@ -172,17 +162,7 @@ public class DocumentConverter : JsonConverter break; } - if (reader.TokenType is not JsonTokenType.PropertyName) - { - throw new JsonException("Expected PropertyName token"); - } - - var propertyName = reader.GetString(); - - if (propertyName is null) - { - throw new NullReferenceException("PropertyName was null"); - } + var propertyName = reader.GetString()!; reader.Read(); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs new file mode 100644 index 00000000..27f82c30 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs @@ -0,0 +1,445 @@ +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class DocumentConverterTests +{ + private readonly JsonSerializerOptions _options; + + public DocumentConverterTests() + { + _options = new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Converters = { new DocumentConverter() } + }; + } + + [Fact] + public void Read_ValidJson_ReturnsDocument() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read"", ""write""], + ""customField"": ""customValue"" + }"; + + var document = JsonSerializer.Deserialize(json, _options); + + Assert.NotNull(document); + Assert.Equal("1", document.Id); + Assert.Equal("col1", document.CollectionId); + Assert.Equal("db1", document.DatabaseId); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00"), document.CreatedAt); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00"), document.UpdatedAt); + Assert.Equal(new List { "read", "write" }, document.Permissions); + Assert.Equal("customValue", document["customField"]); + } + + [Fact] + public void Read_InvalidJson_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""invalid-date"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read"", ""write""] + }"; + + Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); + } + + [Fact] + public void Read_MissingRequiredFields_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read"", ""write""] + }"; + + Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); + } + + [Fact] + public void Write_ValidDocument_WritesJson() + { + var document = new Document( + "1", + "col1", + "db1", + DateTime.Parse("2020-10-15T06:38:00.000+00:00"), + DateTime.Parse("2020-10-15T06:38:00.000+00:00"), + ["read", "write"], + new Dictionary { { "customField", "customValue" } } + ); + + var json = JsonSerializer.Serialize(document, _options); + + var expectedJson = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read"", ""write""], + ""customField"": ""customValue"" + }".ReplaceLineEndings("").Replace(" ", ""); + + Assert.Equal(JsonDocument.Parse(expectedJson).RootElement.ToString(), JsonDocument.Parse(json).RootElement.ToString()); + } + + [Fact] + public void Write_NullValue_WritesNull() + { + var document = new Document( + "1", + "col1", + "db1", + DateTime.Parse("2020-10-15T06:38:00.000+00:00"), + DateTime.Parse("2020-10-15T06:38:00.000+00:00"), + ["read", "write"], + new Dictionary { { "customField", null } } + ); + + var json = JsonSerializer.Serialize(document, _options); + + var expectedJson = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read"", ""write""], + ""customField"": null + }".ReplaceLineEndings("").Replace(" ", ""); + + Assert.Equal(JsonDocument.Parse(expectedJson).RootElement.ToString(), JsonDocument.Parse(json).RootElement.ToString()); + } + + [Fact] + public void ReadValue_UnsupportedTokenType_ThrowsJsonException() + { + var json = @" + { + ""unsupported"": {} + }"; + + Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); + } + + [Fact] + public void ReadArray_ValidJsonArray_ReturnsArray() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read"", ""write""], + ""arrayField"": [""value1"", ""value2""] + }"; + + var document = JsonSerializer.Deserialize(json, _options); + + Assert.NotNull(document); + var arrayField = document["arrayField"] as IReadOnlyCollection; + Assert.NotNull(arrayField); + Assert.Contains("value1", arrayField); + Assert.Contains("value2", arrayField); + } + + [Fact] + public void ReadObject_ValidJsonObject_ReturnsObject() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read"", ""write""], + ""objectField"": { ""key1"": ""value1"", ""key2"": ""value2"" } + }"; + + var document = JsonSerializer.Deserialize(json, _options); + + Assert.NotNull(document); + var objectField = document["objectField"] as Dictionary; + Assert.NotNull(objectField); + Assert.Equal("value1", objectField["key1"]); + Assert.Equal("value2", objectField["key2"]); + } + + [Fact] + public void Read_InvalidJsonTokenType_ThrowsJsonException() + { + var json = @" + [ + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read"", ""write""], + ""customField"": ""customValue"" + } + ]"; + + Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); + } + + [Fact] + public void Read_MissingCollectionId_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read"", ""write""] + }"; + + Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); + } + + [Fact] + public void Read_MissingCreatedAt_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read"", ""write""] + }"; + + Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); + } + + [Fact] + public void Read_MissingUpdatedAt_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read"", ""write""] + }"; + + Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); + } + + [Fact] + public void Read_MissingPermissions_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"" + }"; + + Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); + } + + [Fact] + public void Read_NullProperty_InsertedIntoData() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read"", ""write""], + ""customField"": null + }"; + + var document = JsonSerializer.Deserialize(json, _options); + + Assert.NotNull(document); + Assert.True(document.Data.ContainsKey("customField")); + Assert.Null(document["customField"]); + } + + [Fact] + public void Read_Comment_ThrowsJsonException() + { + var json = @"{}"; + + Assert.Throws(() => + { + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json)); + while (reader.TokenType is not JsonTokenType.EndObject) + { + reader.Read(); + } + + var docReader = new DocumentConverter(); + + docReader.ReadValue(ref reader, _options); + }); + } + + [Fact] + public void Write_IntValue_SerializesCorrectly() + { + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + { + { "intField", 123 } + }); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"intField\":123", json); + } + + [Fact] + public void Write_LongValue_SerializesCorrectly() + { + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + { + { "longField", 12345L } + }); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"longField\":12345", json); + } + + [Fact] + public void Write_FloatValue_SerializesCorrectly() + { + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + { + { "floatField", 1.23f } + }); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"floatField\":1.23", json); + } + + [Fact] + public void Write_DoubleValue_SerializesCorrectly() + { + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + { + { "doubleField", 1.23d } + }); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"doubleField\":1.23", json); + } + + [Fact] + public void Write_DecimalValue_SerializesCorrectly() + { + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + { + { "decimalField", 1.23m } + }); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"decimalField\":1.23", json); + } + + [Fact] + public void Write_BoolValue_SerializesCorrectly() + { + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + { + { "boolField", true } + }); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"boolField\":true", json); + } + + [Fact] + public void Write_DateTimeValue_SerializesCorrectly() + { + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + { + { "datetimeField", DateTime.Parse("2020-10-15T06:38:00.000+00:00") } + }); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"datetimeField\":\"2020-10-15T06:38:00.000+00:00\"", json); + } + + [Fact] + public void Write_ListValue_SerializesCorrectly() + { + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + { + { "listField", new List() { "val1","val2" } } + }); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"listField\":[\"val1\",\"val2\"]", json); + } + + [Fact] + public void Write_DictValue_SerializesCorrectly() + { + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + { + { "dictField", new Dictionary { + { "key", "val" } + } } + }); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"dictField\":{\"key\":\"val\"}}", json); + } + + [Fact] + public void Write_ObjectValue_SerializesCorrectly() + { + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + { + { "objectField", new { } } + }); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"objectField\":{}}", json); + } + + +} From 8c25a81d651bf8bf38ae6f1ca8bf50f3aa75b1b1 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 25 Oct 2024 00:32:02 +0100 Subject: [PATCH 027/303] Added TryGetValue to document --- .../Responses/Document.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/PinguApps.Appwrite.Shared/Responses/Document.cs b/src/PinguApps.Appwrite.Shared/Responses/Document.cs index 9cb82bf0..30fe635b 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Document.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Document.cs @@ -1,10 +1,21 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Converters; namespace PinguApps.Appwrite.Shared.Responses; +/// +/// An Appwrite Document object +/// +/// Document ID +/// Collection ID +/// Database ID +/// Document creation date in ISO 8601 format +/// Document update date in ISO 8601 format +/// Document permissions. Learn more about permissions +/// Document data [JsonConverter(typeof(DocumentConverter))] public record Document( [property: JsonPropertyName("$id")] string Id, @@ -16,8 +27,19 @@ public record Document( [property: JsonExtensionData] Dictionary Data ) { + /// + /// Extract document data by key + /// + /// The attribute key + /// The attribute public object? this[string key] => Data.ContainsKey(key) ? Data[key] : null; + /// + /// Get the value of a given attribute + /// + /// The type of the attribute value + /// The attribute key + /// The attribute value public T GetValue(string key) { if (Data.TryGetValue(key, out var value)) @@ -31,4 +53,28 @@ public T GetValue(string key) } throw new KeyNotFoundException($"Key '{key}' not found."); } + + // TODO: Write tests for this method + /// + /// Try get the value of a given attribute + /// + /// The value type + /// The attribute key + /// The value of this attribute + /// Whether the attribute value could be successfully retrieved or not + public bool TryGetValue(string key, [NotNullWhen(true)] out T? value) + { + value = default; + + if (Data.TryGetValue(key, out var storedValue)) + { + if (storedValue is T tValue) + { + value = tValue; + return true; + } + } + + return false; + } } From 18803f5381ae123e5264239936a680516b170406 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 25 Oct 2024 00:32:13 +0100 Subject: [PATCH 028/303] Added DocumentsList - still needs converter --- .../Responses/DocumentsList.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Responses/DocumentsList.cs diff --git a/src/PinguApps.Appwrite.Shared/Responses/DocumentsList.cs b/src/PinguApps.Appwrite.Shared/Responses/DocumentsList.cs new file mode 100644 index 00000000..66b9927a --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/DocumentsList.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite DocumentsList attribute +/// +/// Total number of documents documents that matched your query +/// List of documents +public record DocumentsList( + [property: JsonPropertyName("total")] int Total, + [property: JsonPropertyName("documents"), JsonConverter(typeof(DocumentListConverter))] IReadOnlyList Documents +); From 7f2cd8a3f5817fefb6adcd8e40c6670c8992e435 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 00:32:33 +0100 Subject: [PATCH 029/303] Added document list converter --- .../Converters/DocumentListConverter.cs | 49 +++++++++++++++++++ .../Responses/DocumentsList.cs | 1 + 2 files changed, 50 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Converters/DocumentListConverter.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/DocumentListConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/DocumentListConverter.cs new file mode 100644 index 00000000..3fc58de1 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/DocumentListConverter.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Shared.Converters; +public class DocumentListConverter : JsonConverter> +{ + private readonly DocumentConverter _documentConverter = new(); + + public override IReadOnlyList? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var documents = new List(); + + if (reader.TokenType is not JsonTokenType.StartArray) + { + throw new JsonException("Expected start of array"); + } + + reader.Read(); + + while (reader.TokenType is not JsonTokenType.EndArray) + { + var document = _documentConverter.Read(ref reader, typeof(Document), options); + + if (document is not null) + { + documents.Add(document); + } + + reader.Read(); + } + + return documents; + } + + public override void Write(Utf8JsonWriter writer, IReadOnlyList value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + + foreach (var document in value) + { + _documentConverter.Write(writer, document, options); + } + + writer.WriteEndArray(); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/DocumentsList.cs b/src/PinguApps.Appwrite.Shared/Responses/DocumentsList.cs index 66b9927a..bd1afea9 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/DocumentsList.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/DocumentsList.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; namespace PinguApps.Appwrite.Shared.Responses; From 70d0939c52c43bdfe07216c4282f4c11faa13441 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 00:38:11 +0100 Subject: [PATCH 030/303] Added tests to covert TryGetValue on document --- .../Responses/DocumentTests.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs index db90aa8a..82ea5b87 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs @@ -112,6 +112,58 @@ public void GetValue_ShouldThrowException_WhenInvalidCast() Assert.Throws(() => document.GetValue("key1")); } + [Fact] + public void TryGetValue_ReturnsTrue_WhenKeyExistsAndTypeMatches() + { + // Arrange + var data = new Dictionary + { + { "key1", "value1" }, + { "key2", 123 } + }; + var document = new Document("id", "collectionId", "databaseId", DateTime.UtcNow, DateTime.UtcNow, [], data); + + // Act + var result = document.TryGetValue("key1", out string? value); + + // Assert + Assert.True(result); + Assert.Equal("value1", value); + } + + [Fact] + public void TryGetValue_ReturnsFalse_WhenKeyDoesNotExist() + { + // Arrange + var data = new Dictionary(); + var document = new Document("id", "collectionId", "databaseId", DateTime.UtcNow, DateTime.UtcNow, [], data); + + // Act + var result = document.TryGetValue("nonexistentKey", out string? value); + + // Assert + Assert.False(result); + Assert.Null(value); + } + + [Fact] + public void TryGetValue_ReturnsFalse_WhenTypeDoesNotMatch() + { + // Arrange + var data = new Dictionary + { + { "key1", "value1" } + }; + var document = new Document("id", "collectionId", "databaseId", DateTime.UtcNow, DateTime.UtcNow, [], data); + + // Act + var result = document.TryGetValue("key1", out int? value); + + // Assert + Assert.False(result); + Assert.Null(value); + } + [Fact] public void Document_ShouldContainUnmatchedProperties() { From 83a6733756b429cd05e4deccf671b54bf3a30c4d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 00:49:48 +0100 Subject: [PATCH 031/303] added tests for documents list --- .../Responses/DocumentsListTests.cs | 47 +++++++++++++++++++ .../TestConstants.cs | 18 +++++++ 2 files changed, 65 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs new file mode 100644 index 00000000..dae08dce --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs @@ -0,0 +1,47 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Shared.Tests.Responses; +public class DocumentsListTests +{ + [Fact] + public void Constructor_AssignsPropertiesCorrectly() + { + // Arrange + var total = 5; + var documents = new List + { + new("5e5ea5c16897e", "5e5ea5c15117e", "5e5ea5c15117e", + DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), + DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), ["read(\"any\")"], + []) + }; + + // Act + var documentsList = new DocumentsList(total, documents); + + // Assert + Assert.Equal(total, documentsList.Total); + Assert.Equal(documents, documentsList.Documents); + } + + [Fact] + public void CanBeDeserialized_FromJson() + { + // Act + var documentsList = JsonSerializer.Deserialize(TestConstants.DocumentsListResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + + // Assert + Assert.NotNull(documentsList); + Assert.Equal(5, documentsList.Total); + Assert.Single(documentsList.Documents); + + var document = documentsList.Documents[0]; + Assert.Equal("5e5ea5c16897e", document.Id); + Assert.Equal("5e5ea5c15117e", document.CollectionId); + Assert.Equal("5e5ea5c15117e", document.DatabaseId); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.CreatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.UpdatedAt.ToUniversalTime()); + Assert.Contains("read(\"any\")", document.Permissions); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index e138842d..7fa034f2 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -747,4 +747,22 @@ public static class TestConstants ] } """; + + public const string DocumentsListResponse = """ + { + "total": 5, + "documents": [ + { + "$id": "5e5ea5c16897e", + "$collectionId": "5e5ea5c15117e", + "$databaseId": "5e5ea5c15117e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "$permissions": [ + "read(\"any\")" + ] + } + ] + } + """; } From 25981499dcfd835d98faa52ed19cdc9b9f6228ad Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 01:00:25 +0100 Subject: [PATCH 032/303] added tests for documents list coverter --- .../Converters/DocumentListConverterTests.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentListConverterTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentListConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentListConverterTests.cs new file mode 100644 index 00000000..b0203b51 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentListConverterTests.cs @@ -0,0 +1,62 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class DocumentListConverterTests +{ + private readonly JsonSerializerOptions _options; + + public DocumentListConverterTests() + { + _options = new JsonSerializerOptions + { + Converters = { new DocumentListConverter() } + }; + } + + [Fact] + public void Read_ShouldDeserializeDocumentList() + { + var json = "[{\"$id\":\"1\",\"$collectionId\":\"c1\",\"$databaseId\":\"d1\",\"$createdAt\":\"2020-10-15T06:38:00.000+00:00\",\"$updatedAt\":\"2020-10-15T06:38:00.000+00:00\",\"$permissions\":[]}, {\"$id\":\"2\",\"$collectionId\":\"c2\",\"$databaseId\":\"d2\",\"$createdAt\":\"2020-10-15T06:38:00.000+00:00\",\"$updatedAt\":\"2020-10-15T06:38:00.000+00:00\",\"$permissions\":[]}]"; + var documents = JsonSerializer.Deserialize>(json, _options); + + Assert.NotNull(documents); + Assert.Equal(2, documents.Count); + Assert.Equal("1", documents[0].Id); + Assert.Equal("2", documents[1].Id); + } + + [Fact] + public void Read_ShouldThrowJsonExceptionForInvalidStartToken() + { + var json = "{\"id\":\"1\"}"; + var exception = Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); + Assert.Equal("Expected start of array", exception.Message); + } + + [Fact] + public void Write_ShouldSerializeDocumentList() + { + var documents = new List + { + new Document("5e5ea5c16897e", "5e5ea5c15117e", "5e5ea5c15117e", DateTime.UtcNow, DateTime.UtcNow, [], []), + new Document("5e5ea5c16897f", "5e5ea5c15117e", "5e5ea5c15117e", DateTime.UtcNow, DateTime.UtcNow, [], []) + }; + + var json = JsonSerializer.Serialize((IReadOnlyList)documents, _options); + + Assert.Contains("\"$id\":\"5e5ea5c16897e\"", json); + Assert.Contains("\"$id\":\"5e5ea5c16897f\"", json); + } + + [Fact] + public void Write_ShouldHandleEmptyList() + { + var documents = new List(); + + var json = JsonSerializer.Serialize((IReadOnlyList)documents, _options); + + Assert.Equal("[]", json); + } +} From d80a192c185ece83c4226be0efe617f0c513a86f Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 01:17:39 +0100 Subject: [PATCH 033/303] added database Id base request, validator, and tests --- .../Databases/DatabaseIdBaseRequest.cs | 22 +++++ .../DatabaseIdBaseRequestValidator.cs | 14 +++ .../Databases/DatabaseIdBaseRequestTests.cs | 89 +++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseIdBaseRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseIdBaseRequestValidator.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseIdBaseRequestTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseIdBaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseIdBaseRequest.cs new file mode 100644 index 00000000..af7e639e --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseIdBaseRequest.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using FluentValidation; +using PinguApps.Appwrite.Shared.Attributes; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The base request but also containing DatabaseId +/// +/// The request type +/// The request validator type +public abstract class DatabaseIdBaseRequest : BaseRequest + where TRequest : class + where TValidator : IValidator, new() +{ + /// + /// Database ID + /// + [JsonPropertyName("databaseId")] + [SdkExclude] + public string DatabaseId { get; set; } = string.Empty; +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseIdBaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseIdBaseRequestValidator.cs new file mode 100644 index 00000000..04435525 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseIdBaseRequestValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class DatabaseIdBaseRequestValidator : AbstractValidator> + where TRequest : class + where TValidator : IValidator, new() +{ + public DatabaseIdBaseRequestValidator() + { + RuleFor(x => x.DatabaseId) + .NotEmpty() + .WithMessage("DatabaseId is required."); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseIdBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseIdBaseRequestTests.cs new file mode 100644 index 00000000..2661cbfb --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseIdBaseRequestTests.cs @@ -0,0 +1,89 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public abstract class DatabaseIdBaseRequestTests + where TRequest : DatabaseIdBaseRequest + where TValidator : AbstractValidator, new() +{ + protected abstract TRequest CreateValidRequest { get; } + + [Fact] + public void TeamIdBase_Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = CreateValidRequest; + + // Assert + Assert.Equal(string.Empty, request.DatabaseId); + } + + [Fact] + public void TeamIdBase_Properties_CanBeSet() + { + // Arrange + var teamIdValue = "validId"; + var request = CreateValidRequest; + + // Act + request.DatabaseId = teamIdValue; + + // Assert + Assert.Equal(teamIdValue, request.DatabaseId); + } + + [Fact] + public void TeamIdBase_IsValid_WithValidTeamId_ReturnsTrue() + { + // Arrange + var request = CreateValidRequest; + request.DatabaseId = "valid_Team-Id."; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void TeamIdBase_IsValid_WithInvalidData_ReturnsFalse(string? id) + { + // Arrange + var request = CreateValidRequest; + request.DatabaseId = id!; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void TeamIdBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = CreateValidRequest; + request.DatabaseId = string.Empty; // Invalid Id + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void TeamIdBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = CreateValidRequest; + request.DatabaseId = string.Empty; // Invalid Id + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From cca96901038144d80757fa80772c91d7b74ed450 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 01:20:31 +0100 Subject: [PATCH 034/303] added database collection id base request --- .../DatabaseCollectionIdBaseRequest.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionIdBaseRequest.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionIdBaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionIdBaseRequest.cs new file mode 100644 index 00000000..5015bcef --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionIdBaseRequest.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using FluentValidation; +using PinguApps.Appwrite.Shared.Attributes; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The base request but also containing DatabaseId and CollectionId +/// +/// The request type +/// The request validator type +public abstract class DatabaseCollectionIdBaseRequest : DatabaseIdBaseRequest + where TRequest : class + where TValidator : IValidator, new() +{ + /// + /// Collection ID + /// + [JsonPropertyName("collectionId")] + [SdkExclude] + public string CollectionId { get; set; } = string.Empty; +} From 1ccfcd8a05dfcada5b71e8816f4aab56588ef152 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 01:23:06 +0100 Subject: [PATCH 035/303] added validator for database collection id base request --- .../DatabaseCollectionIdBaseRequestValidator.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionIdBaseRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionIdBaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionIdBaseRequestValidator.cs new file mode 100644 index 00000000..c2f6e249 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionIdBaseRequestValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class DatabaseCollectionIdBaseRequestValidator : AbstractValidator> + where TRequest : class + where TValidator : IValidator, new() +{ + public DatabaseCollectionIdBaseRequestValidator() + { + Include(new DatabaseIdBaseRequestValidator()); + + RuleFor(x => x.CollectionId) + .NotEmpty() + .WithMessage("CollectionId is required."); + } +} From 394893060081402fb9344eeab98a034d8abc6de1 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 01:34:05 +0100 Subject: [PATCH 036/303] added tests for databaseCollectionid base request --- .../DatabaseCollectionIdBaseRequestTests.cs | 104 ++++++++++++++++++ .../Databases/DatabaseIdBaseRequestTests.cs | 12 +- 2 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionIdBaseRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionIdBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionIdBaseRequestTests.cs new file mode 100644 index 00000000..417f909d --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionIdBaseRequestTests.cs @@ -0,0 +1,104 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public abstract class DatabaseCollectionIdBaseRequestTests : DatabaseIdBaseRequestTests + where TRequest : DatabaseCollectionIdBaseRequest + where TValidator : AbstractValidator, new() +{ + protected sealed override TRequest CreateValidRequest + { + get + { + var request = CreateValidDatabaseCollectionIdRequest; + request.CollectionId = IdUtils.GenerateUniqueId(); + return request; + } + } + + protected abstract TRequest CreateValidDatabaseCollectionIdRequest { get; } + + [Fact] + public void CollectionIdBase_Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = CreateValidDatabaseCollectionIdRequest; + + // Assert + Assert.Equal(string.Empty, request.CollectionId); + } + + [Fact] + public void CollectionIdBase_Properties_CanBeSet() + { + // Arrange + var collectionIdValue = "validId"; + var request = CreateValidDatabaseCollectionIdRequest; + + // Act + request.CollectionId = collectionIdValue; + + // Assert + Assert.Equal(collectionIdValue, request.CollectionId); + } + + [Fact] + public void CollectionIdBase_IsValid_WithValidTeamId_ReturnsTrue() + { + // Arrange + var request = CreateValidDatabaseCollectionIdRequest; + request.DatabaseId = "valid_Team-Id."; + request.CollectionId = "valid_Team-Id."; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void CollectionIdBase_IsValid_WithInvalidData_ReturnsFalse(string? id) + { + // Arrange + var request = CreateValidDatabaseCollectionIdRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = id!; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void CollectionIdBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = CreateValidDatabaseCollectionIdRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = string.Empty; // Invalid Id + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void CollectionIdBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = CreateValidDatabaseCollectionIdRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = string.Empty; // Invalid Id + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseIdBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseIdBaseRequestTests.cs index 2661cbfb..faa2f371 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseIdBaseRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseIdBaseRequestTests.cs @@ -9,7 +9,7 @@ public abstract class DatabaseIdBaseRequestTests protected abstract TRequest CreateValidRequest { get; } [Fact] - public void TeamIdBase_Constructor_InitializesWithExpectedValues() + public void DatabaseIdBase_Constructor_InitializesWithExpectedValues() { // Arrange & Act var request = CreateValidRequest; @@ -19,7 +19,7 @@ public void TeamIdBase_Constructor_InitializesWithExpectedValues() } [Fact] - public void TeamIdBase_Properties_CanBeSet() + public void DatabaseIdBase_Properties_CanBeSet() { // Arrange var teamIdValue = "validId"; @@ -33,7 +33,7 @@ public void TeamIdBase_Properties_CanBeSet() } [Fact] - public void TeamIdBase_IsValid_WithValidTeamId_ReturnsTrue() + public void DatabaseIdBase_IsValid_WithValidTeamId_ReturnsTrue() { // Arrange var request = CreateValidRequest; @@ -49,7 +49,7 @@ public void TeamIdBase_IsValid_WithValidTeamId_ReturnsTrue() [Theory] [InlineData(null)] [InlineData("")] - public void TeamIdBase_IsValid_WithInvalidData_ReturnsFalse(string? id) + public void DatabaseIdBase_IsValid_WithInvalidData_ReturnsFalse(string? id) { // Arrange var request = CreateValidRequest; @@ -63,7 +63,7 @@ public void TeamIdBase_IsValid_WithInvalidData_ReturnsFalse(string? id) } [Fact] - public void TeamIdBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + public void DatabaseIdBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() { // Arrange var request = CreateValidRequest; @@ -74,7 +74,7 @@ public void TeamIdBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptio } [Fact] - public void TeamIdBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + public void DatabaseIdBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() { // Arrange var request = CreateValidRequest; From b2ea947a3193bd988960f111b8211ee59a2d585d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:01:31 +0100 Subject: [PATCH 037/303] added database collection id attribute key base request --- ...baseCollectionIdAttributeKeyBaseRequest.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionIdAttributeKeyBaseRequest.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionIdAttributeKeyBaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionIdAttributeKeyBaseRequest.cs new file mode 100644 index 00000000..08178874 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionIdAttributeKeyBaseRequest.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using FluentValidation; +using PinguApps.Appwrite.Shared.Attributes; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The base request but also containing DatabaseId, CollectionId and Key for attributes +/// +/// The request type +/// The request validator type +public abstract class DatabaseCollectionIdAttributeKeyBaseRequest : DatabaseCollectionIdBaseRequest + where TRequest : class + where TValidator : IValidator, new() +{ + /// + /// Attribute Key + /// + [JsonPropertyName("key")] + [SdkExclude] + public string Key { get; set; } = string.Empty; +} From 2b4a8b23281dd20479124479cfda55845f0d816f Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:03:57 +0100 Subject: [PATCH 038/303] added validator for database collection id attribute key base request --- ...llectionIdAttributeKeyBaseRequestValidator.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionIdAttributeKeyBaseRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionIdAttributeKeyBaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionIdAttributeKeyBaseRequestValidator.cs new file mode 100644 index 00000000..03b53d62 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionIdAttributeKeyBaseRequestValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class DatabaseCollectionIdAttributeKeyBaseRequestValidator : AbstractValidator> + where TRequest : class + where TValidator : IValidator, new() +{ + public DatabaseCollectionIdAttributeKeyBaseRequestValidator() + { + Include(new DatabaseCollectionIdBaseRequestValidator()); + + RuleFor(x => x.Key) + .NotEmpty() + .WithMessage("Key is required."); + } +} From 457e2c293ff0e3a0b15d2688042a9abecd9a8536 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:11:06 +0100 Subject: [PATCH 039/303] Added tests for database collection id attribute key base request --- ...ollectionIdAttributeKeyBaseRequestTests.cs | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionIdAttributeKeyBaseRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionIdAttributeKeyBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionIdAttributeKeyBaseRequestTests.cs new file mode 100644 index 00000000..10d13640 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionIdAttributeKeyBaseRequestTests.cs @@ -0,0 +1,108 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public abstract class DatabaseCollectionIdAttributeKeyBaseRequestTests : DatabaseCollectionIdBaseRequestTests + where TRequest : DatabaseCollectionIdAttributeKeyBaseRequest + where TValidator : AbstractValidator, new() +{ + protected sealed override TRequest CreateValidDatabaseCollectionIdRequest + { + get + { + var request = CreateValidDatabaseCollectionIdAttributeKeyRequest; + request.Key = IdUtils.GenerateUniqueId(); + return request; + } + } + + protected abstract TRequest CreateValidDatabaseCollectionIdAttributeKeyRequest { get; } + + [Fact] + public void KeyBase_Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = CreateValidDatabaseCollectionIdAttributeKeyRequest; + + // Assert + Assert.Equal(string.Empty, request.Key); + } + + [Fact] + public void KeyBase_Properties_CanBeSet() + { + // Arrange + var value = "validId"; + var request = CreateValidDatabaseCollectionIdAttributeKeyRequest; + + // Act + request.Key = value; + + // Assert + Assert.Equal(value, request.Key); + } + + [Fact] + public void KeyBase_IsValid_WithValidTeamId_ReturnsTrue() + { + // Arrange + var request = CreateValidDatabaseCollectionIdAttributeKeyRequest; + request.DatabaseId = "valid_Team-Id."; + request.CollectionId = "valid_Team-Id."; + request.Key = "ValidKey"; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void KeyBase_IsValid_WithInvalidData_ReturnsFalse(string? id) + { + // Arrange + var request = CreateValidDatabaseCollectionIdAttributeKeyRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = id!; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void KeyBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = CreateValidDatabaseCollectionIdAttributeKeyRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = string.Empty; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void KeyBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = CreateValidDatabaseCollectionIdAttributeKeyRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = string.Empty; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 448c3e7490fa3ef48d68a146dae653377ab4ffb5 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:13:38 +0100 Subject: [PATCH 040/303] added database collection document id base request --- ...DatabaseCollectionDocumentIdBaseRequest.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionDocumentIdBaseRequest.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionDocumentIdBaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionDocumentIdBaseRequest.cs new file mode 100644 index 00000000..bf4f77c4 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionDocumentIdBaseRequest.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using FluentValidation; +using PinguApps.Appwrite.Shared.Attributes; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The base request but also containing DatabaseId, CollectionId and DocumentId +/// +/// The request type +/// The request validator type +public abstract class DatabaseCollectionDocumentIdBaseRequest : DatabaseCollectionIdBaseRequest + where TRequest : class + where TValidator : IValidator, new() +{ + /// + /// Document ID + /// + [JsonPropertyName("documentId")] + [SdkExclude] + public string DocumentId { get; set; } = string.Empty; +} From 9908b54052b1d7a59d3b0a761072a1728987fb07 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:15:44 +0100 Subject: [PATCH 041/303] added validator for database collection document id base request --- ...seCollectionDocumentIdBaseRequestValidator.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionDocumentIdBaseRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionDocumentIdBaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionDocumentIdBaseRequestValidator.cs new file mode 100644 index 00000000..015fb9e0 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionDocumentIdBaseRequestValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class DatabaseCollectionDocumentIdBaseRequestValidator : AbstractValidator> + where TRequest : class + where TValidator : IValidator, new() +{ + public DatabaseCollectionDocumentIdBaseRequestValidator() + { + Include(new DatabaseCollectionIdBaseRequestValidator()); + + RuleFor(x => x.DocumentId) + .NotEmpty() + .WithMessage("DocumentId is required."); + } +} From 2f45526680c91b43efb77d6683bcba8f4dacabb6 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:19:27 +0100 Subject: [PATCH 042/303] Added tests for database collection document id base request --- ...aseCollectionDocumentIdBaseRequestTests.cs | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionDocumentIdBaseRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionDocumentIdBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionDocumentIdBaseRequestTests.cs new file mode 100644 index 00000000..03d5f468 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionDocumentIdBaseRequestTests.cs @@ -0,0 +1,108 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public abstract class DatabaseCollectionDocumentIdBaseRequestTests : DatabaseCollectionIdBaseRequestTests + where TRequest : DatabaseCollectionDocumentIdBaseRequest + where TValidator : AbstractValidator, new() +{ + protected sealed override TRequest CreateValidDatabaseCollectionIdRequest + { + get + { + var request = CreateValidDatabaseCollectionDocumentIdRequest; + request.DocumentId = IdUtils.GenerateUniqueId(); + return request; + } + } + + protected abstract TRequest CreateValidDatabaseCollectionDocumentIdRequest { get; } + + [Fact] + public void KeyBase_Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = CreateValidDatabaseCollectionDocumentIdRequest; + + // Assert + Assert.Equal(string.Empty, request.DocumentId); + } + + [Fact] + public void KeyBase_Properties_CanBeSet() + { + // Arrange + var value = "validId"; + var request = CreateValidDatabaseCollectionDocumentIdRequest; + + // Act + request.DocumentId = value; + + // Assert + Assert.Equal(value, request.DocumentId); + } + + [Fact] + public void KeyBase_IsValid_WithValidTeamId_ReturnsTrue() + { + // Arrange + var request = CreateValidDatabaseCollectionDocumentIdRequest; + request.DatabaseId = "valid_Team-Id."; + request.CollectionId = "valid_Team-Id."; + request.DocumentId = "valid_Team-Id."; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void KeyBase_IsValid_WithInvalidData_ReturnsFalse(string? id) + { + // Arrange + var request = CreateValidDatabaseCollectionDocumentIdRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.DocumentId = id!; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void KeyBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = CreateValidDatabaseCollectionDocumentIdRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.DocumentId = string.Empty; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void KeyBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = CreateValidDatabaseCollectionDocumentIdRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.DocumentId = string.Empty; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 61d48f20f612393f0121061b9af52e99a6e2969f Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:23:55 +0100 Subject: [PATCH 043/303] added database collection id index key base request --- ...DatabaseCollectionIdIndexKeyBaseRequest.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionIdIndexKeyBaseRequest.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionIdIndexKeyBaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionIdIndexKeyBaseRequest.cs new file mode 100644 index 00000000..2476a5cd --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/DatabaseCollectionIdIndexKeyBaseRequest.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using FluentValidation; +using PinguApps.Appwrite.Shared.Attributes; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The base request but also containing DatabaseId, CollectionId and Key for indexes +/// +/// The request type +/// The request validator type +public abstract class DatabaseCollectionIdIndexKeyBaseRequest : DatabaseCollectionIdBaseRequest + where TRequest : class + where TValidator : IValidator, new() +{ + /// + /// Index Key + /// + [JsonPropertyName("key")] + [SdkExclude] + public string Key { get; set; } = string.Empty; +} From 86c53acca7a1b5775ec09e775495f6ab77014c4c Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:25:37 +0100 Subject: [PATCH 044/303] added validator for databse collection id index key base request --- ...seCollectionIdIndexKeyBaseRequestValidator.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionIdIndexKeyBaseRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionIdIndexKeyBaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionIdIndexKeyBaseRequestValidator.cs new file mode 100644 index 00000000..c2572b79 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DatabaseCollectionIdIndexKeyBaseRequestValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class DatabaseCollectionIdIndexKeyBaseRequestValidator : AbstractValidator> + where TRequest : class + where TValidator : IValidator, new() +{ + public DatabaseCollectionIdIndexKeyBaseRequestValidator() + { + Include(new DatabaseCollectionIdBaseRequestValidator()); + + RuleFor(x => x.Key) + .NotEmpty() + .WithMessage("Key is required."); + } +} From 8c6c7f9df8d235fe56a2d5fb1f71710aa1cc988a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:32:08 +0100 Subject: [PATCH 045/303] added tests for database colletion id index key base request --- ...aseCollectionIdIndexKeyBaseRequestTests.cs | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionIdIndexKeyBaseRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionIdIndexKeyBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionIdIndexKeyBaseRequestTests.cs new file mode 100644 index 00000000..4c756d65 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DatabaseCollectionIdIndexKeyBaseRequestTests.cs @@ -0,0 +1,108 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public abstract class DatabaseCollectionIdIndexKeyBaseRequestTests : DatabaseCollectionIdBaseRequestTests + where TRequest : DatabaseCollectionIdIndexKeyBaseRequest + where TValidator : AbstractValidator, new() +{ + protected sealed override TRequest CreateValidDatabaseCollectionIdRequest + { + get + { + var request = CreateValidDatabaseCollectionIdIndexKeyRequest; + request.Key = IdUtils.GenerateUniqueId(); + return request; + } + } + + protected abstract TRequest CreateValidDatabaseCollectionIdIndexKeyRequest { get; } + + [Fact] + public void KeyBase_Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = CreateValidDatabaseCollectionIdIndexKeyRequest; + + // Assert + Assert.Equal(string.Empty, request.Key); + } + + [Fact] + public void KeyBase_Properties_CanBeSet() + { + // Arrange + var value = "validId"; + var request = CreateValidDatabaseCollectionIdIndexKeyRequest; + + // Act + request.Key = value; + + // Assert + Assert.Equal(value, request.Key); + } + + [Fact] + public void KeyBase_IsValid_WithValidTeamId_ReturnsTrue() + { + // Arrange + var request = CreateValidDatabaseCollectionIdIndexKeyRequest; + request.DatabaseId = "valid_Team-Id."; + request.CollectionId = "valid_Team-Id."; + request.Key = "validKey"; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void KeyBase_IsValid_WithInvalidData_ReturnsFalse(string? id) + { + // Arrange + var request = CreateValidDatabaseCollectionIdIndexKeyRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = id!; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void KeyBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = CreateValidDatabaseCollectionIdIndexKeyRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = string.Empty; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void KeyBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = CreateValidDatabaseCollectionIdIndexKeyRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = string.Empty; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 05e67c92c43b2aa5904e2a50e77192b53cff792a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:37:06 +0100 Subject: [PATCH 046/303] Added List database request --- .../Requests/Databases/ListDatabasesRequest.cs | 10 ++++++++++ .../Validators/ListDatabasesRequestValidator.cs | 11 +++++++++++ 2 files changed, 21 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/ListDatabasesRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListDatabasesRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/ListDatabasesRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/ListDatabasesRequest.cs new file mode 100644 index 00000000..da3db278 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/ListDatabasesRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request for listing databases +/// +public class ListDatabasesRequest : QuerySearchBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListDatabasesRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListDatabasesRequestValidator.cs new file mode 100644 index 00000000..2075bd56 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListDatabasesRequestValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class ListDatabasesRequestValidator : AbstractValidator +{ + public ListDatabasesRequestValidator() + { + Include(new QuerySearchBaseRequestValidator()); + } +} From 89f6dd23a903ac1c9b247a94b79e819743b008c5 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:40:53 +0100 Subject: [PATCH 047/303] Added tests for list databases request --- .../Requests/Databases/ListDatabasesRequestTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListDatabasesRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListDatabasesRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListDatabasesRequestTests.cs new file mode 100644 index 00000000..5e9f8751 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListDatabasesRequestTests.cs @@ -0,0 +1,8 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class ListDatabasesRequestTests : QuerySearchBaseRequestTests +{ + protected override ListDatabasesRequest CreateValidRequest => new(); +} From 4e76beeb72e203bbe9ea24c64c591459e318a644 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:45:38 +0100 Subject: [PATCH 048/303] Added create database request --- .../Databases/CreateDatabaseRequest.cs | 25 +++++++++++++++++++ .../CreateDatabaseRequestValidator.cs | 24 ++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatabaseRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatabaseRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatabaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatabaseRequest.cs new file mode 100644 index 00000000..27af0b87 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatabaseRequest.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; +public class CreateDatabaseRequest : BaseRequest +{ + /// + /// Unique Id. Choose a custom ID or generate a random ID with ID.unique(). Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars + /// + [JsonPropertyName("databaseId")] + public string DatabaseId { get; set; } = IdUtils.GenerateUniqueId(); + + /// + /// Database name. Max length: 128 chars + /// + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// + /// Is the database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled + /// + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatabaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatabaseRequestValidator.cs new file mode 100644 index 00000000..fda45843 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatabaseRequestValidator.cs @@ -0,0 +1,24 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateDatabaseRequestValidator : AbstractValidator +{ + public CreateDatabaseRequestValidator() + { + RuleFor(x => x.DatabaseId) + .NotEmpty() + .WithMessage("DatabaseId is required.") + .Matches("^[a-zA-Z0-9][a-zA-Z0-9._-]{0,35}$") + .WithMessage("DatabaseId can only contain a-z, A-Z, 0-9, period, hyphen, and underscore, and can't start with a special character. Max length is 36 chars."); + + RuleFor(x => x.Name) + .NotEmpty() + .WithMessage("Name is required.") + .MaximumLength(128) + .WithMessage("Name can have a maximum length of 128 characters."); + + RuleFor(x => x.Enabled) + .NotNull() + .WithMessage("Enabled is required."); + } +} From 6180652a2673c90e9753bef8dc9422692829bd07 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 15:57:58 +0100 Subject: [PATCH 049/303] Added tests for create database request --- .../Databases/CreateDatabaseRequestTests.cs | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatabaseRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatabaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatabaseRequestTests.cs new file mode 100644 index 00000000..13eb4202 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatabaseRequestTests.cs @@ -0,0 +1,153 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateDatabaseRequestTests +{ + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new CreateDatabaseRequest(); + + // Assert + Assert.NotEmpty(request.DatabaseId); + Assert.Equal(string.Empty, request.Name); + Assert.False(request.Enabled); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var databaseId = IdUtils.GenerateUniqueId(); + var name = "My Database"; + var enabled = true; + + var request = new CreateDatabaseRequest(); + + // Act + request.DatabaseId = databaseId; + request.Name = name; + request.Enabled = enabled; + + // Assert + Assert.Equal(databaseId, request.DatabaseId); + Assert.Equal(name, request.Name); + Assert.Equal(enabled, request.Enabled); + } + + public static TheoryData ValidRequestsData = + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "Valid Database Name", + Enabled = true + }, + new() + { + DatabaseId = "validDatabaseId123", + Name = "Another Valid Database", + Enabled = false + } + ]; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(CreateDatabaseRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData = new() + { + new() + { + DatabaseId = null!, + Name = "Pingu" + }, + new() + { + DatabaseId = "", + Name = "Pingu" + }, + new() + { + DatabaseId = "invalid chars!", + Name = "Pingu" + }, + new() + { + DatabaseId = ".startsWithSymbol", + Name = "Pingu" + }, + new() + { + DatabaseId = new string('a', 37), + Name = "Pingu" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = null! + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = new string('a', 129) + } + }; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(CreateDatabaseRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new CreateDatabaseRequest + { + DatabaseId = "", + Name = "" + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new CreateDatabaseRequest + { + DatabaseId = "", + Name = "" + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From bb7e8eb7734858c046a0acf2e0b2046cd5264df0 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 16:00:31 +0100 Subject: [PATCH 050/303] added get database request --- .../Requests/Databases/CreateDatabaseRequest.cs | 4 ++++ .../Requests/Databases/GetDatabaseRequest.cs | 10 ++++++++++ .../Validators/GetDatabaseRequestValidator.cs | 10 ++++++++++ 3 files changed, 24 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/GetDatabaseRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetDatabaseRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatabaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatabaseRequest.cs index 27af0b87..ac3bfdf7 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatabaseRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatabaseRequest.cs @@ -3,6 +3,10 @@ using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create a database +/// public class CreateDatabaseRequest : BaseRequest { /// diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/GetDatabaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/GetDatabaseRequest.cs new file mode 100644 index 00000000..f93ae85d --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/GetDatabaseRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to get a database +/// +public class GetDatabaseRequest : DatabaseIdBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetDatabaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetDatabaseRequestValidator.cs new file mode 100644 index 00000000..9c3d4cca --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetDatabaseRequestValidator.cs @@ -0,0 +1,10 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class GetDatabaseRequestValidator : AbstractValidator +{ + public GetDatabaseRequestValidator() + { + Include(new DatabaseIdBaseRequestValidator()); + } +} From 826acc025ba59870dde25aff8498eb2afd9ca0ce Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 16:01:51 +0100 Subject: [PATCH 051/303] added tests for get database request --- .../Requests/Databases/GetDatabaseRequestTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetDatabaseRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetDatabaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetDatabaseRequestTests.cs new file mode 100644 index 00000000..e9cb681b --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetDatabaseRequestTests.cs @@ -0,0 +1,8 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class GetDatabaseRequestTests : DatabaseIdBaseRequestTests +{ + protected override GetDatabaseRequest CreateValidRequest => new(); +} From a9d6384c7acb3a5ab9d35faa5d6e0945fb130ef9 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 16:11:42 +0100 Subject: [PATCH 052/303] added update database request --- .../Requests/Databases/UpdateDatabase.cs | 18 +++++++++++++++++ .../Validators/UpdateDatabaseValidator.cs | 20 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabase.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatabaseValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabase.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabase.cs new file mode 100644 index 00000000..b6880020 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabase.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; +public class UpdateDatabase : DatabaseIdBaseRequest +{ + /// + /// Database name. Max length: 128 chars + /// + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// + /// Is database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled + /// + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatabaseValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatabaseValidator.cs new file mode 100644 index 00000000..405bc7fd --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatabaseValidator.cs @@ -0,0 +1,20 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateDatabaseValidator : AbstractValidator +{ + public UpdateDatabaseValidator() + { + Include(new DatabaseIdBaseRequestValidator()); + + RuleFor(x => x.Name) + .NotEmpty() + .WithMessage("Name is required.") + .MaximumLength(128) + .WithMessage("Name cannot exceed 128 characters."); + + RuleFor(x => x.Enabled) + .NotNull() + .WithMessage("Enabled is required."); + } +} From f9a0bc026c5ba7cd2985d2f6fbc75e98290d6b9f Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 16:12:10 +0100 Subject: [PATCH 053/303] added tests for update database --- .../Requests/Databases/UpdateDatabaseTests.cs | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatabaseTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatabaseTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatabaseTests.cs new file mode 100644 index 00000000..720955e8 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatabaseTests.cs @@ -0,0 +1,132 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateDatabaseTests : DatabaseIdBaseRequestTests +{ + protected override UpdateDatabase CreateValidRequest => new() + { + Name = "Pingu" + }; + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new UpdateDatabase(); + + // Assert + Assert.Equal(string.Empty, request.Name); + Assert.False(request.Enabled); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var name = "Updated Database"; + var enabled = true; + + var request = new UpdateDatabase(); + + // Act + request.Name = name; + request.Enabled = enabled; + + // Assert + Assert.Equal(name, request.Name); + Assert.Equal(enabled, request.Enabled); + } + + public static TheoryData ValidRequestsData = new() + { + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "Valid Database Name", + Enabled = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "Another Valid Database", + Enabled = false + } + }; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(UpdateDatabase request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData = new() + { + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = null! + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = new string('a', 129) + } + }; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(UpdateDatabase request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new UpdateDatabase + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "", + Enabled = false + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new UpdateDatabase + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "", + Enabled = false + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 887da4df40a69144ad729c0d481adea67729026f Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 16:15:14 +0100 Subject: [PATCH 054/303] added delete database request --- .../Requests/Databases/DeleteDatabaseRequest.cs | 10 ++++++++++ .../Validators/DeleteDatabaseRequestValidator.cs | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteDatabaseRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteDatabaseRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteDatabaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteDatabaseRequest.cs new file mode 100644 index 00000000..ab0b3772 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteDatabaseRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to delete a database +/// +public class DeleteDatabaseRequest : DatabaseIdBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteDatabaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteDatabaseRequestValidator.cs new file mode 100644 index 00000000..9573792d --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteDatabaseRequestValidator.cs @@ -0,0 +1,10 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class DeleteDatabaseRequestValidator : AbstractValidator +{ + public DeleteDatabaseRequestValidator() + { + Include(new DatabaseIdBaseRequestValidator()); + } +} From 6968e117013649b7a00d20c3a955e345b2d3e245 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 16:16:10 +0100 Subject: [PATCH 055/303] added tests for delete database request --- .../Requests/Databases/DeleteDatabaseRequestTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteDatabaseRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteDatabaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteDatabaseRequestTests.cs new file mode 100644 index 00000000..00cce789 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteDatabaseRequestTests.cs @@ -0,0 +1,8 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class DeleteDatabaseRequestTests : DatabaseIdBaseRequestTests +{ + protected override DeleteDatabaseRequest CreateValidRequest => new(); +} From f73814cd1baa75b781903e8d62ffdd9f13ce2dce Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 16:22:13 +0100 Subject: [PATCH 056/303] added list collections request --- .../Databases/ListCollectionsRequest.cs | 18 ++++++++++++++++++ .../ListCollectionsRequestValidator.cs | 15 +++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/ListCollectionsRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListCollectionsRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/ListCollectionsRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/ListCollectionsRequest.cs new file mode 100644 index 00000000..ff7307c2 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/ListCollectionsRequest.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Attributes; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to list collections in a database +/// +public class ListCollectionsRequest : QuerySearchBaseRequest +{ + /// + /// Database ID + /// + [JsonPropertyName("databaseId")] + [SdkExclude] + public string DatabaseId { get; set; } = string.Empty; +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListCollectionsRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListCollectionsRequestValidator.cs new file mode 100644 index 00000000..7d99d1b0 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListCollectionsRequestValidator.cs @@ -0,0 +1,15 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class ListCollectionsRequestValidator : AbstractValidator +{ + public ListCollectionsRequestValidator() + { + Include(new QuerySearchBaseRequestValidator()); + + RuleFor(x => x.DatabaseId) + .NotEmpty() + .WithMessage("DatabaseId is required."); + } +} From 7cb2722f27f924e26ad153cc18b2fcec09d16650 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 16:25:00 +0100 Subject: [PATCH 057/303] added tests for list colelctions request --- .../Databases/ListCollectionsRequestTests.cs | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListCollectionsRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListCollectionsRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListCollectionsRequestTests.cs new file mode 100644 index 00000000..0581ff2f --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListCollectionsRequestTests.cs @@ -0,0 +1,113 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class ListCollectionsRequestTests : QuerySearchBaseRequestTests +{ + protected override ListCollectionsRequest CreateValidRequest => new() + { + DatabaseId = IdUtils.GenerateUniqueId() + }; + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new ListCollectionsRequest(); + + // Assert + Assert.Equal(string.Empty, request.DatabaseId); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var databaseId = "testDatabaseId"; + + var request = new ListCollectionsRequest(); + + // Act + request.DatabaseId = databaseId; + + // Assert + Assert.Equal(databaseId, request.DatabaseId); + } + + public static TheoryData ValidRequestsData = new() + { + new() + { + DatabaseId = "validDatabaseId123" + }, + new() + { + DatabaseId = "anotherValidDatabaseId" + } + }; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(ListCollectionsRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData = new() + { + new() + { + DatabaseId = null! + }, + new() + { + DatabaseId = "" + } + }; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(ListCollectionsRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new ListCollectionsRequest + { + DatabaseId = "" + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new ListCollectionsRequest + { + DatabaseId = "" + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 1c5543b5ea56b016b4da046f072beaa31c5260a1 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 18:06:38 +0100 Subject: [PATCH 058/303] Added permission and role utilities --- .../Enums/PermissionType.cs | 24 ++++++++ .../Enums/RoleStatus.cs | 12 ++++ .../Enums/RoleType.cs | 32 ++++++++++ .../Utils/Permission.cs | 25 ++++++++ src/PinguApps.Appwrite.Shared/Utils/Role.cs | 59 +++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Enums/PermissionType.cs create mode 100644 src/PinguApps.Appwrite.Shared/Enums/RoleStatus.cs create mode 100644 src/PinguApps.Appwrite.Shared/Enums/RoleType.cs create mode 100644 src/PinguApps.Appwrite.Shared/Utils/Permission.cs create mode 100644 src/PinguApps.Appwrite.Shared/Utils/Role.cs diff --git a/src/PinguApps.Appwrite.Shared/Enums/PermissionType.cs b/src/PinguApps.Appwrite.Shared/Enums/PermissionType.cs new file mode 100644 index 00000000..f8e5932c --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Enums/PermissionType.cs @@ -0,0 +1,24 @@ +namespace PinguApps.Appwrite.Shared.Enums; +public enum PermissionType +{ + /// + /// Access to read a resource + /// + Read, + /// + /// Alias to grant create, update, and delete access for collections and buckets and update and delete access for documents and files + /// + Write, + /// + /// Access to create new resources. Does not apply to files or documents. Applying this type of access to files or documents results in an error + /// + Create, + /// + /// Access to change a resource, but not remove or create new resources. Does not apply to functions + /// + Update, + /// + /// Access to remove a resource. Does not apply to functions + /// + Delete +} diff --git a/src/PinguApps.Appwrite.Shared/Enums/RoleStatus.cs b/src/PinguApps.Appwrite.Shared/Enums/RoleStatus.cs new file mode 100644 index 00000000..d059eb5f --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Enums/RoleStatus.cs @@ -0,0 +1,12 @@ +namespace PinguApps.Appwrite.Shared.Enums; +public enum RoleStatus +{ + /// + /// Verified users + /// + Verified, + /// + /// Unverified users + /// + Unverified +} diff --git a/src/PinguApps.Appwrite.Shared/Enums/RoleType.cs b/src/PinguApps.Appwrite.Shared/Enums/RoleType.cs new file mode 100644 index 00000000..1c5308c2 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Enums/RoleType.cs @@ -0,0 +1,32 @@ +namespace PinguApps.Appwrite.Shared.Enums; +public enum RoleType +{ + /// + /// Grants access to anyone + /// + Any, + /// + /// Grants access to a specific user by user ID. You can optionally pass the verified or unverified string to target specific types of users + /// + User, + /// + /// Grants access to any authenticated or anonymous user. You can optionally pass the verified or unverified string to target specific types of users + /// + Users, + /// + /// Grants access to any guest user without a session. Authenticated users don't have access to this role + /// + Guests, + /// + /// Grants access to any member who possesses a specific role in a team. To gain access to this permission, the user must be a member of the specific team and have the given role assigned to them. Team roles can be assigned when inviting a user to become a team member + /// + Team, + /// + /// Grants access to a specific member of a team. When the member is removed from the team, they will no longer have access + /// + Member, + /// + /// Grants access to all accounts with a specific label ID. Once the label is removed from the user, they will no longer have access + /// + Label +} diff --git a/src/PinguApps.Appwrite.Shared/Utils/Permission.cs b/src/PinguApps.Appwrite.Shared/Utils/Permission.cs new file mode 100644 index 00000000..aed92c2e --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Utils/Permission.cs @@ -0,0 +1,25 @@ +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Utils; +public class Permission +{ + public PermissionType PermissionType { get; private set; } + + public Role Role { get; private set; } + + private Permission(PermissionType permissionType, Role role) + { + PermissionType = permissionType; + Role = role; + } + + public static Permission Read(Role role) => new(PermissionType.Read, role); + + public static Permission Write(Role role) => new(PermissionType.Write, role); + + public static Permission Create(Role role) => new(PermissionType.Create, role); + + public static Permission Update(Role role) => new(PermissionType.Update, role); + + public static Permission Delete(Role role) => new(PermissionType.Delete, role); +} diff --git a/src/PinguApps.Appwrite.Shared/Utils/Role.cs b/src/PinguApps.Appwrite.Shared/Utils/Role.cs new file mode 100644 index 00000000..5a40aa8a --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Utils/Role.cs @@ -0,0 +1,59 @@ +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Utils; +public class Role +{ + public RoleType RoleType { get; private set; } + public string? Id { get; private set; } + public RoleStatus? Status { get; private set; } + public string? TeamRole { get; private set; } + public string? LabelName { get; private set; } + + private Role(RoleType roleType) + { + RoleType = roleType; + } + + private Role(RoleType roleType, string id) : this(roleType) + { + Id = id; + } + + private Role(RoleType roleType, string id, RoleStatus status) : this(roleType, id) + { + Status = status; + } + + private Role(RoleType roleType, RoleStatus status) : this(roleType) + { + Status = status; + } + + private Role(RoleType roleType, string id, string teamRole) : this(roleType, id) + { + TeamRole = teamRole; + } + + public static Role Any() => new(RoleType.Any); + + public static Role User(string id) => new(RoleType.User, id); + + public static Role User(string id, RoleStatus status) => new(RoleType.User, id, status); + + public static Role Users() => new(RoleType.Users); + + public static Role Users(RoleStatus status) => new(RoleType.Users, status); + + public static Role Guests() => new(RoleType.Guests); + + public static Role Team(string id) => new(RoleType.Team, id); + + public static Role Team(string id, string teamRole) => new(RoleType.Team, id, teamRole); + + public static Role Member(string id) => new(RoleType.Member, id); + + public static Role Label(string label) => new(RoleType.Label) + { + LabelName = label + }; +} From 1e87c338dfa0fd1543075e45899db15201c7e02a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 20:38:25 +0100 Subject: [PATCH 059/303] Added converters for both role and permission --- .../Converters/PermissionJsonConverter.cs | 52 +++++++++ .../Converters/RoleJsonConverter.cs | 103 ++++++++++++++++++ .../Utils/Permission.cs | 40 ++++++- src/PinguApps.Appwrite.Shared/Utils/Role.cs | 68 +++++++++++- 4 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 src/PinguApps.Appwrite.Shared/Converters/PermissionJsonConverter.cs create mode 100644 src/PinguApps.Appwrite.Shared/Converters/RoleJsonConverter.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/PermissionJsonConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/PermissionJsonConverter.cs new file mode 100644 index 00000000..82ed0a33 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/PermissionJsonConverter.cs @@ -0,0 +1,52 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Converters; +public class PermissionJsonConverter : JsonConverter +{ + public override Permission? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.String) + { + throw new JsonException("Expected string value for Permission"); + } + + var value = reader.GetString()!; + + // Format is "permissionType(\"roleString\")" + var openParenIndex = value.IndexOf('('); + var closeParenIndex = value.LastIndexOf(')'); + + if (openParenIndex == -1 || closeParenIndex == -1) + { + throw new JsonException("Invalid Permission format"); + } + + var permissionTypeStr = value[..openParenIndex].ToLower(); + // Remove the quotes from the role string + var roleString = value[(openParenIndex + 2)..(closeParenIndex - 1)]; + + // Deserialize the role first + var role = JsonSerializer.Deserialize(JsonSerializer.Serialize(roleString), options)!; + + return permissionTypeStr switch + { + "read" => Permission.Read(role), + "write" => Permission.Write(role), + "create" => Permission.Create(role), + "update" => Permission.Update(role), + "delete" => Permission.Delete(role), + _ => throw new JsonException($"Unknown permission type: {permissionTypeStr}") + }; + } + + public override void Write(Utf8JsonWriter writer, Permission value, JsonSerializerOptions options) + { + var roleString = JsonSerializer.Deserialize(JsonSerializer.Serialize(value.Role, options))!; + var result = value.PermissionType.ToString().ToLower() + $"(\"{roleString}\")"; + writer.WriteStringValue(result); + } +} + diff --git a/src/PinguApps.Appwrite.Shared/Converters/RoleJsonConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/RoleJsonConverter.cs new file mode 100644 index 00000000..0131450e --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/RoleJsonConverter.cs @@ -0,0 +1,103 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Converters; +public class RoleJsonConverter : JsonConverter +{ + public override Role? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.String) + { + throw new JsonException("Expected string value for Role"); + } + + var value = reader.GetString()!; + + // Handle the simple cases first + return value switch + { + "any" => Role.Any(), + "users" => Role.Users(), + "guests" => Role.Guests(), + _ => ParseComplexRole(value) + }; + } + + private static Role ParseComplexRole(string value) + { + if (value.StartsWith("user:")) + { + var parts = value[5..].Split('/'); + return parts.Length == 2 + ? Role.User(parts[0], Enum.Parse(parts[1], true)) + : Role.User(parts[0]); + } + + if (value.StartsWith("users/")) + { + return Role.Users(Enum.Parse(value[6..], true)); + } + + if (value.StartsWith("team:")) + { + var parts = value[5..].Split('/'); + return parts.Length == 2 + ? Role.Team(parts[0], parts[1]) + : Role.Team(parts[0]); + } + + if (value.StartsWith("member:")) + { + return Role.Member(value[7..]); + } + + if (value.StartsWith("label:")) + { + return Role.Label(value[6..]); + } + + throw new JsonException($"Unknown role format: {value}"); + } + + public override void Write(Utf8JsonWriter writer, Role value, JsonSerializerOptions options) + { + string result = value.RoleType switch + { + RoleType.Any => "any", + RoleType.User => FormatUserRole(value), + RoleType.Users => FormatUsersRole(value), + RoleType.Guests => "guests", + RoleType.Team => FormatTeamRole(value), + RoleType.Member => $"member:{value.Id}", + RoleType.Label => $"label:{value.LabelName}", + _ => throw new JsonException($"Unknown role type: {value.RoleType}") + }; + + writer.WriteStringValue(result); + } + + private static string FormatUserRole(Role role) + { + return role.Status.HasValue + ? $"user:{role.Id}/{role.Status.ToString()!.ToLower()}" + : $"user:{role.Id}"; + } + + private static string FormatUsersRole(Role role) + { + return role.Status.HasValue + ? $"users/{role.Status.ToString()!.ToLower()}" + : "users"; + } + + private static string FormatTeamRole(Role role) + { + return string.IsNullOrEmpty(role.TeamRole) + ? $"team:{role.Id}" + : $"team:{role.Id}/{role.TeamRole}"; + } +} + diff --git a/src/PinguApps.Appwrite.Shared/Utils/Permission.cs b/src/PinguApps.Appwrite.Shared/Utils/Permission.cs index aed92c2e..96255acd 100644 --- a/src/PinguApps.Appwrite.Shared/Utils/Permission.cs +++ b/src/PinguApps.Appwrite.Shared/Utils/Permission.cs @@ -1,10 +1,23 @@ -using PinguApps.Appwrite.Shared.Enums; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; namespace PinguApps.Appwrite.Shared.Utils; + +/// +/// Helper class to create permission definitions +/// +[JsonConverter(typeof(PermissionJsonConverter))] public class Permission { + /// + /// The type of the permission - What access is being granted to the role(s) + /// public PermissionType PermissionType { get; private set; } + /// + /// The role(s) to grant the permissions to + /// public Role Role { get; private set; } private Permission(PermissionType permissionType, Role role) @@ -13,13 +26,38 @@ private Permission(PermissionType permissionType, Role role) Role = role; } + /// + /// Creates Read pemission for the given role + /// + /// The role to grant this permission level to + /// The permission public static Permission Read(Role role) => new(PermissionType.Read, role); + /// + /// Creates Write pemission for the given role + /// + /// The role to grant this permission level to + /// The permission public static Permission Write(Role role) => new(PermissionType.Write, role); + /// + /// Creates Create pemission for the given role + /// + /// The role to grant this permission level to + /// The permission public static Permission Create(Role role) => new(PermissionType.Create, role); + /// + /// Creates Udpate pemission for the given role + /// + /// The role to grant this permission level to + /// The permission public static Permission Update(Role role) => new(PermissionType.Update, role); + /// + /// Creates Delete pemission for the given role + /// + /// The role to grant this permission level to + /// The permission public static Permission Delete(Role role) => new(PermissionType.Delete, role); } diff --git a/src/PinguApps.Appwrite.Shared/Utils/Role.cs b/src/PinguApps.Appwrite.Shared/Utils/Role.cs index 5a40aa8a..8e1563e0 100644 --- a/src/PinguApps.Appwrite.Shared/Utils/Role.cs +++ b/src/PinguApps.Appwrite.Shared/Utils/Role.cs @@ -1,12 +1,38 @@ -using PinguApps.Appwrite.Shared.Enums; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; namespace PinguApps.Appwrite.Shared.Utils; + +/// +/// Helper class to create Role definitions. Works in tandem with +/// +[JsonConverter(typeof(RoleJsonConverter))] public class Role { + /// + /// The type of role + /// public RoleType RoleType { get; private set; } + + /// + /// The Id of the team, user or member + /// public string? Id { get; private set; } + + /// + /// The verification status of a user group + /// public RoleStatus? Status { get; private set; } + + /// + /// The role of team members + /// public string? TeamRole { get; private set; } + + /// + /// The user label + /// public string? LabelName { get; private set; } private Role(RoleType roleType) @@ -34,24 +60,64 @@ private Role(RoleType roleType, string id, string teamRole) : this(roleType, id) TeamRole = teamRole; } + /// + /// Creates a role defining anyone + /// + /// The role public static Role Any() => new(RoleType.Any); + /// + /// Creates a role defining a specific user + /// + /// The role public static Role User(string id) => new(RoleType.User, id); + /// + /// Creates a role defining a specific user, given their verification status + /// + /// The role public static Role User(string id, RoleStatus status) => new(RoleType.User, id, status); + /// + /// Creates a role defining all users + /// + /// The role public static Role Users() => new(RoleType.Users); + /// + /// Creates a role defining all users, given their verification status + /// + /// The role public static Role Users(RoleStatus status) => new(RoleType.Users, status); + /// + /// Creates a role defining all guests + /// + /// The role public static Role Guests() => new(RoleType.Guests); + /// + /// Creates a role defining anyone belonging to the specified team + /// + /// The role public static Role Team(string id) => new(RoleType.Team, id); + /// + /// Creates a role defining anyone belonging to the specified team, and posessing the specified role within the team + /// + /// The role public static Role Team(string id, string teamRole) => new(RoleType.Team, id, teamRole); + /// + /// Creates a role defining anyone who is a member of the specified team + /// + /// The role public static Role Member(string id) => new(RoleType.Member, id); + /// + /// Creates a role defining anyone with the specified label + /// + /// The role public static Role Label(string label) => new(RoleType.Label) { LabelName = label From 6dba752506a56a8d37eedfa14d5a1682c897f861 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 20:49:36 +0100 Subject: [PATCH 060/303] Added converters for list or readonly lists of permissions --- .../Converters/PermissionListConverter.cs | 50 +++++++++++++++++++ .../PermissionReadOnlyListConverter.cs | 50 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Converters/PermissionListConverter.cs create mode 100644 src/PinguApps.Appwrite.Shared/Converters/PermissionReadOnlyListConverter.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/PermissionListConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/PermissionListConverter.cs new file mode 100644 index 00000000..4db2ef6f --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/PermissionListConverter.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Converters; +public class PermissionListConverter : JsonConverter> +{ + private readonly PermissionJsonConverter _permissionConverter = new(); + + public override List? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType is not JsonTokenType.StartArray) + { + throw new JsonException("Expected start of array"); + } + + var permissions = new List(); + + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndArray) + { + break; + } + + var permission = _permissionConverter.Read(ref reader, typeof(Permission), options); + + if (permission is not null) + { + permissions.Add(permission); + } + } + + return permissions; + } + + public override void Write(Utf8JsonWriter writer, List value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + + foreach (var permission in value) + { + _permissionConverter.Write(writer, permission, options); + } + + writer.WriteEndArray(); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Converters/PermissionReadOnlyListConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/PermissionReadOnlyListConverter.cs new file mode 100644 index 00000000..e69ce127 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/PermissionReadOnlyListConverter.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Converters; +public class PermissionReadOnlyListConverter : JsonConverter> +{ + private readonly PermissionJsonConverter _permissionConverter = new(); + + public override IReadOnlyList? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType is not JsonTokenType.StartArray) + { + throw new JsonException("Expected start of array"); + } + + var permissions = new List(); + + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndArray) + { + break; + } + + var permission = _permissionConverter.Read(ref reader, typeof(Permission), options); + + if (permission is not null) + { + permissions.Add(permission); + } + } + + return permissions.AsReadOnly(); + } + + public override void Write(Utf8JsonWriter writer, IReadOnlyList value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + + foreach (var permission in value) + { + _permissionConverter.Write(writer, permission, options); + } + + writer.WriteEndArray(); + } +} From 1618d8309bfb0fe49a61d2a1cfa171a3ccd89520 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 22:07:06 +0100 Subject: [PATCH 061/303] Added tests to cover permissions, roles, and all their converters --- .../PermissionJsonConverterTests.cs | 138 +++++++++ .../PermissionListConverterTests.cs | 161 ++++++++++ .../PermissionReadOnlyListConverterTests.cs | 183 ++++++++++++ .../Converters/RoleJsonConverterTests.cs | 275 ++++++++++++++++++ .../Utils/PermissionTests.cs | 90 ++++++ .../Utils/RoleTests.cs | 164 +++++++++++ 6 files changed, 1011 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionJsonConverterTests.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionListConverterTests.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionReadOnlyListConverterTests.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/RoleJsonConverterTests.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Utils/PermissionTests.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Utils/RoleTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionJsonConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionJsonConverterTests.cs new file mode 100644 index 00000000..6c1e0db4 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionJsonConverterTests.cs @@ -0,0 +1,138 @@ +using System.Text.Encodings.Web; +using System.Text.Json; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class PermissionJsonConverterTests +{ + private readonly JsonSerializerOptions _options; + private readonly PermissionJsonConverter _converter; + + public PermissionJsonConverterTests() + { + _converter = new PermissionJsonConverter(); + _options = new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Converters = { _converter, new RoleJsonConverter() } + }; + } + + [Theory] + [InlineData(PermissionType.Read, "read")] + [InlineData(PermissionType.Write, "write")] + [InlineData(PermissionType.Create, "create")] + [InlineData(PermissionType.Update, "update")] + [InlineData(PermissionType.Delete, "delete")] + public void Write_AllPermissionTypes_SerializeCorrectly(PermissionType permissionType, string expectedPrefix) + { + // Arrange + var permission = permissionType switch + { + PermissionType.Read => Permission.Read(Role.Any()), + PermissionType.Write => Permission.Write(Role.Any()), + PermissionType.Create => Permission.Create(Role.Any()), + PermissionType.Update => Permission.Update(Role.Any()), + PermissionType.Delete => Permission.Delete(Role.Any()), + _ => throw new ArgumentException("Invalid permission type") + }; + + // Act + var json = JsonSerializer.Serialize(permission, _options); + + // Assert + Assert.Equal($"\"{expectedPrefix}(\\\"any\\\")\"", json); + } + + [Theory] + [InlineData("read(\\\"any\\\")", PermissionType.Read, RoleType.Any)] + [InlineData("write(\\\"user:123\\\")", PermissionType.Write, RoleType.User)] + [InlineData("create(\\\"team:456/admin\\\")", PermissionType.Create, RoleType.Team)] + [InlineData("update(\\\"users/verified\\\")", PermissionType.Update, RoleType.Users)] + [InlineData("delete(\\\"label:test\\\")", PermissionType.Delete, RoleType.Label)] + public void Read_ValidPermissions_DeserializeCorrectly(string json, PermissionType expectedPermissionType, RoleType expectedRoleType) + { + // Act + var permission = JsonSerializer.Deserialize($"\"{json}\"", _options); + + // Assert + Assert.NotNull(permission); + Assert.Equal(expectedPermissionType, permission.PermissionType); + Assert.Equal(expectedRoleType, permission.Role.RoleType); + } + + [Theory] + [InlineData(42)] + [InlineData(true)] + public void Read_NonStringToken_ThrowsJsonException(object? invalidValue) + { + // Arrange + var json = JsonSerializer.Serialize(invalidValue); + + // Act & Assert + Assert.Throws(() => + JsonSerializer.Deserialize(json, _options)); + } + + [Theory] + [InlineData("")] + [InlineData("invalid")] + [InlineData("read")] + [InlineData("read(any)")] + [InlineData("read\"any\")")] + [InlineData("read(\"any\"")] + [InlineData("unknown(\"any\")")] + public void Read_InvalidFormat_ThrowsJsonException(string invalidJson) + { + // Act & Assert + Assert.Throws(() => + JsonSerializer.Deserialize($"\"{invalidJson}\"", _options)); + } + + [Fact] + public void Write_ComplexPermission_SerializesCorrectly() + { + // Arrange + var role = Role.Team("123", "admin"); + var permission = Permission.Write(role); + + // Act + var json = JsonSerializer.Serialize(permission, _options); + + // Assert + Assert.Equal("\"write(\\\"team:123/admin\\\")\"", json); + } + + [Fact] + public void Read_ComplexPermission_DeserializesCorrectly() + { + // Arrange + var json = "\"write(\\\"team:123/admin\\\")\""; + + // Act + var permission = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.NotNull(permission); + Assert.Equal(PermissionType.Write, permission.PermissionType); + Assert.Equal(RoleType.Team, permission.Role.RoleType); + Assert.Equal("123", permission.Role.Id); + Assert.Equal("admin", permission.Role.TeamRole); + } + + [Fact] + public void Read_ThrowsJsonException_ForUnknownPermissionType() + { + // Arrange + var json = "\"invalidPermissionType(\\\"any\\\")\""; + + // Act & Assert + var exception = Assert.Throws(() => + JsonSerializer.Deserialize(json, _options) + ); + + Assert.Equal("Unknown permission type: invalidpermissiontype", exception.Message); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionListConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionListConverterTests.cs new file mode 100644 index 00000000..f473b9eb --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionListConverterTests.cs @@ -0,0 +1,161 @@ +using System.Text.Encodings.Web; +using System.Text.Json; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class PermissionListConverterTests +{ + private readonly JsonSerializerOptions _options; + + public PermissionListConverterTests() + { + _options = new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Converters = + { + new PermissionListConverter(), + new RoleJsonConverter(), + new PermissionJsonConverter() + } + }; + } + + [Fact] + public void Read_EmptyArray_ReturnsEmptyList() + { + // Arrange + var json = "[]"; + + // Act + var result = JsonSerializer.Deserialize>(json, _options); + + // Assert + Assert.NotNull(result); + Assert.Empty(result); + Assert.IsType>(result); + } + + [Fact] + public void Read_SinglePermission_DeserializesCorrectly() + { + // Arrange + var json = "[\"read(\\\"any\\\")\"]"; + + // Act + var result = JsonSerializer.Deserialize>(json, _options); + + // Assert + Assert.NotNull(result); + Assert.Single(result); + Assert.Equal(PermissionType.Read, result[0].PermissionType); + Assert.Equal(RoleType.Any, result[0].Role.RoleType); + } + + [Fact] + public void Read_MultiplePermissions_DeserializesCorrectly() + { + // Arrange + var json = """ + [ + "read(\"any\")", + "write(\"user:123/verified\")", + "create(\"team:456/admin\")" + ] + """; + + // Act + var result = JsonSerializer.Deserialize>(json, _options); + + // Assert + Assert.NotNull(result); + Assert.Equal(3, result.Count); + + Assert.Equal(PermissionType.Read, result[0].PermissionType); + Assert.Equal(RoleType.Any, result[0].Role.RoleType); + + Assert.Equal(PermissionType.Write, result[1].PermissionType); + Assert.Equal(RoleType.User, result[1].Role.RoleType); + Assert.Equal("123", result[1].Role.Id); + Assert.Equal(RoleStatus.Verified, result[1].Role.Status); + + Assert.Equal(PermissionType.Create, result[2].PermissionType); + Assert.Equal(RoleType.Team, result[2].Role.RoleType); + Assert.Equal("456", result[2].Role.Id); + Assert.Equal("admin", result[2].Role.TeamRole); + } + + [Theory] + [InlineData("")] + [InlineData("{}")] + [InlineData("\"not-an-array\"")] + [InlineData("[")] + [InlineData("]")] + public void Read_InvalidJson_ThrowsJsonException(string invalidJson) + { + // Act & Assert + Assert.Throws(() => + JsonSerializer.Deserialize>(invalidJson, _options)); + } + + [Fact] + public void Read_NullValueInArray_ThrowsJsonException() + { + // Arrange + var json = "[\"read(\\\"any\\\")\", null]"; + + // Act & Assert + Assert.Throws(() => + JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Write_EmptyList_SerializesCorrectly() + { + // Arrange + var permissions = new List(); + + // Act + var json = JsonSerializer.Serialize(permissions, _options); + + // Assert + Assert.Equal("[]", json); + } + + [Fact] + public void Write_SinglePermission_SerializesCorrectly() + { + // Arrange + var permissions = new List + { + Permission.Read(Role.Any()) + }; + + // Act + var json = JsonSerializer.Serialize(permissions, _options); + + // Assert + Assert.Equal("[\"read(\\\"any\\\")\"]", json); + } + + [Fact] + public void Write_MultiplePermissions_SerializesCorrectly() + { + // Arrange + var permissions = new List + { + Permission.Read(Role.Any()), + Permission.Write(Role.User("123", RoleStatus.Verified)), + Permission.Create(Role.Team("456", "admin")) + }; + + // Act + var json = JsonSerializer.Serialize(permissions, _options); + + // Assert + var expectedJson = "[\"read(\\\"any\\\")\",\"write(\\\"user:123/verified\\\")\",\"create(\\\"team:456/admin\\\")\"]"; + Assert.Equal(expectedJson, json); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionReadOnlyListConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionReadOnlyListConverterTests.cs new file mode 100644 index 00000000..a078dd84 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionReadOnlyListConverterTests.cs @@ -0,0 +1,183 @@ +using System.Text.Encodings.Web; +using System.Text.Json; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class PermissionReadOnlyListConverterTests +{ + private readonly JsonSerializerOptions _options; + + public PermissionReadOnlyListConverterTests() + { + _options = new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Converters = + { + new PermissionReadOnlyListConverter(), + new RoleJsonConverter(), + new PermissionJsonConverter() + } + }; + } + + [Fact] + public void Read_EmptyArray_ReturnsEmptyReadOnlyList() + { + // Arrange + var json = "[]"; + + // Act + var result = JsonSerializer.Deserialize>(json, _options); + + // Assert + Assert.NotNull(result); + Assert.Empty(result); + Assert.IsAssignableFrom>(result); + } + + [Fact] + public void Read_SinglePermission_DeserializesCorrectly() + { + // Arrange + var json = "[\"read(\\\"any\\\")\"]"; + + // Act + var result = JsonSerializer.Deserialize>(json, _options); + + // Assert + Assert.NotNull(result); + Assert.Single(result); + Assert.Equal(PermissionType.Read, result[0].PermissionType); + Assert.Equal(RoleType.Any, result[0].Role.RoleType); + Assert.IsAssignableFrom>(result); + } + + [Fact] + public void Read_MultiplePermissions_DeserializesCorrectly() + { + // Arrange + var json = """ + [ + "read(\"any\")", + "write(\"user:123/verified\")", + "create(\"team:456/admin\")" + ] + """; + + // Act + var result = JsonSerializer.Deserialize>(json, _options); + + // Assert + Assert.NotNull(result); + Assert.Equal(3, result.Count); + Assert.IsAssignableFrom>(result); + + Assert.Equal(PermissionType.Read, result[0].PermissionType); + Assert.Equal(RoleType.Any, result[0].Role.RoleType); + + Assert.Equal(PermissionType.Write, result[1].PermissionType); + Assert.Equal(RoleType.User, result[1].Role.RoleType); + Assert.Equal("123", result[1].Role.Id); + Assert.Equal(RoleStatus.Verified, result[1].Role.Status); + + Assert.Equal(PermissionType.Create, result[2].PermissionType); + Assert.Equal(RoleType.Team, result[2].Role.RoleType); + Assert.Equal("456", result[2].Role.Id); + Assert.Equal("admin", result[2].Role.TeamRole); + } + + [Theory] + [InlineData("")] + [InlineData("{}")] + [InlineData("\"not-an-array\"")] + [InlineData("[")] + [InlineData("]")] + public void Read_InvalidJson_ThrowsJsonException(string invalidJson) + { + // Act & Assert + Assert.Throws(() => + JsonSerializer.Deserialize>(invalidJson, _options)); + } + + [Fact] + public void Read_NullValueInArray_ThrowsJsonException() + { + // Arrange + var json = "[\"read(\\\"any\\\")\", null]"; + + // Act & Assert + Assert.Throws(() => + JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Write_EmptyList_SerializesCorrectly() + { + // Arrange + IReadOnlyList permissions = new List().AsReadOnly(); + + // Act + var json = JsonSerializer.Serialize(permissions, _options); + + // Assert + Assert.Equal("[]", json); + } + + [Fact] + public void Write_SinglePermission_SerializesCorrectly() + { + // Arrange + IReadOnlyList permissions = new List + { + Permission.Read(Role.Any()) + }.AsReadOnly(); + + // Act + var json = JsonSerializer.Serialize(permissions, _options); + + // Assert + Assert.Equal("[\"read(\\\"any\\\")\"]", json); + } + + [Fact] + public void Write_MultiplePermissions_SerializesCorrectly() + { + // Arrange + IReadOnlyList permissions = new List + { + Permission.Read(Role.Any()), + Permission.Write(Role.User("123", RoleStatus.Verified)), + Permission.Create(Role.Team("456", "admin")) + }.AsReadOnly(); + + // Act + var json = JsonSerializer.Serialize(permissions, _options); + + // Assert + var expectedJson = "[\"read(\\\"any\\\")\",\"write(\\\"user:123/verified\\\")\",\"create(\\\"team:456/admin\\\")\"]"; + Assert.Equal(expectedJson, json); + } + + [Fact] + public void Read_EnsuresReadOnlyBehavior() + { + // Arrange + var json = "[\"read(\\\"any\\\")\"]"; + + // Act + var result = JsonSerializer.Deserialize>(json, _options); + + // Assert + Assert.NotNull(result); + Assert.Single(result); + Assert.IsAssignableFrom>(result); + Assert.Throws(() => + { + var list = result as System.Collections.IList; + list?.Add(Permission.Read(Role.Any())); + }); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/RoleJsonConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/RoleJsonConverterTests.cs new file mode 100644 index 00000000..0dec2a27 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/RoleJsonConverterTests.cs @@ -0,0 +1,275 @@ +using System.Reflection; +using System.Text.Json; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class RoleJsonConverterTests +{ + private readonly JsonSerializerOptions _options; + private readonly RoleJsonConverter _converter; + + public RoleJsonConverterTests() + { + _converter = new(); + _options = new() + { + Converters = { _converter } + }; + } + + [Theory] + [InlineData("any", RoleType.Any)] + [InlineData("users", RoleType.Users)] + [InlineData("guests", RoleType.Guests)] + public void Read_SimpleRoles_DeserializesCorrectly(string json, RoleType expectedType) + { + // Act + var role = JsonSerializer.Deserialize($"\"{json}\"", _options); + + // Assert + Assert.NotNull(role); + Assert.Equal(expectedType, role.RoleType); + } + + [Theory] + [InlineData("user:123", "123", null)] + [InlineData("user:123/verified", "123", RoleStatus.Verified)] + [InlineData("user:456/unverified", "456", RoleStatus.Unverified)] + public void Read_UserRoles_DeserializesCorrectly(string json, string expectedId, RoleStatus? expectedStatus) + { + // Act + var role = JsonSerializer.Deserialize($"\"{json}\"", _options); + + // Assert + Assert.NotNull(role); + Assert.Equal(RoleType.User, role.RoleType); + Assert.Equal(expectedId, role.Id); + Assert.Equal(expectedStatus, role.Status); + } + + [Theory] + [InlineData("users/verified", RoleStatus.Verified)] + [InlineData("users/unverified", RoleStatus.Unverified)] + public void Read_UsersWithStatus_DeserializesCorrectly(string json, RoleStatus expectedStatus) + { + // Act + var role = JsonSerializer.Deserialize($"\"{json}\"", _options); + + // Assert + Assert.NotNull(role); + Assert.Equal(RoleType.Users, role.RoleType); + Assert.Equal(expectedStatus, role.Status); + } + + [Theory] + [InlineData("team:123", "123", null)] + [InlineData("team:456/admin", "456", "admin")] + [InlineData("team:789/member", "789", "member")] + public void Read_TeamRoles_DeserializesCorrectly(string json, string expectedId, string? expectedTeamRole) + { + // Act + var role = JsonSerializer.Deserialize($"\"{json}\"", _options); + + // Assert + Assert.NotNull(role); + Assert.Equal(RoleType.Team, role.RoleType); + Assert.Equal(expectedId, role.Id); + Assert.Equal(expectedTeamRole, role.TeamRole); + } + + [Fact] + public void Read_MemberRole_DeserializesCorrectly() + { + // Arrange + var json = "\"member:123\""; + + // Act + var role = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.NotNull(role); + Assert.Equal(RoleType.Member, role.RoleType); + Assert.Equal("123", role.Id); + } + + [Fact] + public void Read_LabelRole_DeserializesCorrectly() + { + // Arrange + var json = "\"label:testLabel\""; + + // Act + var role = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.NotNull(role); + Assert.Equal(RoleType.Label, role.RoleType); + Assert.Equal("testLabel", role.LabelName); + } + + [Theory] + [InlineData(42)] + [InlineData(true)] + public void Read_NonStringToken_ThrowsJsonException(object? invalidValue) + { + // Arrange + var json = JsonSerializer.Serialize(invalidValue); + + // Act & Assert + Assert.Throws(() => + JsonSerializer.Deserialize(json, _options)); + } + + [Theory] + [InlineData("")] + [InlineData("invalid")] + public void Read_InvalidFormat_ThrowsJsonException(string invalidJson) + { + // Act & Assert + Assert.Throws(() => + JsonSerializer.Deserialize($"\"{invalidJson}\"", _options)); + } + + [Fact] + public void Write_Any_SerializesCorrectly() + { + // Arrange + var role = Role.Any(); + + // Act + var json = JsonSerializer.Serialize(role, _options); + + // Assert + Assert.Equal("\"any\"", json); + } + + [Fact] + public void Write_Guests_SerializesCorrectly() + { + // Arrange + var role = Role.Guests(); + + // Act + var json = JsonSerializer.Serialize(role, _options); + + // Assert + Assert.Equal("\"guests\"", json); + } + + [Theory] + [InlineData(null, "user:123")] + [InlineData(RoleStatus.Verified, "user:123/verified")] + [InlineData(RoleStatus.Unverified, "user:123/unverified")] + public void Write_User_SerializesCorrectly(RoleStatus? status, string expected) + { + // Arrange + var role = status.HasValue ? Role.User("123", status.Value) : Role.User("123"); + + // Act + var json = JsonSerializer.Serialize(role, _options); + + // Assert + Assert.Equal($"\"{expected}\"", json); + } + + [Theory] + [InlineData(null, "users")] + [InlineData(RoleStatus.Verified, "users/verified")] + [InlineData(RoleStatus.Unverified, "users/unverified")] + public void Write_Users_SerializesCorrectly(RoleStatus? status, string expected) + { + // Arrange + var role = status.HasValue ? Role.Users(status.Value) : Role.Users(); + + // Act + var json = JsonSerializer.Serialize(role, _options); + + // Assert + Assert.Equal($"\"{expected}\"", json); + } + + [Theory] + [InlineData(null, "team:123")] + [InlineData("admin", "team:123/admin")] + [InlineData("member", "team:123/member")] + public void Write_Team_SerializesCorrectly(string? teamRole, string expected) + { + // Arrange + var role = teamRole != null ? Role.Team("123", teamRole) : Role.Team("123"); + + // Act + var json = JsonSerializer.Serialize(role, _options); + + // Assert + Assert.Equal($"\"{expected}\"", json); + } + + [Fact] + public void Write_Member_SerializesCorrectly() + { + // Arrange + var role = Role.Member("123"); + + // Act + var json = JsonSerializer.Serialize(role, _options); + + // Assert + Assert.Equal("\"member:123\"", json); + } + + [Fact] + public void Write_Label_SerializesCorrectly() + { + // Arrange + var role = Role.Label("testLabel"); + + // Act + var json = JsonSerializer.Serialize(role, _options); + + // Assert + Assert.Equal("\"label:testLabel\"", json); + } + + [Fact] + public void Write_ThrowsJsonException_ForUnknownRoleType() + { + // Arrange + var options = new JsonSerializerOptions + { + Converters = { new RoleJsonConverter() } + }; + + // Use reflection to create a Role object with an unknown RoleType + var roleType = (RoleType)999; // Assuming 999 is not a valid RoleType + var role = CreateRoleWithInvalidRoleType(roleType); + + // Act & Assert + var exception = Assert.Throws(() => + JsonSerializer.Serialize(role, options) + ); + + Assert.Equal($"Unknown role type: {roleType}", exception.Message); + } + + private Role CreateRoleWithInvalidRoleType(RoleType roleType) + { + // Use reflection to create an instance of Role + var roleTypeInstance = typeof(Role); + var constructor = roleTypeInstance.GetConstructor( + BindingFlags.Instance | BindingFlags.NonPublic, + null, + [typeof(RoleType)], + null + ); + + if (constructor is null) + { + throw new InvalidOperationException("Role constructor not found."); + } + + var role = (Role)constructor.Invoke([roleType]); + return role; + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Utils/PermissionTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Utils/PermissionTests.cs new file mode 100644 index 00000000..de5696d4 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Utils/PermissionTests.cs @@ -0,0 +1,90 @@ +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Utils; +public class PermissionTests +{ + private readonly Role _testRole = Role.Any(); + + [Fact] + public void Read_CreatesCorrectPermission() + { + // Act + var permission = Permission.Read(_testRole); + + // Assert + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Same(_testRole, permission.Role); + } + + [Fact] + public void Write_CreatesCorrectPermission() + { + // Act + var permission = Permission.Write(_testRole); + + // Assert + Assert.Equal(PermissionType.Write, permission.PermissionType); + Assert.Same(_testRole, permission.Role); + } + + [Fact] + public void Create_CreatesCorrectPermission() + { + // Act + var permission = Permission.Create(_testRole); + + // Assert + Assert.Equal(PermissionType.Create, permission.PermissionType); + Assert.Same(_testRole, permission.Role); + } + + [Fact] + public void Update_CreatesCorrectPermission() + { + // Act + var permission = Permission.Update(_testRole); + + // Assert + Assert.Equal(PermissionType.Update, permission.PermissionType); + Assert.Same(_testRole, permission.Role); + } + + [Fact] + public void Delete_CreatesCorrectPermission() + { + // Act + var permission = Permission.Delete(_testRole); + + // Assert + Assert.Equal(PermissionType.Delete, permission.PermissionType); + Assert.Same(_testRole, permission.Role); + } + + [Theory] + [InlineData(PermissionType.Read)] + [InlineData(PermissionType.Write)] + [InlineData(PermissionType.Create)] + [InlineData(PermissionType.Update)] + [InlineData(PermissionType.Delete)] + public void Permission_StoresRoleReference(PermissionType permissionType) + { + // Arrange + var role = Role.User("123", RoleStatus.Verified); + + // Act + var permission = permissionType switch + { + PermissionType.Read => Permission.Read(role), + PermissionType.Write => Permission.Write(role), + PermissionType.Create => Permission.Create(role), + PermissionType.Update => Permission.Update(role), + PermissionType.Delete => Permission.Delete(role), + _ => throw new ArgumentException("Invalid permission type") + }; + + // Assert + Assert.Same(role, permission.Role); + Assert.Equal(permissionType, permission.PermissionType); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Utils/RoleTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Utils/RoleTests.cs new file mode 100644 index 00000000..f8fec995 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Utils/RoleTests.cs @@ -0,0 +1,164 @@ +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Utils; +public class RoleTests +{ + [Fact] + public void Any_CreatesCorrectRole() + { + // Act + var role = Role.Any(); + + // Assert + Assert.Equal(RoleType.Any, role.RoleType); + Assert.Null(role.Id); + Assert.Null(role.Status); + Assert.Null(role.TeamRole); + Assert.Null(role.LabelName); + } + + [Fact] + public void User_WithId_CreatesCorrectRole() + { + // Arrange + var userId = "123"; + + // Act + var role = Role.User(userId); + + // Assert + Assert.Equal(RoleType.User, role.RoleType); + Assert.Equal(userId, role.Id); + Assert.Null(role.Status); + Assert.Null(role.TeamRole); + Assert.Null(role.LabelName); + } + + [Theory] + [InlineData("123", RoleStatus.Verified)] + [InlineData("456", RoleStatus.Unverified)] + public void User_WithIdAndStatus_CreatesCorrectRole(string userId, RoleStatus status) + { + // Act + var role = Role.User(userId, status); + + // Assert + Assert.Equal(RoleType.User, role.RoleType); + Assert.Equal(userId, role.Id); + Assert.Equal(status, role.Status); + Assert.Null(role.TeamRole); + Assert.Null(role.LabelName); + } + + [Fact] + public void Users_CreatesCorrectRole() + { + // Act + var role = Role.Users(); + + // Assert + Assert.Equal(RoleType.Users, role.RoleType); + Assert.Null(role.Id); + Assert.Null(role.Status); + Assert.Null(role.TeamRole); + Assert.Null(role.LabelName); + } + + [Theory] + [InlineData(RoleStatus.Verified)] + [InlineData(RoleStatus.Unverified)] + public void Users_WithStatus_CreatesCorrectRole(RoleStatus status) + { + // Act + var role = Role.Users(status); + + // Assert + Assert.Equal(RoleType.Users, role.RoleType); + Assert.Null(role.Id); + Assert.Equal(status, role.Status); + Assert.Null(role.TeamRole); + Assert.Null(role.LabelName); + } + + [Fact] + public void Guests_CreatesCorrectRole() + { + // Act + var role = Role.Guests(); + + // Assert + Assert.Equal(RoleType.Guests, role.RoleType); + Assert.Null(role.Id); + Assert.Null(role.Status); + Assert.Null(role.TeamRole); + Assert.Null(role.LabelName); + } + + [Fact] + public void Team_WithId_CreatesCorrectRole() + { + // Arrange + var teamId = "team123"; + + // Act + var role = Role.Team(teamId); + + // Assert + Assert.Equal(RoleType.Team, role.RoleType); + Assert.Equal(teamId, role.Id); + Assert.Null(role.Status); + Assert.Null(role.TeamRole); + Assert.Null(role.LabelName); + } + + [Theory] + [InlineData("team123", "admin")] + [InlineData("team456", "member")] + public void Team_WithIdAndRole_CreatesCorrectRole(string teamId, string teamRole) + { + // Act + var role = Role.Team(teamId, teamRole); + + // Assert + Assert.Equal(RoleType.Team, role.RoleType); + Assert.Equal(teamId, role.Id); + Assert.Null(role.Status); + Assert.Equal(teamRole, role.TeamRole); + Assert.Null(role.LabelName); + } + + [Fact] + public void Member_CreatesCorrectRole() + { + // Arrange + var memberId = "member123"; + + // Act + var role = Role.Member(memberId); + + // Assert + Assert.Equal(RoleType.Member, role.RoleType); + Assert.Equal(memberId, role.Id); + Assert.Null(role.Status); + Assert.Null(role.TeamRole); + Assert.Null(role.LabelName); + } + + [Fact] + public void Label_CreatesCorrectRole() + { + // Arrange + var labelName = "testLabel"; + + // Act + var role = Role.Label(labelName); + + // Assert + Assert.Equal(RoleType.Label, role.RoleType); + Assert.Null(role.Id); + Assert.Null(role.Status); + Assert.Null(role.TeamRole); + Assert.Equal(labelName, role.LabelName); + } +} From 82b4ca20fb4634da912e6d1df3b7afb7d71bea29 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 22:37:35 +0100 Subject: [PATCH 062/303] Adopted new permissions type across all current request and responses --- .../Converters/DocumentConverter.cs | 6 ++- .../Responses/Collection.cs | 3 +- .../Responses/Document.cs | 3 +- .../Converters/DocumentConverterTests.cs | 54 ++++++++++--------- .../Responses/CollectionTests.cs | 9 ++-- .../Responses/CollectionsListTests.cs | 18 +++---- .../Responses/DocumentTests.cs | 13 +++-- .../Responses/DocumentsListTests.cs | 8 ++- 8 files changed, 66 insertions(+), 48 deletions(-) diff --git a/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs index 17090b6f..0051db28 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs @@ -3,6 +3,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Converters; public class DocumentConverter : JsonConverter @@ -14,10 +15,11 @@ public class DocumentConverter : JsonConverter string? databaseId = null; DateTime? createdAt = null; DateTime? updatedAt = null; - List? permissions = null; + List? permissions = null; var data = new Dictionary(); var dateTimeConverter = new MultiFormatDateTimeConverter(); + var permissionListConverter = new PermissionListConverter(); if (reader.TokenType is not JsonTokenType.StartObject) { @@ -53,7 +55,7 @@ public class DocumentConverter : JsonConverter updatedAt = dateTimeConverter.Read(ref reader, typeof(DateTime), options); break; case "$permissions": - permissions = JsonSerializer.Deserialize>(ref reader, options); + permissions = permissionListConverter.Read(ref reader, typeof(List), options); break; default: var value = ReadValue(ref reader, options); diff --git a/src/PinguApps.Appwrite.Shared/Responses/Collection.cs b/src/PinguApps.Appwrite.Shared/Responses/Collection.cs index c32568a5..aa2a5c33 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Collection.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Collection.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Responses; @@ -22,7 +23,7 @@ public record Collection( [property: JsonPropertyName("$id")] string Id, [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, - [property: JsonPropertyName("$permissions")] IReadOnlyList Permissions, + [property: JsonPropertyName("$permissions")] IReadOnlyList Permissions, [property: JsonPropertyName("databaseId")] string DatabaseId, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("enabled")] bool Enabled, diff --git a/src/PinguApps.Appwrite.Shared/Responses/Document.cs b/src/PinguApps.Appwrite.Shared/Responses/Document.cs index 30fe635b..e3bd85ac 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Document.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Document.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Responses; @@ -23,7 +24,7 @@ public record Document( [property: JsonPropertyName("$databaseId")] string DatabaseId, [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, - [property: JsonPropertyName("$permissions")] IReadOnlyList Permissions, + [property: JsonPropertyName("$permissions")] IReadOnlyList Permissions, [property: JsonExtensionData] Dictionary Data ) { diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs index 27f82c30..26e2d7a2 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs @@ -2,7 +2,9 @@ using System.Text.Encodings.Web; using System.Text.Json; using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Tests.Converters; public class DocumentConverterTests @@ -28,7 +30,7 @@ public void Read_ValidJson_ReturnsDocument() ""$databaseId"": ""db1"", ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read"", ""write""], + ""$permissions"": [""read(\""any\"")""], ""customField"": ""customValue"" }"; @@ -40,7 +42,9 @@ public void Read_ValidJson_ReturnsDocument() Assert.Equal("db1", document.DatabaseId); Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00"), document.CreatedAt); Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00"), document.UpdatedAt); - Assert.Equal(new List { "read", "write" }, document.Permissions); + Assert.Single(document.Permissions); + Assert.Equal(PermissionType.Read, document.Permissions[0].PermissionType); + Assert.Equal(RoleType.Any, document.Permissions[0].Role.RoleType); Assert.Equal("customValue", document["customField"]); } @@ -54,7 +58,7 @@ public void Read_InvalidJson_ThrowsJsonException() ""$databaseId"": ""db1"", ""$createdAt"": ""invalid-date"", ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read"", ""write""] + ""$permissions"": [""read(\""any\"")""], }"; Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); @@ -69,7 +73,7 @@ public void Read_MissingRequiredFields_ThrowsJsonException() ""$collectionId"": ""col1"", ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read"", ""write""] + ""$permissions"": [""read(\""any\"")""], }"; Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); @@ -84,7 +88,7 @@ public void Write_ValidDocument_WritesJson() "db1", DateTime.Parse("2020-10-15T06:38:00.000+00:00"), DateTime.Parse("2020-10-15T06:38:00.000+00:00"), - ["read", "write"], + [Permission.Read(Role.Any())], new Dictionary { { "customField", "customValue" } } ); @@ -97,7 +101,7 @@ public void Write_ValidDocument_WritesJson() ""$databaseId"": ""db1"", ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read"", ""write""], + ""$permissions"": [""read(\""any\"")""], ""customField"": ""customValue"" }".ReplaceLineEndings("").Replace(" ", ""); @@ -113,7 +117,7 @@ public void Write_NullValue_WritesNull() "db1", DateTime.Parse("2020-10-15T06:38:00.000+00:00"), DateTime.Parse("2020-10-15T06:38:00.000+00:00"), - ["read", "write"], + [Permission.Read(Role.Any())], new Dictionary { { "customField", null } } ); @@ -126,7 +130,7 @@ public void Write_NullValue_WritesNull() ""$databaseId"": ""db1"", ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read"", ""write""], + ""$permissions"": [""read(\""any\"")""], ""customField"": null }".ReplaceLineEndings("").Replace(" ", ""); @@ -154,7 +158,7 @@ public void ReadArray_ValidJsonArray_ReturnsArray() ""$databaseId"": ""db1"", ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read"", ""write""], + ""$permissions"": [""read(\""any\"")""], ""arrayField"": [""value1"", ""value2""] }"; @@ -177,7 +181,7 @@ public void ReadObject_ValidJsonObject_ReturnsObject() ""$databaseId"": ""db1"", ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read"", ""write""], + ""$permissions"": [""read(\""any\"")""], ""objectField"": { ""key1"": ""value1"", ""key2"": ""value2"" } }"; @@ -201,7 +205,7 @@ public void Read_InvalidJsonTokenType_ThrowsJsonException() ""$databaseId"": ""db1"", ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read"", ""write""], + ""$permissions"": [""read(\""any\"")""], ""customField"": ""customValue"" } ]"; @@ -218,7 +222,7 @@ public void Read_MissingCollectionId_ThrowsJsonException() ""$databaseId"": ""db1"", ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read"", ""write""] + ""$permissions"": [""read(\""any\"")""] }"; Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); @@ -233,7 +237,7 @@ public void Read_MissingCreatedAt_ThrowsJsonException() ""$collectionId"": ""col1"", ""$databaseId"": ""db1"", ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read"", ""write""] + ""$permissions"": [""read(\""any\"")""] }"; Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); @@ -248,7 +252,7 @@ public void Read_MissingUpdatedAt_ThrowsJsonException() ""$collectionId"": ""col1"", ""$databaseId"": ""db1"", ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read"", ""write""] + ""$permissions"": [""read(\""any\"")""] }"; Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); @@ -279,7 +283,7 @@ public void Read_NullProperty_InsertedIntoData() ""$databaseId"": ""db1"", ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read"", ""write""], + ""$permissions"": [""read(\""any\"")""], ""customField"": null }"; @@ -312,7 +316,7 @@ public void Read_Comment_ThrowsJsonException() [Fact] public void Write_IntValue_SerializesCorrectly() { - var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, [], new Dictionary { { "intField", 123 } }); @@ -325,7 +329,7 @@ public void Write_IntValue_SerializesCorrectly() [Fact] public void Write_LongValue_SerializesCorrectly() { - var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, [], new Dictionary { { "longField", 12345L } }); @@ -338,7 +342,7 @@ public void Write_LongValue_SerializesCorrectly() [Fact] public void Write_FloatValue_SerializesCorrectly() { - var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, [], new Dictionary { { "floatField", 1.23f } }); @@ -351,7 +355,7 @@ public void Write_FloatValue_SerializesCorrectly() [Fact] public void Write_DoubleValue_SerializesCorrectly() { - var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, [], new Dictionary { { "doubleField", 1.23d } }); @@ -364,7 +368,7 @@ public void Write_DoubleValue_SerializesCorrectly() [Fact] public void Write_DecimalValue_SerializesCorrectly() { - var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, [], new Dictionary { { "decimalField", 1.23m } }); @@ -377,7 +381,7 @@ public void Write_DecimalValue_SerializesCorrectly() [Fact] public void Write_BoolValue_SerializesCorrectly() { - var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, [], new Dictionary { { "boolField", true } }); @@ -390,7 +394,7 @@ public void Write_BoolValue_SerializesCorrectly() [Fact] public void Write_DateTimeValue_SerializesCorrectly() { - var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, [], new Dictionary { { "datetimeField", DateTime.Parse("2020-10-15T06:38:00.000+00:00") } }); @@ -403,7 +407,7 @@ public void Write_DateTimeValue_SerializesCorrectly() [Fact] public void Write_ListValue_SerializesCorrectly() { - var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, [], new Dictionary { { "listField", new List() { "val1","val2" } } }); @@ -416,7 +420,7 @@ public void Write_ListValue_SerializesCorrectly() [Fact] public void Write_DictValue_SerializesCorrectly() { - var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, [], new Dictionary { { "dictField", new Dictionary { { "key", "val" } @@ -431,7 +435,7 @@ public void Write_DictValue_SerializesCorrectly() [Fact] public void Write_ObjectValue_SerializesCorrectly() { - var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, ["read", "write"], new Dictionary + var document = new Document("1", "col1", "db1", DateTime.UtcNow, DateTime.UtcNow, [], new Dictionary { { "objectField", new { } } }); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionTests.cs index 4e561750..b83a733b 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionTests.cs @@ -1,6 +1,7 @@ using System.Text.Json; using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Utils; using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; using Index = PinguApps.Appwrite.Shared.Responses.Index; @@ -14,7 +15,7 @@ public void Constructor_AssignsPropertiesCorrectly() var id = "5e5ea5c16897e"; var createdAt = DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(); var updatedAt = DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(); - var permissions = new List { "read(\"any\")" }; + var permissions = new List { Permission.Read(Role.Any()) }; var databaseId = "5e5ea5c16897e"; var name = "My Collection"; var enabled = false; @@ -25,7 +26,7 @@ public void Constructor_AssignsPropertiesCorrectly() }; var indexes = new List { - new Index("index1", IndexType.Unique, DatabaseElementStatus.Available, "string", new List(), new List(), createdAt, updatedAt) + new("index1", IndexType.Unique, DatabaseElementStatus.Available, "string", [], [], createdAt, updatedAt) }; // Act @@ -55,7 +56,9 @@ public void CanBeDeserialized_FromJson() Assert.Equal("5e5ea5c16897e", collection.Id); Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), collection.CreatedAt.ToUniversalTime()); Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), collection.UpdatedAt.ToUniversalTime()); - Assert.Contains("read(\"any\")", collection.Permissions); + Assert.Single(collection.Permissions); + Assert.Equal(PermissionType.Read, collection.Permissions[0].PermissionType); + Assert.Equal(RoleType.Any, collection.Permissions[0].Role.RoleType); Assert.Equal("5e5ea5c16897e", collection.DatabaseId); Assert.Equal("My Collection", collection.Name); Assert.False(collection.Enabled); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionsListTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionsListTests.cs index 00fffa2a..caf57046 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionsListTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionsListTests.cs @@ -1,7 +1,7 @@ using System.Text.Json; using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Responses; -using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; +using PinguApps.Appwrite.Shared.Utils; using Index = PinguApps.Appwrite.Shared.Responses.Index; namespace PinguApps.Appwrite.Shared.Tests.Responses; @@ -18,19 +18,17 @@ public void Constructor_AssignsPropertiesCorrectly() "5e5ea5c16897e", DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), - new List { "read(\"any\")" }, + [Permission.Read(Role.Any())], "5e5ea5c16897e", "My Collection", false, true, - new List - { + [ new AttributeBoolean("isEnabled", "boolean", DatabaseElementStatus.Available, "string", true, false, DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), false) - }, - new List - { + ], + [ new Index("index1", IndexType.Unique, DatabaseElementStatus.Available, "string", new List(), new List(), DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime()) - } + ] ) }; @@ -56,7 +54,9 @@ public void CanBeDeserialized_FromJson() Assert.Equal("5e5ea5c16897e", collection.Id); Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), collection.CreatedAt.ToUniversalTime()); Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), collection.UpdatedAt.ToUniversalTime()); - Assert.Contains("read(\"any\")", collection.Permissions); + Assert.Single(collection.Permissions); + Assert.Equal(PermissionType.Read, collection.Permissions[0].PermissionType); + Assert.Equal(RoleType.Any, collection.Permissions[0].Role.RoleType); Assert.Equal("5e5ea5c16897e", collection.DatabaseId); Assert.Equal("My Collection", collection.Name); Assert.False(collection.Enabled); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs index 82ea5b87..8a829dd7 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Responses; namespace PinguApps.Appwrite.Shared.Tests.Responses; @@ -15,7 +16,9 @@ public void Document_ShouldBeDeserialized_FromJson() Assert.Equal("5e5ea5c15117e", document.DatabaseId); Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.CreatedAt.ToUniversalTime()); Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.UpdatedAt.ToUniversalTime()); - Assert.Contains("read(\"any\")", document.Permissions); + Assert.Single(document.Permissions); + Assert.Equal(PermissionType.Read, document.Permissions[0].PermissionType); + Assert.Equal(RoleType.Any, document.Permissions[0].Role.RoleType); Assert.Equal("a string prop", document.Data["str"]); var dt = (DateTime?)document.Data["dt"]; Assert.NotNull(dt); @@ -43,7 +46,7 @@ public void Indexer_ShouldReturnCorrectValue() "databaseId", DateTime.UtcNow, DateTime.UtcNow, - new List(), + [], data ); @@ -67,7 +70,7 @@ public void GetValue_ShouldReturnCorrectValue() "databaseId", DateTime.UtcNow, DateTime.UtcNow, - new List(), + [], data ); @@ -84,7 +87,7 @@ public void GetValue_ShouldThrowException_WhenKeyNotFound() "databaseId", DateTime.UtcNow, DateTime.UtcNow, - new List(), + [], new Dictionary() ); @@ -105,7 +108,7 @@ public void GetValue_ShouldThrowException_WhenInvalidCast() "databaseId", DateTime.UtcNow, DateTime.UtcNow, - new List(), + [], data ); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs index dae08dce..62220fac 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs @@ -1,5 +1,7 @@ using System.Text.Json; +using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Tests.Responses; public class DocumentsListTests @@ -13,7 +15,7 @@ public void Constructor_AssignsPropertiesCorrectly() { new("5e5ea5c16897e", "5e5ea5c15117e", "5e5ea5c15117e", DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), - DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), ["read(\"any\")"], + DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), [Permission.Read(Role.Any())], []) }; @@ -42,6 +44,8 @@ public void CanBeDeserialized_FromJson() Assert.Equal("5e5ea5c15117e", document.DatabaseId); Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.CreatedAt.ToUniversalTime()); Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.UpdatedAt.ToUniversalTime()); - Assert.Contains("read(\"any\")", document.Permissions); + Assert.Single(document.Permissions); + Assert.Equal(PermissionType.Read, document.Permissions[0].PermissionType); + Assert.Equal(RoleType.Any, document.Permissions[0].Role.RoleType); } } From b0d07256d0db8a10b363607805960be7532f3ca9 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 22:38:23 +0100 Subject: [PATCH 063/303] increased minimum cyclomatic complexity across all tests --- tests/PinguApps.Appwrite.Client.Tests/test.ps1 | 2 +- tests/PinguApps.Appwrite.Server.Tests/test.ps1 | 2 +- tests/PinguApps.Appwrite.Shared.Tests/test.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/PinguApps.Appwrite.Client.Tests/test.ps1 b/tests/PinguApps.Appwrite.Client.Tests/test.ps1 index 1e7a9698..12a3f6b6 100644 --- a/tests/PinguApps.Appwrite.Client.Tests/test.ps1 +++ b/tests/PinguApps.Appwrite.Client.Tests/test.ps1 @@ -9,7 +9,7 @@ if (-not $toolInstalled) { } # Generate the report -reportgenerator -reports:coverage.opencover.xml -targetdir:coverage-report -assemblyfilters:+PinguApps.Appwrite.Client +reportgenerator -reports:coverage.opencover.xml -targetdir:coverage-report -assemblyfilters:+PinguApps.Appwrite.Client riskHotspotsAnalysisThresholds:metricThresholdForCyclomaticComplexity=30 # Open the generated report in the default browser Start-Process "coverage-report/index.html" diff --git a/tests/PinguApps.Appwrite.Server.Tests/test.ps1 b/tests/PinguApps.Appwrite.Server.Tests/test.ps1 index 12457080..a3db3d38 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/test.ps1 +++ b/tests/PinguApps.Appwrite.Server.Tests/test.ps1 @@ -9,7 +9,7 @@ if (-not $toolInstalled) { } # Generate the report -reportgenerator -reports:coverage.opencover.xml -targetdir:coverage-report -assemblyfilters:+PinguApps.Appwrite.Server +reportgenerator -reports:coverage.opencover.xml -targetdir:coverage-report -assemblyfilters:+PinguApps.Appwrite.Server riskHotspotsAnalysisThresholds:metricThresholdForCyclomaticComplexity=30 # Open the generated report in the default browser Start-Process "coverage-report/index.html" diff --git a/tests/PinguApps.Appwrite.Shared.Tests/test.ps1 b/tests/PinguApps.Appwrite.Shared.Tests/test.ps1 index 57b4af39..72876c6e 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/test.ps1 +++ b/tests/PinguApps.Appwrite.Shared.Tests/test.ps1 @@ -9,7 +9,7 @@ if (-not $toolInstalled) { } # Generate the report -reportgenerator -reports:coverage.opencover.xml -targetdir:coverage-report -assemblyfilters:+PinguApps.Appwrite.Shared +reportgenerator -reports:coverage.opencover.xml -targetdir:coverage-report -assemblyfilters:+PinguApps.Appwrite.Shared riskHotspotsAnalysisThresholds:metricThresholdForCyclomaticComplexity=30 # Open the generated report in the default browser Start-Process "coverage-report/index.html" From bb0da1e8c647fb2ede0e88cec6fb061f4569f30d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 22:42:50 +0100 Subject: [PATCH 064/303] added json converter decorator --- src/PinguApps.Appwrite.Shared/Responses/Collection.cs | 2 +- src/PinguApps.Appwrite.Shared/Responses/Document.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PinguApps.Appwrite.Shared/Responses/Collection.cs b/src/PinguApps.Appwrite.Shared/Responses/Collection.cs index aa2a5c33..5e2d0b3c 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Collection.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Collection.cs @@ -23,7 +23,7 @@ public record Collection( [property: JsonPropertyName("$id")] string Id, [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, - [property: JsonPropertyName("$permissions")] IReadOnlyList Permissions, + [property: JsonPropertyName("$permissions"), JsonConverter(typeof(PermissionReadOnlyListConverter))] IReadOnlyList Permissions, [property: JsonPropertyName("databaseId")] string DatabaseId, [property: JsonPropertyName("name")] string Name, [property: JsonPropertyName("enabled")] bool Enabled, diff --git a/src/PinguApps.Appwrite.Shared/Responses/Document.cs b/src/PinguApps.Appwrite.Shared/Responses/Document.cs index e3bd85ac..5e822dd3 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Document.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Document.cs @@ -24,7 +24,7 @@ public record Document( [property: JsonPropertyName("$databaseId")] string DatabaseId, [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, - [property: JsonPropertyName("$permissions")] IReadOnlyList Permissions, + [property: JsonPropertyName("$permissions"), JsonConverter(typeof(PermissionReadOnlyListConverter))] IReadOnlyList Permissions, [property: JsonExtensionData] Dictionary Data ) { From 3af8d23765685da0122ce7a35f42014d5a457ac8 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 23:00:22 +0100 Subject: [PATCH 065/303] Added create collection request --- .../Databases/CreateCollectionRequest.cs | 44 +++++++++++++++++++ .../CreateCollectionRequestValidator.cs | 34 ++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateCollectionRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateCollectionRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateCollectionRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateCollectionRequest.cs new file mode 100644 index 00000000..8bbb2eeb --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateCollectionRequest.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request for creating a collection +/// +public class CreateCollectionRequest : DatabaseIdBaseRequest +{ + /// + /// Unique Id. Choose a custom ID or generate a random ID with ID.unique(). Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars + /// + [JsonPropertyName("collectionId")] + public string CollectionId { get; set; } = IdUtils.GenerateUniqueId(); + + /// + /// Collection name. Max length: 128 chars + /// + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// + /// An array of permissions strings. By default, no user is granted with any permissions. Learn more about permissions. + /// + [JsonPropertyName("permissions")] + [JsonConverter(typeof(PermissionListConverter))] + public List Permissions { get; set; } = []; + + /// + /// Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. Learn more about permissions. + /// + [JsonPropertyName("documentSecurity")] + public bool DocumentSecurity { get; set; } + + /// + /// Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled. + /// + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateCollectionRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateCollectionRequestValidator.cs new file mode 100644 index 00000000..14db8147 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateCollectionRequestValidator.cs @@ -0,0 +1,34 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateCollectionRequestValidator : AbstractValidator +{ + public CreateCollectionRequestValidator() + { + Include(new DatabaseIdBaseRequestValidator()); + + RuleFor(x => x.CollectionId) + .NotEmpty() + .WithMessage("CollectionId is required.") + .Matches("^[a-zA-Z0-9][a-zA-Z0-9._-]{0,35}$") + .WithMessage("CollectionId can only contain a-z, A-Z, 0-9, period, hyphen, and underscore, and can't start with a special character. Max length is 36 characters."); + + RuleFor(x => x.Name) + .NotEmpty() + .WithMessage("Name is required.") + .MaximumLength(128) + .WithMessage("Name cannot exceed 128 characters."); + + RuleFor(x => x.Permissions) + .NotNull() + .WithMessage("Permissions cannot be null."); + + RuleFor(x => x.DocumentSecurity) + .NotNull() + .WithMessage("DocumentSecurity is required."); + + RuleFor(x => x.Enabled) + .NotNull() + .WithMessage("Enabled is required."); + } +} From 07fc04b2c88daf81f7e5cbc1aab1ea342b1e2ca3 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 23:00:32 +0100 Subject: [PATCH 066/303] added tests for create collection request --- .../Databases/CreateCollectionRequestTests.cs | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateCollectionRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateCollectionRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateCollectionRequestTests.cs new file mode 100644 index 00000000..521e38a2 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateCollectionRequestTests.cs @@ -0,0 +1,197 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateCollectionRequestTests : DatabaseIdBaseRequestTests +{ + protected override CreateCollectionRequest CreateValidRequest => new() + { + CollectionId = IdUtils.GenerateUniqueId(), + Name = "Pingu" + }; + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new CreateCollectionRequest(); + + // Assert + Assert.NotEmpty(request.CollectionId); + Assert.Equal(string.Empty, request.Name); + Assert.NotNull(request.Permissions); + Assert.False(request.DocumentSecurity); + Assert.False(request.Enabled); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var collectionId = IdUtils.GenerateUniqueId(); + var name = "My Collection"; + var permissions = new List { Permission.Read(Role.Any()) }; + var documentSecurity = true; + var enabled = true; + + var request = new CreateCollectionRequest(); + + // Act + request.CollectionId = collectionId; + request.Name = name; + request.Permissions = permissions; + request.DocumentSecurity = documentSecurity; + request.Enabled = enabled; + + // Assert + Assert.Equal(collectionId, request.CollectionId); + Assert.Equal(name, request.Name); + Assert.Equal(permissions, request.Permissions); + Assert.Equal(documentSecurity, request.DocumentSecurity); + Assert.Equal(enabled, request.Enabled); + } + + public static TheoryData ValidRequestsData = + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = "Valid Collection Name", + Permissions = [Permission.Read(Role.Any())], + DocumentSecurity = true, + Enabled = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = "validCollectionId123", + Name = "Another Valid Collection", + Permissions = [], + DocumentSecurity = false, + Enabled = false + } + ]; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(CreateCollectionRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData = new() + { + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = null!, + Name = "Pingu" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = "", + Name = "Pingu" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = "invalid chars!", + Name = "Pingu" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = ".startsWithSymbol", + Name = "Pingu" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = new string('a', 37), + Name = "Pingu" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = null! + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = "" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = new string('a', 129) + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = "Pingu", + Permissions = null! + } + }; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(CreateCollectionRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new CreateCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = "", + Name = "", + Permissions = null!, + DocumentSecurity = false, + Enabled = false + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new CreateCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = "", + Name = "", + Permissions = null!, + DocumentSecurity = false, + Enabled = false + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 4c22a7f27905f745262962386a490f215619968e Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 23:02:47 +0100 Subject: [PATCH 067/303] added get collection request --- .../Requests/Databases/GetCollectionRequest.cs | 10 ++++++++++ .../Validators/GetCollectionRequestValidator.cs | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/GetCollectionRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetCollectionRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/GetCollectionRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/GetCollectionRequest.cs new file mode 100644 index 00000000..4e25af2a --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/GetCollectionRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to get a collection +/// +public class GetCollectionRequest : DatabaseCollectionIdBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetCollectionRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetCollectionRequestValidator.cs new file mode 100644 index 00000000..2ee73033 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetCollectionRequestValidator.cs @@ -0,0 +1,10 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class GetCollectionRequestValidator : AbstractValidator +{ + public GetCollectionRequestValidator() + { + Include(new DatabaseCollectionIdBaseRequestValidator()); + } +} From 257cd13e7b51eb0482a50a98c7f4aae3cef18419 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 23:03:51 +0100 Subject: [PATCH 068/303] added tests for get collection request --- .../Requests/Databases/GetCollectionRequestTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetCollectionRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetCollectionRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetCollectionRequestTests.cs new file mode 100644 index 00000000..136723e2 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetCollectionRequestTests.cs @@ -0,0 +1,8 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class GetCollectionRequestTests : DatabaseCollectionIdBaseRequestTests +{ + protected override GetCollectionRequest CreateValidDatabaseCollectionIdRequest => new(); +} From 4731a5f588a1cd677aff95b5adefa230f3f90e9b Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 23:07:53 +0100 Subject: [PATCH 069/303] Added missing test to document converter tests --- .../Converters/DocumentConverterTests.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs index 26e2d7a2..4727cbf5 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs @@ -228,6 +228,21 @@ public void Read_MissingCollectionId_ThrowsJsonException() Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); } + [Fact] + public void Read_MissingDatabaseId_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""] + }"; + + Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); + } + [Fact] public void Read_MissingCreatedAt_ThrowsJsonException() { From c18c2c367a1d28324731f2d382c671a39ac567b4 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 23:11:34 +0100 Subject: [PATCH 070/303] added update collection request --- .../Databases/UpdateCollectionRequest.cs | 38 +++++++++++++++++++ .../UpdateCollectionRequestValidator.cs | 28 ++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateCollectionRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateCollectionRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateCollectionRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateCollectionRequest.cs new file mode 100644 index 00000000..fdf57972 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateCollectionRequest.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to update a collection +/// +public class UpdateCollectionRequest : DatabaseCollectionIdBaseRequest +{ + /// + /// Collection name. Max length: 128 chars + /// + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// + /// An array of permissions strings. By default, no user is granted with any permissions. Learn more about permissions. + /// + [JsonPropertyName("permissions")] + [JsonConverter(typeof(PermissionListConverter))] + public List Permissions { get; set; } = []; + + /// + /// Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. Learn more about permissions. + /// + [JsonPropertyName("documentSecurity")] + public bool DocumentSecurity { get; set; } + + /// + /// Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled. + /// + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateCollectionRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateCollectionRequestValidator.cs new file mode 100644 index 00000000..0b6908f4 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateCollectionRequestValidator.cs @@ -0,0 +1,28 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateCollectionRequestValidator : AbstractValidator +{ + public UpdateCollectionRequestValidator() + { + Include(new DatabaseCollectionIdBaseRequestValidator()); + + RuleFor(x => x.Name) + .NotEmpty() + .WithMessage("Name is required.") + .MaximumLength(128) + .WithMessage("Name cannot exceed 128 characters."); + + RuleFor(x => x.Permissions) + .NotNull() + .WithMessage("Permissions cannot be null."); + + RuleFor(x => x.DocumentSecurity) + .NotNull() + .WithMessage("DocumentSecurity is required."); + + RuleFor(x => x.Enabled) + .NotNull() + .WithMessage("Enabled is required."); + } +} From f7e143885152739dcb98d657a399cc50cafd6376 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 26 Oct 2024 23:32:24 +0100 Subject: [PATCH 071/303] added tests for update collection request --- .../Databases/UpdateCollectionRequestTests.cs | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateCollectionRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateCollectionRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateCollectionRequestTests.cs new file mode 100644 index 00000000..2c69a016 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateCollectionRequestTests.cs @@ -0,0 +1,162 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateCollectionRequestTests : DatabaseCollectionIdBaseRequestTests +{ + protected override UpdateCollectionRequest CreateValidDatabaseCollectionIdRequest => new() + { + Name = "Pingu" + }; + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new UpdateCollectionRequest(); + + // Assert + Assert.Equal(string.Empty, request.Name); + Assert.NotNull(request.Permissions); + Assert.False(request.DocumentSecurity); + Assert.False(request.Enabled); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var name = "Updated Collection"; + var permissions = new List { Permission.Read(Role.Any()) }; + var documentSecurity = true; + var enabled = true; + + var request = new UpdateCollectionRequest(); + + // Act + request.Name = name; + request.Permissions = permissions; + request.DocumentSecurity = documentSecurity; + request.Enabled = enabled; + + // Assert + Assert.Equal(name, request.Name); + Assert.Equal(permissions, request.Permissions); + Assert.Equal(documentSecurity, request.DocumentSecurity); + Assert.Equal(enabled, request.Enabled); + } + + public static TheoryData ValidRequestsData = new() + { + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = "Valid Collection Name", + Permissions = [Permission.Read(Role.Any())], + DocumentSecurity = true, + Enabled = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = "Another Valid Collection", + Permissions = [], + DocumentSecurity = false, + Enabled = false + } + }; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(UpdateCollectionRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData = new() + { + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = null! + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = "" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = new string('a', 129) + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = "Pingu", + Permissions = null! + } + }; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(UpdateCollectionRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new UpdateCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = "", + Permissions = null!, + DocumentSecurity = false, + Enabled = false + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new UpdateCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = "", + Permissions = null!, + DocumentSecurity = false, + Enabled = false + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From a6603464e09e117f44882a44244821e4940e717d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 00:34:28 +0100 Subject: [PATCH 072/303] Converted Permissions to use builder pattern, and dropped roles --- .../Converters/PermissionJsonConverter.cs | 91 +++++- .../Converters/RoleJsonConverter.cs | 103 ------- .../Utils/Permission.cs | 125 ++++++-- src/PinguApps.Appwrite.Shared/Utils/Role.cs | 125 -------- .../Converters/DocumentConverterTests.cs | 6 +- .../PermissionJsonConverterTests.cs | 272 ++++++++++++++--- .../PermissionListConverterTests.cs | 25 +- .../PermissionReadOnlyListConverterTests.cs | 27 +- .../Converters/RoleJsonConverterTests.cs | 275 ------------------ .../Databases/CreateCollectionRequestTests.cs | 4 +- .../Databases/UpdateCollectionRequestTests.cs | 4 +- .../Responses/CollectionTests.cs | 4 +- .../Responses/CollectionsListTests.cs | 4 +- .../Responses/DocumentTests.cs | 2 +- .../Responses/DocumentsListTests.cs | 4 +- .../Utils/PermissionTests.cs | 178 +++++++++--- .../Utils/RoleTests.cs | 164 ----------- 17 files changed, 586 insertions(+), 827 deletions(-) delete mode 100644 src/PinguApps.Appwrite.Shared/Converters/RoleJsonConverter.cs delete mode 100644 src/PinguApps.Appwrite.Shared/Utils/Role.cs delete mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/RoleJsonConverterTests.cs delete mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Utils/RoleTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/PermissionJsonConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/PermissionJsonConverter.cs index 82ed0a33..0cc00eab 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/PermissionJsonConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/PermissionJsonConverter.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Converters; @@ -8,7 +9,7 @@ public class PermissionJsonConverter : JsonConverter { public override Permission? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (reader.TokenType != JsonTokenType.String) + if (reader.TokenType is not JsonTokenType.String) { throw new JsonException("Expected string value for Permission"); } @@ -28,25 +29,89 @@ public class PermissionJsonConverter : JsonConverter // Remove the quotes from the role string var roleString = value[(openParenIndex + 2)..(closeParenIndex - 1)]; - // Deserialize the role first - var role = JsonSerializer.Deserialize(JsonSerializer.Serialize(roleString), options)!; - - return permissionTypeStr switch + var permissionBuilder = permissionTypeStr switch { - "read" => Permission.Read(role), - "write" => Permission.Write(role), - "create" => Permission.Create(role), - "update" => Permission.Update(role), - "delete" => Permission.Delete(role), + "read" => Permission.Read(), + "write" => Permission.Write(), + "create" => Permission.Create(), + "update" => Permission.Update(), + "delete" => Permission.Delete(), _ => throw new JsonException($"Unknown permission type: {permissionTypeStr}") }; + + // Parse the role string + return roleString switch + { + "any" => permissionBuilder.Any(), + "users" => permissionBuilder.Users(), + "guests" => permissionBuilder.Guests(), + var s when s.StartsWith("user:") => ParseUserRole(permissionBuilder, s[5..]), + var s when s.StartsWith("users/") => ParseUsersRole(permissionBuilder, s[6..]), + var s when s.StartsWith("team:") => ParseTeamRole(permissionBuilder, s[5..]), + var s when s.StartsWith("member:") => permissionBuilder.Member(s[7..]), + var s when s.StartsWith("label:") => permissionBuilder.Label(s[6..]), + _ => throw new JsonException($"Unknown role format: {roleString}") + }; + } + + private static Permission ParseUserRole(Permission.PermissionBuilder builder, string value) + { + var parts = value.Split('/'); + return parts.Length == 2 + ? builder.User(parts[0], Enum.Parse(parts[1], true)) + : builder.User(parts[0]); + } + + private static Permission ParseUsersRole(Permission.PermissionBuilder builder, string value) + { + return builder.Users(Enum.Parse(value, true)); + } + + private static Permission ParseTeamRole(Permission.PermissionBuilder builder, string value) + { + var parts = value.Split('/'); + return parts.Length == 2 + ? builder.Team(parts[0], parts[1]) + : builder.Team(parts[0]); } public override void Write(Utf8JsonWriter writer, Permission value, JsonSerializerOptions options) { - var roleString = JsonSerializer.Deserialize(JsonSerializer.Serialize(value.Role, options))!; - var result = value.PermissionType.ToString().ToLower() + $"(\"{roleString}\")"; - writer.WriteStringValue(result); + var roleString = value.RoleType switch + { + RoleType.Any => "any", + RoleType.User => FormatUserRole(value), + RoleType.Users => FormatUsersRole(value), + RoleType.Guests => "guests", + RoleType.Team => FormatTeamRole(value), + RoleType.Member => $"member:{value.Id}", + RoleType.Label => $"label:{value.Label}", + _ => throw new JsonException($"Unknown role type: {value.RoleType}") + }; + + var permissionString = value.PermissionType.ToString().ToLower() + $"(\"{roleString}\")"; + writer.WriteStringValue(permissionString); + } + + private static string FormatUserRole(Permission permission) + { + return permission.Status.HasValue + ? $"user:{permission.Id}/{permission.Status.ToString()!.ToLower()}" + : $"user:{permission.Id}"; + } + + private static string FormatUsersRole(Permission permission) + { + return permission.Status.HasValue + ? $"users/{permission.Status.ToString()!.ToLower()}" + : "users"; + } + + private static string FormatTeamRole(Permission permission) + { + return string.IsNullOrEmpty(permission.TeamRole) + ? $"team:{permission.Id}" + : $"team:{permission.Id}/{permission.TeamRole}"; } } diff --git a/src/PinguApps.Appwrite.Shared/Converters/RoleJsonConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/RoleJsonConverter.cs deleted file mode 100644 index 0131450e..00000000 --- a/src/PinguApps.Appwrite.Shared/Converters/RoleJsonConverter.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; -using PinguApps.Appwrite.Shared.Enums; -using PinguApps.Appwrite.Shared.Utils; - -namespace PinguApps.Appwrite.Shared.Converters; -public class RoleJsonConverter : JsonConverter -{ - public override Role? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType != JsonTokenType.String) - { - throw new JsonException("Expected string value for Role"); - } - - var value = reader.GetString()!; - - // Handle the simple cases first - return value switch - { - "any" => Role.Any(), - "users" => Role.Users(), - "guests" => Role.Guests(), - _ => ParseComplexRole(value) - }; - } - - private static Role ParseComplexRole(string value) - { - if (value.StartsWith("user:")) - { - var parts = value[5..].Split('/'); - return parts.Length == 2 - ? Role.User(parts[0], Enum.Parse(parts[1], true)) - : Role.User(parts[0]); - } - - if (value.StartsWith("users/")) - { - return Role.Users(Enum.Parse(value[6..], true)); - } - - if (value.StartsWith("team:")) - { - var parts = value[5..].Split('/'); - return parts.Length == 2 - ? Role.Team(parts[0], parts[1]) - : Role.Team(parts[0]); - } - - if (value.StartsWith("member:")) - { - return Role.Member(value[7..]); - } - - if (value.StartsWith("label:")) - { - return Role.Label(value[6..]); - } - - throw new JsonException($"Unknown role format: {value}"); - } - - public override void Write(Utf8JsonWriter writer, Role value, JsonSerializerOptions options) - { - string result = value.RoleType switch - { - RoleType.Any => "any", - RoleType.User => FormatUserRole(value), - RoleType.Users => FormatUsersRole(value), - RoleType.Guests => "guests", - RoleType.Team => FormatTeamRole(value), - RoleType.Member => $"member:{value.Id}", - RoleType.Label => $"label:{value.LabelName}", - _ => throw new JsonException($"Unknown role type: {value.RoleType}") - }; - - writer.WriteStringValue(result); - } - - private static string FormatUserRole(Role role) - { - return role.Status.HasValue - ? $"user:{role.Id}/{role.Status.ToString()!.ToLower()}" - : $"user:{role.Id}"; - } - - private static string FormatUsersRole(Role role) - { - return role.Status.HasValue - ? $"users/{role.Status.ToString()!.ToLower()}" - : "users"; - } - - private static string FormatTeamRole(Role role) - { - return string.IsNullOrEmpty(role.TeamRole) - ? $"team:{role.Id}" - : $"team:{role.Id}/{role.TeamRole}"; - } -} - diff --git a/src/PinguApps.Appwrite.Shared/Utils/Permission.cs b/src/PinguApps.Appwrite.Shared/Utils/Permission.cs index 96255acd..cb8a2c83 100644 --- a/src/PinguApps.Appwrite.Shared/Utils/Permission.cs +++ b/src/PinguApps.Appwrite.Shared/Utils/Permission.cs @@ -16,48 +16,125 @@ public class Permission public PermissionType PermissionType { get; private set; } /// - /// The role(s) to grant the permissions to + /// The type of role /// - public Role Role { get; private set; } + public RoleType RoleType { get; private set; } - private Permission(PermissionType permissionType, Role role) + /// + /// The Id of the team, user or member + /// + public string? Id { get; private set; } + + /// + /// The verification status of a user group + /// + public RoleStatus? Status { get; private set; } + + /// + /// The role of team members + /// + public string? TeamRole { get; private set; } + + /// + /// The user label + /// + public string? Label { get; private set; } + + // Private constructor - can only be created through the builder + private Permission(PermissionType permissionType, RoleType roleType, string? id = null, RoleStatus? status = null, + string? teamRole = null, string? label = null) { PermissionType = permissionType; - Role = role; + RoleType = roleType; + Id = id; + Status = status; + TeamRole = teamRole; + Label = label; } /// - /// Creates Read pemission for the given role + /// Access to read a resource /// - /// The role to grant this permission level to - /// The permission - public static Permission Read(Role role) => new(PermissionType.Read, role); + public static PermissionBuilder Read() => new(PermissionType.Read); /// - /// Creates Write pemission for the given role + /// Alias to grant create, update, and delete access for collections and buckets and update and delete access for documents and files /// - /// The role to grant this permission level to - /// The permission - public static Permission Write(Role role) => new(PermissionType.Write, role); + public static PermissionBuilder Write() => new(PermissionType.Write); /// - /// Creates Create pemission for the given role + /// Access to create new resources. Does not apply to files or documents. Applying this type of access to files or documents results in an error /// - /// The role to grant this permission level to - /// The permission - public static Permission Create(Role role) => new(PermissionType.Create, role); + public static PermissionBuilder Create() => new(PermissionType.Create); /// - /// Creates Udpate pemission for the given role + /// Access to change a resource, but not remove or create new resources. Does not apply to functions /// - /// The role to grant this permission level to - /// The permission - public static Permission Update(Role role) => new(PermissionType.Update, role); + public static PermissionBuilder Update() => new(PermissionType.Update); /// - /// Creates Delete pemission for the given role + /// Access to remove a resource. Does not apply to functions /// - /// The role to grant this permission level to - /// The permission - public static Permission Delete(Role role) => new(PermissionType.Delete, role); + public static PermissionBuilder Delete() => new(PermissionType.Delete); + + public class PermissionBuilder + { + private readonly PermissionType _permissionType; + + internal PermissionBuilder(PermissionType permissionType) + { + _permissionType = permissionType; + } + + /// + /// Grants access to anyone + /// + public Permission Any() => new(_permissionType, RoleType.Any); + + /// + /// Grants access to any authenticated or anonymous user + /// + public Permission Users() => new(_permissionType, RoleType.Users); + + /// + /// Grants access to any authenticated or anonymous user. You can optionally pass the verified or unverified string to target specific types of users + /// + public Permission Users(RoleStatus status) => new(_permissionType, RoleType.Users, status: status); + + /// + /// Grants access to a specific user by user ID + /// + public Permission User(string userId) => new(_permissionType, RoleType.User, id: userId); + + /// + /// Grants access to a specific user by user ID. You can optionally pass the verified or unverified string to target specific types of users + /// + public Permission User(string userId, RoleStatus status) => new(_permissionType, RoleType.User, id: userId, status: status); + + /// + /// Grants access to any guest user without a session. Authenticated users don't have access to this role + /// + public Permission Guests() => new(_permissionType, RoleType.Guests); + + /// + /// Grants access to any member of the specific team. To gain access to this permission, the user must be the team creator (owner), or receive and accept an invitation to join this team + /// + public Permission Team(string teamId) => new(_permissionType, RoleType.Team, id: teamId); + + /// + /// Grants access to any member who possesses a specific role in a team. To gain access to this permission, the user must be a member of the specific team and have the given role assigned to them. Team roles can be assigned when inviting a user to become a team member + /// + public Permission Team(string teamId, string teamRole) => new(_permissionType, RoleType.Team, id: teamId, teamRole: teamRole); + + /// + /// Grants access to a specific member of a team. When the member is removed from the team, they will no longer have access + /// + public Permission Member(string memberId) => new(_permissionType, RoleType.Member, id: memberId); + + /// + /// Grants access to all accounts with a specific label ID. Once the label is removed from the user, they will no longer have access. Learn more about labels. + /// + public Permission Label(string label) => new(_permissionType, RoleType.Label, label: label); + } + } diff --git a/src/PinguApps.Appwrite.Shared/Utils/Role.cs b/src/PinguApps.Appwrite.Shared/Utils/Role.cs deleted file mode 100644 index 8e1563e0..00000000 --- a/src/PinguApps.Appwrite.Shared/Utils/Role.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System.Text.Json.Serialization; -using PinguApps.Appwrite.Shared.Converters; -using PinguApps.Appwrite.Shared.Enums; - -namespace PinguApps.Appwrite.Shared.Utils; - -/// -/// Helper class to create Role definitions. Works in tandem with -/// -[JsonConverter(typeof(RoleJsonConverter))] -public class Role -{ - /// - /// The type of role - /// - public RoleType RoleType { get; private set; } - - /// - /// The Id of the team, user or member - /// - public string? Id { get; private set; } - - /// - /// The verification status of a user group - /// - public RoleStatus? Status { get; private set; } - - /// - /// The role of team members - /// - public string? TeamRole { get; private set; } - - /// - /// The user label - /// - public string? LabelName { get; private set; } - - private Role(RoleType roleType) - { - RoleType = roleType; - } - - private Role(RoleType roleType, string id) : this(roleType) - { - Id = id; - } - - private Role(RoleType roleType, string id, RoleStatus status) : this(roleType, id) - { - Status = status; - } - - private Role(RoleType roleType, RoleStatus status) : this(roleType) - { - Status = status; - } - - private Role(RoleType roleType, string id, string teamRole) : this(roleType, id) - { - TeamRole = teamRole; - } - - /// - /// Creates a role defining anyone - /// - /// The role - public static Role Any() => new(RoleType.Any); - - /// - /// Creates a role defining a specific user - /// - /// The role - public static Role User(string id) => new(RoleType.User, id); - - /// - /// Creates a role defining a specific user, given their verification status - /// - /// The role - public static Role User(string id, RoleStatus status) => new(RoleType.User, id, status); - - /// - /// Creates a role defining all users - /// - /// The role - public static Role Users() => new(RoleType.Users); - - /// - /// Creates a role defining all users, given their verification status - /// - /// The role - public static Role Users(RoleStatus status) => new(RoleType.Users, status); - - /// - /// Creates a role defining all guests - /// - /// The role - public static Role Guests() => new(RoleType.Guests); - - /// - /// Creates a role defining anyone belonging to the specified team - /// - /// The role - public static Role Team(string id) => new(RoleType.Team, id); - - /// - /// Creates a role defining anyone belonging to the specified team, and posessing the specified role within the team - /// - /// The role - public static Role Team(string id, string teamRole) => new(RoleType.Team, id, teamRole); - - /// - /// Creates a role defining anyone who is a member of the specified team - /// - /// The role - public static Role Member(string id) => new(RoleType.Member, id); - - /// - /// Creates a role defining anyone with the specified label - /// - /// The role - public static Role Label(string label) => new(RoleType.Label) - { - LabelName = label - }; -} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs index 4727cbf5..de87ea57 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs @@ -44,7 +44,7 @@ public void Read_ValidJson_ReturnsDocument() Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00"), document.UpdatedAt); Assert.Single(document.Permissions); Assert.Equal(PermissionType.Read, document.Permissions[0].PermissionType); - Assert.Equal(RoleType.Any, document.Permissions[0].Role.RoleType); + Assert.Equal(RoleType.Any, document.Permissions[0].RoleType); Assert.Equal("customValue", document["customField"]); } @@ -88,7 +88,7 @@ public void Write_ValidDocument_WritesJson() "db1", DateTime.Parse("2020-10-15T06:38:00.000+00:00"), DateTime.Parse("2020-10-15T06:38:00.000+00:00"), - [Permission.Read(Role.Any())], + [Permission.Read().Any()], new Dictionary { { "customField", "customValue" } } ); @@ -117,7 +117,7 @@ public void Write_NullValue_WritesNull() "db1", DateTime.Parse("2020-10-15T06:38:00.000+00:00"), DateTime.Parse("2020-10-15T06:38:00.000+00:00"), - [Permission.Read(Role.Any())], + [Permission.Read().Any()], new Dictionary { { "customField", null } } ); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionJsonConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionJsonConverterTests.cs index 6c1e0db4..50eac396 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionJsonConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionJsonConverterTests.cs @@ -1,4 +1,5 @@ -using System.Text.Encodings.Web; +using System.Reflection; +using System.Text.Encodings.Web; using System.Text.Json; using PinguApps.Appwrite.Shared.Converters; using PinguApps.Appwrite.Shared.Enums; @@ -8,59 +9,138 @@ namespace PinguApps.Appwrite.Shared.Tests.Converters; public class PermissionJsonConverterTests { private readonly JsonSerializerOptions _options; - private readonly PermissionJsonConverter _converter; public PermissionJsonConverterTests() { - _converter = new PermissionJsonConverter(); _options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - Converters = { _converter, new RoleJsonConverter() } + Converters = { new PermissionJsonConverter() } }; } [Theory] - [InlineData(PermissionType.Read, "read")] - [InlineData(PermissionType.Write, "write")] - [InlineData(PermissionType.Create, "create")] - [InlineData(PermissionType.Update, "update")] - [InlineData(PermissionType.Delete, "delete")] - public void Write_AllPermissionTypes_SerializeCorrectly(PermissionType permissionType, string expectedPrefix) + [InlineData("read(\\\"any\\\")", PermissionType.Read, RoleType.Any)] + [InlineData("write(\\\"any\\\")", PermissionType.Write, RoleType.Any)] + [InlineData("create(\\\"any\\\")", PermissionType.Create, RoleType.Any)] + [InlineData("update(\\\"any\\\")", PermissionType.Update, RoleType.Any)] + [InlineData("delete(\\\"any\\\")", PermissionType.Delete, RoleType.Any)] + public void Read_SimplePermissions_DeserializeCorrectly(string json, PermissionType expectedPermType, RoleType expectedRoleType) + { + // Act + var permission = JsonSerializer.Deserialize($"\"{json}\"", _options); + + // Assert + Assert.NotNull(permission); + Assert.Equal(expectedPermType, permission.PermissionType); + Assert.Equal(expectedRoleType, permission.RoleType); + } + + [Theory] + [InlineData("user:123", "123", null)] + [InlineData("user:123/verified", "123", RoleStatus.Verified)] + [InlineData("user:456/unverified", "456", RoleStatus.Unverified)] + public void Read_UserPermissions_DeserializeCorrectly(string roleStr, string expectedId, RoleStatus? expectedStatus) + { + // Arrange + var json = $"\"read(\\\"{roleStr}\\\")\""; + + // Act + var permission = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.NotNull(permission); + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Equal(RoleType.User, permission.RoleType); + Assert.Equal(expectedId, permission.Id); + Assert.Equal(expectedStatus, permission.Status); + } + + [Fact] + public void Read_Guests_DeserializeCorrectly() + { + // Arrange + var json = $"\"read(\\\"guests\\\")\""; + + // Act + var permission = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.NotNull(permission); + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Equal(RoleType.Guests, permission.RoleType); + } + + [Theory] + [InlineData("users")] + [InlineData("users/verified")] + [InlineData("users/unverified")] + public void Read_UsersPermissions_DeserializeCorrectly(string roleStr) { // Arrange - var permission = permissionType switch + var json = $"\"read(\\\"{roleStr}\\\")\""; + + // Act + var permission = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.NotNull(permission); + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Equal(RoleType.Users, permission.RoleType); + + if (roleStr.Contains('/')) { - PermissionType.Read => Permission.Read(Role.Any()), - PermissionType.Write => Permission.Write(Role.Any()), - PermissionType.Create => Permission.Create(Role.Any()), - PermissionType.Update => Permission.Update(Role.Any()), - PermissionType.Delete => Permission.Delete(Role.Any()), - _ => throw new ArgumentException("Invalid permission type") - }; + Assert.Equal( + Enum.Parse(roleStr.Split('/')[1], true), + permission.Status); + } + } + + [Theory] + [InlineData("team:123", "123", null)] + [InlineData("team:456/admin", "456", "admin")] + [InlineData("team:789/member", "789", "member")] + public void Read_TeamPermissions_DeserializeCorrectly(string roleStr, string expectedId, string? expectedRole) + { + // Arrange + var json = $"\"write(\\\"{roleStr}\\\")\""; // Act - var json = JsonSerializer.Serialize(permission, _options); + var permission = JsonSerializer.Deserialize(json, _options); // Assert - Assert.Equal($"\"{expectedPrefix}(\\\"any\\\")\"", json); + Assert.NotNull(permission); + Assert.Equal(PermissionType.Write, permission.PermissionType); + Assert.Equal(RoleType.Team, permission.RoleType); + Assert.Equal(expectedId, permission.Id); + Assert.Equal(expectedRole, permission.TeamRole); } [Theory] - [InlineData("read(\\\"any\\\")", PermissionType.Read, RoleType.Any)] - [InlineData("write(\\\"user:123\\\")", PermissionType.Write, RoleType.User)] - [InlineData("create(\\\"team:456/admin\\\")", PermissionType.Create, RoleType.Team)] - [InlineData("update(\\\"users/verified\\\")", PermissionType.Update, RoleType.Users)] - [InlineData("delete(\\\"label:test\\\")", PermissionType.Delete, RoleType.Label)] - public void Read_ValidPermissions_DeserializeCorrectly(string json, PermissionType expectedPermissionType, RoleType expectedRoleType) + [InlineData("member:123")] + [InlineData("label:testLabel")] + public void Read_MemberAndLabelPermissions_DeserializeCorrectly(string roleStr) { + // Arrange + var json = $"\"read(\\\"{roleStr}\\\")\""; + // Act - var permission = JsonSerializer.Deserialize($"\"{json}\"", _options); + var permission = JsonSerializer.Deserialize(json, _options); // Assert Assert.NotNull(permission); - Assert.Equal(expectedPermissionType, permission.PermissionType); - Assert.Equal(expectedRoleType, permission.Role.RoleType); + Assert.Equal(PermissionType.Read, permission.PermissionType); + + if (roleStr.StartsWith("member:")) + { + Assert.Equal(RoleType.Member, permission.RoleType); + Assert.Equal(roleStr[7..], permission.Id); + } + else + { + Assert.Equal(RoleType.Label, permission.RoleType); + Assert.Equal(roleStr[6..], permission.Label); + } } [Theory] @@ -81,9 +161,9 @@ public void Read_NonStringToken_ThrowsJsonException(object? invalidValue) [InlineData("invalid")] [InlineData("read")] [InlineData("read(any)")] - [InlineData("read\"any\")")] - [InlineData("read(\"any\"")] - [InlineData("unknown(\"any\")")] + [InlineData("read\\\"any\\\")")] + [InlineData("read(\\\"any\\\"")] + [InlineData("unknown(\\\"any\\\")")] public void Read_InvalidFormat_ThrowsJsonException(string invalidJson) { // Act & Assert @@ -92,47 +172,143 @@ public void Read_InvalidFormat_ThrowsJsonException(string invalidJson) } [Fact] - public void Write_ComplexPermission_SerializesCorrectly() + public void Write_Any_SerializesCorrectly() { // Arrange - var role = Role.Team("123", "admin"); - var permission = Permission.Write(role); + var permission = Permission.Read().Any(); // Act var json = JsonSerializer.Serialize(permission, _options); // Assert - Assert.Equal("\"write(\\\"team:123/admin\\\")\"", json); + Assert.Equal("\"read(\\\"any\\\")\"", json); + } + + [Theory] + [InlineData(null, "users")] + [InlineData(RoleStatus.Verified, "users/verified")] + [InlineData(RoleStatus.Unverified, "users/unverified")] + public void Write_Users_SerializesCorrectly(RoleStatus? status, string expectedRole) + { + // Arrange + var permission = status.HasValue + ? Permission.Read().Users(status.Value) + : Permission.Read().Users(); + + // Act + var json = JsonSerializer.Serialize(permission, _options); + + // Assert + Assert.Equal($"\"read(\\\"{expectedRole}\\\")\"", json); } [Fact] - public void Read_ComplexPermission_DeserializesCorrectly() + public void Write_Guests_SerializesCorrectly() { // Arrange - var json = "\"write(\\\"team:123/admin\\\")\""; + var permission = Permission.Read().Guests(); // Act - var permission = JsonSerializer.Deserialize(json, _options); + var json = JsonSerializer.Serialize(permission, _options); // Assert - Assert.NotNull(permission); - Assert.Equal(PermissionType.Write, permission.PermissionType); - Assert.Equal(RoleType.Team, permission.Role.RoleType); - Assert.Equal("123", permission.Role.Id); - Assert.Equal("admin", permission.Role.TeamRole); + Assert.Equal("\"read(\\\"guests\\\")\"", json); + } + + [Theory] + [InlineData(null, "user:123")] + [InlineData(RoleStatus.Verified, "user:123/verified")] + [InlineData(RoleStatus.Unverified, "user:123/unverified")] + public void Write_User_SerializesCorrectly(RoleStatus? status, string expectedRole) + { + // Arrange + var permission = status.HasValue + ? Permission.Write().User("123", status.Value) + : Permission.Write().User("123"); + + // Act + var json = JsonSerializer.Serialize(permission, _options); + + // Assert + Assert.Equal($"\"write(\\\"{expectedRole}\\\")\"", json); + } + + [Theory] + [InlineData(null, "team:123")] + [InlineData("admin", "team:123/admin")] + public void Write_Team_SerializesCorrectly(string? teamRole, string expectedRole) + { + // Arrange + var permission = teamRole != null + ? Permission.Create().Team("123", teamRole) + : Permission.Create().Team("123"); + + // Act + var json = JsonSerializer.Serialize(permission, _options); + + // Assert + Assert.Equal($"\"create(\\\"{expectedRole}\\\")\"", json); } [Fact] - public void Read_ThrowsJsonException_ForUnknownPermissionType() + public void Write_Member_SerializesCorrectly() { // Arrange - var json = "\"invalidPermissionType(\\\"any\\\")\""; + var permission = Permission.Create().Member("123"); + + // Act + var json = JsonSerializer.Serialize(permission, _options); + + // Assert + Assert.Equal("\"create(\\\"member:123\\\")\"", json); + } + + [Fact] + public void Write_Label_SerializesCorrectly() + { + // Arrange + var permission = Permission.Write().Label("writer"); + + // Act + var json = JsonSerializer.Serialize(permission, _options); + + // Assert + Assert.Equal("\"write(\\\"label:writer\\\")\"", json); + } + + [Fact] + public void Write_ThrowsJsonException_ForUnknownRoleType() + { + // Arrange + var invalidRoleType = (RoleType)999; + + var permission = CreatePermissionWithCustomProperties(PermissionType.Create, invalidRoleType, null, null, null, null); // Act & Assert var exception = Assert.Throws(() => - JsonSerializer.Deserialize(json, _options) + JsonSerializer.Serialize(permission, _options) ); - Assert.Equal("Unknown permission type: invalidpermissiontype", exception.Message); + Assert.Equal("Unknown role type: 999", exception.Message); + } + + private Permission CreatePermissionWithCustomProperties(PermissionType permissionType, RoleType roleType, string? id, RoleStatus? status, string? teamRole, string? label) + { + // Use reflection to create an instance of Permission + var permissionT = typeof(Permission); + var constructor = permissionT.GetConstructor( + BindingFlags.Instance | BindingFlags.NonPublic, + null, + [typeof(PermissionType), typeof(RoleType), typeof(string), typeof(RoleStatus?), typeof(string), typeof(string)], + null + ); + + if (constructor == null) + { + throw new InvalidOperationException("Permission constructor not found."); + } + + var permission = (Permission)constructor.Invoke([permissionType, roleType, id, status, teamRole, label]); + return permission; } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionListConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionListConverterTests.cs index f473b9eb..f6127e30 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionListConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionListConverterTests.cs @@ -17,7 +17,6 @@ public PermissionListConverterTests() Converters = { new PermissionListConverter(), - new RoleJsonConverter(), new PermissionJsonConverter() } }; @@ -51,7 +50,7 @@ public void Read_SinglePermission_DeserializesCorrectly() Assert.NotNull(result); Assert.Single(result); Assert.Equal(PermissionType.Read, result[0].PermissionType); - Assert.Equal(RoleType.Any, result[0].Role.RoleType); + Assert.Equal(RoleType.Any, result[0].RoleType); } [Fact] @@ -74,17 +73,17 @@ public void Read_MultiplePermissions_DeserializesCorrectly() Assert.Equal(3, result.Count); Assert.Equal(PermissionType.Read, result[0].PermissionType); - Assert.Equal(RoleType.Any, result[0].Role.RoleType); + Assert.Equal(RoleType.Any, result[0].RoleType); Assert.Equal(PermissionType.Write, result[1].PermissionType); - Assert.Equal(RoleType.User, result[1].Role.RoleType); - Assert.Equal("123", result[1].Role.Id); - Assert.Equal(RoleStatus.Verified, result[1].Role.Status); + Assert.Equal(RoleType.User, result[1].RoleType); + Assert.Equal("123", result[1].Id); + Assert.Equal(RoleStatus.Verified, result[1].Status); Assert.Equal(PermissionType.Create, result[2].PermissionType); - Assert.Equal(RoleType.Team, result[2].Role.RoleType); - Assert.Equal("456", result[2].Role.Id); - Assert.Equal("admin", result[2].Role.TeamRole); + Assert.Equal(RoleType.Team, result[2].RoleType); + Assert.Equal("456", result[2].Id); + Assert.Equal("admin", result[2].TeamRole); } [Theory] @@ -130,7 +129,7 @@ public void Write_SinglePermission_SerializesCorrectly() // Arrange var permissions = new List { - Permission.Read(Role.Any()) + Permission.Read().Any() }; // Act @@ -146,9 +145,9 @@ public void Write_MultiplePermissions_SerializesCorrectly() // Arrange var permissions = new List { - Permission.Read(Role.Any()), - Permission.Write(Role.User("123", RoleStatus.Verified)), - Permission.Create(Role.Team("456", "admin")) + Permission.Read().Any(), + Permission.Write().User("123", RoleStatus.Verified), + Permission.Create().Team("456", "admin") }; // Act diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionReadOnlyListConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionReadOnlyListConverterTests.cs index a078dd84..b967f417 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionReadOnlyListConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/PermissionReadOnlyListConverterTests.cs @@ -17,7 +17,6 @@ public PermissionReadOnlyListConverterTests() Converters = { new PermissionReadOnlyListConverter(), - new RoleJsonConverter(), new PermissionJsonConverter() } }; @@ -51,7 +50,7 @@ public void Read_SinglePermission_DeserializesCorrectly() Assert.NotNull(result); Assert.Single(result); Assert.Equal(PermissionType.Read, result[0].PermissionType); - Assert.Equal(RoleType.Any, result[0].Role.RoleType); + Assert.Equal(RoleType.Any, result[0].RoleType); Assert.IsAssignableFrom>(result); } @@ -76,17 +75,17 @@ public void Read_MultiplePermissions_DeserializesCorrectly() Assert.IsAssignableFrom>(result); Assert.Equal(PermissionType.Read, result[0].PermissionType); - Assert.Equal(RoleType.Any, result[0].Role.RoleType); + Assert.Equal(RoleType.Any, result[0].RoleType); Assert.Equal(PermissionType.Write, result[1].PermissionType); - Assert.Equal(RoleType.User, result[1].Role.RoleType); - Assert.Equal("123", result[1].Role.Id); - Assert.Equal(RoleStatus.Verified, result[1].Role.Status); + Assert.Equal(RoleType.User, result[1].RoleType); + Assert.Equal("123", result[1].Id); + Assert.Equal(RoleStatus.Verified, result[1].Status); Assert.Equal(PermissionType.Create, result[2].PermissionType); - Assert.Equal(RoleType.Team, result[2].Role.RoleType); - Assert.Equal("456", result[2].Role.Id); - Assert.Equal("admin", result[2].Role.TeamRole); + Assert.Equal(RoleType.Team, result[2].RoleType); + Assert.Equal("456", result[2].Id); + Assert.Equal("admin", result[2].TeamRole); } [Theory] @@ -132,7 +131,7 @@ public void Write_SinglePermission_SerializesCorrectly() // Arrange IReadOnlyList permissions = new List { - Permission.Read(Role.Any()) + Permission.Read().Any() }.AsReadOnly(); // Act @@ -148,9 +147,9 @@ public void Write_MultiplePermissions_SerializesCorrectly() // Arrange IReadOnlyList permissions = new List { - Permission.Read(Role.Any()), - Permission.Write(Role.User("123", RoleStatus.Verified)), - Permission.Create(Role.Team("456", "admin")) + Permission.Read().Any(), + Permission.Write().User("123", RoleStatus.Verified), + Permission.Create().Team("456", "admin") }.AsReadOnly(); // Act @@ -177,7 +176,7 @@ public void Read_EnsuresReadOnlyBehavior() Assert.Throws(() => { var list = result as System.Collections.IList; - list?.Add(Permission.Read(Role.Any())); + list?.Add(Permission.Read().Any()); }); } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/RoleJsonConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/RoleJsonConverterTests.cs deleted file mode 100644 index 0dec2a27..00000000 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/RoleJsonConverterTests.cs +++ /dev/null @@ -1,275 +0,0 @@ -using System.Reflection; -using System.Text.Json; -using PinguApps.Appwrite.Shared.Converters; -using PinguApps.Appwrite.Shared.Enums; -using PinguApps.Appwrite.Shared.Utils; - -namespace PinguApps.Appwrite.Shared.Tests.Converters; -public class RoleJsonConverterTests -{ - private readonly JsonSerializerOptions _options; - private readonly RoleJsonConverter _converter; - - public RoleJsonConverterTests() - { - _converter = new(); - _options = new() - { - Converters = { _converter } - }; - } - - [Theory] - [InlineData("any", RoleType.Any)] - [InlineData("users", RoleType.Users)] - [InlineData("guests", RoleType.Guests)] - public void Read_SimpleRoles_DeserializesCorrectly(string json, RoleType expectedType) - { - // Act - var role = JsonSerializer.Deserialize($"\"{json}\"", _options); - - // Assert - Assert.NotNull(role); - Assert.Equal(expectedType, role.RoleType); - } - - [Theory] - [InlineData("user:123", "123", null)] - [InlineData("user:123/verified", "123", RoleStatus.Verified)] - [InlineData("user:456/unverified", "456", RoleStatus.Unverified)] - public void Read_UserRoles_DeserializesCorrectly(string json, string expectedId, RoleStatus? expectedStatus) - { - // Act - var role = JsonSerializer.Deserialize($"\"{json}\"", _options); - - // Assert - Assert.NotNull(role); - Assert.Equal(RoleType.User, role.RoleType); - Assert.Equal(expectedId, role.Id); - Assert.Equal(expectedStatus, role.Status); - } - - [Theory] - [InlineData("users/verified", RoleStatus.Verified)] - [InlineData("users/unverified", RoleStatus.Unverified)] - public void Read_UsersWithStatus_DeserializesCorrectly(string json, RoleStatus expectedStatus) - { - // Act - var role = JsonSerializer.Deserialize($"\"{json}\"", _options); - - // Assert - Assert.NotNull(role); - Assert.Equal(RoleType.Users, role.RoleType); - Assert.Equal(expectedStatus, role.Status); - } - - [Theory] - [InlineData("team:123", "123", null)] - [InlineData("team:456/admin", "456", "admin")] - [InlineData("team:789/member", "789", "member")] - public void Read_TeamRoles_DeserializesCorrectly(string json, string expectedId, string? expectedTeamRole) - { - // Act - var role = JsonSerializer.Deserialize($"\"{json}\"", _options); - - // Assert - Assert.NotNull(role); - Assert.Equal(RoleType.Team, role.RoleType); - Assert.Equal(expectedId, role.Id); - Assert.Equal(expectedTeamRole, role.TeamRole); - } - - [Fact] - public void Read_MemberRole_DeserializesCorrectly() - { - // Arrange - var json = "\"member:123\""; - - // Act - var role = JsonSerializer.Deserialize(json, _options); - - // Assert - Assert.NotNull(role); - Assert.Equal(RoleType.Member, role.RoleType); - Assert.Equal("123", role.Id); - } - - [Fact] - public void Read_LabelRole_DeserializesCorrectly() - { - // Arrange - var json = "\"label:testLabel\""; - - // Act - var role = JsonSerializer.Deserialize(json, _options); - - // Assert - Assert.NotNull(role); - Assert.Equal(RoleType.Label, role.RoleType); - Assert.Equal("testLabel", role.LabelName); - } - - [Theory] - [InlineData(42)] - [InlineData(true)] - public void Read_NonStringToken_ThrowsJsonException(object? invalidValue) - { - // Arrange - var json = JsonSerializer.Serialize(invalidValue); - - // Act & Assert - Assert.Throws(() => - JsonSerializer.Deserialize(json, _options)); - } - - [Theory] - [InlineData("")] - [InlineData("invalid")] - public void Read_InvalidFormat_ThrowsJsonException(string invalidJson) - { - // Act & Assert - Assert.Throws(() => - JsonSerializer.Deserialize($"\"{invalidJson}\"", _options)); - } - - [Fact] - public void Write_Any_SerializesCorrectly() - { - // Arrange - var role = Role.Any(); - - // Act - var json = JsonSerializer.Serialize(role, _options); - - // Assert - Assert.Equal("\"any\"", json); - } - - [Fact] - public void Write_Guests_SerializesCorrectly() - { - // Arrange - var role = Role.Guests(); - - // Act - var json = JsonSerializer.Serialize(role, _options); - - // Assert - Assert.Equal("\"guests\"", json); - } - - [Theory] - [InlineData(null, "user:123")] - [InlineData(RoleStatus.Verified, "user:123/verified")] - [InlineData(RoleStatus.Unverified, "user:123/unverified")] - public void Write_User_SerializesCorrectly(RoleStatus? status, string expected) - { - // Arrange - var role = status.HasValue ? Role.User("123", status.Value) : Role.User("123"); - - // Act - var json = JsonSerializer.Serialize(role, _options); - - // Assert - Assert.Equal($"\"{expected}\"", json); - } - - [Theory] - [InlineData(null, "users")] - [InlineData(RoleStatus.Verified, "users/verified")] - [InlineData(RoleStatus.Unverified, "users/unverified")] - public void Write_Users_SerializesCorrectly(RoleStatus? status, string expected) - { - // Arrange - var role = status.HasValue ? Role.Users(status.Value) : Role.Users(); - - // Act - var json = JsonSerializer.Serialize(role, _options); - - // Assert - Assert.Equal($"\"{expected}\"", json); - } - - [Theory] - [InlineData(null, "team:123")] - [InlineData("admin", "team:123/admin")] - [InlineData("member", "team:123/member")] - public void Write_Team_SerializesCorrectly(string? teamRole, string expected) - { - // Arrange - var role = teamRole != null ? Role.Team("123", teamRole) : Role.Team("123"); - - // Act - var json = JsonSerializer.Serialize(role, _options); - - // Assert - Assert.Equal($"\"{expected}\"", json); - } - - [Fact] - public void Write_Member_SerializesCorrectly() - { - // Arrange - var role = Role.Member("123"); - - // Act - var json = JsonSerializer.Serialize(role, _options); - - // Assert - Assert.Equal("\"member:123\"", json); - } - - [Fact] - public void Write_Label_SerializesCorrectly() - { - // Arrange - var role = Role.Label("testLabel"); - - // Act - var json = JsonSerializer.Serialize(role, _options); - - // Assert - Assert.Equal("\"label:testLabel\"", json); - } - - [Fact] - public void Write_ThrowsJsonException_ForUnknownRoleType() - { - // Arrange - var options = new JsonSerializerOptions - { - Converters = { new RoleJsonConverter() } - }; - - // Use reflection to create a Role object with an unknown RoleType - var roleType = (RoleType)999; // Assuming 999 is not a valid RoleType - var role = CreateRoleWithInvalidRoleType(roleType); - - // Act & Assert - var exception = Assert.Throws(() => - JsonSerializer.Serialize(role, options) - ); - - Assert.Equal($"Unknown role type: {roleType}", exception.Message); - } - - private Role CreateRoleWithInvalidRoleType(RoleType roleType) - { - // Use reflection to create an instance of Role - var roleTypeInstance = typeof(Role); - var constructor = roleTypeInstance.GetConstructor( - BindingFlags.Instance | BindingFlags.NonPublic, - null, - [typeof(RoleType)], - null - ); - - if (constructor is null) - { - throw new InvalidOperationException("Role constructor not found."); - } - - var role = (Role)constructor.Invoke([roleType]); - return role; - } -} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateCollectionRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateCollectionRequestTests.cs index 521e38a2..51add175 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateCollectionRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateCollectionRequestTests.cs @@ -32,7 +32,7 @@ public void Properties_CanBeSet() // Arrange var collectionId = IdUtils.GenerateUniqueId(); var name = "My Collection"; - var permissions = new List { Permission.Read(Role.Any()) }; + var permissions = new List { Permission.Read().Any() }; var documentSecurity = true; var enabled = true; @@ -60,7 +60,7 @@ public void Properties_CanBeSet() DatabaseId = IdUtils.GenerateUniqueId(), CollectionId = IdUtils.GenerateUniqueId(), Name = "Valid Collection Name", - Permissions = [Permission.Read(Role.Any())], + Permissions = [Permission.Read().Any()], DocumentSecurity = true, Enabled = true }, diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateCollectionRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateCollectionRequestTests.cs index 2c69a016..99863e54 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateCollectionRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateCollectionRequestTests.cs @@ -29,7 +29,7 @@ public void Properties_CanBeSet() { // Arrange var name = "Updated Collection"; - var permissions = new List { Permission.Read(Role.Any()) }; + var permissions = new List { Permission.Read().Any() }; var documentSecurity = true; var enabled = true; @@ -55,7 +55,7 @@ public void Properties_CanBeSet() DatabaseId = IdUtils.GenerateUniqueId(), CollectionId = IdUtils.GenerateUniqueId(), Name = "Valid Collection Name", - Permissions = [Permission.Read(Role.Any())], + Permissions = [Permission.Read().Any()], DocumentSecurity = true, Enabled = true }, diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionTests.cs index b83a733b..2981ba39 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionTests.cs @@ -15,7 +15,7 @@ public void Constructor_AssignsPropertiesCorrectly() var id = "5e5ea5c16897e"; var createdAt = DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(); var updatedAt = DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(); - var permissions = new List { Permission.Read(Role.Any()) }; + var permissions = new List { Permission.Read().Any() }; var databaseId = "5e5ea5c16897e"; var name = "My Collection"; var enabled = false; @@ -58,7 +58,7 @@ public void CanBeDeserialized_FromJson() Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), collection.UpdatedAt.ToUniversalTime()); Assert.Single(collection.Permissions); Assert.Equal(PermissionType.Read, collection.Permissions[0].PermissionType); - Assert.Equal(RoleType.Any, collection.Permissions[0].Role.RoleType); + Assert.Equal(RoleType.Any, collection.Permissions[0].RoleType); Assert.Equal("5e5ea5c16897e", collection.DatabaseId); Assert.Equal("My Collection", collection.Name); Assert.False(collection.Enabled); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionsListTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionsListTests.cs index caf57046..2dcfe681 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionsListTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/CollectionsListTests.cs @@ -18,7 +18,7 @@ public void Constructor_AssignsPropertiesCorrectly() "5e5ea5c16897e", DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), - [Permission.Read(Role.Any())], + [Permission.Read().Any()], "5e5ea5c16897e", "My Collection", false, @@ -56,7 +56,7 @@ public void CanBeDeserialized_FromJson() Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), collection.UpdatedAt.ToUniversalTime()); Assert.Single(collection.Permissions); Assert.Equal(PermissionType.Read, collection.Permissions[0].PermissionType); - Assert.Equal(RoleType.Any, collection.Permissions[0].Role.RoleType); + Assert.Equal(RoleType.Any, collection.Permissions[0].RoleType); Assert.Equal("5e5ea5c16897e", collection.DatabaseId); Assert.Equal("My Collection", collection.Name); Assert.False(collection.Enabled); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs index 8a829dd7..2ccae015 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs @@ -18,7 +18,7 @@ public void Document_ShouldBeDeserialized_FromJson() Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.UpdatedAt.ToUniversalTime()); Assert.Single(document.Permissions); Assert.Equal(PermissionType.Read, document.Permissions[0].PermissionType); - Assert.Equal(RoleType.Any, document.Permissions[0].Role.RoleType); + Assert.Equal(RoleType.Any, document.Permissions[0].RoleType); Assert.Equal("a string prop", document.Data["str"]); var dt = (DateTime?)document.Data["dt"]; Assert.NotNull(dt); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs index 62220fac..b8f38dbc 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs @@ -15,7 +15,7 @@ public void Constructor_AssignsPropertiesCorrectly() { new("5e5ea5c16897e", "5e5ea5c15117e", "5e5ea5c15117e", DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), - DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), [Permission.Read(Role.Any())], + DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), [Permission.Read().Any()], []) }; @@ -46,6 +46,6 @@ public void CanBeDeserialized_FromJson() Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.UpdatedAt.ToUniversalTime()); Assert.Single(document.Permissions); Assert.Equal(PermissionType.Read, document.Permissions[0].PermissionType); - Assert.Equal(RoleType.Any, document.Permissions[0].Role.RoleType); + Assert.Equal(RoleType.Any, document.Permissions[0].RoleType); } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Utils/PermissionTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Utils/PermissionTests.cs index de5696d4..e879f2ff 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Utils/PermissionTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Utils/PermissionTests.cs @@ -4,87 +4,197 @@ namespace PinguApps.Appwrite.Shared.Tests.Utils; public class PermissionTests { - private readonly Role _testRole = Role.Any(); - [Fact] - public void Read_CreatesCorrectPermission() + public void Read_Any_CreatesCorrectPermission() { // Act - var permission = Permission.Read(_testRole); + var permission = Permission.Read().Any(); // Assert Assert.Equal(PermissionType.Read, permission.PermissionType); - Assert.Same(_testRole, permission.Role); + Assert.Equal(RoleType.Any, permission.RoleType); + Assert.Null(permission.Id); + Assert.Null(permission.Status); + Assert.Null(permission.TeamRole); + Assert.Null(permission.Label); } - [Fact] - public void Write_CreatesCorrectPermission() + [Theory] + [InlineData("user123")] + [InlineData("user456")] + public void Read_User_CreatesCorrectPermission(string userId) { // Act - var permission = Permission.Write(_testRole); + var permission = Permission.Read().User(userId); // Assert - Assert.Equal(PermissionType.Write, permission.PermissionType); - Assert.Same(_testRole, permission.Role); + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Equal(RoleType.User, permission.RoleType); + Assert.Equal(userId, permission.Id); + Assert.Null(permission.Status); + Assert.Null(permission.TeamRole); + Assert.Null(permission.Label); } - [Fact] - public void Create_CreatesCorrectPermission() + [Theory] + [InlineData("user123", RoleStatus.Verified)] + [InlineData("user456", RoleStatus.Unverified)] + public void Read_UserWithStatus_CreatesCorrectPermission(string userId, RoleStatus status) { // Act - var permission = Permission.Create(_testRole); + var permission = Permission.Read().User(userId, status); // Assert - Assert.Equal(PermissionType.Create, permission.PermissionType); - Assert.Same(_testRole, permission.Role); + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Equal(RoleType.User, permission.RoleType); + Assert.Equal(userId, permission.Id); + Assert.Equal(status, permission.Status); + Assert.Null(permission.TeamRole); + Assert.Null(permission.Label); } [Fact] - public void Update_CreatesCorrectPermission() + public void Read_Users_CreatesCorrectPermission() { // Act - var permission = Permission.Update(_testRole); + var permission = Permission.Read().Users(); // Assert - Assert.Equal(PermissionType.Update, permission.PermissionType); - Assert.Same(_testRole, permission.Role); + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Equal(RoleType.Users, permission.RoleType); + Assert.Null(permission.Id); + Assert.Null(permission.Status); + Assert.Null(permission.TeamRole); + Assert.Null(permission.Label); + } + + [Theory] + [InlineData(RoleStatus.Verified)] + [InlineData(RoleStatus.Unverified)] + public void Read_UsersWithStatus_CreatesCorrectPermission(RoleStatus status) + { + // Act + var permission = Permission.Read().Users(status); + + // Assert + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Equal(RoleType.Users, permission.RoleType); + Assert.Null(permission.Id); + Assert.Equal(status, permission.Status); + Assert.Null(permission.TeamRole); + Assert.Null(permission.Label); } [Fact] - public void Delete_CreatesCorrectPermission() + public void Read_Guests_CreatesCorrectPermission() { // Act - var permission = Permission.Delete(_testRole); + var permission = Permission.Read().Guests(); // Assert - Assert.Equal(PermissionType.Delete, permission.PermissionType); - Assert.Same(_testRole, permission.Role); + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Equal(RoleType.Guests, permission.RoleType); + Assert.Null(permission.Id); + Assert.Null(permission.Status); + Assert.Null(permission.TeamRole); + Assert.Null(permission.Label); } + [Theory] + [InlineData("team123")] + [InlineData("team456")] + public void Read_Team_CreatesCorrectPermission(string teamId) + { + // Act + var permission = Permission.Read().Team(teamId); + + // Assert + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Equal(RoleType.Team, permission.RoleType); + Assert.Equal(teamId, permission.Id); + Assert.Null(permission.Status); + Assert.Null(permission.TeamRole); + Assert.Null(permission.Label); + } + + [Theory] + [InlineData("team123", "admin")] + [InlineData("team456", "member")] + public void Read_TeamWithRole_CreatesCorrectPermission(string teamId, string teamRole) + { + // Act + var permission = Permission.Read().Team(teamId, teamRole); + + // Assert + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Equal(RoleType.Team, permission.RoleType); + Assert.Equal(teamId, permission.Id); + Assert.Null(permission.Status); + Assert.Equal(teamRole, permission.TeamRole); + Assert.Null(permission.Label); + } + + [Theory] + [InlineData("member123")] + [InlineData("member456")] + public void Read_Member_CreatesCorrectPermission(string memberId) + { + // Act + var permission = Permission.Read().Member(memberId); + + // Assert + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Equal(RoleType.Member, permission.RoleType); + Assert.Equal(memberId, permission.Id); + Assert.Null(permission.Status); + Assert.Null(permission.TeamRole); + Assert.Null(permission.Label); + } + + [Theory] + [InlineData("label1")] + [InlineData("label2")] + public void Read_Label_CreatesCorrectPermission(string labelName) + { + // Act + var permission = Permission.Read().Label(labelName); + + // Assert + Assert.Equal(PermissionType.Read, permission.PermissionType); + Assert.Equal(RoleType.Label, permission.RoleType); + Assert.Null(permission.Id); + Assert.Null(permission.Status); + Assert.Null(permission.TeamRole); + Assert.Equal(labelName, permission.Label); + } + + // Test all permission types with a sample role [Theory] [InlineData(PermissionType.Read)] [InlineData(PermissionType.Write)] [InlineData(PermissionType.Create)] [InlineData(PermissionType.Update)] [InlineData(PermissionType.Delete)] - public void Permission_StoresRoleReference(PermissionType permissionType) + public void AllPermissionTypes_CreateCorrectPermissions(PermissionType permissionType) { // Arrange - var role = Role.User("123", RoleStatus.Verified); - - // Act - var permission = permissionType switch + var builder = permissionType switch { - PermissionType.Read => Permission.Read(role), - PermissionType.Write => Permission.Write(role), - PermissionType.Create => Permission.Create(role), - PermissionType.Update => Permission.Update(role), - PermissionType.Delete => Permission.Delete(role), + PermissionType.Read => Permission.Read(), + PermissionType.Write => Permission.Write(), + PermissionType.Create => Permission.Create(), + PermissionType.Update => Permission.Update(), + PermissionType.Delete => Permission.Delete(), _ => throw new ArgumentException("Invalid permission type") }; + // Act + var permission = builder.User("123", RoleStatus.Verified); + // Assert - Assert.Same(role, permission.Role); Assert.Equal(permissionType, permission.PermissionType); + Assert.Equal(RoleType.User, permission.RoleType); + Assert.Equal("123", permission.Id); + Assert.Equal(RoleStatus.Verified, permission.Status); } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Utils/RoleTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Utils/RoleTests.cs deleted file mode 100644 index f8fec995..00000000 --- a/tests/PinguApps.Appwrite.Shared.Tests/Utils/RoleTests.cs +++ /dev/null @@ -1,164 +0,0 @@ -using PinguApps.Appwrite.Shared.Enums; -using PinguApps.Appwrite.Shared.Utils; - -namespace PinguApps.Appwrite.Shared.Tests.Utils; -public class RoleTests -{ - [Fact] - public void Any_CreatesCorrectRole() - { - // Act - var role = Role.Any(); - - // Assert - Assert.Equal(RoleType.Any, role.RoleType); - Assert.Null(role.Id); - Assert.Null(role.Status); - Assert.Null(role.TeamRole); - Assert.Null(role.LabelName); - } - - [Fact] - public void User_WithId_CreatesCorrectRole() - { - // Arrange - var userId = "123"; - - // Act - var role = Role.User(userId); - - // Assert - Assert.Equal(RoleType.User, role.RoleType); - Assert.Equal(userId, role.Id); - Assert.Null(role.Status); - Assert.Null(role.TeamRole); - Assert.Null(role.LabelName); - } - - [Theory] - [InlineData("123", RoleStatus.Verified)] - [InlineData("456", RoleStatus.Unverified)] - public void User_WithIdAndStatus_CreatesCorrectRole(string userId, RoleStatus status) - { - // Act - var role = Role.User(userId, status); - - // Assert - Assert.Equal(RoleType.User, role.RoleType); - Assert.Equal(userId, role.Id); - Assert.Equal(status, role.Status); - Assert.Null(role.TeamRole); - Assert.Null(role.LabelName); - } - - [Fact] - public void Users_CreatesCorrectRole() - { - // Act - var role = Role.Users(); - - // Assert - Assert.Equal(RoleType.Users, role.RoleType); - Assert.Null(role.Id); - Assert.Null(role.Status); - Assert.Null(role.TeamRole); - Assert.Null(role.LabelName); - } - - [Theory] - [InlineData(RoleStatus.Verified)] - [InlineData(RoleStatus.Unverified)] - public void Users_WithStatus_CreatesCorrectRole(RoleStatus status) - { - // Act - var role = Role.Users(status); - - // Assert - Assert.Equal(RoleType.Users, role.RoleType); - Assert.Null(role.Id); - Assert.Equal(status, role.Status); - Assert.Null(role.TeamRole); - Assert.Null(role.LabelName); - } - - [Fact] - public void Guests_CreatesCorrectRole() - { - // Act - var role = Role.Guests(); - - // Assert - Assert.Equal(RoleType.Guests, role.RoleType); - Assert.Null(role.Id); - Assert.Null(role.Status); - Assert.Null(role.TeamRole); - Assert.Null(role.LabelName); - } - - [Fact] - public void Team_WithId_CreatesCorrectRole() - { - // Arrange - var teamId = "team123"; - - // Act - var role = Role.Team(teamId); - - // Assert - Assert.Equal(RoleType.Team, role.RoleType); - Assert.Equal(teamId, role.Id); - Assert.Null(role.Status); - Assert.Null(role.TeamRole); - Assert.Null(role.LabelName); - } - - [Theory] - [InlineData("team123", "admin")] - [InlineData("team456", "member")] - public void Team_WithIdAndRole_CreatesCorrectRole(string teamId, string teamRole) - { - // Act - var role = Role.Team(teamId, teamRole); - - // Assert - Assert.Equal(RoleType.Team, role.RoleType); - Assert.Equal(teamId, role.Id); - Assert.Null(role.Status); - Assert.Equal(teamRole, role.TeamRole); - Assert.Null(role.LabelName); - } - - [Fact] - public void Member_CreatesCorrectRole() - { - // Arrange - var memberId = "member123"; - - // Act - var role = Role.Member(memberId); - - // Assert - Assert.Equal(RoleType.Member, role.RoleType); - Assert.Equal(memberId, role.Id); - Assert.Null(role.Status); - Assert.Null(role.TeamRole); - Assert.Null(role.LabelName); - } - - [Fact] - public void Label_CreatesCorrectRole() - { - // Arrange - var labelName = "testLabel"; - - // Act - var role = Role.Label(labelName); - - // Assert - Assert.Equal(RoleType.Label, role.RoleType); - Assert.Null(role.Id); - Assert.Null(role.Status); - Assert.Null(role.TeamRole); - Assert.Equal(labelName, role.LabelName); - } -} From ce1fe4d334533727df28ae8c085e12dcb40de1f7 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 00:37:59 +0100 Subject: [PATCH 073/303] added delete collection request --- .../Requests/Databases/DeleteCollectionRequest.cs | 10 ++++++++++ .../Validators/DeleteCollectionRequestValidator.cs | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteCollectionRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteCollectionRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteCollectionRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteCollectionRequest.cs new file mode 100644 index 00000000..df46face --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteCollectionRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to delete a collection +/// +public class DeleteCollectionRequest : DatabaseCollectionIdBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteCollectionRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteCollectionRequestValidator.cs new file mode 100644 index 00000000..1626b62a --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteCollectionRequestValidator.cs @@ -0,0 +1,10 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class DeleteCollectionRequestValidator : AbstractValidator +{ + public DeleteCollectionRequestValidator() + { + Include(new DatabaseCollectionIdBaseRequestValidator()); + } +} From a275c392c2ece65924738c3249f93436b5bde916 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 00:39:20 +0100 Subject: [PATCH 074/303] added tests for delete collection request --- .../Requests/Databases/DeleteCollectionRequestTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteCollectionRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteCollectionRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteCollectionRequestTests.cs new file mode 100644 index 00000000..4363950b --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteCollectionRequestTests.cs @@ -0,0 +1,8 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class DeleteCollectionRequestTests : DatabaseCollectionIdBaseRequestTests +{ + protected override DeleteCollectionRequest CreateValidDatabaseCollectionIdRequest => new(); +} From 619a8ea57f0a7e71d77266e565ebda0c51f6a8fd Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 00:44:06 +0100 Subject: [PATCH 075/303] added request to list attributes --- .../Databases/ListAttributesRequest.cs | 25 +++++++++++++++++++ .../ListAttributesRequestValidator.cs | 19 ++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/ListAttributesRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListAttributesRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/ListAttributesRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/ListAttributesRequest.cs new file mode 100644 index 00000000..094658e7 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/ListAttributesRequest.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Attributes; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to list attributes of a collection +/// +public class ListAttributesRequest : QueryBaseRequest +{ + /// + /// Database ID + /// + [JsonPropertyName("databaseId")] + [SdkExclude] + public string DatabaseId { get; set; } = string.Empty; + + /// + /// Collection ID + /// + [JsonPropertyName("databaseId")] + [SdkExclude] + public string CollectionId { get; set; } = string.Empty; +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListAttributesRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListAttributesRequestValidator.cs new file mode 100644 index 00000000..bcaa0520 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListAttributesRequestValidator.cs @@ -0,0 +1,19 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class ListAttributesRequestValidator : AbstractValidator +{ + public ListAttributesRequestValidator() + { + Include(new QueryBaseRequestValidator()); + + RuleFor(x => x.DatabaseId) + .NotEmpty() + .WithMessage("DatabaseId is required."); + + RuleFor(x => x.CollectionId) + .NotEmpty() + .WithMessage("CollectionId is required."); + } +} From 1e87489a50570260e1b87aba88edba1ea537e8d9 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 00:48:17 +0100 Subject: [PATCH 076/303] added tests for list attributes request --- .../Databases/ListAttributesRequestTests.cs | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListAttributesRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListAttributesRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListAttributesRequestTests.cs new file mode 100644 index 00000000..6f02eaf7 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListAttributesRequestTests.cs @@ -0,0 +1,134 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class ListAttributesRequestTests : QueryBaseRequestTests +{ + protected override ListAttributesRequest CreateValidRequest => new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new ListAttributesRequest(); + + // Assert + Assert.Equal(string.Empty, request.DatabaseId); + Assert.Equal(string.Empty, request.CollectionId); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var databaseId = "testDatabaseId"; + var collectionId = "testCollectionId"; + + var request = new ListAttributesRequest(); + + // Act + request.DatabaseId = databaseId; + request.CollectionId = collectionId; + + // Assert + Assert.Equal(databaseId, request.DatabaseId); + Assert.Equal(collectionId, request.CollectionId); + } + + public static TheoryData ValidRequestsData => + [ + new() + { + DatabaseId = "validDatabaseId123", + CollectionId = "validCollectionId123" + }, + new() + { + DatabaseId = "anotherValidDatabaseId", + CollectionId = "anotherValidCollectionId" + } + ]; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(ListAttributesRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData => + [ + new() + { + DatabaseId = null!, + CollectionId = IdUtils.GenerateUniqueId() + }, + new() + { + DatabaseId = "", + CollectionId = IdUtils.GenerateUniqueId() + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = null! + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = "" + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(ListAttributesRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new ListAttributesRequest + { + DatabaseId = "", + CollectionId = "" + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new ListAttributesRequest + { + DatabaseId = "", + CollectionId = "" + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 12bc48b7f4ffbd9de15c8b30766fd89e55ea5329 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:04:47 +0100 Subject: [PATCH 077/303] Added CreateAttributeBaseRequest plus tests --- .../Databases/CreateAttributeBaseRequest.cs | 32 +++++ .../CreateAttributeBaseRequestValidator.cs | 24 ++++ .../CreateAttributeBaseRequestTests.cs | 114 ++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateAttributeBaseRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateAttributeBaseRequestValidator.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateAttributeBaseRequestTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateAttributeBaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateAttributeBaseRequest.cs new file mode 100644 index 00000000..e24d8cae --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateAttributeBaseRequest.cs @@ -0,0 +1,32 @@ +using System.Text.Json.Serialization; +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The base request plus everything required to create an attribute (minus attribute specific properties) +/// +/// The request type +/// The request validator type +public abstract class CreateAttributeBaseRequest : DatabaseCollectionIdBaseRequest + where TRequest : class + where TValidator : IValidator, new() +{ + /// + /// Attribute Key + /// + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; + + /// + /// Is attribute required? + /// + [JsonPropertyName("required")] + public bool Required { get; set; } + + /// + /// Is attribute an array? + /// + [JsonPropertyName("array")] + public bool Array { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateAttributeBaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateAttributeBaseRequestValidator.cs new file mode 100644 index 00000000..a71ab0d5 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateAttributeBaseRequestValidator.cs @@ -0,0 +1,24 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateAttributeBaseRequestValidator : AbstractValidator> + where TRequest : class + where TValidator : IValidator, new() +{ + public CreateAttributeBaseRequestValidator() + { + Include(new DatabaseCollectionIdBaseRequestValidator()); + + RuleFor(x => x.Key) + .NotEmpty() + .WithMessage("Key is required."); + + RuleFor(x => x.Required) + .NotEmpty() + .WithMessage("Required is required."); + + RuleFor(x => x.Array) + .NotEmpty() + .WithMessage("Array is required."); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateAttributeBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateAttributeBaseRequestTests.cs new file mode 100644 index 00000000..b268a3b3 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateAttributeBaseRequestTests.cs @@ -0,0 +1,114 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public abstract class CreateAttributeBaseRequestTests : DatabaseCollectionIdBaseRequestTests + where TRequest : CreateAttributeBaseRequest + where TValidator : AbstractValidator, new() +{ + protected sealed override TRequest CreateValidDatabaseCollectionIdRequest + { + get + { + var request = CreateValidCreateAttributeBaseRequest; + request.Key = IdUtils.GenerateUniqueId(); + return request; + } + } + + protected abstract TRequest CreateValidCreateAttributeBaseRequest { get; } + + [Fact] + public void CreateAttributeBase_Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = CreateValidCreateAttributeBaseRequest; + + // Assert + Assert.Equal(string.Empty, request.Key); + Assert.False(request.Required); + Assert.False(request.Array); + } + + [Fact] + public void CreateAttributeBase_Properties_CanBeSet() + { + // Arrange + var keyValue = "validKey"; + var request = CreateValidCreateAttributeBaseRequest; + + // Act + request.Key = keyValue; + request.Required = true; + request.Array = true; + + // Assert + Assert.Equal(keyValue, request.Key); + Assert.True(request.Required); + Assert.True(request.Array); + } + + [Fact] + public void CreateAttributeBase_IsValid_WithValidData_ReturnsTrue() + { + // Arrange + var request = CreateValidCreateAttributeBaseRequest; + request.DatabaseId = "valid_Team-Id."; + request.CollectionId = "valid_Team-Id."; + request.Key = "validKey"; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void CreateAttributeBase_IsValid_WithInvalidData_ReturnsFalse(string? key) + { + // Arrange + var request = CreateValidCreateAttributeBaseRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = key!; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void CreateAttributeBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = CreateValidCreateAttributeBaseRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = ""; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void CreateAttributeBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = CreateValidCreateAttributeBaseRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = ""; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 07bbfd3533252fa15566a3bcad90da369c970639 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:11:13 +0100 Subject: [PATCH 078/303] added update attribute base request --- .../Databases/UpdateAttributeBaseRequest.cs | 26 +++++++++++++++++++ .../UpdateAttributeBaseRequestValidator.cs | 21 +++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateAttributeBaseRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateAttributeBaseRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateAttributeBaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateAttributeBaseRequest.cs new file mode 100644 index 00000000..f1f93a24 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateAttributeBaseRequest.cs @@ -0,0 +1,26 @@ +using System.Text.Json.Serialization; +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The base request plus everything required to update an attribute (minus attribute specific properties) +/// +/// The request type +/// The request validator type +public abstract class UpdateAttributeBaseRequest : DatabaseCollectionIdAttributeKeyBaseRequest + where TRequest : class + where TValidator : IValidator, new() +{ + /// + /// Is attribute required? + /// + [JsonPropertyName("required")] + public bool Required { get; set; } + + /// + /// New attribute key + /// + [JsonPropertyName("newKey")] + public string? NewKey { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateAttributeBaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateAttributeBaseRequestValidator.cs new file mode 100644 index 00000000..0abe3582 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateAttributeBaseRequestValidator.cs @@ -0,0 +1,21 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateAttributeBaseRequestValidator : AbstractValidator> + where TRequest : class + where TValidator : IValidator, new() +{ + public UpdateAttributeBaseRequestValidator() + { + Include(new DatabaseCollectionIdAttributeKeyBaseRequestValidator()); + + RuleFor(x => x.Required) + .NotNull() + .WithMessage("Required is required."); + + RuleFor(x => x.NewKey) + .NotEmpty() + .When(x => x.NewKey is not null) + .WithMessage("NewKey must either be null or a non empty string"); + } +} From 99906522347532e3b1edfaa067ee6f1128c81490 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:20:48 +0100 Subject: [PATCH 079/303] added tests for update attribute base request --- .../UpdateAttributeBaseRequestTests.cs | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateAttributeBaseRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateAttributeBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateAttributeBaseRequestTests.cs new file mode 100644 index 00000000..775c9a48 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateAttributeBaseRequestTests.cs @@ -0,0 +1,109 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public abstract class UpdateAttributeBaseRequestTests : DatabaseCollectionIdAttributeKeyBaseRequestTests + where TRequest : UpdateAttributeBaseRequest + where TValidator : AbstractValidator, new() +{ + protected override TRequest CreateValidDatabaseCollectionIdAttributeKeyRequest => CreateValidDatabaseCollectionIdAttributeKeyRequest; + + protected abstract TRequest CreateValidUpdateAttributeBaseRequest { get; } + + [Fact] + public void UpdateAttributeBase_Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = CreateValidUpdateAttributeBaseRequest; + + // Assert + Assert.False(request.Required); + Assert.Null(request.NewKey); + } + + [Fact] + public void UpdateAttributeBase_Properties_CanBeSet() + { + // Arrange + var keyValue = "validKey"; + var request = CreateValidUpdateAttributeBaseRequest; + + // Act + request.Required = true; + request.NewKey = keyValue; + + // Assert + Assert.True(request.Required); + Assert.Equal(keyValue, request.NewKey); + } + + [Theory] + [InlineData(null, false)] + [InlineData("validKey", true)] + public void UpdateAttributeBase_IsValid_WithValidData_ReturnsTrue(string? key, bool required) + { + // Arrange + var request = CreateValidUpdateAttributeBaseRequest; + request.DatabaseId = "valid_Team-Id."; + request.CollectionId = "valid_Team-Id."; + request.Key = "validKey"; + request.Required = required; + request.NewKey = key; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Theory] + [InlineData("")] + public void UpdateAttributeBase_IsValid_WithInvalidData_ReturnsFalse(string? key) + { + // Arrange + var request = CreateValidUpdateAttributeBaseRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = "validKey"; + request.NewKey = key; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void UpdateAttributeBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = CreateValidUpdateAttributeBaseRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = "validKey"; + request.NewKey = ""; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void UpdateAttributeBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = CreateValidUpdateAttributeBaseRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = "validKey"; + request.NewKey = ""; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From ae3b980c3e0ba2cfa7e8fc409d99ce513a91364f Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:27:15 +0100 Subject: [PATCH 080/303] added create boolean attribute request --- .../Databases/CreateBooleanAttributeRequest.cs | 12 ++++++++++++ .../CreateBooleanAttributeRequestValidator.cs | 14 ++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateBooleanAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateBooleanAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateBooleanAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateBooleanAttributeRequest.cs new file mode 100644 index 00000000..b18268e2 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateBooleanAttributeRequest.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; +public class CreateBooleanAttributeRequest : CreateAttributeBaseRequest +{ + /// + /// Default value for attribute when not provided. Cannot be set when attribute is required + /// + [JsonPropertyName("default")] + public bool? Default { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateBooleanAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateBooleanAttributeRequestValidator.cs new file mode 100644 index 00000000..0942ea01 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateBooleanAttributeRequestValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateBooleanAttributeRequestValidator : AbstractValidator +{ + public CreateBooleanAttributeRequestValidator() + { + Include(new CreateAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .Must((request, defaultValue) => !request.Required || defaultValue is null) + .WithMessage("Default value cannot be set when attribute is required."); + } +} From a7a576a812bf73ea1ae82547a59d55a252278d80 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:54:18 +0100 Subject: [PATCH 081/303] added tests for create boolean attribute --- .../CreateAttributeBaseRequestValidator.cs | 4 +- .../CreateBooleanAttributeRequestTests.cs | 135 ++++++++++++++++++ 2 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateBooleanAttributeRequestTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateAttributeBaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateAttributeBaseRequestValidator.cs index a71ab0d5..8158299f 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateAttributeBaseRequestValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateAttributeBaseRequestValidator.cs @@ -14,11 +14,11 @@ public CreateAttributeBaseRequestValidator() .WithMessage("Key is required."); RuleFor(x => x.Required) - .NotEmpty() + .NotNull() .WithMessage("Required is required."); RuleFor(x => x.Array) - .NotEmpty() + .NotNull() .WithMessage("Array is required."); } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateBooleanAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateBooleanAttributeRequestTests.cs new file mode 100644 index 00000000..0056b38b --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateBooleanAttributeRequestTests.cs @@ -0,0 +1,135 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateBooleanAttributeRequestTests : CreateAttributeBaseRequestTests +{ + protected override CreateBooleanAttributeRequest CreateValidCreateAttributeBaseRequest => new(); + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new CreateBooleanAttributeRequest(); + + // Assert + Assert.Null(request.Default); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var defaultValue = true; + + var request = new CreateBooleanAttributeRequest(); + + // Act + request.Default = defaultValue; + + // Assert + Assert.Equal(defaultValue, request.Default); + } + + public static TheoryData ValidRequestsData = new() + { + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = null, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = true, + Required = false + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = false, + Required = false + } + }; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(CreateBooleanAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData = new() + { + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = true, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = false, + Required = true + } + }; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(CreateBooleanAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new CreateBooleanAttributeRequest + { + Default = true, + Required = true + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new CreateBooleanAttributeRequest + { + Default = true, + Required = true + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 76431fdf15480aee9de08531cd57dc6275d18e85 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:56:49 +0100 Subject: [PATCH 082/303] added update boolean attribute request --- .../Databases/UpdateBooleanAttributeRequest.cs | 17 +++++++++++++++++ .../UpdateBooleanAttributeRequestValidator.cs | 14 ++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateBooleanAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateBooleanAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateBooleanAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateBooleanAttributeRequest.cs new file mode 100644 index 00000000..500d2ec1 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateBooleanAttributeRequest.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to update a boolean attribute +/// +public class UpdateBooleanAttributeRequest : UpdateAttributeBaseRequest +{ + /// + /// Default value for attribute when not provided. Cannot be set when attribute is required + /// + [JsonPropertyName("default")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public bool? Default { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateBooleanAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateBooleanAttributeRequestValidator.cs new file mode 100644 index 00000000..bf430807 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateBooleanAttributeRequestValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateBooleanAttributeRequestValidator : AbstractValidator +{ + public UpdateBooleanAttributeRequestValidator() + { + Include(new UpdateAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .Must((request, defaultValue) => !request.Required || defaultValue == null) + .WithMessage("Default value cannot be set when attribute is required."); + } +} From afc9837bd8f88203d3cd438093556bff72aa7ce3 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:57:06 +0100 Subject: [PATCH 083/303] added logic in main json converter to handle null#s which MUST be written --- .../IgnoreSdkExcludedPropertiesConverterFactory.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/PinguApps.Appwrite.Shared/Converters/IgnoreSdkExcludedPropertiesConverterFactory.cs b/src/PinguApps.Appwrite.Shared/Converters/IgnoreSdkExcludedPropertiesConverterFactory.cs index 766cefa4..046c2a50 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/IgnoreSdkExcludedPropertiesConverterFactory.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/IgnoreSdkExcludedPropertiesConverterFactory.cs @@ -86,7 +86,11 @@ public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions var propValue = prop.GetValue(value); - if (propValue is null && options.DefaultIgnoreCondition == JsonIgnoreCondition.WhenWritingNull) + // Check for JsonIgnoreAttribute with Never condition + var jsonIgnoreAttr = prop.GetCustomAttribute(); + var shouldIncludeNull = jsonIgnoreAttr?.Condition == JsonIgnoreCondition.Never; + + if (propValue is null && !shouldIncludeNull && options.DefaultIgnoreCondition == JsonIgnoreCondition.WhenWritingNull) continue; writer.WritePropertyName(jsonPropertyName); From e71ab60bf45473193855633b163d702304ea7da0 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:03:30 +0000 Subject: [PATCH 084/303] added tests for update bool attribute --- .../UpdateAttributeBaseRequestTests.cs | 2 +- .../UpdateBooleanAttributeRequestTests.cs | 135 ++++++++++++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateBooleanAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateAttributeBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateAttributeBaseRequestTests.cs index 775c9a48..3766b409 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateAttributeBaseRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateAttributeBaseRequestTests.cs @@ -7,7 +7,7 @@ public abstract class UpdateAttributeBaseRequestTests : Da where TRequest : UpdateAttributeBaseRequest where TValidator : AbstractValidator, new() { - protected override TRequest CreateValidDatabaseCollectionIdAttributeKeyRequest => CreateValidDatabaseCollectionIdAttributeKeyRequest; + protected override TRequest CreateValidDatabaseCollectionIdAttributeKeyRequest => CreateValidUpdateAttributeBaseRequest; protected abstract TRequest CreateValidUpdateAttributeBaseRequest { get; } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateBooleanAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateBooleanAttributeRequestTests.cs new file mode 100644 index 00000000..f6554e03 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateBooleanAttributeRequestTests.cs @@ -0,0 +1,135 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateBooleanAttributeRequestTests : UpdateAttributeBaseRequestTests +{ + protected override UpdateBooleanAttributeRequest CreateValidUpdateAttributeBaseRequest => new(); + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new UpdateBooleanAttributeRequest(); + + // Assert + Assert.Null(request.Default); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var defaultValue = true; + + var request = new UpdateBooleanAttributeRequest(); + + // Act + request.Default = defaultValue; + + // Assert + Assert.Equal(defaultValue, request.Default); + } + + public static TheoryData ValidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = null, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = true, + Required = false + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = false, + Required = false + } + ]; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(UpdateBooleanAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = true, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = false, + Required = true + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(UpdateBooleanAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new UpdateBooleanAttributeRequest + { + Default = true, + Required = true + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new UpdateBooleanAttributeRequest + { + Default = true, + Required = true + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 9423326bb06625b23763c8aa8fd75ddbbf4bef0f Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:09:16 +0000 Subject: [PATCH 085/303] added test to cover additional case with main converter --- ...eSdkExcludedPropertiesConverterFactoryTests.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/IgnoreSdkExcludedPropertiesConverterFactoryTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/IgnoreSdkExcludedPropertiesConverterFactoryTests.cs index cdc67218..4249f5d9 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/IgnoreSdkExcludedPropertiesConverterFactoryTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/IgnoreSdkExcludedPropertiesConverterFactoryTests.cs @@ -234,6 +234,15 @@ public void Write_PropertyValueIsNullAndDefaultIgnoreConditionIsWhenWritingNull_ Assert.DoesNotContain("\"Name\"", json); } + [Fact] + public void Write_JsonIgnoreAttributeWithNeverCondition_PropertyIsNotIgnored() + { + var testClass = new TestClassWithJsonIgnoreNever { Val = null }; + var json = JsonSerializer.Serialize(testClass, _options); + + Assert.Contains("\"Val\":null", json); + } + private class TestClass { public string Name { get; set; } = string.Empty; @@ -307,4 +316,10 @@ private class TestClassWithNullProperty { public string? Name { get; set; } } + + private class TestClassWithJsonIgnoreNever + { + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public bool? Val { get; set; } + } } From ba6aee82fe88da4f569d4b146e3bcac2ded8fd8a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:23:44 +0000 Subject: [PATCH 086/303] Added converter for nullable datetimes which we always want to write null values for --- .../AlwaysWriteNullableDateTimeConverter.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Converters/AlwaysWriteNullableDateTimeConverter.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/AlwaysWriteNullableDateTimeConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/AlwaysWriteNullableDateTimeConverter.cs new file mode 100644 index 00000000..3201890f --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/AlwaysWriteNullableDateTimeConverter.cs @@ -0,0 +1,18 @@ +using System; +using System.Text.Json; + +namespace PinguApps.Appwrite.Shared.Converters; +public class AlwaysWriteNullableDateTimeConverter : NullableDateTimeConverter +{ + public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + } + else + { + base.Write(writer, value, options); + } + } +} From 41b81fc43aadadcd6a17d1732328a0799fae570c Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:30:15 +0000 Subject: [PATCH 087/303] Added tests for always null dt converter --- ...waysWriteNullableDateTimeConverterTests.cs | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/AlwaysWriteNullableDateTimeConverterTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/AlwaysWriteNullableDateTimeConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AlwaysWriteNullableDateTimeConverterTests.cs new file mode 100644 index 00000000..d89c0cbb --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AlwaysWriteNullableDateTimeConverterTests.cs @@ -0,0 +1,95 @@ +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class AlwaysWriteNullableDateTimeConverterTests +{ + private readonly AlwaysWriteNullableDateTimeConverter _converter; + private readonly JsonSerializerOptions _options; + + public AlwaysWriteNullableDateTimeConverterTests() + { + _converter = new AlwaysWriteNullableDateTimeConverter(); + _options = new JsonSerializerOptions() + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + _options.Converters.Add(_converter); + } + + [Fact] + public void Write_WhenValueIsNull_WritesNullValue() + { + // Arrange + DateTime? value = null; + var json = JsonSerializer.SerializeToUtf8Bytes(value, _options); + using var doc = JsonDocument.Parse(json); + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + // Act + _converter.Write(writer, value, _options); + writer.Flush(); + + // Assert + stream.Position = 0; + using var result = JsonDocument.Parse(stream); + Assert.Equal(JsonValueKind.Null, result.RootElement.ValueKind); + } + + [Fact] + public void Write_WhenValueHasValue_CallsBaseConverter() + { + // Arrange + var testDate = new DateTime(2024, 1, 1, 12, 0, 0, DateTimeKind.Utc); + DateTime? value = testDate; + + // Act + var json = JsonSerializer.Serialize(value, _options); + var deserializedValue = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.NotNull(deserializedValue); + Assert.Equal(testDate, deserializedValue.Value); + } + + // Test class to verify serialization behavior in a property context + private class TestClass + { + [JsonConverter(typeof(AlwaysWriteNullableDateTimeConverter))] + public DateTime? Date { get; set; } + } + + [Fact] + public void Serialize_WhenPropertyIsNull_IncludesNullInOutput() + { + // Arrange + var testObject = new TestClass { Date = null }; + + // Act + var json = JsonSerializer.Serialize(testObject, _options); + + // Assert + Assert.Contains("\"Date\":null", json); + } + + [Fact] + public void Serialize_WhenPropertyHasValue_SerializesDateTime() + { + // Arrange + var testDate = new DateTime(2024, 1, 1, 12, 0, 0, DateTimeKind.Utc); + var testObject = new TestClass { Date = testDate }; + + // Act + var json = JsonSerializer.Serialize(testObject, _options); + var deserialized = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.NotNull(deserialized?.Date); + Assert.Equal(testDate, deserialized.Date.Value); + } + +} From 8cc482830056f615deafcfc19aec4f791534680c Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:35:29 +0000 Subject: [PATCH 088/303] added create dateime attribute request --- .../Requests/Databases/CreateDatetimeAttribute.cs | 15 +++++++++++++++ .../CreateDatetimeAttributeValidator.cs | 14 ++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttribute.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatetimeAttributeValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttribute.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttribute.cs new file mode 100644 index 00000000..26e86c36 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttribute.cs @@ -0,0 +1,15 @@ +using System; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; +public class CreateDatetimeAttribute : CreateAttributeBaseRequest +{ + /// + /// Default value for the attribute in ISO 8601 format. Cannot be set when attribute is required + /// + [JsonPropertyName("default")] + [JsonConverter(typeof(NullableDateTimeConverter))] + public DateTime? Default { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatetimeAttributeValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatetimeAttributeValidator.cs new file mode 100644 index 00000000..15d98888 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatetimeAttributeValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateDatetimeAttributeValidator : AbstractValidator +{ + public CreateDatetimeAttributeValidator() + { + Include(new CreateAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .Must((request, defaultValue) => !request.Required || defaultValue == null) + .WithMessage("Default value cannot be set when attribute is required."); + } +} From c4526276bfc26d57c6d4bd099b29ba8f2bc8dc0a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:39:50 +0000 Subject: [PATCH 089/303] added tests for create dateime attribute --- .../Databases/CreateDatetimeAttributeTests.cs | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatetimeAttributeTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatetimeAttributeTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatetimeAttributeTests.cs new file mode 100644 index 00000000..8b72ceea --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatetimeAttributeTests.cs @@ -0,0 +1,120 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateDatetimeAttributeTests : CreateAttributeBaseRequestTests +{ + protected override CreateBooleanAttributeRequest CreateValidCreateAttributeBaseRequest => new(); + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new CreateDatetimeAttribute(); + + // Assert + Assert.Null(request.Default); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var defaultValue = DateTime.UtcNow; + + var request = new CreateDatetimeAttribute(); + + // Act + request.Default = defaultValue; + + // Assert + Assert.Equal(defaultValue, request.Default); + } + + public static TheoryData ValidRequestsData = new() + { + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = null, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = DateTime.UtcNow, + Required = false + } + }; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(CreateDatetimeAttribute request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData = new() + { + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = DateTime.UtcNow, + Required = true + } + }; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(CreateDatetimeAttribute request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new CreateDatetimeAttribute + { + Default = DateTime.UtcNow, + Required = true + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new CreateDatetimeAttribute + { + Default = DateTime.UtcNow, + Required = true + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 0eb2023e55f07af0ce436572b3a43b105fe9bed6 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:46:01 +0000 Subject: [PATCH 090/303] added update datetime atttribute request --- .../Databases/UpdateDatetimeAttributeRequest.cs | 16 ++++++++++++++++ .../UpdateDatetimeAttributeRequestValidator.cs | 14 ++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatetimeAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatetimeAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatetimeAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatetimeAttributeRequest.cs new file mode 100644 index 00000000..b2a84f10 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatetimeAttributeRequest.cs @@ -0,0 +1,16 @@ +using System; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; +public class UpdateDatetimeAttributeRequest : UpdateAttributeBaseRequest +{ + /// + /// Default value for attribute when not provided. Cannot be set when attribute is required + /// + [JsonPropertyName("default")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [JsonConverter(typeof(AlwaysWriteNullableDateTimeConverter))] + public DateTime? Default { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatetimeAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatetimeAttributeRequestValidator.cs new file mode 100644 index 00000000..d2c1d4da --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatetimeAttributeRequestValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateDatetimeAttributeRequestValidator : AbstractValidator +{ + public UpdateDatetimeAttributeRequestValidator() + { + Include(new UpdateAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .Must((request, defaultValue) => !request.Required || defaultValue == null) + .WithMessage("Default value cannot be set when attribute is required."); + } +} From c178f176d4746c27dd8d6d361159a61b78d7c9e6 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 01:46:16 +0000 Subject: [PATCH 091/303] added tests for update attribute request --- .../UpdateDatetimeAttributeRequestTests.cs | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatetimeAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatetimeAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatetimeAttributeRequestTests.cs new file mode 100644 index 00000000..58d4ea5d --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatetimeAttributeRequestTests.cs @@ -0,0 +1,120 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateDatetimeAttributeRequestTests : UpdateAttributeBaseRequestTests +{ + protected override UpdateDatetimeAttributeRequest CreateValidUpdateAttributeBaseRequest => new(); + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new UpdateDatetimeAttributeRequest(); + + // Assert + Assert.Null(request.Default); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var defaultValue = DateTime.UtcNow; + + var request = new UpdateDatetimeAttributeRequest(); + + // Act + request.Default = defaultValue; + + // Assert + Assert.Equal(defaultValue, request.Default); + } + + public static TheoryData ValidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = null, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = DateTime.UtcNow, + Required = false + } + ]; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(UpdateDatetimeAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = DateTime.UtcNow, + Required = true + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(UpdateDatetimeAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new UpdateDatetimeAttributeRequest + { + Default = DateTime.UtcNow, + Required = true + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new UpdateDatetimeAttributeRequest + { + Default = DateTime.UtcNow, + Required = true + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 264a9f276f34bced8b90601218e67acedffcb944 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 13:26:30 +0000 Subject: [PATCH 092/303] added create string attribute base request --- .../CreateStringAttributeBaseRequest.cs | 20 +++++++++++++++++++ ...eateStringAttributeBaseRequestValidator.cs | 16 +++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateStringAttributeBaseRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateStringAttributeBaseRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateStringAttributeBaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateStringAttributeBaseRequest.cs new file mode 100644 index 00000000..afad736c --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateStringAttributeBaseRequest.cs @@ -0,0 +1,20 @@ +using System.Text.Json.Serialization; +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The base request for any string based attribute +/// +/// The request type +/// The request validator type +public abstract class CreateStringAttributeBaseRequest : CreateAttributeBaseRequest + where TRequest : class + where TValidator : IValidator, new() +{ + /// + /// Default value for attribute when not provided. Cannot be set when attribute is required + /// + [JsonPropertyName("default")] + public string? Default { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateStringAttributeBaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateStringAttributeBaseRequestValidator.cs new file mode 100644 index 00000000..7e30d2c2 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateStringAttributeBaseRequestValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateStringAttributeBaseRequestValidator : AbstractValidator> + where TRequest : class + where TValidator : IValidator, new() +{ + public CreateStringAttributeBaseRequestValidator() + { + Include(new CreateAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .Must((request, defaultValue) => !request.Required || defaultValue is null) + .WithMessage("Default value cannot be set when attribute is required."); + } +} From b2d2245042a1ba7795465b73ae42c9e15aade690 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 13:36:33 +0000 Subject: [PATCH 093/303] added tests for create string attribute base request --- .../CreateBooleanAttributeRequestTests.cs | 6 +- .../CreateStringAttributeBaseRequestTests.cs | 105 ++++++++++++++++++ 2 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeBaseRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateBooleanAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateBooleanAttributeRequestTests.cs index 0056b38b..a82e4912 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateBooleanAttributeRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateBooleanAttributeRequestTests.cs @@ -32,8 +32,8 @@ public void Properties_CanBeSet() Assert.Equal(defaultValue, request.Default); } - public static TheoryData ValidRequestsData = new() - { + public static TheoryData ValidRequestsData => + [ new() { DatabaseId = IdUtils.GenerateUniqueId(), @@ -58,7 +58,7 @@ public void Properties_CanBeSet() Default = false, Required = false } - }; + ]; [Theory] [MemberData(nameof(ValidRequestsData))] diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeBaseRequestTests.cs new file mode 100644 index 00000000..459d9dd3 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeBaseRequestTests.cs @@ -0,0 +1,105 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public abstract class CreateStringAttributeBaseRequestTests : CreateAttributeBaseRequestTests + where TRequest : CreateStringAttributeBaseRequest + where TValidator : AbstractValidator, new() +{ + protected sealed override TRequest CreateValidCreateAttributeBaseRequest => CreateValidCreateStringAttributeBaseRequest; + + protected abstract TRequest CreateValidCreateStringAttributeBaseRequest { get; } + + [Fact] + public void CreateStringAttributeBase_Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = CreateValidCreateStringAttributeBaseRequest; + + // Assert + Assert.Null(request.Default); + } + + [Fact] + public void CreateStringAttributeBase_Properties_CanBeSet() + { + // Arrange + var defaultValue = "validKey"; + var request = CreateValidCreateStringAttributeBaseRequest; + + // Act + request.Default = defaultValue; + + // Assert + Assert.Equal(defaultValue, request.Default); + } + + [Fact] + public void CreateStringAttributeBase_IsValid_WithValidData_ReturnsTrue() + { + // Arrange + var request = CreateValidCreateStringAttributeBaseRequest; + request.DatabaseId = "valid_Team-Id."; + request.CollectionId = "valid_Team-Id."; + request.Key = "validKey"; + request.Default = null; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Fact] + public void CreateStringAttributeBase_IsValid_WithInvalidData_ReturnsFalse() + { + // Arrange + var request = CreateValidCreateStringAttributeBaseRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = "validKey"; + request.Required = true; + request.Default = ""; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void CreateStringAttributeBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = CreateValidCreateStringAttributeBaseRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = "validKey"; + request.Required = true; + request.Default = ""; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void CreateStringAttributeBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = CreateValidCreateStringAttributeBaseRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = "validKey"; + request.Required = true; + request.Default = ""; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 512d9551913c41277a4b6250d3ad300244b7a270 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 13:42:29 +0000 Subject: [PATCH 094/303] added base request for updating string based attributes --- .../UpdateStringAttributeBaseRequest.cs | 21 +++++++++++++++++++ ...dateStringAttributeBaseRequestValidator.cs | 16 ++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateStringAttributeBaseRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateStringAttributeBaseRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateStringAttributeBaseRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateStringAttributeBaseRequest.cs new file mode 100644 index 00000000..8093d219 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateStringAttributeBaseRequest.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The base request for any string based attribute +/// +/// The request type +/// The request validator type +public abstract class UpdateStringAttributeBaseRequest : UpdateAttributeBaseRequest + where TRequest : class + where TValidator : IValidator, new() +{ + /// + /// Default value for attribute when not provided. Cannot be set when attribute is required + /// + [JsonPropertyName("default")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public string? Default { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateStringAttributeBaseRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateStringAttributeBaseRequestValidator.cs new file mode 100644 index 00000000..7ecedabd --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateStringAttributeBaseRequestValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateStringAttributeBaseRequestValidator : AbstractValidator> + where TRequest : class + where TValidator : IValidator, new() +{ + public UpdateStringAttributeBaseRequestValidator() + { + Include(new UpdateAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .Must((request, defaultValue) => !request.Required || defaultValue is null) + .WithMessage("Default value cannot be set when attribute is required."); + } +} From d367316cb01cf7791ae8f2ca23129a4bb26194a6 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 13:49:22 +0000 Subject: [PATCH 095/303] added tests for update string base attribute request --- .../UpdateAttributeBaseRequestTests.cs | 2 +- .../UpdateStringAttributeBaseRequestTests.cs | 105 ++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateStringAttributeBaseRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateAttributeBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateAttributeBaseRequestTests.cs index 3766b409..c13f3fb7 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateAttributeBaseRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateAttributeBaseRequestTests.cs @@ -7,7 +7,7 @@ public abstract class UpdateAttributeBaseRequestTests : Da where TRequest : UpdateAttributeBaseRequest where TValidator : AbstractValidator, new() { - protected override TRequest CreateValidDatabaseCollectionIdAttributeKeyRequest => CreateValidUpdateAttributeBaseRequest; + protected sealed override TRequest CreateValidDatabaseCollectionIdAttributeKeyRequest => CreateValidUpdateAttributeBaseRequest; protected abstract TRequest CreateValidUpdateAttributeBaseRequest { get; } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateStringAttributeBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateStringAttributeBaseRequestTests.cs new file mode 100644 index 00000000..497a9f02 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateStringAttributeBaseRequestTests.cs @@ -0,0 +1,105 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public abstract class UpdateStringAttributeBaseRequestTests : UpdateAttributeBaseRequestTests + where TRequest : UpdateStringAttributeBaseRequest + where TValidator : AbstractValidator, new() +{ + protected sealed override TRequest CreateValidUpdateAttributeBaseRequest => CreateValidUpdateStringAttributeBaseRequest; + + protected abstract TRequest CreateValidUpdateStringAttributeBaseRequest { get; } + + [Fact] + public void UpdateStringAttributeBase_Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = CreateValidUpdateStringAttributeBaseRequest; + + // Assert + Assert.Null(request.Default); + } + + [Fact] + public void UpdateStringAttributeBase_Properties_CanBeSet() + { + // Arrange + var defaultValue = "validKey"; + var request = CreateValidUpdateStringAttributeBaseRequest; + + // Act + request.Default = defaultValue; + + // Assert + Assert.Equal(defaultValue, request.Default); + } + + [Fact] + public void UpdateStringAttributeBase_IsValid_WithValidData_ReturnsTrue() + { + // Arrange + var request = CreateValidUpdateStringAttributeBaseRequest; + request.DatabaseId = "valid_Team-Id."; + request.CollectionId = "valid_Team-Id."; + request.Key = "validKey"; + request.Default = null; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Fact] + public void UpdateStringAttributeBase_IsValid_WithInvalidData_ReturnsFalse() + { + // Arrange + var request = CreateValidUpdateStringAttributeBaseRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = "validKey"; + request.Required = true; + request.Default = ""; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void UpdateStringAttributeBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = CreateValidUpdateStringAttributeBaseRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = "validKey"; + request.Required = true; + request.Default = ""; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void UpdateStringAttributeBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = CreateValidUpdateStringAttributeBaseRequest; + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = "validKey"; + request.Required = true; + request.Default = ""; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 522474fd374e1a2786f3886dd139931270b08d6a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 13:53:26 +0000 Subject: [PATCH 096/303] added create email attribute request --- .../Databases/CreateEmailAttributeRequest.cs | 10 ++++++++++ .../CreateEmailAttributeRequestValidator.cs | 14 ++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateEmailAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateEmailAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateEmailAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateEmailAttributeRequest.cs new file mode 100644 index 00000000..c6330eda --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateEmailAttributeRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create an email attribute +/// +public class CreateEmailAttributeRequest : CreateStringAttributeBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateEmailAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateEmailAttributeRequestValidator.cs new file mode 100644 index 00000000..8cfa714e --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateEmailAttributeRequestValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateEmailAttributeRequestValidator : AbstractValidator +{ + public CreateEmailAttributeRequestValidator() + { + Include(new CreateStringAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .EmailAddress() + .WithMessage("Default should be formatted as an email address"); + } +} From e533b9e1f4341849ff25bcca8d686b7f5734de7b Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 14:02:41 +0000 Subject: [PATCH 097/303] added tests for create email attribute request --- .../CreateEmailAttributeRequestTests.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateEmailAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateEmailAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateEmailAttributeRequestTests.cs new file mode 100644 index 00000000..8dac58ee --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateEmailAttributeRequestTests.cs @@ -0,0 +1,62 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateEmailAttributeRequestTests : CreateStringAttributeBaseRequestTests +{ + protected override CreateEmailAttributeRequest CreateValidCreateStringAttributeBaseRequest => new(); + + public static TheoryData ValidDefaultValues => + [ + "pingu@example.com", + "ugnip@mydomain.co.uk" + ]; + + [Theory] + [MemberData(nameof(ValidDefaultValues))] + public void IsValid_WithValidDefaults_ReturnsTrue(string defaultValue) + { + // Arrange + var request = new CreateEmailAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = defaultValue + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidDefaultValues => + [ + "not an email address", + "something at something else dot com", + "@!.d" + ]; + + [Theory] + [MemberData(nameof(InvalidDefaultValues))] + public void IsValid_WithInvalidDefaults_ReturnsFalse(string defaultValue) + { + // Arrange + var request = new CreateEmailAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = defaultValue + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } +} From 52b8a3cce097ae6ed6fa98f2b11a47c37a56c901 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 14:06:25 +0000 Subject: [PATCH 098/303] added update email attribute request --- .../Databases/UpdateEmailAttributeRequest.cs | 10 ++++++++++ .../UpdateEmailAttributeRequestValidator.cs | 14 ++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateEmailAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateEmailAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateEmailAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateEmailAttributeRequest.cs new file mode 100644 index 00000000..31496c9c --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateEmailAttributeRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to update an email attribute +/// +public class UpdateEmailAttributeRequest : UpdateStringAttributeBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateEmailAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateEmailAttributeRequestValidator.cs new file mode 100644 index 00000000..b9591e1c --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateEmailAttributeRequestValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateEmailAttributeRequestValidator : AbstractValidator +{ + public UpdateEmailAttributeRequestValidator() + { + Include(new UpdateStringAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .EmailAddress() + .WithMessage("Default should be formatted as an email address"); + } +} From b1f1605150023cc83d1d399703ed3959977f3b04 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 14:09:17 +0000 Subject: [PATCH 099/303] added tests for update email attribute request --- .../UpdateEmailAttributeRequestTests.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateEmailAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateEmailAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateEmailAttributeRequestTests.cs new file mode 100644 index 00000000..d8b823b8 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateEmailAttributeRequestTests.cs @@ -0,0 +1,62 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateEmailAttributeRequestTests : UpdateStringAttributeBaseRequestTests +{ + protected override UpdateEmailAttributeRequest CreateValidUpdateStringAttributeBaseRequest => new(); + + public static TheoryData ValidDefaultValues => + [ + "pingu@example.com", + "ugnip@mydomain.co.uk" + ]; + + [Theory] + [MemberData(nameof(ValidDefaultValues))] + public void IsValid_WithValidDefaults_ReturnsTrue(string defaultValue) + { + // Arrange + var request = new UpdateEmailAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = defaultValue + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidDefaultValues => + [ + "not an email address", + "something at something else dot com", + "@!.d" + ]; + + [Theory] + [MemberData(nameof(InvalidDefaultValues))] + public void IsValid_WithInvalidDefaults_ReturnsFalse(string defaultValue) + { + // Arrange + var request = new UpdateEmailAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = defaultValue + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } +} From 1ddfcf05048499aca7ffcc768d67cf764eabe59b Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 14:14:35 +0000 Subject: [PATCH 100/303] Adjusted create string attribute base tests to take valid default value --- .../Databases/CreateEmailAttributeRequestTests.cs | 2 ++ .../Databases/CreateStringAttributeBaseRequestTests.cs | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateEmailAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateEmailAttributeRequestTests.cs index 8dac58ee..9d725ee8 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateEmailAttributeRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateEmailAttributeRequestTests.cs @@ -7,6 +7,8 @@ public class CreateEmailAttributeRequestTests : CreateStringAttributeBaseRequest { protected override CreateEmailAttributeRequest CreateValidCreateStringAttributeBaseRequest => new(); + protected override string ValidDefaultValue => "pingu@example.com"; + public static TheoryData ValidDefaultValues => [ "pingu@example.com", diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeBaseRequestTests.cs index 459d9dd3..7d3b6ff8 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeBaseRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeBaseRequestTests.cs @@ -11,6 +11,8 @@ public abstract class CreateStringAttributeBaseRequestTests(() => request.Validate(true)); @@ -94,7 +96,7 @@ public void CreateStringAttributeBase_Validate_WithThrowOnFailuresFalse_ReturnsI request.CollectionId = IdUtils.GenerateUniqueId(); request.Key = "validKey"; request.Required = true; - request.Default = ""; + request.Default = ValidDefaultValue; // Act var result = request.Validate(false); From 81651a547afad916206e337e9956273c7c41b82a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 14:16:04 +0000 Subject: [PATCH 101/303] updated update string base request tests to take a valid default value also --- .../Databases/UpdateEmailAttributeRequestTests.cs | 2 ++ .../Databases/UpdateStringAttributeBaseRequestTests.cs | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateEmailAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateEmailAttributeRequestTests.cs index d8b823b8..8019ba61 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateEmailAttributeRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateEmailAttributeRequestTests.cs @@ -7,6 +7,8 @@ public class UpdateEmailAttributeRequestTests : UpdateStringAttributeBaseRequest { protected override UpdateEmailAttributeRequest CreateValidUpdateStringAttributeBaseRequest => new(); + protected override string ValidDefaultValue => "pingu@example.com"; + public static TheoryData ValidDefaultValues => [ "pingu@example.com", diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateStringAttributeBaseRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateStringAttributeBaseRequestTests.cs index 497a9f02..ee9e3781 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateStringAttributeBaseRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateStringAttributeBaseRequestTests.cs @@ -11,6 +11,8 @@ public abstract class UpdateStringAttributeBaseRequestTests(() => request.Validate(true)); @@ -94,7 +96,7 @@ public void UpdateStringAttributeBase_Validate_WithThrowOnFailuresFalse_ReturnsI request.CollectionId = IdUtils.GenerateUniqueId(); request.Key = "validKey"; request.Required = true; - request.Default = ""; + request.Default = ValidDefaultValue; // Act var result = request.Validate(false); From 3e96febaddec8d1e8426f6cac342aa8daa3dbd1f Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 14:27:26 +0000 Subject: [PATCH 102/303] Added create enum attribute request --- .../Databases/CreateEnumAttributeRequest.cs | 17 +++++++++++ .../CreateEnumAttributeRequestValidator.cs | 28 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateEnumAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateEnumAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateEnumAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateEnumAttributeRequest.cs new file mode 100644 index 00000000..a1588a3c --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateEnumAttributeRequest.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create an enum attribute +/// +public class CreateEnumAttributeRequest : CreateStringAttributeBaseRequest +{ + /// + /// Array of elements in enumerated type. Uses length of longest element to determine size. Maximum of 100 elements are allowed, each 255 characters long + /// + [JsonPropertyName("elements")] + public List Elements { get; set; } = []; +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateEnumAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateEnumAttributeRequestValidator.cs new file mode 100644 index 00000000..0b605b2f --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateEnumAttributeRequestValidator.cs @@ -0,0 +1,28 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateEnumAttributeRequestValidator : AbstractValidator +{ + public CreateEnumAttributeRequestValidator() + { + Include(new CreateStringAttributeBaseRequestValidator()); + + RuleFor(x => x.Elements) + .NotNull() + .WithMessage("Elements is required.") + .Must(x => x.Count <= 100) + .WithMessage("A maximum of 100 elements are allowed.") + .ForEach(x => + { + x + .NotEmpty() + .WithMessage("Element cannot be empty.") + .MaximumLength(255) + .WithMessage("Element cannot be longer than 255 characters"); + }); + + RuleFor(x => x.Default) + .Must((x, defaultValue) => defaultValue is null || x.Elements.Contains(defaultValue)) + .WithMessage("Default must either be null or match an existing element."); + } +} From 791d85d70223e71cd4066405dec8f719ec7a92cb Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 14:39:55 +0000 Subject: [PATCH 103/303] Added tests for create enum attribute request --- .../CreateEnumAttributeRequestValidator.cs | 2 + .../CreateEnumAttributeRequestTests.cs | 99 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateEnumAttributeRequestTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateEnumAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateEnumAttributeRequestValidator.cs index 0b605b2f..ef769e6e 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateEnumAttributeRequestValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateEnumAttributeRequestValidator.cs @@ -10,6 +10,8 @@ public CreateEnumAttributeRequestValidator() RuleFor(x => x.Elements) .NotNull() .WithMessage("Elements is required.") + .Must(x => x.Count > 0) + .WithMessage("At least one element must be specified.") .Must(x => x.Count <= 100) .WithMessage("A maximum of 100 elements are allowed.") .ForEach(x => diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateEnumAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateEnumAttributeRequestTests.cs new file mode 100644 index 00000000..fc93636f --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateEnumAttributeRequestTests.cs @@ -0,0 +1,99 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateEnumAttributeRequestTests : CreateStringAttributeBaseRequestTests +{ + protected override CreateEnumAttributeRequest CreateValidCreateStringAttributeBaseRequest => new() + { + Elements = ["element1", "element2", "element3"] + }; + + protected override string ValidDefaultValue => "element2"; + + public static TheoryData ValidDefaultValues => + [ + "element1", + "element2", + "element3", + null + ]; + + [Theory] + [MemberData(nameof(ValidDefaultValues))] + public void IsValid_WithValidDefaults_ReturnsTrue(string? value) + { + // Arrange + var request = new CreateEnumAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Elements = ["element1", "element2", "element3"], + Default = value + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidDefaultValues => + [ + "element4", + "", + "not an existing element at all" + ]; + + [Theory] + [MemberData(nameof(InvalidDefaultValues))] + public void IsValid_WithInvalidDefaults_ReturnsTrue(string value) + { + // Arrange + var request = new CreateEnumAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Elements = ["element1", "element2", "element3"], + Default = value + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + public static TheoryData> InvalidElementsValues => + [ + [], + [new string('a', 256)], + Enumerable.Range(0,101).Select(x => x.ToString()).ToList(), + [""] + ]; + + [Theory] + [MemberData(nameof(InvalidElementsValues))] + public static void IsValid_WithInvalidElements_ReturnsFalse(List elements) + { + // Arrange + var request = new CreateEnumAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Elements = elements + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } +} From 445ec9a1ae718dfcaf01ff38392eea095d715070 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 17:03:10 +0000 Subject: [PATCH 104/303] added update enum attribute request --- .../Databases/UpdateEnumAttributeRequest.cs | 17 +++++++++++ .../UpdateEnumAttributeRequestValidator.cs | 30 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateEnumAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateEnumAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateEnumAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateEnumAttributeRequest.cs new file mode 100644 index 00000000..96337ce3 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateEnumAttributeRequest.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to update an enum attribute +/// +public class UpdateEnumAttributeRequest : UpdateStringAttributeBaseRequest +{ + /// + /// Array of elements in enumerated type. Uses length of longest element to determine size. Maximum of 100 elements are allowed, each 255 characters long + /// + [JsonPropertyName("elements")] + public List Elements { get; set; } = []; +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateEnumAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateEnumAttributeRequestValidator.cs new file mode 100644 index 00000000..43c07f92 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateEnumAttributeRequestValidator.cs @@ -0,0 +1,30 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateEnumAttributeRequestValidator : AbstractValidator +{ + public UpdateEnumAttributeRequestValidator() + { + Include(new UpdateStringAttributeBaseRequestValidator()); + + RuleFor(x => x.Elements) + .NotNull() + .WithMessage("Elements is required.") + .Must(x => x.Count > 0) + .WithMessage("At least one element must be specified.") + .Must(x => x.Count <= 100) + .WithMessage("A maximum of 100 elements are allowed.") + .ForEach(x => + { + x + .NotEmpty() + .WithMessage("Element cannot be empty.") + .MaximumLength(255) + .WithMessage("Element cannot be longer than 255 characters"); + }); + + RuleFor(x => x.Default) + .Must((x, defaultValue) => defaultValue is null || x.Elements.Contains(defaultValue)) + .WithMessage("Default must either be null or match an existing element."); + } +} From e2c69a903a0e27ec22f883da75b8610d9852c587 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 17:05:29 +0000 Subject: [PATCH 105/303] added tests for update enum attribute request --- .../UpdateEnumAttributeRequestTests.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateEnumAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateEnumAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateEnumAttributeRequestTests.cs new file mode 100644 index 00000000..e595bffa --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateEnumAttributeRequestTests.cs @@ -0,0 +1,99 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateEnumAttributeRequestTests : UpdateStringAttributeBaseRequestTests +{ + protected override UpdateEnumAttributeRequest CreateValidUpdateStringAttributeBaseRequest => new() + { + Elements = ["element1", "element2", "element3"] + }; + + protected override string ValidDefaultValue => "element2"; + + public static TheoryData ValidDefaultValues => + [ + "element1", + "element2", + "element3", + null + ]; + + [Theory] + [MemberData(nameof(ValidDefaultValues))] + public void IsValid_WithValidDefaults_ReturnsTrue(string? value) + { + // Arrange + var request = new UpdateEnumAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Elements = ["element1", "element2", "element3"], + Default = value + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidDefaultValues => + [ + "element4", + "", + "not an existing element at all" + ]; + + [Theory] + [MemberData(nameof(InvalidDefaultValues))] + public void IsValid_WithInvalidDefaults_ReturnsTrue(string value) + { + // Arrange + var request = new UpdateEnumAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Elements = ["element1", "element2", "element3"], + Default = value + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + public static TheoryData> InvalidElementsValues => + [ + [], + [new string('a', 256)], + Enumerable.Range(0,101).Select(x => x.ToString()).ToList(), + [""] + ]; + + [Theory] + [MemberData(nameof(InvalidElementsValues))] + public static void IsValid_WithInvalidElements_ReturnsFalse(List elements) + { + // Arrange + var request = new UpdateEnumAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Elements = elements + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } +} From e194c94d39888251df4907cb4e2900ce7f609813 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 17:20:33 +0000 Subject: [PATCH 106/303] Added CreateFloatAttributeRequest --- .../Databases/CreateFloatAttributeRequest.cs | 28 +++++++++++++++++++ .../CreateFloatAttributeRequestValidator.cs | 22 +++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateFloatAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateFloatAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateFloatAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateFloatAttributeRequest.cs new file mode 100644 index 00000000..b7bc6374 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateFloatAttributeRequest.cs @@ -0,0 +1,28 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create a float attribute +/// +public class CreateFloatAttributeRequest : CreateAttributeBaseRequest +{ + /// + /// Default value for attribute when not provided. Cannot be set when attribute is required + /// + [JsonPropertyName("default")] + public float? Default { get; set; } + + /// + /// Minimum value to enforce on new documents + /// + [JsonPropertyName("min")] + public float? Min { get; set; } + + /// + /// Maximum value to enforce on new documents + /// + [JsonPropertyName("max")] + public float? Max { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateFloatAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateFloatAttributeRequestValidator.cs new file mode 100644 index 00000000..5fae7361 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateFloatAttributeRequestValidator.cs @@ -0,0 +1,22 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateFloatAttributeRequestValidator : AbstractValidator +{ + public CreateFloatAttributeRequestValidator() + { + Include(new CreateAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .Must((request, defaultValue) => !request.Required || defaultValue is null) + .WithMessage("Default value cannot be set when attribute is required.") + .Must((request, defaultValue) => (request.Min is null || defaultValue is null) || defaultValue >= request.Min) + .WithMessage("Default cannot be a smaller value than Min.") + .Must((request, defaultValue) => (request.Max is null || defaultValue is null) || defaultValue <= request.Max) + .WithMessage("Default cannot be a larger value than Max."); + + RuleFor(x => x.Max) + .Must((request, max) => (request.Min is null || max is null) || max >= request.Min) + .WithMessage("Max can not be a lower value than Min."); + } +} From 25c80bcc2e365cc90c032c52cb1aca8ad4d6ba30 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 17:28:48 +0000 Subject: [PATCH 107/303] added tests for create float attribute request --- .../CreateFloatAttributeRequestTests.cs | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateFloatAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateFloatAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateFloatAttributeRequestTests.cs new file mode 100644 index 00000000..ac83ca76 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateFloatAttributeRequestTests.cs @@ -0,0 +1,174 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateFloatAttributeRequestTests : CreateAttributeBaseRequestTests +{ + protected override CreateFloatAttributeRequest CreateValidCreateAttributeBaseRequest => new(); + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new CreateFloatAttributeRequest(); + + // Assert + Assert.Null(request.Default); + Assert.Null(request.Min); + Assert.Null(request.Max); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var min = 1f; + var defaultValue = 5f; + var max = 10f; + + var request = new CreateFloatAttributeRequest(); + + // Act + request.Min = min; + request.Default = defaultValue; + request.Max = max; + + // Assert + Assert.Equal(min, request.Min); + Assert.Equal(defaultValue, request.Default); + Assert.Equal(max, request.Max); + } + + public static TheoryData ValidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = null, + Min = null, + Max = null, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 5, + Min = 0, + Max = 10, + Required = false + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = -10, + Min = null, + Max = null, + Required = false + } + ]; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(CreateFloatAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 0f, + Min = null, + Max = null, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 0f, + Min = 1f, + Max = null, + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 1f, + Min = null, + Max = 0f, + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = null, + Min = 1f, + Max = 0f, + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(CreateFloatAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new CreateFloatAttributeRequest + { + Default = 5f, + Required = true, + Min = 10f, + Max = 0f + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new CreateFloatAttributeRequest + { + Default = 5f, + Required = true, + Min = 10f, + Max = 0f + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 5af00904a8c526e98d4d2af259099782caba679d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 17:48:18 +0000 Subject: [PATCH 108/303] added update float attribute request --- .../Databases/UpdateFloatAttributeRequest.cs | 31 +++++++++++++++++++ .../UpdateFloatAttributeRequestValidator.cs | 22 +++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateFloatAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateFloatAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateFloatAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateFloatAttributeRequest.cs new file mode 100644 index 00000000..6483a089 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateFloatAttributeRequest.cs @@ -0,0 +1,31 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to update a float attribute +/// +public class UpdateFloatAttributeRequest : UpdateAttributeBaseRequest +{ + /// + /// Default value for attribute when not provided. Cannot be set when attribute is required + /// + [JsonPropertyName("default")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public float? Default { get; set; } + + /// + /// Minimum value to enforce on new documents + /// + [JsonPropertyName("min")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public float? Min { get; set; } + + /// + /// Maximum value to enforce on new documents + /// + [JsonPropertyName("max")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public float? Max { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateFloatAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateFloatAttributeRequestValidator.cs new file mode 100644 index 00000000..91338e87 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateFloatAttributeRequestValidator.cs @@ -0,0 +1,22 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateFloatAttributeRequestValidator : AbstractValidator +{ + public UpdateFloatAttributeRequestValidator() + { + Include(new UpdateAttributeBaseRequestValidator); + + RuleFor(x => x.Default) + .Must((request, defaultValue) => !request.Required || defaultValue is null) + .WithMessage("Default value cannot be set when attribute is required.") + .Must((request, defaultValue) => (request.Min is null || defaultValue is null) || defaultValue >= request.Min) + .WithMessage("Default cannot be a smaller value than Min.") + .Must((request, defaultValue) => (request.Max is null || defaultValue is null) || defaultValue <= request.Max) + .WithMessage("Default cannot be a larger value than Max."); + + RuleFor(x => x.Max) + .Must((request, max) => (request.Min is null || max is null) || max >= request.Min) + .WithMessage("Max can not be a lower value than Min."); + } +} From 5d94c17eeea7da012c40a9778ed929f9ef1cf8fc Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 17:52:23 +0000 Subject: [PATCH 109/303] added tests for update float attribute request --- .../UpdateFloatAttributeRequestValidator.cs | 2 +- .../CreateFloatAttributeRequestTests.cs | 1 + .../UpdateFloatAttributeRequestTests.cs | 175 ++++++++++++++++++ 3 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateFloatAttributeRequestTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateFloatAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateFloatAttributeRequestValidator.cs index 91338e87..52bfd65d 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateFloatAttributeRequestValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateFloatAttributeRequestValidator.cs @@ -5,7 +5,7 @@ public class UpdateFloatAttributeRequestValidator : AbstractValidator); + Include(new UpdateAttributeBaseRequestValidator()); RuleFor(x => x.Default) .Must((request, defaultValue) => !request.Required || defaultValue is null) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateFloatAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateFloatAttributeRequestTests.cs index ac83ca76..a790d3d1 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateFloatAttributeRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateFloatAttributeRequestTests.cs @@ -7,6 +7,7 @@ namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; public class CreateFloatAttributeRequestTests : CreateAttributeBaseRequestTests { protected override CreateFloatAttributeRequest CreateValidCreateAttributeBaseRequest => new(); + [Fact] public void Constructor_InitializesWithExpectedValues() { diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateFloatAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateFloatAttributeRequestTests.cs new file mode 100644 index 00000000..8b649a25 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateFloatAttributeRequestTests.cs @@ -0,0 +1,175 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateFloatAttributeRequestTests : UpdateAttributeBaseRequestTests +{ + protected override UpdateFloatAttributeRequest CreateValidUpdateAttributeBaseRequest => new(); + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new UpdateFloatAttributeRequest(); + + // Assert + Assert.Null(request.Default); + Assert.Null(request.Min); + Assert.Null(request.Max); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var min = 1f; + var defaultValue = 5f; + var max = 10f; + + var request = new UpdateFloatAttributeRequest(); + + // Act + request.Min = min; + request.Default = defaultValue; + request.Max = max; + + // Assert + Assert.Equal(min, request.Min); + Assert.Equal(defaultValue, request.Default); + Assert.Equal(max, request.Max); + } + + public static TheoryData ValidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = null, + Min = null, + Max = null, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 5, + Min = 0, + Max = 10, + Required = false + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = -10, + Min = null, + Max = null, + Required = false + } + ]; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(UpdateFloatAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 0f, + Min = null, + Max = null, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 0f, + Min = 1f, + Max = null, + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 1f, + Min = null, + Max = 0f, + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = null, + Min = 1f, + Max = 0f, + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(UpdateFloatAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new UpdateFloatAttributeRequest + { + Default = 5f, + Required = true, + Min = 10f, + Max = 0f + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new UpdateFloatAttributeRequest + { + Default = 5f, + Required = true, + Min = 10f, + Max = 0f + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 079b3239ac75879ab34fa3d795c48caee4ec7e81 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 18:09:28 +0000 Subject: [PATCH 110/303] added create integer attribute request --- .../CreateIntegerAttributeRequest.cs | 28 +++++++++++++++++++ .../CreateIntegerAttributeRequestValidator.cs | 22 +++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIntegerAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIntegerAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIntegerAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIntegerAttributeRequest.cs new file mode 100644 index 00000000..ea8c1d91 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIntegerAttributeRequest.cs @@ -0,0 +1,28 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create an integer attribute +/// +public class CreateIntegerAttributeRequest : CreateAttributeBaseRequest +{ + /// + /// Default value for attribute when not provided. Cannot be set when attribute is required + /// + [JsonPropertyName("default")] + public int? Default { get; set; } + + /// + /// Minimum value to enforce on new documents + /// + [JsonPropertyName("min")] + public int? Min { get; set; } + + /// + /// Maximum value to enforce on new documents + /// + [JsonPropertyName("max")] + public int? Max { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIntegerAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIntegerAttributeRequestValidator.cs new file mode 100644 index 00000000..d957b02f --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIntegerAttributeRequestValidator.cs @@ -0,0 +1,22 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateIntegerAttributeRequestValidator : AbstractValidator +{ + public CreateIntegerAttributeRequestValidator() + { + Include(new CreateAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .Must((request, defaultValue) => !request.Required || defaultValue is null) + .WithMessage("Default value cannot be set when attribute is required.") + .Must((request, defaultValue) => (request.Min is null || defaultValue is null) || defaultValue >= request.Min) + .WithMessage("Default cannot be a smaller value than Min.") + .Must((request, defaultValue) => (request.Max is null || defaultValue is null) || defaultValue <= request.Max) + .WithMessage("Default cannot be a larger value than Max."); + + RuleFor(x => x.Max) + .Must((request, max) => (request.Min is null || max is null) || max >= request.Min) + .WithMessage("Max can not be a lower value than Min."); + } +} From d0e33b2dbb480fb72c3fb8fa99e28dea8c1dde35 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 18:12:09 +0000 Subject: [PATCH 111/303] added tests for create integer attribute request --- .../CreateIntegerAttributeRequestTests.cs | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIntegerAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIntegerAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIntegerAttributeRequestTests.cs new file mode 100644 index 00000000..3149cba0 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIntegerAttributeRequestTests.cs @@ -0,0 +1,175 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateIntegerAttributeRequestTests : CreateAttributeBaseRequestTests +{ + protected override CreateIntegerAttributeRequest CreateValidCreateAttributeBaseRequest => new(); + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new CreateIntegerAttributeRequest(); + + // Assert + Assert.Null(request.Default); + Assert.Null(request.Min); + Assert.Null(request.Max); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var min = 1; + var defaultValue = 5; + var max = 10; + + var request = new CreateIntegerAttributeRequest(); + + // Act + request.Min = min; + request.Default = defaultValue; + request.Max = max; + + // Assert + Assert.Equal(min, request.Min); + Assert.Equal(defaultValue, request.Default); + Assert.Equal(max, request.Max); + } + + public static TheoryData ValidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = null, + Min = null, + Max = null, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 5, + Min = 0, + Max = 10, + Required = false + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = -10, + Min = null, + Max = null, + Required = false + } + ]; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(CreateIntegerAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 0, + Min = null, + Max = null, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 0, + Min = 1, + Max = null, + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 1, + Min = null, + Max = 0, + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = null, + Min = 1, + Max = 0, + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(CreateIntegerAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new CreateIntegerAttributeRequest + { + Default = 5, + Required = true, + Min = 10, + Max = 0 + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new CreateIntegerAttributeRequest + { + Default = 5, + Required = true, + Min = 10, + Max = 0 + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From ea541359c4275765da7f82e76e655af891fe80b2 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 18:16:04 +0000 Subject: [PATCH 112/303] added update integer attribute request --- .../UpdateIntegerAttributeRequest.cs | 27 +++++++++++++++++++ .../UpdateIntegerAttributeRequestValidator.cs | 22 +++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIntegerAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIntegerAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIntegerAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIntegerAttributeRequest.cs new file mode 100644 index 00000000..199a43ec --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIntegerAttributeRequest.cs @@ -0,0 +1,27 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; +public class UpdateIntegerAttributeRequest : UpdateAttributeBaseRequest +{ + /// + /// Default value for attribute when not provided. Cannot be set when attribute is required + /// + [JsonPropertyName("default")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public int? Default { get; set; } + + /// + /// Minimum value to enforce on new documents + /// + [JsonPropertyName("min")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public int? Min { get; set; } + + /// + /// Maximum value to enforce on new documents + /// + [JsonPropertyName("max")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public int? Max { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIntegerAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIntegerAttributeRequestValidator.cs new file mode 100644 index 00000000..37c0ac7a --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIntegerAttributeRequestValidator.cs @@ -0,0 +1,22 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateIntegerAttributeRequestValidator : AbstractValidator +{ + public UpdateIntegerAttributeRequestValidator() + { + Include(new UpdateAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .Must((request, defaultValue) => !request.Required || defaultValue is null) + .WithMessage("Default value cannot be set when attribute is required.") + .Must((request, defaultValue) => (request.Min is null || defaultValue is null) || defaultValue >= request.Min) + .WithMessage("Default cannot be a smaller value than Min.") + .Must((request, defaultValue) => (request.Max is null || defaultValue is null) || defaultValue <= request.Max) + .WithMessage("Default cannot be a larger value than Max."); + + RuleFor(x => x.Max) + .Must((request, max) => (request.Min is null || max is null) || max >= request.Min) + .WithMessage("Max can not be a lower value than Min."); + } +} From 30fa7bfbe03387f98c1bb0f5f6a29663a327acd4 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 18:18:52 +0000 Subject: [PATCH 113/303] added tests for update integer attribute request --- .../UpdateIntegerAttributeRequestTests.cs | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIntegerAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIntegerAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIntegerAttributeRequestTests.cs new file mode 100644 index 00000000..0d7ef447 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIntegerAttributeRequestTests.cs @@ -0,0 +1,175 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateIntegerAttributeRequestTests : UpdateAttributeBaseRequestTests +{ + protected override UpdateIntegerAttributeRequest CreateValidUpdateAttributeBaseRequest => new(); + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new UpdateIntegerAttributeRequest(); + + // Assert + Assert.Null(request.Default); + Assert.Null(request.Min); + Assert.Null(request.Max); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var min = 1; + var defaultValue = 5; + var max = 10; + + var request = new UpdateIntegerAttributeRequest(); + + // Act + request.Min = min; + request.Default = defaultValue; + request.Max = max; + + // Assert + Assert.Equal(min, request.Min); + Assert.Equal(defaultValue, request.Default); + Assert.Equal(max, request.Max); + } + + public static TheoryData ValidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = null, + Min = null, + Max = null, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 5, + Min = 0, + Max = 10, + Required = false + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = -10, + Min = null, + Max = null, + Required = false + } + ]; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(UpdateIntegerAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 0, + Min = null, + Max = null, + Required = true + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 0, + Min = 1, + Max = null, + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = 1, + Min = null, + Max = 0, + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = IdUtils.GenerateUniqueId(), + Default = null, + Min = 1, + Max = 0, + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(UpdateIntegerAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new UpdateIntegerAttributeRequest + { + Default = 5, + Required = true, + Min = 10, + Max = 0 + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new UpdateIntegerAttributeRequest + { + Default = 5, + Required = true, + Min = 10, + Max = 0 + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 7a1aab27d1c353f2a114edf50d6344a49ace04b7 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 18:26:38 +0000 Subject: [PATCH 114/303] Added create IP Address attreibute request --- .../Requests/Databases/CreateIPAddressRequest.cs | 10 ++++++++++ .../Validators/CreateIPAddressRequestValidator.cs | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIPAddressRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAddressRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIPAddressRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIPAddressRequest.cs new file mode 100644 index 00000000..ecb567b9 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIPAddressRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create an IP Address attribute +/// +public class CreateIPAddressRequest : CreateStringAttributeBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAddressRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAddressRequestValidator.cs new file mode 100644 index 00000000..4108443b --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAddressRequestValidator.cs @@ -0,0 +1,10 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateIPAddressRequestValidator : AbstractValidator +{ + public CreateIPAddressRequestValidator() + { + Include(new CreateStringAttributeBaseRequestValidator()); + } +} From 60a4ec46edaef626cd8ae02bc099594034ff8758 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 27 Oct 2024 19:16:20 +0000 Subject: [PATCH 115/303] Added tests for create ip address request --- .../CreateIPAddressRequestValidator.cs | 27 ++++++- .../Databases/CreateIPAddressRequestTests.cs | 81 +++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAddressRequestTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAddressRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAddressRequestValidator.cs index 4108443b..09774268 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAddressRequestValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAddressRequestValidator.cs @@ -1,4 +1,6 @@ -using FluentValidation; +using System.Net; +using System.Net.Sockets; +using FluentValidation; namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; public class CreateIPAddressRequestValidator : AbstractValidator @@ -6,5 +8,28 @@ public class CreateIPAddressRequestValidator : AbstractValidator()); + + RuleFor(x => x.Default) + .NotEmpty() + .When(x => x.Default is not null, ApplyConditionTo.CurrentValidator) + .WithMessage("Default must not be an empty string.") + .Must(BeValidIpAddress) + .WithMessage("Default is not a valid IP Address format."); + } + + private bool BeValidIpAddress(string? ipAddress) + { + if (string.IsNullOrEmpty(ipAddress)) + return true; + + // Try parsing as IP address (supports both IPv4 and IPv6) + if (IPAddress.TryParse(ipAddress, out var parsedIp)) + { + // Accept both IPv4 and IPv6 + return parsedIp.AddressFamily == AddressFamily.InterNetwork || + parsedIp.AddressFamily == AddressFamily.InterNetworkV6; + } + + return false; } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAddressRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAddressRequestTests.cs new file mode 100644 index 00000000..8d8279bb --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAddressRequestTests.cs @@ -0,0 +1,81 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateIPAddressRequestTests : CreateStringAttributeBaseRequestTests +{ + protected override CreateIPAddressRequest CreateValidCreateStringAttributeBaseRequest => new(); + + protected override string ValidDefaultValue => "192.168.1.1"; + + public static TheoryData ValidDefaultValues => + [ + "192.0.2.1", + "192.168.1.1", + "2001:db8::1", + "2001:db8:85a3::8a2e:370:7334", + "::1", + "fe80::1" + ]; + + [Theory] + [MemberData(nameof(ValidDefaultValues))] + public void IsValid_WithValidDefaults_ReturnsTrue(string defaultValue) + { + // Arrange + var request = new CreateIPAddressRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = defaultValue + }; + + // Act + var isValid = request.IsValid(); + + // TODO: Delete + var DELETE = request.Validate(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidDefaultValues => + [ + "256.256.256.256", + "192.168.1.1.1", + "192.168.1.", + ".192.168.1.1", + "192.168.1.1/24", + "192.168.1.x", + "192.168.1.*", + "abcd", + "", + "2001:db8::::1", + "2001:db8", + "2001:db8::/32", + "2001:dg8::1" + ]; + + [Theory] + [MemberData(nameof(InvalidDefaultValues))] + public void IsValid_WithInvalidDefaults_ReturnsFalse(string defaultValue) + { + // Arrange + var request = new CreateIPAddressRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = defaultValue + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } +} From f8ca503e6199143c3b90dc7508f6509676a8f97d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 31 Oct 2024 16:40:32 +0000 Subject: [PATCH 116/303] added update ip address attribute request --- .../UpdateIPAddressAttributeRequest.cs | 10 ++++++ ...pdateIPAddressAttributeRequestValidator.cs | 35 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIPAddressAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIPAddressAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIPAddressAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIPAddressAttributeRequest.cs new file mode 100644 index 00000000..f945a50c --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIPAddressAttributeRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The requset to update an up address attribute +/// +public class UpdateIPAddressAttributeRequest : UpdateStringAttributeBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIPAddressAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIPAddressAttributeRequestValidator.cs new file mode 100644 index 00000000..e1bdb1d1 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIPAddressAttributeRequestValidator.cs @@ -0,0 +1,35 @@ +using System.Net; +using System.Net.Sockets; +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateIPAddressAttributeRequestValidator : AbstractValidator +{ + public UpdateIPAddressAttributeRequestValidator() + { + Include(new UpdateStringAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .NotEmpty() + .When(x => x.Default is not null, ApplyConditionTo.CurrentValidator) + .WithMessage("Default must not be an empty string.") + .Must(BeValidIpAddress) + .WithMessage("Default is not a valid IP Address format."); + } + + private bool BeValidIpAddress(string? ipAddress) + { + if (string.IsNullOrEmpty(ipAddress)) + return true; + + // Try parsing as IP address (supports both IPv4 and IPv6) + if (IPAddress.TryParse(ipAddress, out var parsedIp)) + { + // Accept both IPv4 and IPv6 + return parsedIp.AddressFamily == AddressFamily.InterNetwork || + parsedIp.AddressFamily == AddressFamily.InterNetworkV6; + } + + return false; + } +} From f16de16db3ce30fd2a99f6dfbb4a53b1d8ad70c7 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 31 Oct 2024 16:49:26 +0000 Subject: [PATCH 117/303] added tests for udpate up address attribute request --- .../Databases/CreateIPAddressRequestTests.cs | 3 - .../UpdateIPAddressAttributeRequestTests.cs | 78 +++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIPAddressAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAddressRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAddressRequestTests.cs index 8d8279bb..001b71f5 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAddressRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAddressRequestTests.cs @@ -35,9 +35,6 @@ public void IsValid_WithValidDefaults_ReturnsTrue(string defaultValue) // Act var isValid = request.IsValid(); - // TODO: Delete - var DELETE = request.Validate(); - // Assert Assert.True(isValid); } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIPAddressAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIPAddressAttributeRequestTests.cs new file mode 100644 index 00000000..5aa45e02 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIPAddressAttributeRequestTests.cs @@ -0,0 +1,78 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateIPAddressAttributeRequestTests : UpdateStringAttributeBaseRequestTests +{ + protected override UpdateIPAddressAttributeRequest CreateValidUpdateStringAttributeBaseRequest => new(); + + protected override string ValidDefaultValue => "192.168.1.1"; + + public static TheoryData ValidDefaultValues => + [ + "192.0.2.1", + "192.168.1.1", + "2001:db8::1", + "2001:db8:85a3::8a2e:370:7334", + "::1", + "fe80::1" + ]; + + [Theory] + [MemberData(nameof(ValidDefaultValues))] + public void IsValid_WithValidDefaults_ReturnsTrue(string defaultValue) + { + // Arrange + var request = new UpdateIPAddressAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = defaultValue + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidDefaultValues => + [ + "256.256.256.256", + "192.168.1.1.1", + "192.168.1.", + ".192.168.1.1", + "192.168.1.1/24", + "192.168.1.x", + "192.168.1.*", + "abcd", + "", + "2001:db8::::1", + "2001:db8", + "2001:db8::/32", + "2001:dg8::1" + ]; + + [Theory] + [MemberData(nameof(InvalidDefaultValues))] + public void IsValid_WithInvalidDefaults_ReturnsFalse(string defaultValue) + { + // Arrange + var request = new UpdateIPAddressAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = defaultValue + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } +} From c5657235072ce5fce1bcc03bd9a98e2eb99e0303 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 31 Oct 2024 17:23:09 +0000 Subject: [PATCH 118/303] added create relationship attribute request --- .../CreateRelationshipAttributeRequest.cs | 48 +++++++++++++++++++ ...teRelationshipAttributeRequestValidator.cs | 30 ++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateRelationshipAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateRelationshipAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateRelationshipAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateRelationshipAttributeRequest.cs new file mode 100644 index 00000000..27d4465f --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateRelationshipAttributeRequest.cs @@ -0,0 +1,48 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create a realtionship attribute +/// +public class CreateRelationshipAttributeRequest : DatabaseCollectionIdBaseRequest +{ + /// + /// Related Collection ID. You can create a new collection using the Database service server integration. + /// + [JsonPropertyName("relatedCollectionId")] + public string RelatedCollectionId { get; set; } = string.Empty; + + /// + /// Relation type + /// + [JsonPropertyName("type")] + public RelationType Type { get; set; } + + /// + /// Is Two Way? + /// + [JsonPropertyName("twoWay")] + public bool TwoWay { get; set; } + + /// + /// Attribute Key + /// + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; + + /// + /// Two Way Attribute Key + /// + [JsonPropertyName("twoWayKey")] + public string TwoWayKey { get; set; } = IdUtils.GenerateUniqueId(); + + /// + /// Constraints option + /// + [JsonPropertyName("onDelete")] + public OnDelete OnDelete { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateRelationshipAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateRelationshipAttributeRequestValidator.cs new file mode 100644 index 00000000..a437cf4f --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateRelationshipAttributeRequestValidator.cs @@ -0,0 +1,30 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateRelationshipAttributeRequestValidator : AbstractValidator +{ + public CreateRelationshipAttributeRequestValidator() + { + Include(new DatabaseCollectionIdBaseRequestValidator()); + + RuleFor(x => x.RelatedCollectionId) + .NotEmpty() + .WithMessage("RelatedCollectionId is required."); + + RuleFor(x => x.Type) + .IsInEnum() + .WithMessage("Type must be a valid RelationType."); + + RuleFor(x => x.Key) + .NotEmpty() + .WithMessage("Key is required."); + + RuleFor(x => x.TwoWayKey) + .NotEmpty() + .WithMessage("TwoWayKey is required."); + + RuleFor(x => x.OnDelete) + .IsInEnum() + .WithMessage("OnDelete must be a valid OnDelete value."); + } +} From 27e7014c0a9a50e959cdfe2c7bd34d757c933faa Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 31 Oct 2024 18:40:35 +0000 Subject: [PATCH 119/303] added tests for create relationship attribute request --- ...CreateRelationshipAttributeRequestTests.cs | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateRelationshipAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateRelationshipAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateRelationshipAttributeRequestTests.cs new file mode 100644 index 00000000..2b1a0267 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateRelationshipAttributeRequestTests.cs @@ -0,0 +1,194 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateRelationshipAttributeRequestTests : DatabaseCollectionIdBaseRequestTests +{ + protected override CreateRelationshipAttributeRequest CreateValidDatabaseCollectionIdRequest => new() + { + RelatedCollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey" + }; + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new CreateRelationshipAttributeRequest(); + + // Assert + Assert.Equal(string.Empty, request.RelatedCollectionId); + Assert.Equal(RelationType.OneToOne, request.Type); + Assert.False(request.TwoWay); + Assert.Equal(string.Empty, request.Key); + Assert.NotEmpty(request.TwoWayKey); + Assert.Equal(OnDelete.Restrict, request.OnDelete); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var relatedCollectionId = IdUtils.GenerateUniqueId(); + var type = RelationType.ManyToMany; + var twoWay = true; + var key = "validKey"; + var twoWayKey = "validTwoWayKey"; + var onDelete = OnDelete.Cascade; + var request = new CreateRelationshipAttributeRequest(); + + // Act + request.RelatedCollectionId = relatedCollectionId; + request.Type = type; + request.TwoWay = twoWay; + request.Key = key; + request.TwoWayKey = twoWayKey; + request.OnDelete = onDelete; + + // Assert + Assert.Equal(relatedCollectionId, request.RelatedCollectionId); + Assert.Equal(type, request.Type); + Assert.Equal(twoWay, request.TwoWay); + Assert.Equal(key, request.Key); + Assert.Equal(twoWayKey, request.TwoWayKey); + Assert.Equal(onDelete, request.OnDelete); + } + + [Fact] + public void IsValid_WithValidData_ReturnsTrue() + { + // Arrange + var request = new CreateRelationshipAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + RelatedCollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey" + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequests => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + RelatedCollectionId = null!, + Key = "validKey" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + RelatedCollectionId = "", + Key = "validKey" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + RelatedCollectionId = IdUtils.GenerateUniqueId(), + Key = null! + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + RelatedCollectionId = IdUtils.GenerateUniqueId(), + Key = "" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + RelatedCollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Type = (RelationType) 999 + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + RelatedCollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + TwoWayKey = null! + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + RelatedCollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + TwoWayKey = "" + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + RelatedCollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + OnDelete = (OnDelete) 999 + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequests))] + public void IsValid_WithInvalidData_ReturnsFalse(CreateRelationshipAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new CreateRelationshipAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + RelatedCollectionId = IdUtils.GenerateUniqueId(), + Key = "", + Type = (RelationType)999, + TwoWayKey = "", + OnDelete = (OnDelete)999 + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new CreateRelationshipAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + RelatedCollectionId = IdUtils.GenerateUniqueId(), + Key = "", + Type = (RelationType)999, + TwoWayKey = "", + OnDelete = (OnDelete)999 + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From b20950e5cb6e474816a9f9e3f1fec2dc0f497489 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 7 Nov 2024 18:56:03 +0000 Subject: [PATCH 120/303] added enum json converters to create relationship attribute request --- .../Requests/Databases/CreateRelationshipAttributeRequest.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateRelationshipAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateRelationshipAttributeRequest.cs index 27d4465f..caac3b69 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateRelationshipAttributeRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateRelationshipAttributeRequest.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Requests.Databases.Validators; using PinguApps.Appwrite.Shared.Utils; @@ -20,6 +21,7 @@ public class CreateRelationshipAttributeRequest : DatabaseCollectionIdBaseReques /// Relation type /// [JsonPropertyName("type")] + [JsonConverter(typeof(CamelCaseEnumConverter))] public RelationType Type { get; set; } /// @@ -44,5 +46,6 @@ public class CreateRelationshipAttributeRequest : DatabaseCollectionIdBaseReques /// Constraints option /// [JsonPropertyName("onDelete")] + [JsonConverter(typeof(CamelCaseEnumConverter))] public OnDelete OnDelete { get; set; } } From 345029b87110d6b72adc764f959692be287310d8 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 7 Nov 2024 18:56:20 +0000 Subject: [PATCH 121/303] added update relationship attribute request --- .../UpdateRelationshipAttributeRequest.cs | 23 +++++++++++++++++++ ...teRelationshipAttributeRequestValidator.cs | 20 ++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateRelationshipAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateRelationshipAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateRelationshipAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateRelationshipAttributeRequest.cs new file mode 100644 index 00000000..57550afb --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateRelationshipAttributeRequest.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to update a relationship attribute +/// +public class UpdateRelationshipAttributeRequest : DatabaseCollectionIdAttributeKeyBaseRequest +{ + /// + /// New attribute key + /// + [JsonPropertyName("newKey")] + public string? NewKey { get; set; } + + /// + /// Constraints option + /// + [JsonPropertyName("onDelete")] + public OnDelete? OnDelete { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateRelationshipAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateRelationshipAttributeRequestValidator.cs new file mode 100644 index 00000000..753faf80 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateRelationshipAttributeRequestValidator.cs @@ -0,0 +1,20 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateRelationshipAttributeRequestValidator : AbstractValidator +{ + public UpdateRelationshipAttributeRequestValidator() + { + Include(new DatabaseCollectionIdAttributeKeyBaseRequestValidator()); + + RuleFor(x => x.NewKey) + .NotEmpty() + .When(x => x.NewKey is not null) + .WithMessage("NewKey must either be null or a non empty string"); + + RuleFor(x => x.OnDelete) + .IsInEnum() + .When(x => x.OnDelete is not null) + .WithMessage("OnDelete must be a valid OnDelete value."); + } +} From 430033135a93bcdb1f2e1a5a519597ebee77b270 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 7 Nov 2024 19:05:05 +0000 Subject: [PATCH 122/303] added tests for update relationship attribute request --- ...UpdateRelationshipAttributeRequestTests.cs | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateRelationshipAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateRelationshipAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateRelationshipAttributeRequestTests.cs new file mode 100644 index 00000000..272f97d3 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateRelationshipAttributeRequestTests.cs @@ -0,0 +1,114 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateRelationshipAttributeRequestTests : DatabaseCollectionIdAttributeKeyBaseRequestTests +{ + protected override UpdateRelationshipAttributeRequest CreateValidDatabaseCollectionIdAttributeKeyRequest => new(); + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new UpdateRelationshipAttributeRequest(); + + // Assert + Assert.Null(request.NewKey); + Assert.Null(request.OnDelete); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var keyValue = "validKey"; + var onDeleteValue = OnDelete.SetNull; + var request = new UpdateRelationshipAttributeRequest(); + + // Act + request.NewKey = keyValue; + request.OnDelete = onDeleteValue; + + // Assert + Assert.Equal(keyValue, request.NewKey); + Assert.Equal(onDeleteValue, request.OnDelete); + } + + [Theory] + [InlineData(null, null)] + [InlineData("validKey", OnDelete.Restrict)] + [InlineData("validKey", OnDelete.Cascade)] + [InlineData("validKey", OnDelete.SetNull)] + public void IsValid_WithValidData_ReturnsTrue(string? key, OnDelete? onDelete) + { + // Arrange + var request = new UpdateRelationshipAttributeRequest(); + request.DatabaseId = "valid_Team-Id."; + request.CollectionId = "valid_Team-Id."; + request.Key = "validKey"; + request.NewKey = key; + request.OnDelete = onDelete; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Theory] + [InlineData("", null)] + [InlineData(null, (OnDelete)999)] + public void IsValid_WithInvalidData_ReturnsFalse(string? key, OnDelete? onDelete) + { + // Arrange + var request = new UpdateRelationshipAttributeRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = "validKey"; + request.NewKey = key; + request.OnDelete = onDelete; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new UpdateRelationshipAttributeRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = "validKey"; + request.NewKey = ""; + request.OnDelete = (OnDelete)999; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new UpdateRelationshipAttributeRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Key = "validKey"; + request.NewKey = ""; + request.OnDelete = (OnDelete)999; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 613bb823027e550e26670ee3fccf9f4771c83cf8 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 7 Nov 2024 19:15:44 +0000 Subject: [PATCH 123/303] added create string attirbute request --- .../Databases/CreateStringAttributeRequest.cs | 22 +++++++++++++++++++ .../CreateStringAttributeRequestValidator.cs | 20 +++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateStringAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateStringAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateStringAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateStringAttributeRequest.cs new file mode 100644 index 00000000..529e14d4 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateStringAttributeRequest.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create a string attribute +/// +public class CreateStringAttributeRequest : CreateStringAttributeBaseRequest +{ + /// + /// Attribute size for text attributes, in number of characters + /// + [JsonPropertyName("size")] + public int Size { get; set; } + + /// + /// Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried + /// + [JsonPropertyName("encrypt")] + public bool Encrypt { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateStringAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateStringAttributeRequestValidator.cs new file mode 100644 index 00000000..31ce02ea --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateStringAttributeRequestValidator.cs @@ -0,0 +1,20 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateStringAttributeRequestValidator : AbstractValidator +{ + public CreateStringAttributeRequestValidator() + { + Include(new CreateStringAttributeBaseRequestValidator()); + + RuleFor(x => x.Size) + .GreaterThan(0) + .WithMessage("Size should be greater than 0") + .LessThan(1_073_741_825) + .WithMessage("Size should be less than 1,073,741,825"); + + RuleFor(x => x.Encrypt) + .NotNull() + .WithMessage("Encrypt should not be null"); + } +} From 446d9177ecb74e6433c4755116997c5455537882 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 7 Nov 2024 19:25:30 +0000 Subject: [PATCH 124/303] partially added tests for create string attribute request --- .../CreateStringAttributeRequestValidator.cs | 5 ++++ .../CreateStringAttributeRequestTests.cs | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeRequestTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateStringAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateStringAttributeRequestValidator.cs index 31ce02ea..5a021cb2 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateStringAttributeRequestValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateStringAttributeRequestValidator.cs @@ -16,5 +16,10 @@ public CreateStringAttributeRequestValidator() RuleFor(x => x.Encrypt) .NotNull() .WithMessage("Encrypt should not be null"); + + RuleFor(x => x.Default) + .Length(x => 0, x => x.Size) + .When(x => x.Default is not null) + .WithMessage("Default should be between {MinLength} and {MaxLength} characters long"); } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeRequestTests.cs new file mode 100644 index 00000000..b99b9d82 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeRequestTests.cs @@ -0,0 +1,27 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateStringAttributeRequestTests : CreateStringAttributeBaseRequestTests +{ + protected override CreateStringAttributeRequest CreateValidCreateStringAttributeBaseRequest => new() + { + Size = 100 + }; + + protected override string ValidDefaultValue => "A valid string"; + + public static TheoryData ValidRequests => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = null, + Size = 1, + Encrypt = false + } + ]; +} From 13c3db47d50e9c4200cf8b93448fa2057f5cb816 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 15:58:42 +0000 Subject: [PATCH 125/303] Update alstr/todo-to-issue-action digest to efb8b98 --- .github/workflows/dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 271d5e64..3fd43285 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -30,7 +30,7 @@ jobs: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: "TODO to Issue" - uses: "alstr/todo-to-issue-action@53d8a86de9f2224b24c6a9009a8a1f56d4e1324d" # v5 + uses: "alstr/todo-to-issue-action@efb8b9830dd1eb59314ac2e886af7b917fad7d21" # v5 with: INSERT_ISSUE_URLS: true AUTO_ASSIGN: true From a60dc8c725d7c1d80e2147d51f202b4020aea8b1 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 08:58:06 +0000 Subject: [PATCH 126/303] add security checkbox to pr template --- .github/pull_request_template.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b03c8142..6b05ac36 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -9,6 +9,7 @@ - [ ] `feature` - [ ] `bug` - [ ] `docs` +- [ ] `security` - [ ] `meta` - [ ] `patch` - [ ] `minor` From 3d2b6e58cec81ed0acb8f041a920b7d6d6fb3e7a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 08:59:58 +0000 Subject: [PATCH 127/303] added security section to release drafter --- .github/release-drafter.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 39566e76..c3591cf4 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -4,6 +4,9 @@ change-template: "- [#$NUMBER] $TITLE - Thanks to @$AUTHOR!" exclude-labels: - automated categories: + - title: "🔒 Security" + labels: + - "security" - title: "🚀 Features" labels: - "feature" From 7c3bfd86e06d8c7d90200a760ad96f432da83346 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 09:12:18 +0000 Subject: [PATCH 128/303] completed tests for create string attribute request --- .../CreateStringAttributeRequestTests.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeRequestTests.cs index b99b9d82..52cfc5c6 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateStringAttributeRequestTests.cs @@ -22,6 +22,68 @@ public class CreateStringAttributeRequestTests : CreateStringAttributeBaseReques Default = null, Size = 1, Encrypt = false + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = new string('1', 10), + Size = 10, + Encrypt = true } ]; + + [Theory] + [MemberData(nameof(ValidRequests))] + public void IsValid_WithValidRequest_ReturnsTrue(CreateStringAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequests => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = null, + Size = 0, + Encrypt = false + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = null, + Size = 1_073_741_825, + Encrypt = false + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = new string('a', 11), + Size = 10, + Encrypt = false + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequests))] + public void IsValid_WithInvalidRequest_ReturnsFalse(CreateStringAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } } From fedbc41530f208331ab8f6106ef6d6bb7ef2cbda Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 09:18:55 +0000 Subject: [PATCH 129/303] Added update string attribute request --- .../Databases/UpdateStringAttributeRequest.cs | 16 +++++++++++++ .../UpdateStringAttributeRequestValidator.cs | 23 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateStringAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateStringAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateStringAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateStringAttributeRequest.cs new file mode 100644 index 00000000..43335b51 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateStringAttributeRequest.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to update a string attribute +/// +public class UpdateStringAttributeRequest : UpdateStringAttributeBaseRequest +{ + /// + /// Attribute size for text attributes, in number of characters + /// + [JsonPropertyName("size")] + public int? Size { get; set; } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateStringAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateStringAttributeRequestValidator.cs new file mode 100644 index 00000000..7b5d1c6f --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateStringAttributeRequestValidator.cs @@ -0,0 +1,23 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateStringAttributeRequestValidator : AbstractValidator +{ + public UpdateStringAttributeRequestValidator() + { + Include(new UpdateStringAttributeBaseRequestValidator()); + + RuleFor(x => x.Size) + .GreaterThan(0) + .WithMessage("Size should be greater than 0") + .LessThan(1_073_741_825) + .WithMessage("Size should be less than 1,073,741,825") + .When(x => x.Size is not null); + + RuleFor(x => x.Default) + .Length(x => 0, x => x.Size!.Value) + .When(x => x.Default is not null) + .WithMessage("Default should be between {MinLength} and {MaxLength} characters long") + .When(x => x.Size is not null && x.Default is not null); + } +} From abcea9ce7a1fbcdd006638391c9d9e45a68cfdae Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 09:24:58 +0000 Subject: [PATCH 130/303] added tests for update string attribute request --- .../UpdateStringAttributeRequestTests.cs | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateStringAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateStringAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateStringAttributeRequestTests.cs new file mode 100644 index 00000000..b6ef0dc4 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateStringAttributeRequestTests.cs @@ -0,0 +1,105 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateStringAttributeRequestTests : UpdateStringAttributeBaseRequestTests +{ + protected override UpdateStringAttributeRequest CreateValidUpdateStringAttributeBaseRequest => new(); + + protected override string ValidDefaultValue => "Valid Value"; + + public static TheoryData ValidRequests => + [ + new UpdateStringAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Size = 10, + Default = "Valid" + }, + new UpdateStringAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Size = 1_073_741_824, + Default = "Valid Value" + }, + new UpdateStringAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Size = 1_073_741_824, + Default = null + }, + new UpdateStringAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Size = null, + Default = "Valid" + }, + new UpdateStringAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Size = null, + Default = null + } + ]; + + [Theory] + [MemberData(nameof(ValidRequests))] + public void IsValid_WithValidRequest_ReturnsTrue(UpdateStringAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequests => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = null, + Size = 0 + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = null, + Size = 1_073_741_825 + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = new string('a', 11), + Size = 10 + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequests))] + public void IsValid_WithInvalidRequest_ReturnsFalse(UpdateStringAttributeRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } +} From 046522f6af21651055852d206055beba18e5b218 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 09:27:34 +0000 Subject: [PATCH 131/303] added request to create url attribute --- .../Databases/CreateURLAttributeRequest.cs | 10 ++++++++++ .../CreateURLAttributeRequestValidator.cs | 15 +++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateURLAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateURLAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateURLAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateURLAttributeRequest.cs new file mode 100644 index 00000000..78829e75 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateURLAttributeRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create a URL attribute +/// +public class CreateURLAttributeRequest : CreateStringAttributeBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateURLAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateURLAttributeRequestValidator.cs new file mode 100644 index 00000000..30b2990c --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateURLAttributeRequestValidator.cs @@ -0,0 +1,15 @@ +using System; +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateURLAttributeRequestValidator : AbstractValidator +{ + public CreateURLAttributeRequestValidator() + { + Include(new CreateStringAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .Must(x => Uri.TryCreate(x, UriKind.Absolute, out _)) + .WithMessage("Default should be formatted as a URL"); + } +} From 848e74235cc22510c8e03052b64e51484d7b6de5 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 09:43:33 +0000 Subject: [PATCH 132/303] Added tests for create url attribute request --- .../CreateURLAttributeRequestValidator.cs | 3 +- .../CreateURLAttributeRequestTests.cs | 88 +++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateURLAttributeRequestTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateURLAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateURLAttributeRequestValidator.cs index 30b2990c..d237aa5c 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateURLAttributeRequestValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateURLAttributeRequestValidator.cs @@ -10,6 +10,7 @@ public CreateURLAttributeRequestValidator() RuleFor(x => x.Default) .Must(x => Uri.TryCreate(x, UriKind.Absolute, out _)) - .WithMessage("Default should be formatted as a URL"); + .WithMessage("Default should be formatted as a URL") + .When(x => x.Default is not null); } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateURLAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateURLAttributeRequestTests.cs new file mode 100644 index 00000000..9e02df61 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateURLAttributeRequestTests.cs @@ -0,0 +1,88 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateURLAttributeRequestTests : CreateStringAttributeBaseRequestTests +{ + protected override CreateURLAttributeRequest CreateValidCreateStringAttributeBaseRequest => new(); + + protected override string ValidDefaultValue => "https://example.com"; + + public static TheoryData ValidDefaultValues => + [ + "https://example.com", + "https://www.example.com", + "http://example.com", + "https://example.com/path/to/resource.html", + "https://example.com/path/to/resource", + "https://example.com/path-with-hyphens", + "https://example.com/path_with_underscores", + "https://example.com?key=value", + "https://example.com/?key=value", + "https://example.com/path?key1=value1&key2=value2", + "https://example.com/path?key=value%20with%20encoded%20spaces", + "https://localhost:1234", + "http://127.0.0.1:8080", + "https://example.com:8080/path", + "https://user:pass@example.com", + "https://user:pass@example.com:8080/path?query=value", + "https://example.com/path?search=R%26D", + "https://example.com/path?currency=%24100", + "https://example.com/path(1)/resource", + "https://example.com/path?name=John+Doe", + "https://example.com#section", + "https://example.com/#section", + "https://example.com/path#section", + "https://example.com/path?query=value#section" + ]; + + [Theory] + [MemberData(nameof(ValidDefaultValues))] + public void IsValid_WithValidDefaults_ReturnsTrue(string defaultValue) + { + // Arrange + var request = new CreateURLAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = defaultValue + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidDefaultValues => + [ + "example.com", + "www.example.com", + "https:/example.com", + "https//example.com", + "https://exam ple.com", + ]; + + [Theory] + [MemberData(nameof(InvalidDefaultValues))] + public void IsValid_WithInvalidDefaults_ReturnsFalse(string defaultValue) + { + // Arrange + var request = new CreateURLAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = defaultValue + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } +} From 626d90b12706621019cd793478e88423922d9761 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 09:46:13 +0000 Subject: [PATCH 133/303] added update url attribute request --- .../Databases/UpdateURLAttributeRequest.cs | 10 ++++++++++ .../UpdateURLAttributeRequestValidator.cs | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateURLAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateURLAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateURLAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateURLAttributeRequest.cs new file mode 100644 index 00000000..a7bdb77b --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateURLAttributeRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to update a URL attribute +/// +public class UpdateURLAttributeRequest : UpdateStringAttributeBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateURLAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateURLAttributeRequestValidator.cs new file mode 100644 index 00000000..7ce156b4 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateURLAttributeRequestValidator.cs @@ -0,0 +1,16 @@ +using System; +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateURLAttributeRequestValidator : AbstractValidator +{ + public UpdateURLAttributeRequestValidator() + { + Include(new UpdateStringAttributeBaseRequestValidator()); + + RuleFor(x => x.Default) + .Must(x => Uri.TryCreate(x, UriKind.Absolute, out _)) + .WithMessage("Default should be formatted as a URL") + .When(x => x.Default is not null); + } +} From 287f7adaec97653092bfddb38e5b998f9039c828 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 09:48:32 +0000 Subject: [PATCH 134/303] added tests for update url attribute request --- .../UpdateURLAttributeRequestTests.cs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateURLAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateURLAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateURLAttributeRequestTests.cs new file mode 100644 index 00000000..29dfe090 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateURLAttributeRequestTests.cs @@ -0,0 +1,88 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateURLAttributeRequestTests : UpdateStringAttributeBaseRequestTests +{ + protected override UpdateURLAttributeRequest CreateValidUpdateStringAttributeBaseRequest => new(); + + protected override string ValidDefaultValue => "https://example.com"; + + public static TheoryData ValidDefaultValues => + [ + "https://example.com", + "https://www.example.com", + "http://example.com", + "https://example.com/path/to/resource.html", + "https://example.com/path/to/resource", + "https://example.com/path-with-hyphens", + "https://example.com/path_with_underscores", + "https://example.com?key=value", + "https://example.com/?key=value", + "https://example.com/path?key1=value1&key2=value2", + "https://example.com/path?key=value%20with%20encoded%20spaces", + "https://localhost:1234", + "http://127.0.0.1:8080", + "https://example.com:8080/path", + "https://user:pass@example.com", + "https://user:pass@example.com:8080/path?query=value", + "https://example.com/path?search=R%26D", + "https://example.com/path?currency=%24100", + "https://example.com/path(1)/resource", + "https://example.com/path?name=John+Doe", + "https://example.com#section", + "https://example.com/#section", + "https://example.com/path#section", + "https://example.com/path?query=value#section" + ]; + + [Theory] + [MemberData(nameof(ValidDefaultValues))] + public void IsValid_WithValidDefaults_ReturnsTrue(string defaultValue) + { + // Arrange + var request = new UpdateURLAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = defaultValue + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidDefaultValues => + [ + "example.com", + "www.example.com", + "https:/example.com", + "https//example.com", + "https://exam ple.com", + ]; + + [Theory] + [MemberData(nameof(InvalidDefaultValues))] + public void IsValid_WithInvalidDefaults_ReturnsFalse(string defaultValue) + { + // Arrange + var request = new UpdateURLAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "validKey", + Default = defaultValue + }; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } +} From 4f75d856aa94ca4200ba7f0495e0b1f511b3e689 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 09:50:46 +0000 Subject: [PATCH 135/303] added get attribute request --- .../Requests/Databases/GetAttributeRequest.cs | 10 ++++++++++ .../Validators/GetAttributeRequestValidator.cs | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/GetAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/GetAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/GetAttributeRequest.cs new file mode 100644 index 00000000..820e65f8 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/GetAttributeRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to get an attribute +/// +public class GetAttributeRequest : DatabaseCollectionIdAttributeKeyBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetAttributeRequestValidator.cs new file mode 100644 index 00000000..36fcc0a3 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetAttributeRequestValidator.cs @@ -0,0 +1,10 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class GetAttributeRequestValidator : AbstractValidator +{ + public GetAttributeRequestValidator() + { + Include(new DatabaseCollectionIdAttributeKeyBaseRequestValidator()); + } +} From ebe09b9e0dd35d2ba13c495c49d0f6d595e06107 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 09:53:04 +0000 Subject: [PATCH 136/303] added tests for get attribute request --- .../Requests/Databases/GetAttributeRequestTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetAttributeRequestTests.cs new file mode 100644 index 00000000..2c764618 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetAttributeRequestTests.cs @@ -0,0 +1,8 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class GetAttributeRequestTests : DatabaseCollectionIdAttributeKeyBaseRequestTests +{ + protected override GetAttributeRequest CreateValidDatabaseCollectionIdAttributeKeyRequest => new(); +} From 81df6d00179e2ba79dcf4113abe7510586409073 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 09:55:10 +0000 Subject: [PATCH 137/303] added delete attribute request --- .../Requests/Databases/DeleteAttributeRequest.cs | 10 ++++++++++ .../Validators/DeleteAttributeRequestValidator.cs | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteAttributeRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteAttributeRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteAttributeRequest.cs new file mode 100644 index 00000000..51782c94 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteAttributeRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to delete an attribute +/// +public class DeleteAttributeRequest : DatabaseCollectionIdAttributeKeyBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteAttributeRequestValidator.cs new file mode 100644 index 00000000..7aa373e3 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteAttributeRequestValidator.cs @@ -0,0 +1,10 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class DeleteAttributeRequestValidator : AbstractValidator +{ + public DeleteAttributeRequestValidator() + { + Include(new DatabaseCollectionIdAttributeKeyBaseRequestValidator()); + } +} From 3c1b638f363f629f0a0a1b40b99b101378b65b13 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 9 Nov 2024 09:56:39 +0000 Subject: [PATCH 138/303] added tests for delete attribute request --- .../Requests/Databases/DeleteAttributeRequestTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteAttributeRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteAttributeRequestTests.cs new file mode 100644 index 00000000..a6568d87 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteAttributeRequestTests.cs @@ -0,0 +1,8 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class DeleteAttributeRequestTests : DatabaseCollectionIdAttributeKeyBaseRequestTests +{ + protected override DeleteAttributeRequest CreateValidDatabaseCollectionIdAttributeKeyRequest => new(); +} From 6c682b191051012d5405d4d7ae6682898508b765 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:25:34 +0000 Subject: [PATCH 139/303] Update alstr/todo-to-issue-action digest to 59c6b53 --- .github/workflows/dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 3fd43285..ccfbd1a9 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -30,7 +30,7 @@ jobs: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: "TODO to Issue" - uses: "alstr/todo-to-issue-action@efb8b9830dd1eb59314ac2e886af7b917fad7d21" # v5 + uses: "alstr/todo-to-issue-action@59c6b5395c643afa84300961802646c21a2718c3" # v5 with: INSERT_ISSUE_URLS: true AUTO_ASSIGN: true From a72865476edc4cebfef49f5c20f12529fda73309 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 12 Nov 2024 17:34:40 +0000 Subject: [PATCH 140/303] Added request for list documents --- .../Databases/ListDocumentsRequest.cs | 25 +++++++++++++++++++ .../ListDocumentsRequestValidator.cs | 19 ++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/ListDocumentsRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListDocumentsRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/ListDocumentsRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/ListDocumentsRequest.cs new file mode 100644 index 00000000..db1c4d85 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/ListDocumentsRequest.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Attributes; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to list documents +/// +public class ListDocumentsRequest : QueryBaseRequest +{ + /// + /// Database ID + /// + [JsonPropertyName("databaseId")] + [SdkExclude] + public string DatabaseId { get; set; } = string.Empty; + + /// + /// Collection ID + /// + [JsonPropertyName("databaseId")] + [SdkExclude] + public string CollectionId { get; set; } = string.Empty; +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListDocumentsRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListDocumentsRequestValidator.cs new file mode 100644 index 00000000..07e71438 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListDocumentsRequestValidator.cs @@ -0,0 +1,19 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class ListDocumentsRequestValidator : AbstractValidator +{ + public ListDocumentsRequestValidator() + { + Include(new QueryBaseRequestValidator()); + + RuleFor(x => x.DatabaseId) + .NotEmpty() + .WithMessage("DatabaseId is required."); + + RuleFor(x => x.CollectionId) + .NotEmpty() + .WithMessage("CollectionId is required."); + } +} From 5422c0628cebf59bfd753e2612007483ce2cf1c7 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 12 Nov 2024 17:42:54 +0000 Subject: [PATCH 141/303] added tests for list documents request --- .../Databases/ListDocumentsRequestTests.cs | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListDocumentsRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListDocumentsRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListDocumentsRequestTests.cs new file mode 100644 index 00000000..6f1264a2 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListDocumentsRequestTests.cs @@ -0,0 +1,134 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class ListDocumentsRequestTests : QueryBaseRequestTests +{ + protected override ListDocumentsRequest CreateValidRequest => new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new ListDocumentsRequest(); + + // Assert + Assert.Equal(string.Empty, request.DatabaseId); + Assert.Equal(string.Empty, request.CollectionId); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var databaseId = "testDatabaseId"; + var collectionId = "testCollectionId"; + + var request = new ListDocumentsRequest(); + + // Act + request.DatabaseId = databaseId; + request.CollectionId = collectionId; + + // Assert + Assert.Equal(databaseId, request.DatabaseId); + Assert.Equal(collectionId, request.CollectionId); + } + + public static TheoryData ValidRequestsData => + [ + new() + { + DatabaseId = "validDatabaseId123", + CollectionId = "validCollectionId123" + }, + new() + { + DatabaseId = "anotherValidDatabaseId", + CollectionId = "anotherValidCollectionId" + } + ]; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(ListDocumentsRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData => + [ + new() + { + DatabaseId = null!, + CollectionId = IdUtils.GenerateUniqueId() + }, + new() + { + DatabaseId = "", + CollectionId = IdUtils.GenerateUniqueId() + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = null! + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = "" + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(ListDocumentsRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new ListDocumentsRequest + { + DatabaseId = "", + CollectionId = "" + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new ListDocumentsRequest + { + DatabaseId = "", + CollectionId = "" + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From c1e5b61faf9a3ace6826c8b36fde2e8151c9ab51 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 12 Nov 2024 18:13:09 +0000 Subject: [PATCH 142/303] Added generic version of create document request generic --- .../Databases/CreateDocumentRequestGeneric.cs | 34 +++++++++++++++++++ .../CreateDocumentRequestValidator.cs | 25 ++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestGeneric.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDocumentRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestGeneric.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestGeneric.cs new file mode 100644 index 00000000..f60b3ddc --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestGeneric.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create a new document +/// +/// The type of the document data +public class CreateDocumentRequest : DatabaseCollectionIdBaseRequest, CreateDocumentRequestValidator> + where TData : class +{ + /// + /// + /// + [JsonPropertyName("documentId")] + public string DocumentId { get; set; } = IdUtils.GenerateUniqueId(); + + /// + /// + /// + [JsonPropertyName("data")] + public TData Data { get; set; } = default!; + + /// + /// + /// + [JsonPropertyName("permissions")] + [JsonConverter(typeof(PermissionListConverter))] + public List Permissions { get; set; } = []; +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDocumentRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDocumentRequestValidator.cs new file mode 100644 index 00000000..d0e97244 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDocumentRequestValidator.cs @@ -0,0 +1,25 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateDocumentRequestValidator : AbstractValidator> + where TData : class +{ + public CreateDocumentRequestValidator() + { + Include(new DatabaseCollectionIdBaseRequestValidator, CreateDocumentRequestValidator>()); + + RuleFor(x => x.DocumentId) + .NotEmpty() + .WithMessage("DocumentId is required.") + .Matches("^[a-zA-Z0-9][a-zA-Z0-9._-]{0,35}$") + .WithMessage("DocumentId can only contain a-z, A-Z, 0-9, period, hyphen, and underscore, and can't start with a special character. Max length is 36 characters."); + + RuleFor(x => x.Data) + .NotNull() + .WithMessage("Data is required."); + + RuleFor(x => x.Permissions) + .NotNull() + .WithMessage("Permissions cannot be null."); + } +} From bf48d19925a80d1b38c48ea8ba37f0085bf1142a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 12 Nov 2024 18:29:57 +0000 Subject: [PATCH 143/303] Added create document request builder and utils --- .../Databases/CreateDocumentRequest.cs | 10 +++++ .../Databases/CreateDocumentRequestBuilder.cs | 39 +++++++++++++++++++ .../ICreateDocumentRequestBuilder.cs | 35 +++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestBuilder.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs new file mode 100644 index 00000000..bcecd739 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; +public class CreateDocumentRequest : CreateDocumentRequest> +{ + /// + /// Creates a new builder for creating a document request + /// + public static ICreateDocumentRequestBuilder CreateBuilder() => new CreateDocumentRequestBuilder(); +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestBuilder.cs new file mode 100644 index 00000000..e5363e5c --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestBuilder.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; +internal class CreateDocumentRequestBuilder : ICreateDocumentRequestBuilder +{ + private readonly CreateDocumentRequest _request = new(); + private readonly Dictionary _data = []; + + public ICreateDocumentRequestBuilder WithDocumentId(string documentId) + { + _request.DocumentId = documentId; + return this; + } + + public ICreateDocumentRequestBuilder WithPermissions(List permissions) + { + _request.Permissions = permissions; + return this; + } + + public ICreateDocumentRequestBuilder AddPermission(Permission permission) + { + _request.Permissions.Add(permission); + return this; + } + + public ICreateDocumentRequestBuilder AddField(string name, object? value) + { + _data[name] = value; + return this; + } + + public CreateDocumentRequest Build() + { + _request.Data = _data; + return _request; + } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs new file mode 100644 index 00000000..a5715c41 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// Builder interface for creating document requests +/// +public interface ICreateDocumentRequestBuilder +{ + /// + /// Sets the document identifier + /// + ICreateDocumentRequestBuilder WithDocumentId(string documentId); + + /// + /// Sets the document permissions + /// + ICreateDocumentRequestBuilder WithPermissions(List permissions); + + /// + /// Adds a permission for the document + /// + ICreateDocumentRequestBuilder AddPermission(Permission permission); + + /// + /// Adds a field to the document data + /// + ICreateDocumentRequestBuilder AddField(string name, object? value); + + /// + /// Builds the document request + /// + CreateDocumentRequest Build(); +} From 2d2d3b75f8d58c8d702aa73821bf3c5a04abe795 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 12 Nov 2024 18:54:18 +0000 Subject: [PATCH 144/303] added tests for create document request builder --- .../CreateDocumentRequestBuilderTests.cs | 157 +++++++++++++++ .../CreateDocumentRequestGenericTests.cs | 189 ++++++++++++++++++ 2 files changed, 346 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestBuilderTests.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestGenericTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestBuilderTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestBuilderTests.cs new file mode 100644 index 00000000..b9c6998b --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestBuilderTests.cs @@ -0,0 +1,157 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateDocumentRequestBuilderTests +{ + [Fact] + public void CreateBuilder_ReturnsNewBuilderInstance() + { + // Act + var builder = CreateDocumentRequest.CreateBuilder(); + + // Assert + Assert.NotNull(builder); + Assert.IsAssignableFrom(builder); + } + + [Fact] + public void WithDocumentId_SetsDocumentId_ReturnsBuilder() + { + // Arrange + var builder = CreateDocumentRequest.CreateBuilder(); + var documentId = IdUtils.GenerateUniqueId(); + + // Act + var result = builder.WithDocumentId(documentId); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Equal(documentId, request.DocumentId); + } + + [Fact] + public void WithPermissions_SetsPermissions_ReturnsBuilder() + { + // Arrange + var builder = CreateDocumentRequest.CreateBuilder(); + var permissions = new List { Permission.Read().Any() }; + + // Act + var result = builder.WithPermissions(permissions); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Same(permissions, request.Permissions); + } + + [Fact] + public void AddPermission_AddsPermissionToList_ReturnsBuilder() + { + // Arrange + var builder = CreateDocumentRequest.CreateBuilder(); + var permission = Permission.Read().Any(); + + // Act + var result = builder.AddPermission(permission); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Contains(permission, request.Permissions); + Assert.Single(request.Permissions); + } + + [Fact] + public void AddPermission_CanAddMultiplePermissions_ReturnsBuilder() + { + // Arrange + var builder = CreateDocumentRequest.CreateBuilder(); + var permission1 = Permission.Read().Any(); + var permission2 = Permission.Write().Any(); + + // Act + builder.AddPermission(permission1) + .AddPermission(permission2); + var request = builder.Build(); + + // Assert + Assert.Equal(2, request.Permissions.Count); + Assert.Contains(permission1, request.Permissions); + Assert.Contains(permission2, request.Permissions); + } + + [Fact] + public void AddField_AddsFieldToData_ReturnsBuilder() + { + // Arrange + var builder = CreateDocumentRequest.CreateBuilder(); + const string fieldName = "testField"; + const string fieldValue = "testValue"; + + // Act + var result = builder.AddField(fieldName, fieldValue); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Equal(fieldValue, request.Data[fieldName]); + } + + [Fact] + public void AddField_CanAddMultipleFields_ReturnsBuilder() + { + // Arrange + var builder = CreateDocumentRequest.CreateBuilder(); + + // Act + builder.AddField("string", "value") + .AddField("number", 42) + .AddField("boolean", true) + .AddField("null", null); + + var request = builder.Build(); + + // Assert + Assert.Equal(4, request.Data.Count); + Assert.Equal("value", request.Data["string"]); + Assert.Equal(42, request.Data["number"]); + Assert.Equal(true, request.Data["boolean"]); + Assert.Null(request.Data["null"]); + } + + [Fact] + public void Build_CreatesRequestWithAllSetValues() + { + // Arrange + var documentId = IdUtils.GenerateUniqueId(); + var permissions = new List { Permission.Read().Any() }; + const string fieldName = "testField"; + const string fieldValue = "testValue"; + + // Act + var request = CreateDocumentRequest.CreateBuilder() + .WithDocumentId(documentId) + .WithPermissions(permissions) + .AddField(fieldName, fieldValue) + .Build(); + + // Assert + Assert.Equal(documentId, request.DocumentId); + Assert.Same(permissions, request.Permissions); + Assert.Equal(fieldValue, request.Data[fieldName]); + } + + [Fact] + public void Build_WithNoFieldsAdded_CreatesEmptyDataDictionary() + { + // Act + var request = CreateDocumentRequest.CreateBuilder().Build(); + + // Assert + Assert.NotNull(request.Data); + Assert.Empty(request.Data); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestGenericTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestGenericTests.cs new file mode 100644 index 00000000..e14d0911 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestGenericTests.cs @@ -0,0 +1,189 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; + +// Test model for our generic type +public class CreateDocumentTestData +{ + public string Name { get; set; } = string.Empty; + public int Age { get; set; } +} + +public class CreateDocumentRequestGenericTests : DatabaseCollectionIdBaseRequestTests, CreateDocumentRequestValidator> +{ + protected override CreateDocumentRequest CreateValidDatabaseCollectionIdRequest => new() + { + DocumentId = IdUtils.GenerateUniqueId(), + Data = new CreateDocumentTestData { Name = "Pingu", Age = 25 } + }; + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new CreateDocumentRequest(); + + // Assert + Assert.NotEmpty(request.DocumentId); + Assert.Null(request.Data); + Assert.NotNull(request.Permissions); + Assert.Empty(request.Permissions); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var documentId = IdUtils.GenerateUniqueId(); + var data = new CreateDocumentTestData { Name = "Pingu", Age = 30 }; + var permissions = new List { Permission.Read().Any() }; + + var request = new CreateDocumentRequest(); + + // Act + request.DocumentId = documentId; + request.Data = data; + request.Permissions = permissions; + + // Assert + Assert.Equal(documentId, request.DocumentId); + Assert.Equal(data, request.Data); + Assert.Equal(permissions, request.Permissions); + } + + public static TheoryData> ValidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + Data = new CreateDocumentTestData { Name = "Valid Name", Age = 25 }, + Permissions = [Permission.Read().Any()] + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = "validDocumentId123", + Data = new CreateDocumentTestData { Name = "Another Valid Name", Age = 30 }, + Permissions = [] + } + ]; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(CreateDocumentRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData> InvalidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = null!, + Data = new CreateDocumentTestData { Name = "Test", Age = 25 } + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = "", + Data = new CreateDocumentTestData { Name = "Test", Age = 25 } + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = "invalid chars!", + Data = new CreateDocumentTestData { Name = "Test", Age = 25 } + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = ".startsWithSymbol", + Data = new CreateDocumentTestData { Name = "Test", Age = 25 } + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = new string('a', 37), + Data = new CreateDocumentTestData { Name = "Test", Age = 25 } + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + Data = null! + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + Data = new CreateDocumentTestData { Name = "Test", Age = 25 }, + Permissions = null! + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(CreateDocumentRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new CreateDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = "", + Data = null!, + Permissions = null! + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new CreateDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = "", + Data = null!, + Permissions = null! + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From db989a956bc949499304cd89191796bb581f9887 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 12 Nov 2024 19:03:36 +0000 Subject: [PATCH 145/303] Added tests for non generic create document request --- .../NonGenericCreateDocumentRequestTests.cs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/NonGenericCreateDocumentRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/NonGenericCreateDocumentRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/NonGenericCreateDocumentRequestTests.cs new file mode 100644 index 00000000..4732365d --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/NonGenericCreateDocumentRequestTests.cs @@ -0,0 +1,55 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class NonGenericCreateDocumentRequestTests : DatabaseCollectionIdBaseRequestTests>, CreateDocumentRequestValidator>> +{ + protected override CreateDocumentRequest> CreateValidDatabaseCollectionIdRequest => CreateDocumentRequest + .CreateBuilder() + .WithDocumentId(IdUtils.GenerateUniqueId()) + .AddField("name", "Pingu") + .Build(); + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new CreateDocumentRequest(); + + // Assert + Assert.NotEmpty(request.DocumentId); + Assert.Null(request.Data); + Assert.NotNull(request.Permissions); + Assert.Empty(request.Permissions); + } + + [Theory] + [InlineData("string", "value")] + [InlineData("number", 42)] + [InlineData("boolean", true)] + [InlineData("null", null)] + public void Data_CanStoreVariousTypes(string key, object? value) + { + // Arrange + var request = new CreateDocumentRequest(); + request.Data = []; + + // Act + request.Data[key] = value; + + // Assert + Assert.Equal(value, request.Data[key]); + } + + [Fact] + public void CreateBuilder_ReturnsNewBuilder() + { + // Act + var builder = CreateDocumentRequest.CreateBuilder(); + + // Assert + Assert.NotNull(builder); + Assert.IsAssignableFrom(builder); + } +} From 265ff8ac9d7af11072bdcf412d4216dcbb05b48d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 12 Nov 2024 19:09:30 +0000 Subject: [PATCH 146/303] Ensured that CreateDocumentRequest (non generic) cannot be instantiated without the builder --- .../Requests/Databases/CreateDocumentRequest.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs index bcecd739..401e6f19 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs @@ -3,6 +3,8 @@ namespace PinguApps.Appwrite.Shared.Requests.Databases; public class CreateDocumentRequest : CreateDocumentRequest> { + internal CreateDocumentRequest() { } + /// /// Creates a new builder for creating a document request /// From 3ab0d08721cabde7305ab05a649ae209391666b4 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 12 Nov 2024 19:13:35 +0000 Subject: [PATCH 147/303] added intellisense docs for generic create document request --- .../Requests/Databases/CreateDocumentRequestGeneric.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestGeneric.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestGeneric.cs index f60b3ddc..3eee0bbc 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestGeneric.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestGeneric.cs @@ -14,19 +14,19 @@ public class CreateDocumentRequest : DatabaseCollectionIdBaseRequest - /// + /// Document ID. Choose a custom ID or generate a random ID with ID.unique(). Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars /// [JsonPropertyName("documentId")] public string DocumentId { get; set; } = IdUtils.GenerateUniqueId(); /// - /// + /// Document data - Provide your own type to match your database schema, or build this up using /// [JsonPropertyName("data")] public TData Data { get; set; } = default!; /// - /// + /// An array of permissions strings. By default, only the current user is granted all permissions. Learn more about permissions. /// [JsonPropertyName("permissions")] [JsonConverter(typeof(PermissionListConverter))] From 943acc4c435ed14171bee97463cb0461915d5a14 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 12 Nov 2024 19:16:02 +0000 Subject: [PATCH 148/303] Added request for get document --- .../Requests/Databases/GetDocumentRequest.cs | 16 ++++++++++++++++ .../Validators/GetDocumentRequestValidator.cs | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/GetDocumentRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetDocumentRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/GetDocumentRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/GetDocumentRequest.cs new file mode 100644 index 00000000..94207c9a --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/GetDocumentRequest.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Attributes; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; +public class GetDocumentRequest : DatabaseCollectionDocumentIdBaseRequest +{ + /// + /// Array of query strings generated using the Query class provided by the SDK. Learn more about queries. Maximum of 100 queries are allowed, each 4096 characters long. + /// + [JsonPropertyName("queries")] + [SdkExclude] + public List? Queries { get; set; } = null; +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetDocumentRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetDocumentRequestValidator.cs new file mode 100644 index 00000000..531b0a4c --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetDocumentRequestValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class GetDocumentRequestValidator : AbstractValidator +{ + public GetDocumentRequestValidator() + { + Include(new DatabaseCollectionDocumentIdBaseRequestValidator()); + + RuleFor(x => x.Queries) + .Must(queries => queries is null || queries.Count <= 100) + .WithMessage("A maximum of 100 queries are allowed.") + .ForEach(query => query.Must(q => q.GetQueryString().Length <= 4096) + .WithMessage("Each query can be a maximum of 4096 characters long.")); + } +} From 7d2f342f904697d1f84a7419930b84fdd4a037cf Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 12 Nov 2024 19:21:52 +0000 Subject: [PATCH 149/303] added tests for get document request --- .../Databases/GetDocumentRequestTests.cs | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetDocumentRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetDocumentRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetDocumentRequestTests.cs new file mode 100644 index 00000000..e466a7fc --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetDocumentRequestTests.cs @@ -0,0 +1,151 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class GetDocumentRequestTests : DatabaseCollectionDocumentIdBaseRequestTests +{ + protected override GetDocumentRequest CreateValidDatabaseCollectionDocumentIdRequest => new(); + + + [Fact] + public void QueryBase_Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new GetDocumentRequest(); + + // Assert + Assert.Null(request.Queries); + } + + [Fact] + public void QueryBase_Properties_CanBeSet() + { + // Arrange + var attributeName = "attributeName"; + var value = "value"; + List queries = [Query.Equal(attributeName, value)]; + + var request = new GetDocumentRequest(); + + // Act + request.Queries = queries; + + // Assert + Assert.Collection(request.Queries, query => + { + Assert.Equal(attributeName, query.Attribute); + Assert.NotNull(query.Values); + Assert.Collection(query.Values, v => + { + Assert.Equal(value, v); + }); + Assert.Equal("equal", query.Method); + }); + } + + [Fact] + public void QueryBase_IsValid_WithValidData_ReturnsTrue() + { + // Arrange + var request = new GetDocumentRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.DocumentId = IdUtils.GenerateUniqueId(); + request.Queries = [Query.Equal("attributeName", "value")]; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Fact] + public void QueryBase_IsValid_WithNullQueries_ReturnsTrue() + { + // Arrange + var request = new GetDocumentRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.DocumentId = IdUtils.GenerateUniqueId(); + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Fact] + public void QueryBase_IsValid_WithInvalidData_QueryTooLarge_ReturnsFalse() + { + // Arrange + var request = new GetDocumentRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.DocumentId = IdUtils.GenerateUniqueId(); + request.Queries = [Query.Equal("attributeName", new string('a', 4097))]; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void QueryBase_IsValid_WithInvalidData_TooManyQueries_ReturnsFalse() + { + // Arrange + var request = new GetDocumentRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.DocumentId = IdUtils.GenerateUniqueId(); + request.Queries = Enumerable.Range(0, 101) + .Select(_ => Query.Equal("attributeName", "value")) + .ToList(); + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void QueryBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new GetDocumentRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.DocumentId = IdUtils.GenerateUniqueId(); + request.Queries = Enumerable.Range(0, 101) + .Select(_ => Query.Equal("attributeName", "value")) + .ToList(); + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void QueryBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new GetDocumentRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.DocumentId = IdUtils.GenerateUniqueId(); + request.Queries = Enumerable.Range(0, 101) + .Select(_ => Query.Equal("attributeName", "value")) + .ToList(); + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 37c76c5e5cf7df05cf6e3da85d42c38806257318 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 21:02:42 +0000 Subject: [PATCH 150/303] Update alstr/todo-to-issue-action digest to 5157ba9 --- .github/workflows/dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index ccfbd1a9..cf9a6d20 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -30,7 +30,7 @@ jobs: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: "TODO to Issue" - uses: "alstr/todo-to-issue-action@59c6b5395c643afa84300961802646c21a2718c3" # v5 + uses: "alstr/todo-to-issue-action@5157ba918f83a649b1d4b45fb4186916127eaf8d" # v5 with: INSERT_ISSUE_URLS: true AUTO_ASSIGN: true From 1a9f9a4f92fc71c9fa5940a722f85f852cc8d439 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 06:44:58 +0000 Subject: [PATCH 151/303] Update dotnet monorepo to v9 --- src/PinguApps.Appwrite.Client/PinguApps.Appwrite.Client.csproj | 2 +- .../PinguApps.Appwrite.Playground.csproj | 2 +- src/PinguApps.Appwrite.Server/PinguApps.Appwrite.Server.csproj | 2 +- src/PinguApps.Appwrite.Shared/PinguApps.Appwrite.Shared.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/PinguApps.Appwrite.Client/PinguApps.Appwrite.Client.csproj b/src/PinguApps.Appwrite.Client/PinguApps.Appwrite.Client.csproj index 22ff2101..d6d68fd8 100644 --- a/src/PinguApps.Appwrite.Client/PinguApps.Appwrite.Client.csproj +++ b/src/PinguApps.Appwrite.Client/PinguApps.Appwrite.Client.csproj @@ -23,7 +23,7 @@ - + diff --git a/src/PinguApps.Appwrite.Playground/PinguApps.Appwrite.Playground.csproj b/src/PinguApps.Appwrite.Playground/PinguApps.Appwrite.Playground.csproj index 3b09ab5e..9c725139 100644 --- a/src/PinguApps.Appwrite.Playground/PinguApps.Appwrite.Playground.csproj +++ b/src/PinguApps.Appwrite.Playground/PinguApps.Appwrite.Playground.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/PinguApps.Appwrite.Server/PinguApps.Appwrite.Server.csproj b/src/PinguApps.Appwrite.Server/PinguApps.Appwrite.Server.csproj index a562a11b..38c36209 100644 --- a/src/PinguApps.Appwrite.Server/PinguApps.Appwrite.Server.csproj +++ b/src/PinguApps.Appwrite.Server/PinguApps.Appwrite.Server.csproj @@ -23,7 +23,7 @@ - + diff --git a/src/PinguApps.Appwrite.Shared/PinguApps.Appwrite.Shared.csproj b/src/PinguApps.Appwrite.Shared/PinguApps.Appwrite.Shared.csproj index 5700ebe1..877e89da 100644 --- a/src/PinguApps.Appwrite.Shared/PinguApps.Appwrite.Shared.csproj +++ b/src/PinguApps.Appwrite.Shared/PinguApps.Appwrite.Shared.csproj @@ -11,7 +11,7 @@ - + From 74d41ce18cb07dc33a49f10d28a066fa5f7dd080 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:57:14 +0000 Subject: [PATCH 152/303] Update danielpalme/ReportGenerator-GitHub-Action action to v5.4.0 --- .github/workflows/_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index 1a64ce40..939969f6 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -42,7 +42,7 @@ jobs: run: dotnet test -c Release --no-build --verbosity normal --logger trx --collect:"XPlat Code Coverage" - name: Combine Coverage Reports - uses: danielpalme/ReportGenerator-GitHub-Action@62f9e70ab348d56eee76d446b4db903a85ab0ea8 # v5.3.11 + uses: danielpalme/ReportGenerator-GitHub-Action@51bf1a384133df0710ee9a3e37dbf1c829f33677 # v5.4.0 with: reports: "**/*.cobertura.xml" targetdir: "${{ github.workspace }}" From 0b230e423e82b647a2f43b2632da3746515e436c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:31:53 +0000 Subject: [PATCH 153/303] Update danielpalme/ReportGenerator-GitHub-Action action to v5.4.1 --- .github/workflows/_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index 939969f6..9404811d 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -42,7 +42,7 @@ jobs: run: dotnet test -c Release --no-build --verbosity normal --logger trx --collect:"XPlat Code Coverage" - name: Combine Coverage Reports - uses: danielpalme/ReportGenerator-GitHub-Action@51bf1a384133df0710ee9a3e37dbf1c829f33677 # v5.4.0 + uses: danielpalme/ReportGenerator-GitHub-Action@810356ce07a94200154301fb73d878e327b2dd58 # v5.4.1 with: reports: "**/*.cobertura.xml" targetdir: "${{ github.workspace }}" From 53325efedb437253892a2626863e0a60dbc8320a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 14 Nov 2024 18:56:57 +0000 Subject: [PATCH 154/303] Added update document request, as well as adding missing methods to previous builder pattern for creat edocument request --- .../Databases/CreateDocumentRequestBuilder.cs | 12 +++++ .../ICreateDocumentRequestBuilder.cs | 10 ++++ .../IUpdateDocumentRequestBuilder.cs | 44 ++++++++++++++++ .../Databases/UpdateDocumentRequestBuilder.cs | 51 +++++++++++++++++++ .../Databases/UpdateDocumentRequestGeneric.cs | 26 ++++++++++ .../UpdateDocumentRequestValidator.cs | 18 +++++++ 6 files changed, 161 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/IUpdateDocumentRequestBuilder.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestGeneric.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDocumentRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestBuilder.cs index e5363e5c..108ce1ff 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestBuilder.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestBuilder.cs @@ -7,6 +7,18 @@ internal class CreateDocumentRequestBuilder : ICreateDocumentRequestBuilder private readonly CreateDocumentRequest _request = new(); private readonly Dictionary _data = []; + public ICreateDocumentRequestBuilder WithDatabaseId(string databaseId) + { + _request.DatabaseId = databaseId; + return this; + } + + public ICreateDocumentRequestBuilder WithCollectionId(string collectionId) + { + _request.CollectionId = collectionId; + return this; + } + public ICreateDocumentRequestBuilder WithDocumentId(string documentId) { _request.DocumentId = documentId; diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs index a5715c41..165071b6 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs @@ -8,6 +8,16 @@ namespace PinguApps.Appwrite.Shared.Requests.Databases; /// public interface ICreateDocumentRequestBuilder { + /// + /// Sets the database identifier + /// + ICreateDocumentRequestBuilder WithDatabaseId(string databaseId); + + /// + /// Sets the collection identifier + /// + ICreateDocumentRequestBuilder WithCollectionId(string collectionId); + /// /// Sets the document identifier /// diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/IUpdateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/IUpdateDocumentRequestBuilder.cs new file mode 100644 index 00000000..aa299e78 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/IUpdateDocumentRequestBuilder.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; +/// +/// Builder interface for creating document requests +/// +public interface IUpdateDocumentRequestBuilder +{ + /// + /// Sets the database identifier + /// + IUpdateDocumentRequestBuilder WithDatabaseId(string databaseId); + + /// + /// Sets the collection identifier + /// + IUpdateDocumentRequestBuilder WithCollectionId(string collectionId); + + /// + /// Sets the document identifier + /// + IUpdateDocumentRequestBuilder WithDocumentId(string documentId); + + /// + /// Sets the document permissions + /// + IUpdateDocumentRequestBuilder WithPermissions(List permissions); + + /// + /// Adds a permission for the document + /// + IUpdateDocumentRequestBuilder AddPermission(Permission permission); + + /// + /// Adds a field to the document data + /// + IUpdateDocumentRequestBuilder AddField(string name, object? value); + + /// + /// Builds the document request + /// + UpdateDocumentRequest Build(); +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs new file mode 100644 index 00000000..547f8d68 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; +internal class UpdateDocumentRequestBuilder : IUpdateDocumentRequestBuilder +{ + private readonly UpdateDocumentRequest _request = new(); + private readonly Dictionary _data = []; + + public IUpdateDocumentRequestBuilder WithDatabaseId(string databaseId) + { + _request.DatabaseId = databaseId; + return this; + } + + public IUpdateDocumentRequestBuilder WithCollectionId(string collectionId) + { + _request.CollectionId = collectionId; + return this; + } + + public IUpdateDocumentRequestBuilder WithDocumentId(string documentId) + { + _request.DocumentId = documentId; + return this; + } + + public IUpdateDocumentRequestBuilder WithPermissions(List permissions) + { + _request.Permissions = permissions; + return this; + } + + public IUpdateDocumentRequestBuilder AddPermission(Permission permission) + { + _request.Permissions.Add(permission); + return this; + } + + public IUpdateDocumentRequestBuilder AddField(string name, object? value) + { + _data[name] = value; + return this; + } + + public UpdateDocumentRequest Build() + { + _request.Data = _data; + return _request; + } +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestGeneric.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestGeneric.cs new file mode 100644 index 00000000..efa83673 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestGeneric.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to update a document +/// +public class UpdateDocumentRequest : DatabaseCollectionDocumentIdBaseRequest +{ + /// + /// Document data. Include only attribute and value pairs to be updated. Build this up using + /// + [JsonPropertyName("data")] + public Dictionary Data { get; set; } = []; + + /// + /// An array of permissions strings. By default, the current permissions are inherited. Learn more about permissions. + /// + [JsonPropertyName("permissions")] + [JsonConverter(typeof(PermissionListConverter))] + public List Permissions { get; set; } = []; +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDocumentRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDocumentRequestValidator.cs new file mode 100644 index 00000000..bf1a5baa --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDocumentRequestValidator.cs @@ -0,0 +1,18 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class UpdateDocumentRequestValidator : AbstractValidator +{ + public UpdateDocumentRequestValidator() + { + Include(new DatabaseCollectionDocumentIdBaseRequestValidator()); + + RuleFor(x => x.Data) + .NotNull() + .WithMessage("Data is required."); + + RuleFor(x => x.Permissions) + .NotNull() + .WithMessage("Permissions cannot be null."); + } +} From 69a1d6a6e3b47f4ad008086c4d01016c5ffff37f Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 14 Nov 2024 19:16:27 +0000 Subject: [PATCH 155/303] Added tests for update document request builder --- ...estGeneric.cs => UpdateDocumentRequest.cs} | 9 +- .../CreateDocumentRequestBuilderTests.cs | 38 ++++ .../Databases/UpdateDocumentRequestTests.cs | 195 ++++++++++++++++++ 3 files changed, 241 insertions(+), 1 deletion(-) rename src/PinguApps.Appwrite.Shared/Requests/Databases/{UpdateDocumentRequestGeneric.cs => UpdateDocumentRequest.cs} (76%) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestGeneric.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequest.cs similarity index 76% rename from src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestGeneric.cs rename to src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequest.cs index efa83673..ff940f18 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestGeneric.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequest.cs @@ -7,10 +7,12 @@ namespace PinguApps.Appwrite.Shared.Requests.Databases; /// -/// The request to update a document +/// The request to update a document. Can only be created with /// public class UpdateDocumentRequest : DatabaseCollectionDocumentIdBaseRequest { + internal UpdateDocumentRequest() { } + /// /// Document data. Include only attribute and value pairs to be updated. Build this up using /// @@ -23,4 +25,9 @@ public class UpdateDocumentRequest : DatabaseCollectionDocumentIdBaseRequest Permissions { get; set; } = []; + + /// + /// Creates a new builder for creating a document request + /// + public static IUpdateDocumentRequestBuilder CreateBuilder() => new UpdateDocumentRequestBuilder(); } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestBuilderTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestBuilderTests.cs index b9c6998b..4c684fa5 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestBuilderTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestBuilderTests.cs @@ -15,6 +15,38 @@ public void CreateBuilder_ReturnsNewBuilderInstance() Assert.IsAssignableFrom(builder); } + [Fact] + public void WithDatabaseId_SetsDatabaseId_ReturnsBuilder() + { + // Arrange + var builder = CreateDocumentRequest.CreateBuilder(); + var databaseId = IdUtils.GenerateUniqueId(); + + // Act + var result = builder.WithDatabaseId(databaseId); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Equal(databaseId, request.DatabaseId); + } + + [Fact] + public void WithCollectionId_SetsCollectionId_ReturnsBuilder() + { + // Arrange + var builder = CreateDocumentRequest.CreateBuilder(); + var collectionId = IdUtils.GenerateUniqueId(); + + // Act + var result = builder.WithCollectionId(collectionId); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Equal(collectionId, request.CollectionId); + } + [Fact] public void WithDocumentId_SetsDocumentId_ReturnsBuilder() { @@ -126,6 +158,8 @@ public void AddField_CanAddMultipleFields_ReturnsBuilder() public void Build_CreatesRequestWithAllSetValues() { // Arrange + var databaseId = IdUtils.GenerateUniqueId(); + var collectionId = IdUtils.GenerateUniqueId(); var documentId = IdUtils.GenerateUniqueId(); var permissions = new List { Permission.Read().Any() }; const string fieldName = "testField"; @@ -133,12 +167,16 @@ public void Build_CreatesRequestWithAllSetValues() // Act var request = CreateDocumentRequest.CreateBuilder() + .WithDatabaseId(databaseId) + .WithCollectionId(collectionId) .WithDocumentId(documentId) .WithPermissions(permissions) .AddField(fieldName, fieldValue) .Build(); // Assert + Assert.Equal(databaseId, request.DatabaseId); + Assert.Equal(collectionId, request.CollectionId); Assert.Equal(documentId, request.DocumentId); Assert.Same(permissions, request.Permissions); Assert.Equal(fieldValue, request.Data[fieldName]); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestTests.cs new file mode 100644 index 00000000..6191af56 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestTests.cs @@ -0,0 +1,195 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateDocumentRequestTests +{ + [Fact] + public void CreateBuilder_ReturnsNewBuilderInstance() + { + // Act + var builder = UpdateDocumentRequest.CreateBuilder(); + + // Assert + Assert.NotNull(builder); + Assert.IsAssignableFrom(builder); + } + + [Fact] + public void WithDatabaseId_SetsDatabaseId_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var databaseId = IdUtils.GenerateUniqueId(); + + // Act + var result = builder.WithDatabaseId(databaseId); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Equal(databaseId, request.DatabaseId); + } + + [Fact] + public void WithCollectionId_SetsCollectionId_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var collectionId = IdUtils.GenerateUniqueId(); + + // Act + var result = builder.WithCollectionId(collectionId); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Equal(collectionId, request.CollectionId); + } + + [Fact] + public void WithDocumentId_SetsDocumentId_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var documentId = IdUtils.GenerateUniqueId(); + + // Act + var result = builder.WithDocumentId(documentId); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Equal(documentId, request.DocumentId); + } + + [Fact] + public void WithPermissions_SetsPermissions_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var permissions = new List { Permission.Read().Any() }; + + // Act + var result = builder.WithPermissions(permissions); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Same(permissions, request.Permissions); + } + + [Fact] + public void AddPermission_AddsPermissionToList_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var permission = Permission.Read().Any(); + + // Act + var result = builder.AddPermission(permission); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Contains(permission, request.Permissions); + Assert.Single(request.Permissions); + } + + [Fact] + public void AddPermission_CanAddMultiplePermissions_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var permission1 = Permission.Read().Any(); + var permission2 = Permission.Write().Any(); + + // Act + builder.AddPermission(permission1) + .AddPermission(permission2); + var request = builder.Build(); + + // Assert + Assert.Equal(2, request.Permissions.Count); + Assert.Contains(permission1, request.Permissions); + Assert.Contains(permission2, request.Permissions); + } + + [Fact] + public void AddField_AddsFieldToData_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + const string fieldName = "testField"; + const string fieldValue = "testValue"; + + // Act + var result = builder.AddField(fieldName, fieldValue); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Equal(fieldValue, request.Data[fieldName]); + } + + [Fact] + public void AddField_CanAddMultipleFields_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + + // Act + builder.AddField("string", "value") + .AddField("number", 42) + .AddField("boolean", true) + .AddField("null", null); + + var request = builder.Build(); + + // Assert + Assert.Equal(4, request.Data.Count); + Assert.Equal("value", request.Data["string"]); + Assert.Equal(42, request.Data["number"]); + Assert.Equal(true, request.Data["boolean"]); + Assert.Null(request.Data["null"]); + } + + [Fact] + public void Build_CreatesRequestWithAllSetValues() + { + // Arrange + var databaseId = IdUtils.GenerateUniqueId(); + var collectionId = IdUtils.GenerateUniqueId(); + var documentId = IdUtils.GenerateUniqueId(); + var permissions = new List { Permission.Read().Any() }; + const string fieldName = "testField"; + const string fieldValue = "testValue"; + + // Act + var request = UpdateDocumentRequest.CreateBuilder() + .WithDatabaseId(databaseId) + .WithCollectionId(collectionId) + .WithDocumentId(documentId) + .WithPermissions(permissions) + .AddField(fieldName, fieldValue) + .Build(); + + // Assert + Assert.Equal(databaseId, request.DatabaseId); + Assert.Equal(collectionId, request.CollectionId); + Assert.Equal(documentId, request.DocumentId); + Assert.Same(permissions, request.Permissions); + Assert.Equal(fieldValue, request.Data[fieldName]); + } + + [Fact] + public void Build_WithNoFieldsAdded_CreatesEmptyDataDictionary() + { + // Act + var request = UpdateDocumentRequest.CreateBuilder().Build(); + + // Assert + Assert.NotNull(request.Data); + Assert.Empty(request.Data); + } +} From 399e31d01bc9ab03b07a051bbc70b218190baba8 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 14 Nov 2024 19:27:47 +0000 Subject: [PATCH 156/303] partial completion of tests for update document request ALSO - Add a utility to make adding stuff easier as we won't know what fields have changed in advance --- .../UpdateDocumentRequestBuilderTests.cs | 195 ++++++++++++ .../Databases/UpdateDocumentRequestTests.cs | 282 +++++++++--------- 2 files changed, 331 insertions(+), 146 deletions(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestBuilderTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestBuilderTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestBuilderTests.cs new file mode 100644 index 00000000..16520bc8 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestBuilderTests.cs @@ -0,0 +1,195 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class UpdateDocumentRequestBuilderTests +{ + [Fact] + public void CreateBuilder_ReturnsNewBuilderInstance() + { + // Act + var builder = UpdateDocumentRequest.CreateBuilder(); + + // Assert + Assert.NotNull(builder); + Assert.IsAssignableFrom(builder); + } + + [Fact] + public void WithDatabaseId_SetsDatabaseId_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var databaseId = IdUtils.GenerateUniqueId(); + + // Act + var result = builder.WithDatabaseId(databaseId); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Equal(databaseId, request.DatabaseId); + } + + [Fact] + public void WithCollectionId_SetsCollectionId_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var collectionId = IdUtils.GenerateUniqueId(); + + // Act + var result = builder.WithCollectionId(collectionId); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Equal(collectionId, request.CollectionId); + } + + [Fact] + public void WithDocumentId_SetsDocumentId_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var documentId = IdUtils.GenerateUniqueId(); + + // Act + var result = builder.WithDocumentId(documentId); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Equal(documentId, request.DocumentId); + } + + [Fact] + public void WithPermissions_SetsPermissions_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var permissions = new List { Permission.Read().Any() }; + + // Act + var result = builder.WithPermissions(permissions); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Same(permissions, request.Permissions); + } + + [Fact] + public void AddPermission_AddsPermissionToList_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var permission = Permission.Read().Any(); + + // Act + var result = builder.AddPermission(permission); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Contains(permission, request.Permissions); + Assert.Single(request.Permissions); + } + + [Fact] + public void AddPermission_CanAddMultiplePermissions_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var permission1 = Permission.Read().Any(); + var permission2 = Permission.Write().Any(); + + // Act + builder.AddPermission(permission1) + .AddPermission(permission2); + var request = builder.Build(); + + // Assert + Assert.Equal(2, request.Permissions.Count); + Assert.Contains(permission1, request.Permissions); + Assert.Contains(permission2, request.Permissions); + } + + [Fact] + public void AddField_AddsFieldToData_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + const string fieldName = "testField"; + const string fieldValue = "testValue"; + + // Act + var result = builder.AddField(fieldName, fieldValue); + var request = result.Build(); + + // Assert + Assert.Same(builder, result); + Assert.Equal(fieldValue, request.Data[fieldName]); + } + + [Fact] + public void AddField_CanAddMultipleFields_ReturnsBuilder() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + + // Act + builder.AddField("string", "value") + .AddField("number", 42) + .AddField("boolean", true) + .AddField("null", null); + + var request = builder.Build(); + + // Assert + Assert.Equal(4, request.Data.Count); + Assert.Equal("value", request.Data["string"]); + Assert.Equal(42, request.Data["number"]); + Assert.Equal(true, request.Data["boolean"]); + Assert.Null(request.Data["null"]); + } + + [Fact] + public void Build_CreatesRequestWithAllSetValues() + { + // Arrange + var databaseId = IdUtils.GenerateUniqueId(); + var collectionId = IdUtils.GenerateUniqueId(); + var documentId = IdUtils.GenerateUniqueId(); + var permissions = new List { Permission.Read().Any() }; + const string fieldName = "testField"; + const string fieldValue = "testValue"; + + // Act + var request = UpdateDocumentRequest.CreateBuilder() + .WithDatabaseId(databaseId) + .WithCollectionId(collectionId) + .WithDocumentId(documentId) + .WithPermissions(permissions) + .AddField(fieldName, fieldValue) + .Build(); + + // Assert + Assert.Equal(databaseId, request.DatabaseId); + Assert.Equal(collectionId, request.CollectionId); + Assert.Equal(documentId, request.DocumentId); + Assert.Same(permissions, request.Permissions); + Assert.Equal(fieldValue, request.Data[fieldName]); + } + + [Fact] + public void Build_WithNoFieldsAdded_CreatesEmptyDataDictionary() + { + // Act + var request = UpdateDocumentRequest.CreateBuilder().Build(); + + // Assert + Assert.NotNull(request.Data); + Assert.Empty(request.Data); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestTests.cs index 6191af56..fbff3330 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestTests.cs @@ -1,195 +1,185 @@ using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; -public class UpdateDocumentRequestTests +public class UpdateDocumentRequestTests : DatabaseCollectionDocumentIdBaseRequestTests { - [Fact] - public void CreateBuilder_ReturnsNewBuilderInstance() + protected override UpdateDocumentRequest CreateValidDatabaseCollectionDocumentIdRequest => new() { - // Act - var builder = UpdateDocumentRequest.CreateBuilder(); - - // Assert - Assert.NotNull(builder); - Assert.IsAssignableFrom(builder); - } + Data = new Dictionary + { + { "name", "Pingu" }, + { "age", 25 } + } + }; [Fact] - public void WithDatabaseId_SetsDatabaseId_ReturnsBuilder() + public void Constructor_InitializesWithExpectedValues() { - // Arrange - var builder = UpdateDocumentRequest.CreateBuilder(); - var databaseId = IdUtils.GenerateUniqueId(); - - // Act - var result = builder.WithDatabaseId(databaseId); - var request = result.Build(); + // Arrange & Act + var request = new UpdateDocumentRequest(); // Assert - Assert.Same(builder, result); - Assert.Equal(databaseId, request.DatabaseId); + Assert.Null(request.Data); + Assert.NotNull(request.Permissions); + Assert.Empty(request.Permissions); } [Fact] - public void WithCollectionId_SetsCollectionId_ReturnsBuilder() + public void Properties_CanBeSet() { // Arrange - var builder = UpdateDocumentRequest.CreateBuilder(); - var collectionId = IdUtils.GenerateUniqueId(); - - // Act - var result = builder.WithCollectionId(collectionId); - var request = result.Build(); - - // Assert - Assert.Same(builder, result); - Assert.Equal(collectionId, request.CollectionId); - } - - [Fact] - public void WithDocumentId_SetsDocumentId_ReturnsBuilder() - { - // Arrange - var builder = UpdateDocumentRequest.CreateBuilder(); - var documentId = IdUtils.GenerateUniqueId(); - - // Act - var result = builder.WithDocumentId(documentId); - var request = result.Build(); - - // Assert - Assert.Same(builder, result); - Assert.Equal(documentId, request.DocumentId); - } - - [Fact] - public void WithPermissions_SetsPermissions_ReturnsBuilder() - { - // Arrange - var builder = UpdateDocumentRequest.CreateBuilder(); - var permissions = new List { Permission.Read().Any() }; - - // Act - var result = builder.WithPermissions(permissions); - var request = result.Build(); - - // Assert - Assert.Same(builder, result); - Assert.Same(permissions, request.Permissions); - } - - [Fact] - public void AddPermission_AddsPermissionToList_ReturnsBuilder() - { - // Arrange - var builder = UpdateDocumentRequest.CreateBuilder(); - var permission = Permission.Read().Any(); - - // Act - var result = builder.AddPermission(permission); - var request = result.Build(); + var request = UpdateDocumentRequest + .CreateBuilder() + .AddField("name", "Pingu") + .AddField("age", 30) + .AddPermission(Permission.Read().Any()) + .Build(); // Assert - Assert.Same(builder, result); - Assert.Contains(permission, request.Permissions); + Assert.NotNull(request.Data); + var name = request.Data["name"]; + Assert.Equal("Pingu", name); + var age = request.Data["age"]; + Assert.Equal(30, age); + Assert.NotNull(request.Permissions); Assert.Single(request.Permissions); + Assert.Equal(Permission.Read().Any(), request.Permissions.First()); } - [Fact] - public void AddPermission_CanAddMultiplePermissions_ReturnsBuilder() + public static TheoryData ValidRequestsData => + [ + UpdateDocumentRequest + .CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .AddField("name", "Pingu") + .AddField("age", 25) + .AddPermission(Permission.Read().Any()) + .Build(), + + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = "validDocumentId123", + Data = new CreateDocumentTestData { Name = "Another Valid Name", Age = 30 }, + Permissions = [] + } + ]; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(CreateDocumentRequest request) { - // Arrange - var builder = UpdateDocumentRequest.CreateBuilder(); - var permission1 = Permission.Read().Any(); - var permission2 = Permission.Write().Any(); - // Act - builder.AddPermission(permission1) - .AddPermission(permission2); - var request = builder.Build(); + var isValid = request.IsValid(); // Assert - Assert.Equal(2, request.Permissions.Count); - Assert.Contains(permission1, request.Permissions); - Assert.Contains(permission2, request.Permissions); + Assert.True(isValid); } - [Fact] - public void AddField_AddsFieldToData_ReturnsBuilder() + public static TheoryData> InvalidRequestsData => + [ + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = null!, + Data = new CreateDocumentTestData { Name = "Test", Age = 25 } + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = "", + Data = new CreateDocumentTestData { Name = "Test", Age = 25 } + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = "invalid chars!", + Data = new CreateDocumentTestData { Name = "Test", Age = 25 } + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = ".startsWithSymbol", + Data = new CreateDocumentTestData { Name = "Test", Age = 25 } + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = new string('a', 37), + Data = new CreateDocumentTestData { Name = "Test", Age = 25 } + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + Data = null! + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + Data = new CreateDocumentTestData { Name = "Test", Age = 25 }, + Permissions = null! + } + ]; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(CreateDocumentRequest request) { - // Arrange - var builder = UpdateDocumentRequest.CreateBuilder(); - const string fieldName = "testField"; - const string fieldValue = "testValue"; - // Act - var result = builder.AddField(fieldName, fieldValue); - var request = result.Build(); + var isValid = request.IsValid(); // Assert - Assert.Same(builder, result); - Assert.Equal(fieldValue, request.Data[fieldName]); + Assert.False(isValid); } [Fact] - public void AddField_CanAddMultipleFields_ReturnsBuilder() + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() { // Arrange - var builder = UpdateDocumentRequest.CreateBuilder(); - - // Act - builder.AddField("string", "value") - .AddField("number", 42) - .AddField("boolean", true) - .AddField("null", null); - - var request = builder.Build(); + var request = new CreateDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = "", + Data = null!, + Permissions = null! + }; // Assert - Assert.Equal(4, request.Data.Count); - Assert.Equal("value", request.Data["string"]); - Assert.Equal(42, request.Data["number"]); - Assert.Equal(true, request.Data["boolean"]); - Assert.Null(request.Data["null"]); + Assert.Throws(() => request.Validate(true)); } [Fact] - public void Build_CreatesRequestWithAllSetValues() + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() { // Arrange - var databaseId = IdUtils.GenerateUniqueId(); - var collectionId = IdUtils.GenerateUniqueId(); - var documentId = IdUtils.GenerateUniqueId(); - var permissions = new List { Permission.Read().Any() }; - const string fieldName = "testField"; - const string fieldValue = "testValue"; - - // Act - var request = UpdateDocumentRequest.CreateBuilder() - .WithDatabaseId(databaseId) - .WithCollectionId(collectionId) - .WithDocumentId(documentId) - .WithPermissions(permissions) - .AddField(fieldName, fieldValue) - .Build(); - - // Assert - Assert.Equal(databaseId, request.DatabaseId); - Assert.Equal(collectionId, request.CollectionId); - Assert.Equal(documentId, request.DocumentId); - Assert.Same(permissions, request.Permissions); - Assert.Equal(fieldValue, request.Data[fieldName]); - } + var request = new CreateDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = "", + Data = null!, + Permissions = null! + }; - [Fact] - public void Build_WithNoFieldsAdded_CreatesEmptyDataDictionary() - { // Act - var request = UpdateDocumentRequest.CreateBuilder().Build(); + var result = request.Validate(false); // Assert - Assert.NotNull(request.Data); - Assert.Empty(request.Data); + Assert.False(result.IsValid); } } From 9129c26442a7eb4c9e01db76196cd23b673265e4 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 14 Nov 2024 21:26:50 +0000 Subject: [PATCH 157/303] Completed tests for update document request --- .../Databases/UpdateDocumentRequestTests.cs | 142 ++++++++---------- 1 file changed, 61 insertions(+), 81 deletions(-) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestTests.cs index fbff3330..fbd6a62f 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestTests.cs @@ -1,4 +1,5 @@ -using PinguApps.Appwrite.Shared.Requests.Databases; +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; using PinguApps.Appwrite.Shared.Requests.Databases.Validators; using PinguApps.Appwrite.Shared.Utils; @@ -21,7 +22,8 @@ public void Constructor_InitializesWithExpectedValues() var request = new UpdateDocumentRequest(); // Assert - Assert.Null(request.Data); + Assert.NotNull(request.Data); + Assert.Empty(request.Data); Assert.NotNull(request.Permissions); Assert.Empty(request.Permissions); } @@ -45,7 +47,6 @@ public void Properties_CanBeSet() Assert.Equal(30, age); Assert.NotNull(request.Permissions); Assert.Single(request.Permissions); - Assert.Equal(Permission.Read().Any(), request.Permissions.First()); } public static TheoryData ValidRequestsData => @@ -59,20 +60,19 @@ public void Properties_CanBeSet() .AddField("age", 25) .AddPermission(Permission.Read().Any()) .Build(), - - new() - { - DatabaseId = IdUtils.GenerateUniqueId(), - CollectionId = IdUtils.GenerateUniqueId(), - DocumentId = "validDocumentId123", - Data = new CreateDocumentTestData { Name = "Another Valid Name", Age = 30 }, - Permissions = [] - } + UpdateDocumentRequest + .CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .AddField("name", "Valid Name") + .AddField("age", 30) + .Build() ]; [Theory] [MemberData(nameof(ValidRequestsData))] - public void IsValid_WithValidData_ReturnsTrue(CreateDocumentRequest request) + public void IsValid_WithValidData_ReturnsTrue(UpdateDocumentRequest request) { // Act var isValid = request.IsValid(); @@ -81,63 +81,41 @@ public void IsValid_WithValidData_ReturnsTrue(CreateDocumentRequest> InvalidRequestsData => - [ - new() - { - DatabaseId = IdUtils.GenerateUniqueId(), - CollectionId = IdUtils.GenerateUniqueId(), - DocumentId = null!, - Data = new CreateDocumentTestData { Name = "Test", Age = 25 } - }, - new() - { - DatabaseId = IdUtils.GenerateUniqueId(), - CollectionId = IdUtils.GenerateUniqueId(), - DocumentId = "", - Data = new CreateDocumentTestData { Name = "Test", Age = 25 } - }, - new() - { - DatabaseId = IdUtils.GenerateUniqueId(), - CollectionId = IdUtils.GenerateUniqueId(), - DocumentId = "invalid chars!", - Data = new CreateDocumentTestData { Name = "Test", Age = 25 } - }, - new() - { - DatabaseId = IdUtils.GenerateUniqueId(), - CollectionId = IdUtils.GenerateUniqueId(), - DocumentId = ".startsWithSymbol", - Data = new CreateDocumentTestData { Name = "Test", Age = 25 } - }, - new() - { - DatabaseId = IdUtils.GenerateUniqueId(), - CollectionId = IdUtils.GenerateUniqueId(), - DocumentId = new string('a', 37), - Data = new CreateDocumentTestData { Name = "Test", Age = 25 } - }, - new() - { - DatabaseId = IdUtils.GenerateUniqueId(), - CollectionId = IdUtils.GenerateUniqueId(), - DocumentId = IdUtils.GenerateUniqueId(), - Data = null! - }, - new() + public static TheoryData InvalidRequestsData + { + get { - DatabaseId = IdUtils.GenerateUniqueId(), - CollectionId = IdUtils.GenerateUniqueId(), - DocumentId = IdUtils.GenerateUniqueId(), - Data = new CreateDocumentTestData { Name = "Test", Age = 25 }, - Permissions = null! + var data = new TheoryData(); + + var nullData = UpdateDocumentRequest + .CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .Build(); + + nullData.Data = null!; + + data.Add(nullData); + + var nullPermissions = UpdateDocumentRequest + .CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .Build(); + + nullPermissions.Permissions = null!; + + data.Add(nullPermissions); + + return data; } - ]; + } [Theory] [MemberData(nameof(InvalidRequestsData))] - public void IsValid_WithInvalidData_ReturnsFalse(CreateDocumentRequest request) + public void IsValid_WithInvalidData_ReturnsFalse(UpdateDocumentRequest request) { // Act var isValid = request.IsValid(); @@ -150,14 +128,15 @@ public void IsValid_WithInvalidData_ReturnsFalse(CreateDocumentRequest - { - DatabaseId = IdUtils.GenerateUniqueId(), - CollectionId = IdUtils.GenerateUniqueId(), - DocumentId = "", - Data = null!, - Permissions = null! - }; + var request = UpdateDocumentRequest + .CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .Build(); + + request.Data = null!; + request.Permissions = null!; // Assert Assert.Throws(() => request.Validate(true)); @@ -167,14 +146,15 @@ public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure( public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() { // Arrange - var request = new CreateDocumentRequest - { - DatabaseId = IdUtils.GenerateUniqueId(), - CollectionId = IdUtils.GenerateUniqueId(), - DocumentId = "", - Data = null!, - Permissions = null! - }; + var request = UpdateDocumentRequest + .CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .Build(); + + request.Data = null!; + request.Permissions = null!; // Act var result = request.Validate(false); From ba71303f85c2c73edc0989de06bcc8850d031b3a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 14 Nov 2024 21:42:51 +0000 Subject: [PATCH 158/303] Added utility to compare a before and after object in order to update changes to a document in the builder --- .../IUpdateDocumentRequestBuilder.cs | 1 + .../Databases/UpdateDocumentRequestBuilder.cs | 69 ++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/IUpdateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/IUpdateDocumentRequestBuilder.cs index aa299e78..b6052f2b 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/IUpdateDocumentRequestBuilder.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/IUpdateDocumentRequestBuilder.cs @@ -41,4 +41,5 @@ public interface IUpdateDocumentRequestBuilder /// Builds the document request /// UpdateDocumentRequest Build(); + IUpdateDocumentRequestBuilder WithChanges(T before, T after) where T : class; } diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs index 547f8d68..79aa0d6b 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Requests.Databases; @@ -43,9 +47,72 @@ public IUpdateDocumentRequestBuilder AddField(string name, object? value) return this; } + public IUpdateDocumentRequestBuilder WithChanges(T before, T after) where T : class + { + if (before is null) + { + throw new ArgumentNullException(nameof(before)); + } + if (after is null) + { + throw new ArgumentNullException(nameof(after)); + } + + var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); + + foreach (var property in properties) + { + if (!property.CanRead) continue; + + var beforeValue = property.GetValue(before); + var afterValue = property.GetValue(after); + + if (!AreValuesEqual(beforeValue, afterValue)) + { + var jsonPropertyName = GetJsonPropertyName(property); + AddField(jsonPropertyName, afterValue); + } + } + + return this; + } + + public UpdateDocumentRequest Build() { _request.Data = _data; return _request; } + + private static string GetJsonPropertyName(PropertyInfo property) + { + var jsonPropertyAttribute = property.GetCustomAttribute(); + + return jsonPropertyAttribute?.Name ?? property.Name; + } + + private static bool AreValuesEqual(object? value1, object? value2) + { + if (ReferenceEquals(value1, value2)) + { + return true; + } + + if (value1 is null && value2 is null) + { + return true; + } + + if (value1 is null || value2 is null) + { + return false; + } + + if (value1 is IEnumerable enumerable1 && value2 is IEnumerable enumerable2) + { + return enumerable1.SequenceEqual(enumerable2); + } + + return value1.Equals(value2); + } } From 677b415b3f15fdd0186f67057b08052560ef241b Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 14 Nov 2024 21:48:13 +0000 Subject: [PATCH 159/303] added tests to cover additional functionality within update docuemnt request builder --- .../Databases/UpdateDocumentRequestBuilder.cs | 5 - .../UpdateDocumentRequestBuilderTests.cs | 156 +++++++++++++++++- 2 files changed, 155 insertions(+), 6 deletions(-) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs index 79aa0d6b..05cc9ab6 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs @@ -98,11 +98,6 @@ private static bool AreValuesEqual(object? value1, object? value2) return true; } - if (value1 is null && value2 is null) - { - return true; - } - if (value1 is null || value2 is null) { return false; diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestBuilderTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestBuilderTests.cs index 16520bc8..11feb85c 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestBuilderTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestBuilderTests.cs @@ -1,4 +1,5 @@ -using PinguApps.Appwrite.Shared.Requests.Databases; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases; using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; @@ -192,4 +193,157 @@ public void Build_WithNoFieldsAdded_CreatesEmptyDataDictionary() Assert.NotNull(request.Data); Assert.Empty(request.Data); } + + private class TestModel + { + public string? RegularProperty { get; set; } + + [JsonPropertyName("custom_name")] + public string? PropertyWithJsonName { get; set; } + + // Property that can't be read + public string WriteOnlyProperty + { + set { } + } + + public IEnumerable? CollectionProperty { get; set; } + } + + [Fact] + public void WithChanges_WhenBeforeIsNull_ThrowsArgumentNullException() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + TestModel? before = null; + var after = new TestModel(); + + // Act & Assert + var exception = Assert.Throws(() => builder.WithChanges(before!, after)); + Assert.Equal("before", exception.ParamName); + } + + [Fact] + public void WithChanges_WhenAfterIsNull_ThrowsArgumentNullException() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var before = new TestModel(); + TestModel? after = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.WithChanges(before, after!)); + Assert.Equal("after", exception.ParamName); + } + + [Fact] + public void WithChanges_WithWriteOnlyProperty_SkipsProperty() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var before = new TestModel(); + var after = new TestModel(); + + // Act + var request = builder.WithChanges(before, after).Build(); + + // Assert + Assert.Empty(request.Data); + } + + [Fact] + public void WithChanges_WithJsonPropertyNameAttribute_UsesCustomName() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var before = new TestModel { PropertyWithJsonName = "old" }; + var after = new TestModel { PropertyWithJsonName = "new" }; + + // Act + var request = builder.WithChanges(before, after).Build(); + + // Assert + Assert.True(request.Data.ContainsKey("custom_name")); + Assert.Equal("new", request.Data["custom_name"]); + } + + [Fact] + public void WithChanges_BothValuesNull_NoChange() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var before = new TestModel { RegularProperty = null }; + var after = new TestModel { RegularProperty = null }; + + // Act + var request = builder.WithChanges(before, after).Build(); + + // Assert + Assert.Empty(request.Data); + } + + [Fact] + public void WithChanges_CollectionPropertyChanged_AddsToData() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var before = new TestModel { CollectionProperty = [new object(), new object()] }; + var after = new TestModel { CollectionProperty = [new object()] }; + + // Act + var request = builder.WithChanges(before, after).Build(); + + // Assert + Assert.True(request.Data.ContainsKey("CollectionProperty")); + Assert.Equal(after.CollectionProperty, request.Data["CollectionProperty"]); + } + + [Fact] + public void WithChanges_CollectionPropertySameReference_NoChange() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var collection = new[] { new object() }; + var before = new TestModel { CollectionProperty = collection }; + var after = new TestModel { CollectionProperty = collection }; + + // Act + var request = builder.WithChanges(before, after).Build(); + + // Assert + Assert.Empty(request.Data); + } + + [Fact] + public void WithChanges_PropertyChangedFromNullToValue_AddsToData() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var before = new TestModel { RegularProperty = null }; + var after = new TestModel { RegularProperty = "value" }; + + // Act + var request = builder.WithChanges(before, after).Build(); + + // Assert + Assert.True(request.Data.ContainsKey("RegularProperty")); + Assert.Equal("value", request.Data["RegularProperty"]); + } + + [Fact] + public void WithChanges_PropertyChangedFromValueToNull_AddsToData() + { + // Arrange + var builder = UpdateDocumentRequest.CreateBuilder(); + var before = new TestModel { RegularProperty = "value" }; + var after = new TestModel { RegularProperty = null }; + + // Act + var request = builder.WithChanges(before, after).Build(); + + // Assert + Assert.True(request.Data.ContainsKey("RegularProperty")); + Assert.Null(request.Data["RegularProperty"]); + } + } From df4e35150ac724fce7972fded160ec3458853401 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 09:20:24 +0000 Subject: [PATCH 160/303] Update dependency FluentValidation to 11.11.0 --- src/PinguApps.Appwrite.Shared/PinguApps.Appwrite.Shared.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PinguApps.Appwrite.Shared/PinguApps.Appwrite.Shared.csproj b/src/PinguApps.Appwrite.Shared/PinguApps.Appwrite.Shared.csproj index 877e89da..4dbf2dcd 100644 --- a/src/PinguApps.Appwrite.Shared/PinguApps.Appwrite.Shared.csproj +++ b/src/PinguApps.Appwrite.Shared/PinguApps.Appwrite.Shared.csproj @@ -7,7 +7,7 @@ - + From b63c14fe9c33bb2e8cc39e4f6377b83f0b3739a9 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 19 Nov 2024 18:43:45 +0000 Subject: [PATCH 161/303] #538 Ensured that PR's labelled as dependencies cannot be marked as stale --- .github/workflows/stale.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 3b84a8fc..e5610f10 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -20,3 +20,4 @@ jobs: close-issue-message: 'Closing issue as it has been marked as stale for 3 days.' close-pr-message: 'Closing PR as it has been marked as stale for 3 days.' exempt-all-milestones: true + exempt-pr-labels: 'dependencies' From 471140d70f68bc68e8d9b71a22eae5a17effaa7c Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 19 Nov 2024 18:50:19 +0000 Subject: [PATCH 162/303] addded delete document request --- .../Requests/Databases/DeleteDocumentRequest.cs | 10 ++++++++++ .../Requests/Databases/GetDocumentRequest.cs | 4 ++++ .../Validators/DeleteDocumentRequestValidator.cs | 10 ++++++++++ 3 files changed, 24 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteDocumentRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteDocumentRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteDocumentRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteDocumentRequest.cs new file mode 100644 index 00000000..089a17c7 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteDocumentRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to delete a document +/// +public class DeleteDocumentRequest : DatabaseCollectionDocumentIdBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/GetDocumentRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/GetDocumentRequest.cs index 94207c9a..924f2055 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/GetDocumentRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/GetDocumentRequest.cs @@ -5,6 +5,10 @@ using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to get a document +/// public class GetDocumentRequest : DatabaseCollectionDocumentIdBaseRequest { /// diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteDocumentRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteDocumentRequestValidator.cs new file mode 100644 index 00000000..88bcb83f --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteDocumentRequestValidator.cs @@ -0,0 +1,10 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class DeleteDocumentRequestValidator : AbstractValidator +{ + public DeleteDocumentRequestValidator() + { + Include(new DatabaseCollectionDocumentIdBaseRequestValidator()); + } +} From 21d89ff449596158b5c381e8b9c1d323ad27a06a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 19 Nov 2024 18:52:13 +0000 Subject: [PATCH 163/303] added tests for delete document request --- .../Requests/Databases/DeleteDocumentRequestTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteDocumentRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteDocumentRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteDocumentRequestTests.cs new file mode 100644 index 00000000..919a8ace --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteDocumentRequestTests.cs @@ -0,0 +1,8 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class DeleteDocumentRequestTests : DatabaseCollectionDocumentIdBaseRequestTests +{ + protected override DeleteDocumentRequest CreateValidDatabaseCollectionDocumentIdRequest => new(); +} From 37d1f0ca05a7469e71909d193e97bcdc713f4e63 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 19 Nov 2024 18:55:59 +0000 Subject: [PATCH 164/303] added request for list indexes --- .../Requests/Databases/ListIndexesRequest.cs | 20 +++++++++++++++++++ .../Validators/ListIndexesRequestValidator.cs | 16 +++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/ListIndexesRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListIndexesRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/ListIndexesRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/ListIndexesRequest.cs new file mode 100644 index 00000000..4161d9e5 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/ListIndexesRequest.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Attributes; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to list indexes of a collection +/// +public class ListIndexesRequest : DatabaseCollectionIdBaseRequest +{ + /// + /// Array of query strings generated using the Query class provided by the SDK. Learn more about queries. Maximum of 100 queries are allowed, each 4096 characters long. + /// + [JsonPropertyName("queries")] + [SdkExclude] + public List? Queries { get; set; } = null; +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListIndexesRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListIndexesRequestValidator.cs new file mode 100644 index 00000000..00acc4b5 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/ListIndexesRequestValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class ListIndexesRequestValidator : AbstractValidator +{ + public ListIndexesRequestValidator() + { + Include(new DatabaseCollectionIdBaseRequestValidator()); + + RuleFor(x => x.Queries) + .Must(queries => queries is null || queries.Count <= 100) + .WithMessage("A maximum of 100 queries are allowed.") + .ForEach(query => query.Must(q => q.GetQueryString().Length <= 4096) + .WithMessage("Each query can be a maximum of 4096 characters long.")); + } +} From 97cb5da5b0453e629f80097d23d2601ab1d0b911 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 19 Nov 2024 18:59:18 +0000 Subject: [PATCH 165/303] added tests for list indexes request --- .../Databases/GetDocumentRequestTests.cs | 1 - .../Databases/ListIndexesRequestTests.cs | 144 ++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListIndexesRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetDocumentRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetDocumentRequestTests.cs index e466a7fc..9dcd6c89 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetDocumentRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetDocumentRequestTests.cs @@ -8,7 +8,6 @@ public class GetDocumentRequestTests : DatabaseCollectionDocumentIdBaseRequestTe { protected override GetDocumentRequest CreateValidDatabaseCollectionDocumentIdRequest => new(); - [Fact] public void QueryBase_Constructor_InitializesWithExpectedValues() { diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListIndexesRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListIndexesRequestTests.cs new file mode 100644 index 00000000..760b15c8 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/ListIndexesRequestTests.cs @@ -0,0 +1,144 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class ListIndexesRequestTests : DatabaseCollectionIdBaseRequestTests +{ + protected override ListIndexesRequest CreateValidDatabaseCollectionIdRequest => new(); + + + [Fact] + public void QueryBase_Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new ListIndexesRequest(); + + // Assert + Assert.Null(request.Queries); + } + + [Fact] + public void QueryBase_Properties_CanBeSet() + { + // Arrange + var attributeName = "attributeName"; + var value = "value"; + List queries = [Query.Equal(attributeName, value)]; + + var request = new ListIndexesRequest(); + + // Act + request.Queries = queries; + + // Assert + Assert.Collection(request.Queries, query => + { + Assert.Equal(attributeName, query.Attribute); + Assert.NotNull(query.Values); + Assert.Collection(query.Values, v => + { + Assert.Equal(value, v); + }); + Assert.Equal("equal", query.Method); + }); + } + + [Fact] + public void QueryBase_IsValid_WithValidData_ReturnsTrue() + { + // Arrange + var request = new ListIndexesRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Queries = [Query.Equal("attributeName", "value")]; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Fact] + public void QueryBase_IsValid_WithNullQueries_ReturnsTrue() + { + // Arrange + var request = new ListIndexesRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + [Fact] + public void QueryBase_IsValid_WithInvalidData_QueryTooLarge_ReturnsFalse() + { + // Arrange + var request = new ListIndexesRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Queries = [Query.Equal("attributeName", new string('a', 4097))]; + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void QueryBase_IsValid_WithInvalidData_TooManyQueries_ReturnsFalse() + { + // Arrange + var request = new ListIndexesRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.Queries = Enumerable.Range(0, 101) + .Select(_ => Query.Equal("attributeName", "value")) + .ToList(); + + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void QueryBase_Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new ListIndexesRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Queries = Enumerable.Range(0, 101) + .Select(_ => Query.Equal("attributeName", "value")) + .ToList(); + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void QueryBase_Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new ListIndexesRequest(); + request.DatabaseId = IdUtils.GenerateUniqueId(); + request.CollectionId = IdUtils.GenerateUniqueId(); + request.Queries = Enumerable.Range(0, 101) + .Select(_ => Query.Equal("attributeName", "value")) + .ToList(); + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From c9f422dcde43ff65dae17a92110e245ef53b5322 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 19 Nov 2024 19:17:05 +0000 Subject: [PATCH 166/303] added upper case enum converter --- .../Converters/UpperCaseEnumConverter.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Converters/UpperCaseEnumConverter.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/UpperCaseEnumConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/UpperCaseEnumConverter.cs new file mode 100644 index 00000000..7b45732e --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/UpperCaseEnumConverter.cs @@ -0,0 +1,23 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace PinguApps.Appwrite.Shared.Converters; +public class UpperCaseEnumConverter : JsonConverter +{ + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert.IsEnum; + } + + public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var value = reader.GetString(); + return Enum.Parse(typeToConvert, value, true); + } + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString().ToUpper()); + } +} From a3b9b1a16f3a28506bb957083f1d7f8779be6f29 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 19 Nov 2024 19:21:59 +0000 Subject: [PATCH 167/303] added upper case enum list converter --- .../Converters/UpperCaseEnumListConverter.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Converters/UpperCaseEnumListConverter.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/UpperCaseEnumListConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/UpperCaseEnumListConverter.cs new file mode 100644 index 00000000..a1d4f380 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/UpperCaseEnumListConverter.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace PinguApps.Appwrite.Shared.Converters; +public class UpperCaseEnumListConverter : JsonConverter> where T : Enum +{ + public override List Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType is not JsonTokenType.StartArray) + { + throw new JsonException("Expected start of array"); + } + + var list = new List(); + + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndArray) + { + return list; + } + + var value = reader.GetString(); + list.Add((T)Enum.Parse(typeof(T), value, true)); + } + + throw new JsonException("Expected end of array"); + } + + public override void Write(Utf8JsonWriter writer, List value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + + foreach (var item in value) + { + writer.WriteStringValue(item.ToString().ToUpper()); + } + + writer.WriteEndArray(); + } +} From e61da5931af96a596fb146875faeff6006be608d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 19 Nov 2024 19:26:22 +0000 Subject: [PATCH 168/303] added sort direction enum --- .../Enums/SortDirection.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Enums/SortDirection.cs diff --git a/src/PinguApps.Appwrite.Shared/Enums/SortDirection.cs b/src/PinguApps.Appwrite.Shared/Enums/SortDirection.cs new file mode 100644 index 00000000..38bbcd98 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Enums/SortDirection.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; + +namespace PinguApps.Appwrite.Shared.Enums; + +/// +/// Indicates the sort direction +/// +[JsonConverter(typeof(UpperCaseEnumConverter))] +public enum SortDirection +{ + Asc, + Desc +} From c1a27240698e0e23b765f743dfb143ea6ccab9a6 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 19 Nov 2024 19:26:32 +0000 Subject: [PATCH 169/303] added create index request (needs validator) --- .../Requests/Databases/CreateIndexRequest.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIndexRequest.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIndexRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIndexRequest.cs new file mode 100644 index 00000000..6b22caaa --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIndexRequest.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create an index on a collection +/// +public class CreateIndexRequest : DatabaseCollectionIdBaseRequest +{ + /// + /// Index Key + /// + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; + + /// + /// Index type + /// + [JsonPropertyName("type")] + [JsonConverter(typeof(CamelCaseEnumConverter))] + public IndexType IndexType { get; set; } + + /// + /// Array of attributes (referenced via their Key) to index. Maximum of 100 attributes are allowed, each 32 characters long. Must match quantity of + /// + [JsonPropertyName("attributes")] + public List Attributes { get; set; } = []; + + /// + /// Array of index orders. Maximum of 100 orders are allowed. Must match quantity of + /// + [JsonPropertyName("orders")] + [JsonConverter(typeof(UpperCaseEnumListConverter))] + public List Orders { get; set; } = []; +} From 058d2f10e80df7b286765e3d457c925438789371 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 05:31:00 +0000 Subject: [PATCH 170/303] Update dependency Microsoft.NET.Test.Sdk to 17.12.0 --- .../PinguApps.Appwrite.Client.Tests.csproj | 2 +- .../PinguApps.Appwrite.Server.Tests.csproj | 2 +- .../PinguApps.Appwrite.Shared.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/PinguApps.Appwrite.Client.Tests/PinguApps.Appwrite.Client.Tests.csproj b/tests/PinguApps.Appwrite.Client.Tests/PinguApps.Appwrite.Client.Tests.csproj index e10d1eef..14178769 100644 --- a/tests/PinguApps.Appwrite.Client.Tests/PinguApps.Appwrite.Client.Tests.csproj +++ b/tests/PinguApps.Appwrite.Client.Tests/PinguApps.Appwrite.Client.Tests.csproj @@ -15,7 +15,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/tests/PinguApps.Appwrite.Server.Tests/PinguApps.Appwrite.Server.Tests.csproj b/tests/PinguApps.Appwrite.Server.Tests/PinguApps.Appwrite.Server.Tests.csproj index 33974eea..8e041aa8 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/PinguApps.Appwrite.Server.Tests.csproj +++ b/tests/PinguApps.Appwrite.Server.Tests/PinguApps.Appwrite.Server.Tests.csproj @@ -15,7 +15,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/tests/PinguApps.Appwrite.Shared.Tests/PinguApps.Appwrite.Shared.Tests.csproj b/tests/PinguApps.Appwrite.Shared.Tests/PinguApps.Appwrite.Shared.Tests.csproj index 562fe4bb..de47fce6 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/PinguApps.Appwrite.Shared.Tests.csproj +++ b/tests/PinguApps.Appwrite.Shared.Tests/PinguApps.Appwrite.Shared.Tests.csproj @@ -15,7 +15,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 411df5034e8abb8420930b303f02cbd2cc08dd77 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 21 Nov 2024 17:55:59 +0000 Subject: [PATCH 171/303] Added validator for Create Index Request --- .../Requests/Databases/CreateIndexRequest.cs | 1 + .../Validators/CreateIndexRequestValidator.cs | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIndexRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIndexRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIndexRequest.cs index 6b22caaa..429b5011 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIndexRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIndexRequest.cs @@ -2,6 +2,7 @@ using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Converters; using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; namespace PinguApps.Appwrite.Shared.Requests.Databases; diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIndexRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIndexRequestValidator.cs new file mode 100644 index 00000000..2ef1694b --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIndexRequestValidator.cs @@ -0,0 +1,33 @@ +using System.Linq; +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class CreateIndexRequestValidator : AbstractValidator +{ + public CreateIndexRequestValidator() + { + Include(new DatabaseCollectionIdBaseRequestValidator()); + + RuleFor(x => x.Key) + .NotEmpty() + .WithMessage("Key is required"); + + RuleFor(x => x.IndexType) + .IsInEnum() + .WithMessage("IndexType must be within the enums range"); + + RuleFor(x => x.Attributes) + .NotNull() + .Must(x => x.Count <= 100) + .WithMessage("Maximum of 100 attributes are allowed") + .Must(x => x.All(attr => attr.Length <= 32)) + .WithMessage("Each attribute must be 32 characters or less"); + + RuleFor(x => x.Orders) + .NotNull() + .Must(x => x.Count <= 100) + .WithMessage("Maximum of 100 orders are allowed") + .Must((model, orders) => orders.Count == model.Attributes.Count) + .WithMessage("Number of orders must match number of attributes"); + } +} From 460fbb13425721059eaf21ccd03779875e97ca6d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 21 Nov 2024 18:39:10 +0000 Subject: [PATCH 172/303] added tests for create index request --- .../Databases/CreateIndexRequestTests.cs | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIndexRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIndexRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIndexRequestTests.cs new file mode 100644 index 00000000..5e29a672 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIndexRequestTests.cs @@ -0,0 +1,195 @@ +using FluentValidation; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class CreateIndexRequestTests : DatabaseCollectionIdBaseRequestTests +{ + protected override CreateIndexRequest CreateValidDatabaseCollectionIdRequest => new() + { + Key = "test_index", + IndexType = IndexType.Key, + Attributes = ["attr1"], + Orders = [SortDirection.Asc] + }; + + [Fact] + public void Constructor_InitializesWithExpectedValues() + { + // Arrange & Act + var request = new CreateIndexRequest(); + + // Assert + Assert.Equal(string.Empty, request.Key); + Assert.Equal(IndexType.Key, request.IndexType); + Assert.Empty(request.Attributes); + Assert.Empty(request.Orders); + } + + [Fact] + public void Properties_CanBeSet() + { + // Arrange + var key = "test_index"; + var indexType = IndexType.Unique; + var attributes = new List { "attr1", "attr2" }; + var orders = new List { SortDirection.Asc, SortDirection.Desc }; + + var request = new CreateIndexRequest(); + + // Act + request.Key = key; + request.IndexType = indexType; + request.Attributes = attributes; + request.Orders = orders; + + // Assert + Assert.Equal(key, request.Key); + Assert.Equal(indexType, request.IndexType); + Assert.Equal(attributes, request.Attributes); + Assert.Equal(orders, request.Orders); + } + + public static TheoryData ValidRequestsData = new() + { + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "valid_index", + IndexType = IndexType.Key, + Attributes = ["attr1"], + Orders = [SortDirection.Asc] + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "multi_index", + IndexType = IndexType.Unique, + Attributes = ["attr1", "attr2"], + Orders = [SortDirection.Asc, SortDirection.Desc] + } + }; + + [Theory] + [MemberData(nameof(ValidRequestsData))] + public void IsValid_WithValidData_ReturnsTrue(CreateIndexRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.True(isValid); + } + + public static TheoryData InvalidRequestsData = new() + { + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "", + IndexType = IndexType.Key, + Attributes = ["attr1"], + Orders = [SortDirection.Asc] + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = null!, + IndexType = IndexType.Key, + Attributes = ["attr1"], + Orders = [SortDirection.Asc] + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "valid_key", + IndexType = (IndexType) 9999, + Attributes = ["attr1"], + Orders = [SortDirection.Asc] + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "valid_key", + IndexType = IndexType.Key, + Attributes = [new string('a', 33)], + Orders = [SortDirection.Asc] + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "valid_key", + IndexType = IndexType.Key, + Attributes = Enumerable.Range(0,101).Select(x => x.ToString()).ToList(), + Orders = Enumerable.Range(0,101).Select(x => SortDirection.Asc).ToList() + }, + new() + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "valid_key", + IndexType = IndexType.Key, + Attributes = ["attr1", "attr2"], + Orders = [SortDirection.Asc] + } + }; + + [Theory] + [MemberData(nameof(InvalidRequestsData))] + public void IsValid_WithInvalidData_ReturnsFalse(CreateIndexRequest request) + { + // Act + var isValid = request.IsValid(); + + // Assert + Assert.False(isValid); + } + + [Fact] + public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() + { + // Arrange + var request = new CreateIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "", + IndexType = IndexType.Key, + Attributes = [], + Orders = [] + }; + + // Assert + Assert.Throws(() => request.Validate(true)); + } + + [Fact] + public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() + { + // Arrange + var request = new CreateIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "", + IndexType = IndexType.Key, + Attributes = [], + Orders = [] + }; + + // Act + var result = request.Validate(false); + + // Assert + Assert.False(result.IsValid); + } +} From 681c637c589305b73618b125089c0437fe1c7a14 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 21 Nov 2024 18:39:24 +0000 Subject: [PATCH 173/303] added tests for upper case enum converter --- .../Converters/UpperCaseEnumConverterTests.cs | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/UpperCaseEnumConverterTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/UpperCaseEnumConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/UpperCaseEnumConverterTests.cs new file mode 100644 index 00000000..afe848b2 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/UpperCaseEnumConverterTests.cs @@ -0,0 +1,104 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Converters; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class UpperCaseEnumConverterTests +{ + public enum TestEnum + { + FirstValue, + SecondValue, + UPPERCASE_VALUE + } + + private readonly UpperCaseEnumConverter _converter; + private readonly JsonSerializerOptions _options; + + public UpperCaseEnumConverterTests() + { + _converter = new UpperCaseEnumConverter(); + _options = new JsonSerializerOptions(); + } + + [Fact] + public void CanConvert_WhenTypeIsEnum_ReturnsTrue() + { + // Arrange + var enumType = typeof(TestEnum); + + // Act + var result = _converter.CanConvert(enumType); + + // Assert + Assert.True(result); + } + + [Fact] + public void CanConvert_WhenTypeIsNotEnum_ReturnsFalse() + { + // Arrange + var nonEnumType = typeof(string); + + // Act + var result = _converter.CanConvert(nonEnumType); + + // Assert + Assert.False(result); + } + + [Theory] + [InlineData("FirstValue", TestEnum.FirstValue)] + [InlineData("FIRSTVALUE", TestEnum.FirstValue)] + [InlineData("secondValue", TestEnum.SecondValue)] + [InlineData("UPPERCASE_VALUE", TestEnum.UPPERCASE_VALUE)] + public void Read_ValidEnumString_ReturnsEnumValue(string input, TestEnum expected) + { + // Arrange + var json = $"\"{input}\""; + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + reader.Read(); // Move to first token + + // Act + var result = _converter.Read(ref reader, typeof(TestEnum), _options); + + // Assert + Assert.Equal(expected, result); + } + + [Fact] + public void Read_InvalidEnumString_ThrowsArgumentException() + { + // Arrange + var json = "\"InvalidValue\""; + var jsonBytes = System.Text.Encoding.UTF8.GetBytes(json); + + // Act & Assert + void TestCode() + { + var reader = new Utf8JsonReader(jsonBytes); + reader.Read(); // Move to first token + _converter.Read(ref reader, typeof(TestEnum), _options); + } + + Assert.Throws(TestCode); + } + + [Theory] + [InlineData(TestEnum.FirstValue, "FIRSTVALUE")] + [InlineData(TestEnum.SecondValue, "SECONDVALUE")] + [InlineData(TestEnum.UPPERCASE_VALUE, "UPPERCASE_VALUE")] + public void Write_EnumValue_WritesUpperCaseString(TestEnum input, string expected) + { + // Arrange + using var stream = new System.IO.MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + // Act + _converter.Write(writer, input, _options); + writer.Flush(); + + // Assert + var json = System.Text.Encoding.UTF8.GetString(stream.ToArray()); + Assert.Equal($"\"{expected}\"", json); + } +} From 0d91e4b57f8ff21e4ee3838742fe4f6a049deaf5 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 21 Nov 2024 19:01:16 +0000 Subject: [PATCH 174/303] Added tests for upper case enum list converter --- .../Converters/UpperCaseEnumListConverter.cs | 14 ++-- .../UpperCaseEnumListConverterTests.cs | 83 +++++++++++++++++++ 2 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/UpperCaseEnumListConverterTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/UpperCaseEnumListConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/UpperCaseEnumListConverter.cs index a1d4f380..7a6371eb 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/UpperCaseEnumListConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/UpperCaseEnumListConverter.cs @@ -14,30 +14,26 @@ public override List Read(ref Utf8JsonReader reader, Type typeToConvert, Json } var list = new List(); + reader.Read(); - while (reader.Read()) + while (reader.TokenType is not JsonTokenType.EndArray) { - if (reader.TokenType is JsonTokenType.EndArray) - { - return list; - } - var value = reader.GetString(); list.Add((T)Enum.Parse(typeof(T), value, true)); + + reader.Read(); } - throw new JsonException("Expected end of array"); + return list; } public override void Write(Utf8JsonWriter writer, List value, JsonSerializerOptions options) { writer.WriteStartArray(); - foreach (var item in value) { writer.WriteStringValue(item.ToString().ToUpper()); } - writer.WriteEndArray(); } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/UpperCaseEnumListConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/UpperCaseEnumListConverterTests.cs new file mode 100644 index 00000000..cbda3251 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/UpperCaseEnumListConverterTests.cs @@ -0,0 +1,83 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Converters; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class UpperCaseEnumListConverterTests +{ + private enum TestEnum + { + FirstValue, + SecondValue, + UPPERCASE_VALUE + } + + private readonly JsonSerializerOptions _options; + + public UpperCaseEnumListConverterTests() + { + _options = new JsonSerializerOptions + { + Converters = { new UpperCaseEnumListConverter() } + }; + } + + [Fact] + public void Read_NotArray_ThrowsJsonException() + { + var json = "\"NotAnArray\""; + Assert.ThrowsAny(() => + JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Read_ValidArray_ReturnsEnumList() + { + var json = "[\"FirstValue\", \"SECONDVALUE\", \"uppercase_value\"]"; + var result = JsonSerializer.Deserialize>(json, _options); + + Assert.NotNull(result); + Assert.Collection(result, + item => Assert.Equal(TestEnum.FirstValue, item), + item => Assert.Equal(TestEnum.SecondValue, item), + item => Assert.Equal(TestEnum.UPPERCASE_VALUE, item)); + } + + [Fact] + public void Read_EmptyArray_ReturnsEmptyList() + { + var json = "[]"; + var result = JsonSerializer.Deserialize>(json, _options); + Assert.NotNull(result); + Assert.Empty(result); + } + + [Fact] + public void Read_InvalidEnumValue_ThrowsArgumentException() + { + var json = "[\"InvalidValue\"]"; + Assert.Throws(() => + JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Write_ValidList_WritesUpperCaseArray() + { + var list = new List + { + TestEnum.FirstValue, + TestEnum.SecondValue, + TestEnum.UPPERCASE_VALUE + }; + + var json = JsonSerializer.Serialize(list, _options); + Assert.Equal("[\"FIRSTVALUE\",\"SECONDVALUE\",\"UPPERCASE_VALUE\"]", json); + } + + [Fact] + public void Write_EmptyList_WritesEmptyArray() + { + var list = new List(); + var json = JsonSerializer.Serialize(list, _options); + Assert.Equal("[]", json); + } +} From 2a5932d68ba09482f20cf7fea702c471f0ccae32 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 21 Nov 2024 19:03:58 +0000 Subject: [PATCH 175/303] added get index request --- .../Requests/Databases/GetIndexRequest.cs | 10 ++++++++++ .../Databases/Validators/GetIndexRequestValidator.cs | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/GetIndexRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetIndexRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/GetIndexRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/GetIndexRequest.cs new file mode 100644 index 00000000..2454b087 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/GetIndexRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to get an index +/// +public class GetIndexRequest : DatabaseCollectionIdIndexKeyBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetIndexRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetIndexRequestValidator.cs new file mode 100644 index 00000000..25264c83 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/GetIndexRequestValidator.cs @@ -0,0 +1,10 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class GetIndexRequestValidator : AbstractValidator +{ + public GetIndexRequestValidator() + { + Include(new DatabaseCollectionIdIndexKeyBaseRequestValidator()); + } +} From 589743d6f4ebf2af87a2d9827e34c7aabed23062 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 21 Nov 2024 19:05:39 +0000 Subject: [PATCH 176/303] added tests for get index request --- .../Requests/Databases/GetIndexRequestTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetIndexRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetIndexRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetIndexRequestTests.cs new file mode 100644 index 00000000..0f5273e9 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/GetIndexRequestTests.cs @@ -0,0 +1,8 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class GetIndexRequestTests : DatabaseCollectionIdIndexKeyBaseRequestTests +{ + protected override GetIndexRequest CreateValidDatabaseCollectionIdIndexKeyRequest => new(); +} From 979a61b34bd61819c95d6910e135ff2d7f5848fe Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 21 Nov 2024 19:07:39 +0000 Subject: [PATCH 177/303] added delete index request --- .../Requests/Databases/DeleteIndexRequest.cs | 10 ++++++++++ .../Validators/DeleteIndexRequestValidator.cs | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteIndexRequest.cs create mode 100644 src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteIndexRequestValidator.cs diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteIndexRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteIndexRequest.cs new file mode 100644 index 00000000..8e973808 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/DeleteIndexRequest.cs @@ -0,0 +1,10 @@ +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to delete an index +/// +public class DeleteIndexRequest : DatabaseCollectionIdIndexKeyBaseRequest +{ +} diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteIndexRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteIndexRequestValidator.cs new file mode 100644 index 00000000..e438fe9b --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/DeleteIndexRequestValidator.cs @@ -0,0 +1,10 @@ +using FluentValidation; + +namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; +public class DeleteIndexRequestValidator : AbstractValidator +{ + public DeleteIndexRequestValidator() + { + Include(new DatabaseCollectionIdIndexKeyBaseRequestValidator()); + } +} From 89ff014221f9b34cd92932418fcdd04f94f1f32c Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 21 Nov 2024 19:09:12 +0000 Subject: [PATCH 178/303] added delete index request tests --- .../Requests/Databases/DeleteIndexRequestTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteIndexRequestTests.cs diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteIndexRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteIndexRequestTests.cs new file mode 100644 index 00000000..a0a1f395 --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/DeleteIndexRequestTests.cs @@ -0,0 +1,8 @@ +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Requests.Databases.Validators; + +namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; +public class DeleteIndexRequestTests : DatabaseCollectionIdIndexKeyBaseRequestTests +{ + protected override DeleteIndexRequest CreateValidDatabaseCollectionIdIndexKeyRequest => new(); +} From c2f12e78113a86a12bda44b03140f15571b963da Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 21 Nov 2024 19:19:32 +0000 Subject: [PATCH 179/303] cleaned up comments etc --- .../Requests/Databases/CreateBooleanAttributeRequest.cs | 4 ++++ .../Requests/Databases/CreateDatetimeAttribute.cs | 4 ++++ .../Requests/Databases/CreateDocumentRequest.cs | 4 ++++ .../Requests/Databases/IUpdateDocumentRequestBuilder.cs | 8 ++++++++ .../Requests/Databases/UpdateDatabase.cs | 4 ++++ .../Requests/Databases/UpdateDatetimeAttributeRequest.cs | 4 ++++ .../Requests/Databases/UpdateDocumentRequestBuilder.cs | 2 +- .../Requests/Databases/UpdateIntegerAttributeRequest.cs | 4 ++++ .../Databases/UpdateRelationshipAttributeRequest.cs | 2 ++ 9 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateBooleanAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateBooleanAttributeRequest.cs index b18268e2..5b62d34d 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateBooleanAttributeRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateBooleanAttributeRequest.cs @@ -2,6 +2,10 @@ using PinguApps.Appwrite.Shared.Requests.Databases.Validators; namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create a boolean attribute +/// public class CreateBooleanAttributeRequest : CreateAttributeBaseRequest { /// diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttribute.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttribute.cs index 26e86c36..c50c799e 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttribute.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttribute.cs @@ -4,6 +4,10 @@ using PinguApps.Appwrite.Shared.Requests.Databases.Validators; namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create a datetime attribute +/// public class CreateDatetimeAttribute : CreateAttributeBaseRequest { /// diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs index 401e6f19..afc131f1 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs @@ -1,6 +1,10 @@ using System.Collections.Generic; namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to create a document +/// public class CreateDocumentRequest : CreateDocumentRequest> { internal CreateDocumentRequest() { } diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/IUpdateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/IUpdateDocumentRequestBuilder.cs index b6052f2b..23133576 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/IUpdateDocumentRequestBuilder.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/IUpdateDocumentRequestBuilder.cs @@ -41,5 +41,13 @@ public interface IUpdateDocumentRequestBuilder /// Builds the document request /// UpdateDocumentRequest Build(); + + /// + /// Compares a before and after snapshop of an object, and adds any changed values to the document data + /// + /// The model type + /// The values before any modifications + /// The values after modifications + /// IUpdateDocumentRequestBuilder WithChanges(T before, T after) where T : class; } diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabase.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabase.cs index b6880020..6f553a61 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabase.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabase.cs @@ -2,6 +2,10 @@ using PinguApps.Appwrite.Shared.Requests.Databases.Validators; namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to update a database +/// public class UpdateDatabase : DatabaseIdBaseRequest { /// diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatetimeAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatetimeAttributeRequest.cs index b2a84f10..40688fe7 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatetimeAttributeRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatetimeAttributeRequest.cs @@ -4,6 +4,10 @@ using PinguApps.Appwrite.Shared.Requests.Databases.Validators; namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to update a datetime attribute +/// public class UpdateDatetimeAttributeRequest : UpdateAttributeBaseRequest { /// diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs index 05cc9ab6..f89d195d 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDocumentRequestBuilder.cs @@ -6,6 +6,7 @@ using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Requests.Databases; + internal class UpdateDocumentRequestBuilder : IUpdateDocumentRequestBuilder { private readonly UpdateDocumentRequest _request = new(); @@ -77,7 +78,6 @@ public IUpdateDocumentRequestBuilder WithChanges(T before, T after) where T : return this; } - public UpdateDocumentRequest Build() { _request.Data = _data; diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIntegerAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIntegerAttributeRequest.cs index 199a43ec..a722f19b 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIntegerAttributeRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIntegerAttributeRequest.cs @@ -2,6 +2,10 @@ using PinguApps.Appwrite.Shared.Requests.Databases.Validators; namespace PinguApps.Appwrite.Shared.Requests.Databases; + +/// +/// The request to update integer attributes +/// public class UpdateIntegerAttributeRequest : UpdateAttributeBaseRequest { /// diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateRelationshipAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateRelationshipAttributeRequest.cs index 57550afb..30e26701 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateRelationshipAttributeRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateRelationshipAttributeRequest.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Requests.Databases.Validators; @@ -19,5 +20,6 @@ public class UpdateRelationshipAttributeRequest : DatabaseCollectionIdAttributeK /// Constraints option /// [JsonPropertyName("onDelete")] + [JsonConverter(typeof(CamelCaseEnumConverter))] public OnDelete? OnDelete { get; set; } } From f077778ffeb303beb2656ccf6f9404e5f8d8d9eb Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 21 Nov 2024 19:24:40 +0000 Subject: [PATCH 180/303] Opened the IDatabaseApi ready for adding more endpoints to --- .../Internals/IDatabasesApi.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs diff --git a/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs b/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs new file mode 100644 index 00000000..100de4b4 --- /dev/null +++ b/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Responses; +using Refit; + +namespace PinguApps.Appwrite.Server.Internals; +internal interface IDatabasesApi : IBaseApi +{ + [Get("/databases")] + Task> GetDatabase(GetDatabaseRequest request); +} From b483a338075895a8868c197e70bcb85f164debcb Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 21 Nov 2024 19:26:50 +0000 Subject: [PATCH 181/303] altared get to list, and added create database to intenranl --- .../Internals/IDatabasesApi.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs b/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs index 100de4b4..bd1fbb54 100644 --- a/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs +++ b/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; using PinguApps.Appwrite.Shared.Requests.Databases; using PinguApps.Appwrite.Shared.Responses; using Refit; @@ -7,5 +9,10 @@ namespace PinguApps.Appwrite.Server.Internals; internal interface IDatabasesApi : IBaseApi { [Get("/databases")] - Task> GetDatabase(GetDatabaseRequest request); + [QueryUriFormat(UriFormat.Unescaped)] + Task> ListDatabase([Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries, string? seearch); + + [Post("/databases")] + Task> CreateDatabase(CreateDatabaseRequest request); + } From 24990b3e2184add736b02df7143ebef94c32e39c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:27:32 +0000 Subject: [PATCH 182/303] Update alstr/todo-to-issue-action digest to 7b4aab7 --- .github/workflows/dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index cf9a6d20..06d266d3 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -30,7 +30,7 @@ jobs: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: "TODO to Issue" - uses: "alstr/todo-to-issue-action@5157ba918f83a649b1d4b45fb4186916127eaf8d" # v5 + uses: "alstr/todo-to-issue-action@7b4aab71051f6c80dbb899b9c3458c49507eea6e" # v5 with: INSERT_ISSUE_URLS: true AUTO_ASSIGN: true From 5b346b1660719706e3b127c0f7d2ec996c663c5c Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 09:37:24 +0000 Subject: [PATCH 183/303] Renamed some requests etc --- ...e.cs => CreateDatetimeAttributeRequest.cs} | 2 +- ...Request.cs => CreateIPAttributeRequest.cs} | 2 +- ...teDatabase.cs => UpdateDatabaseRequest.cs} | 2 +- ...Request.cs => UpdateIPAttributeRequest.cs} | 2 +- ...reateDatetimeAttributeRequestValidator.cs} | 6 +++--- ...s => CreateIPAttributeRequestValidator.cs} | 6 +++--- ...r.cs => UpdateDatabaseRequestValidator.cs} | 6 +++--- ...s => UpdateIPAttributeRequestValidator.cs} | 6 +++--- ...=> CreateDatetimeAttributeRequestTests.cs} | 20 +++++++++---------- ...ts.cs => CreateIPAttributeRequestTests.cs} | 8 ++++---- ...Tests.cs => UpdateDatabaseRequestTests.cs} | 20 +++++++++---------- ...ts.cs => UpdateIPAttributeRequestTests.cs} | 8 ++++---- 12 files changed, 44 insertions(+), 44 deletions(-) rename src/PinguApps.Appwrite.Shared/Requests/Databases/{CreateDatetimeAttribute.cs => CreateDatetimeAttributeRequest.cs} (79%) rename src/PinguApps.Appwrite.Shared/Requests/Databases/{CreateIPAddressRequest.cs => CreateIPAttributeRequest.cs} (60%) rename src/PinguApps.Appwrite.Shared/Requests/Databases/{UpdateDatabase.cs => UpdateDatabaseRequest.cs} (86%) rename src/PinguApps.Appwrite.Shared/Requests/Databases/{UpdateIPAddressAttributeRequest.cs => UpdateIPAttributeRequest.cs} (57%) rename src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/{CreateDatetimeAttributeValidator.cs => CreateDatetimeAttributeRequestValidator.cs} (62%) rename src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/{CreateIPAddressRequestValidator.cs => CreateIPAttributeRequestValidator.cs} (83%) rename src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/{UpdateDatabaseValidator.cs => UpdateDatabaseRequestValidator.cs} (72%) rename src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/{UpdateIPAddressAttributeRequestValidator.cs => UpdateIPAttributeRequestValidator.cs} (81%) rename tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/{CreateDatetimeAttributeTests.cs => CreateDatetimeAttributeRequestTests.cs} (78%) rename tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/{CreateIPAddressRequestTests.cs => CreateIPAttributeRequestTests.cs} (84%) rename tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/{UpdateDatabaseTests.cs => UpdateDatabaseRequestTests.cs} (82%) rename tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/{UpdateIPAddressAttributeRequestTests.cs => UpdateIPAttributeRequestTests.cs} (82%) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttribute.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttributeRequest.cs similarity index 79% rename from src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttribute.cs rename to src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttributeRequest.cs index c50c799e..c27ea29f 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttribute.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDatetimeAttributeRequest.cs @@ -8,7 +8,7 @@ namespace PinguApps.Appwrite.Shared.Requests.Databases; /// /// The request to create a datetime attribute /// -public class CreateDatetimeAttribute : CreateAttributeBaseRequest +public class CreateDatetimeAttributeRequest : CreateAttributeBaseRequest { /// /// Default value for the attribute in ISO 8601 format. Cannot be set when attribute is required diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIPAddressRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIPAttributeRequest.cs similarity index 60% rename from src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIPAddressRequest.cs rename to src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIPAttributeRequest.cs index ecb567b9..1878192e 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIPAddressRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIPAttributeRequest.cs @@ -5,6 +5,6 @@ namespace PinguApps.Appwrite.Shared.Requests.Databases; /// /// The request to create an IP Address attribute /// -public class CreateIPAddressRequest : CreateStringAttributeBaseRequest +public class CreateIPAttributeRequest : CreateStringAttributeBaseRequest { } diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabase.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabaseRequest.cs similarity index 86% rename from src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabase.cs rename to src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabaseRequest.cs index 6f553a61..27e18038 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabase.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateDatabaseRequest.cs @@ -6,7 +6,7 @@ namespace PinguApps.Appwrite.Shared.Requests.Databases; /// /// The request to update a database /// -public class UpdateDatabase : DatabaseIdBaseRequest +public class UpdateDatabaseRequest : DatabaseIdBaseRequest { /// /// Database name. Max length: 128 chars diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIPAddressAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIPAttributeRequest.cs similarity index 57% rename from src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIPAddressAttributeRequest.cs rename to src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIPAttributeRequest.cs index f945a50c..d4d92938 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIPAddressAttributeRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIPAttributeRequest.cs @@ -5,6 +5,6 @@ namespace PinguApps.Appwrite.Shared.Requests.Databases; /// /// The requset to update an up address attribute /// -public class UpdateIPAddressAttributeRequest : UpdateStringAttributeBaseRequest +public class UpdateIPAttributeRequest : UpdateStringAttributeBaseRequest { } diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatetimeAttributeValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatetimeAttributeRequestValidator.cs similarity index 62% rename from src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatetimeAttributeValidator.cs rename to src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatetimeAttributeRequestValidator.cs index 15d98888..a66892a0 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatetimeAttributeValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateDatetimeAttributeRequestValidator.cs @@ -1,11 +1,11 @@ using FluentValidation; namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; -public class CreateDatetimeAttributeValidator : AbstractValidator +public class CreateDatetimeAttributeRequestValidator : AbstractValidator { - public CreateDatetimeAttributeValidator() + public CreateDatetimeAttributeRequestValidator() { - Include(new CreateAttributeBaseRequestValidator()); + Include(new CreateAttributeBaseRequestValidator()); RuleFor(x => x.Default) .Must((request, defaultValue) => !request.Required || defaultValue == null) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAddressRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAttributeRequestValidator.cs similarity index 83% rename from src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAddressRequestValidator.cs rename to src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAttributeRequestValidator.cs index 09774268..b36dc73d 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAddressRequestValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/CreateIPAttributeRequestValidator.cs @@ -3,11 +3,11 @@ using FluentValidation; namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; -public class CreateIPAddressRequestValidator : AbstractValidator +public class CreateIPAttributeRequestValidator : AbstractValidator { - public CreateIPAddressRequestValidator() + public CreateIPAttributeRequestValidator() { - Include(new CreateStringAttributeBaseRequestValidator()); + Include(new CreateStringAttributeBaseRequestValidator()); RuleFor(x => x.Default) .NotEmpty() diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatabaseValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatabaseRequestValidator.cs similarity index 72% rename from src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatabaseValidator.cs rename to src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatabaseRequestValidator.cs index 405bc7fd..485d8132 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatabaseValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateDatabaseRequestValidator.cs @@ -1,11 +1,11 @@ using FluentValidation; namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; -public class UpdateDatabaseValidator : AbstractValidator +public class UpdateDatabaseRequestValidator : AbstractValidator { - public UpdateDatabaseValidator() + public UpdateDatabaseRequestValidator() { - Include(new DatabaseIdBaseRequestValidator()); + Include(new DatabaseIdBaseRequestValidator()); RuleFor(x => x.Name) .NotEmpty() diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIPAddressAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIPAttributeRequestValidator.cs similarity index 81% rename from src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIPAddressAttributeRequestValidator.cs rename to src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIPAttributeRequestValidator.cs index e1bdb1d1..02ce1009 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIPAddressAttributeRequestValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIPAttributeRequestValidator.cs @@ -3,11 +3,11 @@ using FluentValidation; namespace PinguApps.Appwrite.Shared.Requests.Databases.Validators; -public class UpdateIPAddressAttributeRequestValidator : AbstractValidator +public class UpdateIPAttributeRequestValidator : AbstractValidator { - public UpdateIPAddressAttributeRequestValidator() + public UpdateIPAttributeRequestValidator() { - Include(new UpdateStringAttributeBaseRequestValidator()); + Include(new UpdateStringAttributeBaseRequestValidator()); RuleFor(x => x.Default) .NotEmpty() diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatetimeAttributeTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatetimeAttributeRequestTests.cs similarity index 78% rename from tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatetimeAttributeTests.cs rename to tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatetimeAttributeRequestTests.cs index 8b72ceea..65e82615 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatetimeAttributeTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDatetimeAttributeRequestTests.cs @@ -4,15 +4,15 @@ using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; -public class CreateDatetimeAttributeTests : CreateAttributeBaseRequestTests +public class CreateDatetimeAttributeRequestTests : CreateAttributeBaseRequestTests { - protected override CreateBooleanAttributeRequest CreateValidCreateAttributeBaseRequest => new(); + protected override CreateDatetimeAttributeRequest CreateValidCreateAttributeBaseRequest => new(); [Fact] public void Constructor_InitializesWithExpectedValues() { // Arrange & Act - var request = new CreateDatetimeAttribute(); + var request = new CreateDatetimeAttributeRequest(); // Assert Assert.Null(request.Default); @@ -24,7 +24,7 @@ public void Properties_CanBeSet() // Arrange var defaultValue = DateTime.UtcNow; - var request = new CreateDatetimeAttribute(); + var request = new CreateDatetimeAttributeRequest(); // Act request.Default = defaultValue; @@ -33,7 +33,7 @@ public void Properties_CanBeSet() Assert.Equal(defaultValue, request.Default); } - public static TheoryData ValidRequestsData = new() + public static TheoryData ValidRequestsData = new() { new() { @@ -55,7 +55,7 @@ public void Properties_CanBeSet() [Theory] [MemberData(nameof(ValidRequestsData))] - public void IsValid_WithValidData_ReturnsTrue(CreateDatetimeAttribute request) + public void IsValid_WithValidData_ReturnsTrue(CreateDatetimeAttributeRequest request) { // Act var isValid = request.IsValid(); @@ -64,7 +64,7 @@ public void IsValid_WithValidData_ReturnsTrue(CreateDatetimeAttribute request) Assert.True(isValid); } - public static TheoryData InvalidRequestsData = new() + public static TheoryData InvalidRequestsData = new() { new() { @@ -78,7 +78,7 @@ public void IsValid_WithValidData_ReturnsTrue(CreateDatetimeAttribute request) [Theory] [MemberData(nameof(InvalidRequestsData))] - public void IsValid_WithInvalidData_ReturnsFalse(CreateDatetimeAttribute request) + public void IsValid_WithInvalidData_ReturnsFalse(CreateDatetimeAttributeRequest request) { // Act var isValid = request.IsValid(); @@ -91,7 +91,7 @@ public void IsValid_WithInvalidData_ReturnsFalse(CreateDatetimeAttribute request public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() { // Arrange - var request = new CreateDatetimeAttribute + var request = new CreateDatetimeAttributeRequest { Default = DateTime.UtcNow, Required = true @@ -105,7 +105,7 @@ public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure( public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() { // Arrange - var request = new CreateDatetimeAttribute + var request = new CreateDatetimeAttributeRequest { Default = DateTime.UtcNow, Required = true diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAddressRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAttributeRequestTests.cs similarity index 84% rename from tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAddressRequestTests.cs rename to tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAttributeRequestTests.cs index 001b71f5..2d000a63 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAddressRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateIPAttributeRequestTests.cs @@ -3,9 +3,9 @@ using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; -public class CreateIPAddressRequestTests : CreateStringAttributeBaseRequestTests +public class CreateIPAttributeRequestTests : CreateStringAttributeBaseRequestTests { - protected override CreateIPAddressRequest CreateValidCreateStringAttributeBaseRequest => new(); + protected override CreateIPAttributeRequest CreateValidCreateStringAttributeBaseRequest => new(); protected override string ValidDefaultValue => "192.168.1.1"; @@ -24,7 +24,7 @@ public class CreateIPAddressRequestTests : CreateStringAttributeBaseRequestTests public void IsValid_WithValidDefaults_ReturnsTrue(string defaultValue) { // Arrange - var request = new CreateIPAddressRequest + var request = new CreateIPAttributeRequest { DatabaseId = IdUtils.GenerateUniqueId(), CollectionId = IdUtils.GenerateUniqueId(), @@ -61,7 +61,7 @@ public void IsValid_WithValidDefaults_ReturnsTrue(string defaultValue) public void IsValid_WithInvalidDefaults_ReturnsFalse(string defaultValue) { // Arrange - var request = new CreateIPAddressRequest + var request = new CreateIPAttributeRequest { DatabaseId = IdUtils.GenerateUniqueId(), CollectionId = IdUtils.GenerateUniqueId(), diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatabaseTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatabaseRequestTests.cs similarity index 82% rename from tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatabaseTests.cs rename to tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatabaseRequestTests.cs index 720955e8..78392f07 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatabaseTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDatabaseRequestTests.cs @@ -4,9 +4,9 @@ using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; -public class UpdateDatabaseTests : DatabaseIdBaseRequestTests +public class UpdateDatabaseRequestTests : DatabaseIdBaseRequestTests { - protected override UpdateDatabase CreateValidRequest => new() + protected override UpdateDatabaseRequest CreateValidRequest => new() { Name = "Pingu" }; @@ -15,7 +15,7 @@ public class UpdateDatabaseTests : DatabaseIdBaseRequestTests ValidRequestsData = new() + public static TheoryData ValidRequestsData = new() { new() { @@ -58,7 +58,7 @@ public void Properties_CanBeSet() [Theory] [MemberData(nameof(ValidRequestsData))] - public void IsValid_WithValidData_ReturnsTrue(UpdateDatabase request) + public void IsValid_WithValidData_ReturnsTrue(UpdateDatabaseRequest request) { // Act var isValid = request.IsValid(); @@ -67,7 +67,7 @@ public void IsValid_WithValidData_ReturnsTrue(UpdateDatabase request) Assert.True(isValid); } - public static TheoryData InvalidRequestsData = new() + public static TheoryData InvalidRequestsData = new() { new() { @@ -88,7 +88,7 @@ public void IsValid_WithValidData_ReturnsTrue(UpdateDatabase request) [Theory] [MemberData(nameof(InvalidRequestsData))] - public void IsValid_WithInvalidData_ReturnsFalse(UpdateDatabase request) + public void IsValid_WithInvalidData_ReturnsFalse(UpdateDatabaseRequest request) { // Act var isValid = request.IsValid(); @@ -101,7 +101,7 @@ public void IsValid_WithInvalidData_ReturnsFalse(UpdateDatabase request) public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure() { // Arrange - var request = new UpdateDatabase + var request = new UpdateDatabaseRequest { DatabaseId = IdUtils.GenerateUniqueId(), Name = "", @@ -116,7 +116,7 @@ public void Validate_WithThrowOnFailuresTrue_ThrowsValidationExceptionOnFailure( public void Validate_WithThrowOnFailuresFalse_ReturnsInvalidResultOnFailure() { // Arrange - var request = new UpdateDatabase + var request = new UpdateDatabaseRequest { DatabaseId = IdUtils.GenerateUniqueId(), Name = "", diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIPAddressAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIPAttributeRequestTests.cs similarity index 82% rename from tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIPAddressAttributeRequestTests.cs rename to tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIPAttributeRequestTests.cs index 5aa45e02..763f9f94 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIPAddressAttributeRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIPAttributeRequestTests.cs @@ -3,9 +3,9 @@ using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; -public class UpdateIPAddressAttributeRequestTests : UpdateStringAttributeBaseRequestTests +public class UpdateIPAttributeRequestTests : UpdateStringAttributeBaseRequestTests { - protected override UpdateIPAddressAttributeRequest CreateValidUpdateStringAttributeBaseRequest => new(); + protected override UpdateIPAttributeRequest CreateValidUpdateStringAttributeBaseRequest => new(); protected override string ValidDefaultValue => "192.168.1.1"; @@ -24,7 +24,7 @@ public class UpdateIPAddressAttributeRequestTests : UpdateStringAttributeBaseReq public void IsValid_WithValidDefaults_ReturnsTrue(string defaultValue) { // Arrange - var request = new UpdateIPAddressAttributeRequest + var request = new UpdateIPAttributeRequest { DatabaseId = IdUtils.GenerateUniqueId(), CollectionId = IdUtils.GenerateUniqueId(), @@ -61,7 +61,7 @@ public void IsValid_WithValidDefaults_ReturnsTrue(string defaultValue) public void IsValid_WithInvalidDefaults_ReturnsFalse(string defaultValue) { // Arrange - var request = new UpdateIPAddressAttributeRequest + var request = new UpdateIPAttributeRequest { DatabaseId = IdUtils.GenerateUniqueId(), CollectionId = IdUtils.GenerateUniqueId(), From 6338db060a6914c1ecd9efd06a83be3b7a8c2893 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 09:37:51 +0000 Subject: [PATCH 184/303] Completed internal DB interface with AI auto complete - Will need to check for correctness and completeness --- .../Internals/IDatabasesApi.cs | 134 +++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs b/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs index bd1fbb54..c0fc0ab7 100644 --- a/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs +++ b/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs @@ -4,15 +4,147 @@ using PinguApps.Appwrite.Shared.Requests.Databases; using PinguApps.Appwrite.Shared.Responses; using Refit; +using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; +using Index = PinguApps.Appwrite.Shared.Responses.Index; namespace PinguApps.Appwrite.Server.Internals; internal interface IDatabasesApi : IBaseApi { + // Database Operations [Get("/databases")] [QueryUriFormat(UriFormat.Unescaped)] - Task> ListDatabase([Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries, string? seearch); + Task> ListDatabases([Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries, [AliasAs("search")] string? search); [Post("/databases")] Task> CreateDatabase(CreateDatabaseRequest request); + [Get("/databases/{databaseId}")] + Task> GetDatabase(string databaseId); + + [Put("/databases/{databaseId}")] + Task> UpdateDatabase(string databaseId, UpdateDatabaseRequest request); + + [Delete("/databases/{databaseId}")] + Task DeleteDatabase(string databaseId); + + // Collection Operations + [Get("/databases/{databaseId}/collections")] + [QueryUriFormat(UriFormat.Unescaped)] + Task> ListCollections(string databaseId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries, [AliasAs("search")] string? search); + + [Post("/databases/{databaseId}/collections")] + Task> CreateCollection(string databaseId, CreateCollectionRequest request); + + [Get("/databases/{databaseId}/collections/{collectionId}")] + Task> GetCollection(string databaseId, string collectionId); + + [Put("/databases/{databaseId}/collections/{collectionId}")] + Task> UpdateCollection(string databaseId, string collectionId, UpdateCollectionRequest request); + + [Delete("/databases/{databaseId}/collections/{collectionId}")] + Task DeleteCollection(string databaseId, string collectionId); + + // Attribute Operations + [Get("/databases/{databaseId}/collections/{collectionId}/attributes")] + [QueryUriFormat(UriFormat.Unescaped)] + Task> ListAttributes(string databaseId, string collectionId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); + + [Post("/databases/{databaseId}/collections/{collectionId}/attributes/boolean")] + Task> CreateBooleanAttribute(string databaseId, string collectionId, CreateBooleanAttributeRequest request); + + [Patch("/databases/{databaseId}/collections/{collectionId}/attributes/boolean/{key}")] + Task> UpdateBooleanAttribute(string databaseId, string collectionId, string key, UpdateBooleanAttributeRequest request); + + [Post("/databases/{databaseId}/collections/{collectionId}/attributes/datetime")] + Task> CreateDatetimeAttribute(string databaseId, string collectionId, CreateDatetimeAttributeRequest request); + + [Patch("/databases/{databaseId}/collections/{collectionId}/attributes/datetime/{key}")] + Task> UpdateDatetimeAttribute(string databaseId, string collectionId, string key, UpdateDatetimeAttributeRequest request); + + [Post("/databases/{databaseId}/collections/{collectionId}/attributes/email")] + Task> CreateEmailAttribute(string databaseId, string collectionId, CreateEmailAttributeRequest request); + + [Patch("/databases/{databaseId}/collections/{collectionId}/attributes/email/{key}")] + Task> UpdateEmailAttribute(string databaseId, string collectionId, string key, UpdateEmailAttributeRequest request); + + [Post("/databases/{databaseId}/collections/{collectionId}/attributes/enum")] + Task> CreateEnumAttribute(string databaseId, string collectionId, CreateEnumAttributeRequest request); + + [Patch("/databases/{databaseId}/collections/{collectionId}/attributes/enum/{key}")] + Task> UpdateEnumAttribute(string databaseId, string collectionId, string key, UpdateEnumAttributeRequest request); + + [Post("/databases/{databaseId}/collections/{collectionId}/attributes/float")] + Task> CreateFloatAttribute(string databaseId, string collectionId, CreateFloatAttributeRequest request); + + [Patch("/databases/{databaseId}/collections/{collectionId}/attributes/float/{key}")] + Task> UpdateFloatAttribute(string databaseId, string collectionId, string key, UpdateFloatAttributeRequest request); + + [Post("/databases/{databaseId}/collections/{collectionId}/attributes/integer")] + Task> CreateIntegerAttribute(string databaseId, string collectionId, CreateIntegerAttributeRequest request); + + [Patch("/databases/{databaseId}/collections/{collectionId}/attributes/integer/{key}")] + Task> UpdateIntegerAttribute(string databaseId, string collectionId, string key, UpdateIntegerAttributeRequest request); + + [Post("/databases/{databaseId}/collections/{collectionId}/attributes/ip")] + Task> CreateIpAttribute(string databaseId, string collectionId, CreateIPAttributeRequest request); + + [Patch("/databases/{databaseId}/collections/{collectionId}/attributes/ip/{key}")] + Task> UpdateIpAttribute(string databaseId, string collectionId, string key, UpdateIPAttributeRequest request); + + [Post("/databases/{databaseId}/collections/{collectionId}/attributes/relationship")] + Task> CreateRelationshipAttribute(string databaseId, string collectionId, CreateRelationshipAttributeRequest request); + + [Post("/databases/{databaseId}/collections/{collectionId}/attributes/string")] + Task> CreateStringAttribute(string databaseId, string collectionId, CreateStringAttributeRequest request); + + [Patch("/databases/{databaseId}/collections/{collectionId}/attributes/string/{key}")] + Task> UpdateStringAttribute(string databaseId, string collectionId, string key, UpdateStringAttributeRequest request); + + [Post("/databases/{databaseId}/collections/{collectionId}/attributes/url")] + Task> CreateUrlAttribute(string databaseId, string collectionId, CreateURLAttributeRequest request); + + [Patch("/databases/{databaseId}/collections/{collectionId}/attributes/url/{key}")] + Task> UpdateUrlAttribute(string databaseId, string collectionId, string key, UpdateURLAttributeRequest request); + + [Get("/databases/{databaseId}/collections/{collectionId}/attributes/{key}")] + Task> GetAttribute(string databaseId, string collectionId, string key); + + [Delete("/databases/{databaseId}/collections/{collectionId}/attributes/{key}")] + Task DeleteAttribute(string databaseId, string collectionId, string key); + + [Patch("/databases/{databaseId}/collections/{collectionId}/attributes/{key}/relationship")] + Task> UpdateRelationshipAttribute(string databaseId, string collectionId, string key, UpdateRelationshipAttributeRequest request); + + // Document Operations + [Get("/databases/{databaseId}/collections/{collectionId}/documents")] + [QueryUriFormat(UriFormat.Unescaped)] + Task> ListDocuments(string databaseId, string collectionId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); + + [Post("/databases/{databaseId}/collections/{collectionId}/documents")] + Task> CreateDocument(string databaseId, string collectionId, CreateDocumentRequest request); + + [Get("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] + [QueryUriFormat(UriFormat.Unescaped)] + Task> GetDocument(string databaseId, string collectionId, string documentId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); + + [Patch("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] + Task> UpdateDocument(string databaseId, string collectionId, string documentId, UpdateDocumentRequest request); + + [Delete("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] + Task DeleteDocument(string databaseId, string collectionId, string documentId); + + // Index Operations + [Get("/databases/{databaseId}/collections/{collectionId}/indexes")] + [QueryUriFormat(UriFormat.Unescaped)] + Task> ListIndexes(string databaseId, string collectionId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); + + [Post("/databases/{databaseId}/collections/{collectionId}/indexes")] + Task> CreateIndex(string databaseId, string collectionId, CreateIndexRequest request); + + [Get("/databases/{databaseId}/collections/{collectionId}/indexes/{key}")] + Task> GetIndex(string databaseId, string collectionId, string key); + + [Delete("/databases/{databaseId}/collections/{collectionId}/indexes/{key}")] + Task DeleteIndex(string databaseId, string collectionId, string key); + } From 8359212b9561b97d6189409469f1a8aa7680d760 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 09:49:00 +0000 Subject: [PATCH 185/303] Reorganised the order of IDatabaseApi --- .../Internals/IDatabasesApi.cs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs b/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs index c0fc0ab7..90c5711f 100644 --- a/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs +++ b/src/PinguApps.Appwrite.Server/Internals/IDatabasesApi.cs @@ -18,15 +18,15 @@ internal interface IDatabasesApi : IBaseApi [Post("/databases")] Task> CreateDatabase(CreateDatabaseRequest request); + [Delete("/databases/{databaseId}")] + Task DeleteDatabase(string databaseId); + [Get("/databases/{databaseId}")] Task> GetDatabase(string databaseId); [Put("/databases/{databaseId}")] Task> UpdateDatabase(string databaseId, UpdateDatabaseRequest request); - [Delete("/databases/{databaseId}")] - Task DeleteDatabase(string databaseId); - // Collection Operations [Get("/databases/{databaseId}/collections")] [QueryUriFormat(UriFormat.Unescaped)] @@ -35,15 +35,15 @@ internal interface IDatabasesApi : IBaseApi [Post("/databases/{databaseId}/collections")] Task> CreateCollection(string databaseId, CreateCollectionRequest request); + [Delete("/databases/{databaseId}/collections/{collectionId}")] + Task DeleteCollection(string databaseId, string collectionId); + [Get("/databases/{databaseId}/collections/{collectionId}")] Task> GetCollection(string databaseId, string collectionId); [Put("/databases/{databaseId}/collections/{collectionId}")] Task> UpdateCollection(string databaseId, string collectionId, UpdateCollectionRequest request); - [Delete("/databases/{databaseId}/collections/{collectionId}")] - Task DeleteCollection(string databaseId, string collectionId); - // Attribute Operations [Get("/databases/{databaseId}/collections/{collectionId}/attributes")] [QueryUriFormat(UriFormat.Unescaped)] @@ -106,12 +106,12 @@ internal interface IDatabasesApi : IBaseApi [Patch("/databases/{databaseId}/collections/{collectionId}/attributes/url/{key}")] Task> UpdateUrlAttribute(string databaseId, string collectionId, string key, UpdateURLAttributeRequest request); - [Get("/databases/{databaseId}/collections/{collectionId}/attributes/{key}")] - Task> GetAttribute(string databaseId, string collectionId, string key); - [Delete("/databases/{databaseId}/collections/{collectionId}/attributes/{key}")] Task DeleteAttribute(string databaseId, string collectionId, string key); + [Get("/databases/{databaseId}/collections/{collectionId}/attributes/{key}")] + Task> GetAttribute(string databaseId, string collectionId, string key); + [Patch("/databases/{databaseId}/collections/{collectionId}/attributes/{key}/relationship")] Task> UpdateRelationshipAttribute(string databaseId, string collectionId, string key, UpdateRelationshipAttributeRequest request); @@ -123,6 +123,9 @@ internal interface IDatabasesApi : IBaseApi [Post("/databases/{databaseId}/collections/{collectionId}/documents")] Task> CreateDocument(string databaseId, string collectionId, CreateDocumentRequest request); + [Delete("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] + Task DeleteDocument(string databaseId, string collectionId, string documentId); + [Get("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] [QueryUriFormat(UriFormat.Unescaped)] Task> GetDocument(string databaseId, string collectionId, string documentId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); @@ -130,9 +133,6 @@ internal interface IDatabasesApi : IBaseApi [Patch("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] Task> UpdateDocument(string databaseId, string collectionId, string documentId, UpdateDocumentRequest request); - [Delete("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] - Task DeleteDocument(string databaseId, string collectionId, string documentId); - // Index Operations [Get("/databases/{databaseId}/collections/{collectionId}/indexes")] [QueryUriFormat(UriFormat.Unescaped)] @@ -141,10 +141,9 @@ internal interface IDatabasesApi : IBaseApi [Post("/databases/{databaseId}/collections/{collectionId}/indexes")] Task> CreateIndex(string databaseId, string collectionId, CreateIndexRequest request); - [Get("/databases/{databaseId}/collections/{collectionId}/indexes/{key}")] - Task> GetIndex(string databaseId, string collectionId, string key); - [Delete("/databases/{databaseId}/collections/{collectionId}/indexes/{key}")] Task DeleteIndex(string databaseId, string collectionId, string key); + [Get("/databases/{databaseId}/collections/{collectionId}/indexes/{key}")] + Task> GetIndex(string databaseId, string collectionId, string key); } From 35d51da7b2a90944590d64062aa113c71d1e6d76 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 09:56:58 +0000 Subject: [PATCH 186/303] added base for databases client oin server --- .../Clients/DatabasesClient.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs new file mode 100644 index 00000000..52a20816 --- /dev/null +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -0,0 +1,29 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using PinguApps.Appwrite.Server.Internals; +using PinguApps.Appwrite.Shared; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Server.Clients; + +/// +public class DatabasesClient +{ + private readonly IDatabasesApi _databasesApi; + + public DatabasesClient(IServiceProvider services) + { + _databasesApi = services.GetRequiredService(); + } + + [ExcludeFromCodeCoverage] + /// + public async Task> ListDatabases(ListDatabasesRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public async Task> CreateDatabase(CreateDatabaseRequest request) => throw new NotImplementedException(); +} From 045e9c27f7c48ab53c60638588241614b84c414c Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 20:44:51 +0000 Subject: [PATCH 187/303] Completed Database Client on server --- .../Clients/DatabasesClient.cs | 168 +++++++++++++++++- .../Clients/IDatabasesClient.cs | 145 +++++++++++++++ 2 files changed, 310 insertions(+), 3 deletions(-) create mode 100644 src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 52a20816..aeeff636 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -6,11 +6,13 @@ using PinguApps.Appwrite.Shared; using PinguApps.Appwrite.Shared.Requests.Databases; using PinguApps.Appwrite.Shared.Responses; +using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; +using Index = PinguApps.Appwrite.Shared.Responses.Index; namespace PinguApps.Appwrite.Server.Clients; /// -public class DatabasesClient +public class DatabasesClient : IDatabasesClient { private readonly IDatabasesApi _databasesApi; @@ -21,9 +23,169 @@ public DatabasesClient(IServiceProvider services) [ExcludeFromCodeCoverage] /// - public async Task> ListDatabases(ListDatabasesRequest request) => throw new NotImplementedException(); + public Task> ListDatabases(ListDatabasesRequest request) => throw new NotImplementedException(); [ExcludeFromCodeCoverage] /// - public async Task> CreateDatabase(CreateDatabaseRequest request) => throw new NotImplementedException(); + public Task> CreateDatabase(CreateDatabaseRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task DeleteDatabase(DeleteDatabaseRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> GetDatabase(GetDatabaseRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateDatabase(UpdateDatabaseRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> ListCollections(ListCollectionsRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateCollection(CreateCollectionRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task DeleteCollection(DeleteCollectionRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> GetCollection(GetCollectionRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateCollection(UpdateCollectionRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> ListAttributes(ListAttributesRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateBooleanAttribute(UpdateBooleanAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateDatetimeAttribute(CreateDatetimeAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateDatetimeAttribute(UpdateDatetimeAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateEmailAttribute(CreateEmailAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateEmailAttribute(UpdateEmailAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateEnumAttribute(CreateEnumAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateEnumAttribute(UpdateEnumAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateFloatAttribute(CreateFloatAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateFloatAttribute(UpdateFloatAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateIntegerAttribute(CreateIntegerAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateIntegerAttribute(UpdateIntegerAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateIpAttribute(CreateIPAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateIpAttribute(UpdateIPAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateRelationshipAttribute(CreateRelationshipAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateStringAttribute(CreateStringAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateStringAttribute(UpdateStringAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateUrlAttribute(CreateURLAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateUrlAttribute(UpdateURLAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task DeleteAttribute(DeleteAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> GetAttribute(GetAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateRelationshipAttribute(UpdateRelationshipAttributeRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> ListDocuments(ListDocumentsRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateDocument(CreateDocumentRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task DeleteDocument(DeleteDocumentRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> GetDocument(GetDocumentRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateDocument(UpdateDocumentRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> ListIndexes(ListIndexesRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateIndex(CreateIndexRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task DeleteIndex(DeleteIndexRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> GetIndex(GetIndexRequest request) => throw new NotImplementedException(); } diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs new file mode 100644 index 00000000..89066b24 --- /dev/null +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -0,0 +1,145 @@ +using System; +using System.Threading.Tasks; +using PinguApps.Appwrite.Shared; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Responses; +using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; +using Index = PinguApps.Appwrite.Shared.Responses.Index; + +namespace PinguApps.Appwrite.Server.Clients; + +/// +/// The Databases service allows you to create structured collections of documents, query and filter lists of documents, and manage an advanced set of read and write access permissions. +/// All data returned by the Databases service are represented as structured JSON documents. +/// The Databases service can contain multiple databases, each database can contain multiple collections. A collection is a group of similarly structured documents. The accepted structure of documents is defined by collection attributes. The collection attributes help you ensure all your user-submitted data is validated and stored according to the collection structure. +/// Using Appwrite permissions architecture, you can assign read or write access to each collection or document in your project for either a specific user, team, user role, or even grant it with public access (any). You can learn more about how Appwrite handles permissions and access control. +/// Appwrite Docs +/// +public interface IDatabasesClient +{ + [Obsolete("Endpoint not yet implemented.")] + Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateCollection(CreateCollectionRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateDatabase(CreateDatabaseRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateDatetimeAttribute(CreateDatetimeAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateDocument(CreateDocumentRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateEmailAttribute(CreateEmailAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateEnumAttribute(CreateEnumAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateFloatAttribute(CreateFloatAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateIndex(CreateIndexRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateIntegerAttribute(CreateIntegerAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateIpAttribute(CreateIPAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateRelationshipAttribute(CreateRelationshipAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateStringAttribute(CreateStringAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> CreateUrlAttribute(CreateURLAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task DeleteAttribute(DeleteAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task DeleteCollection(DeleteCollectionRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task DeleteDatabase(DeleteDatabaseRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task DeleteDocument(DeleteDocumentRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task DeleteIndex(DeleteIndexRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> GetAttribute(GetAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> GetCollection(GetCollectionRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> GetDatabase(GetDatabaseRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> GetDocument(GetDocumentRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> GetIndex(GetIndexRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> ListAttributes(ListAttributesRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> ListCollections(ListCollectionsRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> ListDatabases(ListDatabasesRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> ListDocuments(ListDocumentsRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> ListIndexes(ListIndexesRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateBooleanAttribute(UpdateBooleanAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateCollection(UpdateCollectionRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateDatabase(UpdateDatabaseRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateDatetimeAttribute(UpdateDatetimeAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateDocument(UpdateDocumentRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateEmailAttribute(UpdateEmailAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateEnumAttribute(UpdateEnumAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateFloatAttribute(UpdateFloatAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateIntegerAttribute(UpdateIntegerAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateIpAttribute(UpdateIPAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateRelationshipAttribute(UpdateRelationshipAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateStringAttribute(UpdateStringAttributeRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateUrlAttribute(UpdateURLAttributeRequest request); +} From 4c709881164611ea4664ace1c6fe47c118942521 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 20:47:49 +0000 Subject: [PATCH 188/303] Added db client to appwrite client --- .../Clients/AppwriteClient.cs | 4 +++- .../Clients/IAppwriteClient.cs | 15 +++++++++++++++ .../Clients/ITeamsClient.cs | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/PinguApps.Appwrite.Server/Clients/AppwriteClient.cs b/src/PinguApps.Appwrite.Server/Clients/AppwriteClient.cs index 0906935e..11f9df1a 100644 --- a/src/PinguApps.Appwrite.Server/Clients/AppwriteClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/AppwriteClient.cs @@ -6,11 +6,13 @@ public class AppwriteClient : IAppwriteClient public IAccountClient Account { get; } public IUsersClient Users { get; } public ITeamsClient Teams { get; } + public IDatabasesClient DatabasesClient { get; } - public AppwriteClient(IAccountClient accountClient, IUsersClient usersClient, ITeamsClient teamsClient) + public AppwriteClient(IAccountClient accountClient, IUsersClient usersClient, ITeamsClient teamsClient, IDatabasesClient databasesClient) { Account = accountClient; Users = usersClient; Teams = teamsClient; + DatabasesClient = databasesClient; } } diff --git a/src/PinguApps.Appwrite.Server/Clients/IAppwriteClient.cs b/src/PinguApps.Appwrite.Server/Clients/IAppwriteClient.cs index 3be2226e..5727c16f 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IAppwriteClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IAppwriteClient.cs @@ -18,5 +18,20 @@ public interface IAppwriteClient /// Appwrite Docs /// IUsersClient Users { get; } + + /// + /// The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources, such as database documents or storage files. + /// Each user who creates a team becomes the team owner and can delegate the ownership role by inviting a new team member. Only team owners can invite new users to their team. + /// Appwrite Docs + /// ITeamsClient Teams { get; } + + /// + /// The Databases service allows you to create structured collections of documents, query and filter lists of documents, and manage an advanced set of read and write access permissions. + /// All data returned by the Databases service are represented as structured JSON documents. + /// The Databases service can contain multiple databases, each database can contain multiple collections. A collection is a group of similarly structured documents. The accepted structure of documents is defined by collection attributes. The collection attributes help you ensure all your user-submitted data is validated and stored according to the collection structure. + /// Using Appwrite permissions architecture, you can assign read or write access to each collection or document in your project for either a specific user, team, user role, or even grant it with public access (any). You can learn more about how Appwrite handles permissions and access control. + /// Appwrite Docs + /// + IDatabasesClient DatabasesClient { get; } } diff --git a/src/PinguApps.Appwrite.Server/Clients/ITeamsClient.cs b/src/PinguApps.Appwrite.Server/Clients/ITeamsClient.cs index f21c3b30..e469d974 100644 --- a/src/PinguApps.Appwrite.Server/Clients/ITeamsClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/ITeamsClient.cs @@ -9,6 +9,7 @@ namespace PinguApps.Appwrite.Server.Clients; /// /// The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources, such as database documents or storage files. /// Each user who creates a team becomes the team owner and can delegate the ownership role by inviting a new team member. Only team owners can invite new users to their team. +/// Appwrite Docs /// public interface ITeamsClient { From af487ca6b148fb40192b24c769f27325caab4c04 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 20:48:56 +0000 Subject: [PATCH 189/303] added db api and client to DI extensions --- .../ServiceCollectionExtensions.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/PinguApps.Appwrite.Server/ServiceCollectionExtensions.cs b/src/PinguApps.Appwrite.Server/ServiceCollectionExtensions.cs index fe1d9419..b40bb81e 100644 --- a/src/PinguApps.Appwrite.Server/ServiceCollectionExtensions.cs +++ b/src/PinguApps.Appwrite.Server/ServiceCollectionExtensions.cs @@ -49,9 +49,15 @@ public static IServiceCollection AddAppwriteServer(this IServiceCollection servi .AddHttpMessageHandler() .ConfigurePrimaryHttpMessageHandler(ConfigurePrimaryHttpMessageHandler); + services.AddRefitClient(customRefitSettings) + .ConfigureHttpClient(x => ConfigureHttpClient(x, endpoint)) + .AddHttpMessageHandler() + .ConfigurePrimaryHttpMessageHandler(ConfigurePrimaryHttpMessageHandler); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); return services; From d5008c183b9fc40b514054193b2468c09be47120 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 20:50:42 +0000 Subject: [PATCH 190/303] added IDatabasesApi for client --- .../Internals/IDatabasesApi.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/PinguApps.Appwrite.Client/Internals/IDatabasesApi.cs diff --git a/src/PinguApps.Appwrite.Client/Internals/IDatabasesApi.cs b/src/PinguApps.Appwrite.Client/Internals/IDatabasesApi.cs new file mode 100644 index 00000000..408af82a --- /dev/null +++ b/src/PinguApps.Appwrite.Client/Internals/IDatabasesApi.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Responses; +using Refit; + +namespace PinguApps.Appwrite.Client.Internals; +internal interface IDatabasesApi : IBaseApi +{ + [Get("/databases/{databaseId}/collections/{collectionId}/documents")] + [QueryUriFormat(UriFormat.Unescaped)] + Task> ListDocuments(string databaseId, string collectionId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); + + [Post("/databases/{databaseId}/collections/{collectionId}/documents")] + Task> CreateDocument(string databaseId, string collectionId, CreateDocumentRequest request); + + [Delete("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] + Task DeleteDocument(string databaseId, string collectionId, string documentId); + + [Get("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] + [QueryUriFormat(UriFormat.Unescaped)] + Task> GetDocument(string databaseId, string collectionId, string documentId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); + + [Patch("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] + Task> UpdateDocument(string databaseId, string collectionId, string documentId, UpdateDocumentRequest request); +} From d0bf2cc3b024d4195bfbd1167538fbdff8fd53ad Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 20:54:13 +0000 Subject: [PATCH 191/303] added databases client for client --- .../Clients/DatabasesClient.cs | 41 +++++++++++++++++++ .../Clients/IDatabasesClient.cs | 32 +++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs create mode 100644 src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs diff --git a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs new file mode 100644 index 00000000..8c2b3682 --- /dev/null +++ b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs @@ -0,0 +1,41 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using PinguApps.Appwrite.Client.Internals; +using PinguApps.Appwrite.Shared; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Client.Clients; + +/// +public class DatabasesClient : IDatabasesClient +{ + private readonly IDatabasesApi _databasesApi; + + public DatabasesClient(IServiceProvider services) + { + _databasesApi = services.GetRequiredService(); + } + + [ExcludeFromCodeCoverage] + /// + public Task> ListDocuments(ListDocumentsRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> CreateDocument(CreateDocumentRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task DeleteDocument(DeleteDocumentRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> GetDocument(GetDocumentRequest request) => throw new NotImplementedException(); + + [ExcludeFromCodeCoverage] + /// + public Task> UpdateDocument(UpdateDocumentRequest request) => throw new NotImplementedException(); +} diff --git a/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs new file mode 100644 index 00000000..9b337035 --- /dev/null +++ b/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; +using PinguApps.Appwrite.Shared; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Client.Clients; + +/// +/// The Databases service allows you to create structured collections of documents, query and filter lists of documents, and manage an advanced set of read and write access permissions. +/// All data returned by the Databases service are represented as structured JSON documents. +/// The Databases service can contain multiple databases, each database can contain multiple collections. A collection is a group of similarly structured documents. The accepted structure of documents is defined by collection attributes. The collection attributes help you ensure all your user-submitted data is validated and stored according to the collection structure. +/// Using Appwrite permissions architecture, you can assign read or write access to each collection or document in your project for either a specific user, team, user role, or even grant it with public access (any). You can learn more about how Appwrite handles permissions and access control. +/// Appwrite Docs +/// +public interface IDatabasesClient +{ + [Obsolete("Endpoint not yet implemented.")] + Task> CreateDocument(CreateDocumentRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task DeleteDocument(DeleteDocumentRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> GetDocument(GetDocumentRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> ListDocuments(ListDocumentsRequest request); + + [Obsolete("Endpoint not yet implemented.")] + Task> UpdateDocument(UpdateDocumentRequest request); +} From 9d3ea4a9ad060d0b7fcf4ce8ee66e88d70e2e2f5 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 20:58:33 +0000 Subject: [PATCH 192/303] Added databases client to appwrite client --- src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs | 5 ++++- src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs b/src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs index 447f6176..42b69e0f 100644 --- a/src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs @@ -8,11 +8,13 @@ public class AppwriteClient : IAppwriteClient, ISessionAware /// public IAccountClient Account { get; } public ITeamsClient Teams { get; } + public IDatabasesClient DatabasesClient { get; } - public AppwriteClient(IAccountClient accountClient, ITeamsClient teams) + public AppwriteClient(IAccountClient accountClient, ITeamsClient teams, IDatabasesClient databasesClient) { Account = accountClient; Teams = teams; + DatabasesClient = databasesClient; } string? ISessionAware.Session { get; set; } @@ -36,5 +38,6 @@ public void SetSession(string? session) (this as ISessionAware).UpdateSession(session); (Account as ISessionAware)!.UpdateSession(session); (Teams as ISessionAware)!.UpdateSession(session); + (DatabasesClient as ISessionAware)!.UpdateSession(session); } } diff --git a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs index 8c2b3682..b7a14ee9 100644 --- a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs @@ -10,7 +10,7 @@ namespace PinguApps.Appwrite.Client.Clients; /// -public class DatabasesClient : IDatabasesClient +public class DatabasesClient : SessionAwareClientBase, IDatabasesClient { private readonly IDatabasesApi _databasesApi; From 9705703392cc72586da2430c3240c5b240ecce4d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 21:00:52 +0000 Subject: [PATCH 193/303] pulled up to interface --- .../Clients/AppwriteClient.cs | 4 ++++ .../Clients/IAppwriteClient.cs | 17 ++++++++++++++++- .../Clients/ITeamsClient.cs | 1 + .../Clients/AppwriteClient.cs | 7 +++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs b/src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs index 42b69e0f..8d1d6a79 100644 --- a/src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs @@ -7,7 +7,11 @@ public class AppwriteClient : IAppwriteClient, ISessionAware { /// public IAccountClient Account { get; } + + /// public ITeamsClient Teams { get; } + + /// public IDatabasesClient DatabasesClient { get; } public AppwriteClient(IAccountClient accountClient, ITeamsClient teams, IDatabasesClient databasesClient) diff --git a/src/PinguApps.Appwrite.Client/Clients/IAppwriteClient.cs b/src/PinguApps.Appwrite.Client/Clients/IAppwriteClient.cs index c212a5b3..4e8f70b4 100644 --- a/src/PinguApps.Appwrite.Client/Clients/IAppwriteClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/IAppwriteClient.cs @@ -22,8 +22,23 @@ public interface IAppwriteClient void SetSession(string? session); /// - /// The sessio of the currently logged in user + /// The session of the currently logged in user /// string? Session { get; } + + /// + /// The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources, such as database documents or storage files. + /// Each user who creates a team becomes the team owner and can delegate the ownership role by inviting a new team member. Only team owners can invite new users to their team. + /// Appwrite Docs + /// ITeamsClient Teams { get; } + + /// + /// The Databases service allows you to create structured collections of documents, query and filter lists of documents, and manage an advanced set of read and write access permissions. + /// All data returned by the Databases service are represented as structured JSON documents. + /// The Databases service can contain multiple databases, each database can contain multiple collections. A collection is a group of similarly structured documents. The accepted structure of documents is defined by collection attributes. The collection attributes help you ensure all your user-submitted data is validated and stored according to the collection structure. + /// Using Appwrite permissions architecture, you can assign read or write access to each collection or document in your project for either a specific user, team, user role, or even grant it with public access (any). You can learn more about how Appwrite handles permissions and access control. + /// Appwrite Docs + /// + IDatabasesClient DatabasesClient { get; } } diff --git a/src/PinguApps.Appwrite.Client/Clients/ITeamsClient.cs b/src/PinguApps.Appwrite.Client/Clients/ITeamsClient.cs index 03a52a93..3c88139d 100644 --- a/src/PinguApps.Appwrite.Client/Clients/ITeamsClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/ITeamsClient.cs @@ -9,6 +9,7 @@ namespace PinguApps.Appwrite.Client.Clients; /// /// The Teams service allows you to group users of your project and to enable them to share read and write access to your project resources, such as database documents or storage files. /// Each user who creates a team becomes the team owner and can delegate the ownership role by inviting a new team member. Only team owners can invite new users to their team. +/// Appwrite Docs /// public interface ITeamsClient { diff --git a/src/PinguApps.Appwrite.Server/Clients/AppwriteClient.cs b/src/PinguApps.Appwrite.Server/Clients/AppwriteClient.cs index 11f9df1a..e4db347c 100644 --- a/src/PinguApps.Appwrite.Server/Clients/AppwriteClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/AppwriteClient.cs @@ -3,9 +3,16 @@ /// public class AppwriteClient : IAppwriteClient { + /// public IAccountClient Account { get; } + + /// public IUsersClient Users { get; } + + /// public ITeamsClient Teams { get; } + + /// public IDatabasesClient DatabasesClient { get; } public AppwriteClient(IAccountClient accountClient, IUsersClient usersClient, ITeamsClient teamsClient, IDatabasesClient databasesClient) From 6e21f0f8971f28598250e6fe1065bb9d523d5f13 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 21:05:06 +0000 Subject: [PATCH 194/303] added db client etc to service collection extensions --- .../ServiceCollectionExtensions.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/PinguApps.Appwrite.Client/ServiceCollectionExtensions.cs b/src/PinguApps.Appwrite.Client/ServiceCollectionExtensions.cs index 4e03823d..d2b79611 100644 --- a/src/PinguApps.Appwrite.Client/ServiceCollectionExtensions.cs +++ b/src/PinguApps.Appwrite.Client/ServiceCollectionExtensions.cs @@ -44,8 +44,14 @@ public static IServiceCollection AddAppwriteClient(this IServiceCollection servi .AddHttpMessageHandler() .AddHttpMessageHandler(); + services.AddRefitClient(customRefitSettings) + .ConfigureHttpClient(x => ConfigureHttpClient(x, endpoint)) + .AddHttpMessageHandler() + .AddHttpMessageHandler(); + services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(x => new Lazy(() => x.GetRequiredService())); @@ -77,8 +83,14 @@ public static IServiceCollection AddAppwriteClientForServer(this IServiceCollect .AddHttpMessageHandler() .ConfigurePrimaryHttpMessageHandler(ConfigurePrimaryHttpMessageHandler); + services.AddRefitClient(customRefitSettings) + .ConfigureHttpClient(x => ConfigureHttpClient(x, endpoint)) + .AddHttpMessageHandler() + .ConfigurePrimaryHttpMessageHandler(ConfigurePrimaryHttpMessageHandler); + services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); return services; From 10eeebea24c8088736bab161547ea75ade9f55ba Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 21:08:27 +0000 Subject: [PATCH 195/303] Fixed tests --- .../Clients/AppwriteClient.cs | 6 +++--- .../Clients/IAppwriteClient.cs | 2 +- .../Clients/AppwriteClient.cs | 4 ++-- .../Clients/IAppwriteClient.cs | 2 +- .../Clients/AppwriteClientTests.cs | 15 +++++++++++---- .../Clients/AppwriteServerTests.cs | 4 +++- 6 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs b/src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs index 8d1d6a79..2d4d0f31 100644 --- a/src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs @@ -12,13 +12,13 @@ public class AppwriteClient : IAppwriteClient, ISessionAware public ITeamsClient Teams { get; } /// - public IDatabasesClient DatabasesClient { get; } + public IDatabasesClient Databases { get; } public AppwriteClient(IAccountClient accountClient, ITeamsClient teams, IDatabasesClient databasesClient) { Account = accountClient; Teams = teams; - DatabasesClient = databasesClient; + Databases = databasesClient; } string? ISessionAware.Session { get; set; } @@ -42,6 +42,6 @@ public void SetSession(string? session) (this as ISessionAware).UpdateSession(session); (Account as ISessionAware)!.UpdateSession(session); (Teams as ISessionAware)!.UpdateSession(session); - (DatabasesClient as ISessionAware)!.UpdateSession(session); + (Databases as ISessionAware)!.UpdateSession(session); } } diff --git a/src/PinguApps.Appwrite.Client/Clients/IAppwriteClient.cs b/src/PinguApps.Appwrite.Client/Clients/IAppwriteClient.cs index 4e8f70b4..94bb5b18 100644 --- a/src/PinguApps.Appwrite.Client/Clients/IAppwriteClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/IAppwriteClient.cs @@ -40,5 +40,5 @@ public interface IAppwriteClient /// Using Appwrite permissions architecture, you can assign read or write access to each collection or document in your project for either a specific user, team, user role, or even grant it with public access (any). You can learn more about how Appwrite handles permissions and access control. /// Appwrite Docs /// - IDatabasesClient DatabasesClient { get; } + IDatabasesClient Databases { get; } } diff --git a/src/PinguApps.Appwrite.Server/Clients/AppwriteClient.cs b/src/PinguApps.Appwrite.Server/Clients/AppwriteClient.cs index e4db347c..ffe3eb27 100644 --- a/src/PinguApps.Appwrite.Server/Clients/AppwriteClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/AppwriteClient.cs @@ -13,13 +13,13 @@ public class AppwriteClient : IAppwriteClient public ITeamsClient Teams { get; } /// - public IDatabasesClient DatabasesClient { get; } + public IDatabasesClient Databases { get; } public AppwriteClient(IAccountClient accountClient, IUsersClient usersClient, ITeamsClient teamsClient, IDatabasesClient databasesClient) { Account = accountClient; Users = usersClient; Teams = teamsClient; - DatabasesClient = databasesClient; + Databases = databasesClient; } } diff --git a/src/PinguApps.Appwrite.Server/Clients/IAppwriteClient.cs b/src/PinguApps.Appwrite.Server/Clients/IAppwriteClient.cs index 5727c16f..881d5c5d 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IAppwriteClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IAppwriteClient.cs @@ -33,5 +33,5 @@ public interface IAppwriteClient /// Using Appwrite permissions architecture, you can assign read or write access to each collection or document in your project for either a specific user, team, user role, or even grant it with public access (any). You can learn more about how Appwrite handles permissions and access control. /// Appwrite Docs /// - IDatabasesClient DatabasesClient { get; } + IDatabasesClient Databases { get; } } diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/AppwriteClientTests.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/AppwriteClientTests.cs index 780e65c0..af231641 100644 --- a/tests/PinguApps.Appwrite.Client.Tests/Clients/AppwriteClientTests.cs +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/AppwriteClientTests.cs @@ -11,13 +11,15 @@ public void Constructor_SetsAccountClient() // Arrange var mockAccountClient = new Mock(); var mockTeamsClient = new Mock(); + var mockDatabasesClient = new Mock(); // Act - var appwriteClient = new AppwriteClient(mockAccountClient.Object, mockTeamsClient.Object); + var appwriteClient = new AppwriteClient(mockAccountClient.Object, mockTeamsClient.Object, mockDatabasesClient.Object); // Assert Assert.Equal(mockAccountClient.Object, appwriteClient.Account); Assert.Equal(mockTeamsClient.Object, appwriteClient.Teams); + Assert.Equal(mockDatabasesClient.Object, appwriteClient.Databases); } [Fact] @@ -26,7 +28,8 @@ public void Session_InitiallyNull_ReturnsNull() // Arrange var mockAccountClient = new Mock(); var mockTeamsClient = new Mock(); - var appwriteClient = new AppwriteClient(mockAccountClient.Object, mockTeamsClient.Object); + var mockDatabasesClient = new Mock(); + var appwriteClient = new AppwriteClient(mockAccountClient.Object, mockTeamsClient.Object, mockDatabasesClient.Object); // Act var session = appwriteClient.Session; @@ -41,9 +44,11 @@ public void SetSession_UpdatesSession() // Arrange var mockAccountClient = new Mock(); var mockTeamsClient = new Mock(); + var mockDatabasesClient = new Mock(); mockAccountClient.As(); mockTeamsClient.As(); - var appwriteClient = new AppwriteClient(mockAccountClient.Object, mockTeamsClient.Object); + mockDatabasesClient.As(); + var appwriteClient = new AppwriteClient(mockAccountClient.Object, mockTeamsClient.Object, mockDatabasesClient.Object); // Act appwriteClient.SetSession(TestConstants.Session); @@ -58,9 +63,11 @@ public void SetSession_UpdatesSessionInAccountClient() // Arrange var mockAccountClient = new Mock(); var mockTeamsClient = new Mock(); + var mockDatabasesClient = new Mock(); mockAccountClient.As(); mockTeamsClient.As(); - var appwriteClient = new AppwriteClient(mockAccountClient.Object, mockTeamsClient.Object); + mockDatabasesClient.As(); + var appwriteClient = new AppwriteClient(mockAccountClient.Object, mockTeamsClient.Object, mockDatabasesClient.Object); // Act appwriteClient.SetSession(TestConstants.Session); diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/AppwriteServerTests.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/AppwriteServerTests.cs index 326ee814..bb9f29e0 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/AppwriteServerTests.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/AppwriteServerTests.cs @@ -11,11 +11,13 @@ public void Constructor_AssignsAccountServerCorrectly() var mockAccountClient = new Mock(); var mockUsersClient = new Mock(); var mockTeamsClient = new Mock(); + var mockDatabasesClient = new Mock(); // Act - var appwriteServer = new AppwriteClient(mockAccountClient.Object, mockUsersClient.Object, mockTeamsClient.Object); + var appwriteServer = new AppwriteClient(mockAccountClient.Object, mockUsersClient.Object, mockTeamsClient.Object, mockDatabasesClient.Object); // Assert Assert.Equal(mockAccountClient.Object, appwriteServer.Account); Assert.Equal(mockUsersClient.Object, appwriteServer.Users); Assert.Equal(mockTeamsClient.Object, appwriteServer.Teams); + Assert.Equal(mockDatabasesClient.Object, appwriteServer.Databases); } } From c52f938cec954897fbab8ed87550375ed1638205 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 21:21:34 +0000 Subject: [PATCH 196/303] #541 Changed injection of clients to factory pattern --- .../Clients/AccountClient.cs | 5 +-- .../Clients/DatabasesClient.cs | 5 +-- .../Clients/TeamsClient.cs | 5 +-- .../ServiceCollectionExtensions.cs | 40 ++++++++++++++++--- .../Clients/AccountClient.cs | 5 +-- .../Clients/DatabasesClient.cs | 5 +-- .../Clients/TeamsClient.cs | 5 +-- .../Clients/UsersClient.cs | 5 +-- .../ServiceCollectionExtensions.cs | 27 +++++++++++-- .../Clients/Account/AccountClientTests.cs | 4 +- 10 files changed, 72 insertions(+), 34 deletions(-) diff --git a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs index 581bd728..eabb67dc 100644 --- a/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/AccountClient.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using PinguApps.Appwrite.Client.Clients; using PinguApps.Appwrite.Client.Internals; using PinguApps.Appwrite.Client.Utils; @@ -17,9 +16,9 @@ public class AccountClient : SessionAwareClientBase, IAccountClient private readonly IAccountApi _accountApi; private readonly Config _config; - public AccountClient(IServiceProvider services, Config config) + internal AccountClient(IAccountApi accountApi, Config config) { - _accountApi = services.GetRequiredService(); + _accountApi = accountApi; _config = config; } diff --git a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs index b7a14ee9..06f0cb89 100644 --- a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using PinguApps.Appwrite.Client.Internals; using PinguApps.Appwrite.Shared; using PinguApps.Appwrite.Shared.Requests.Databases; @@ -14,9 +13,9 @@ public class DatabasesClient : SessionAwareClientBase, IDatabasesClient { private readonly IDatabasesApi _databasesApi; - public DatabasesClient(IServiceProvider services) + internal DatabasesClient(IDatabasesApi databasesApi) { - _databasesApi = services.GetRequiredService(); + _databasesApi = databasesApi; } [ExcludeFromCodeCoverage] diff --git a/src/PinguApps.Appwrite.Client/Clients/TeamsClient.cs b/src/PinguApps.Appwrite.Client/Clients/TeamsClient.cs index a076aebb..9a6864df 100644 --- a/src/PinguApps.Appwrite.Client/Clients/TeamsClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/TeamsClient.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using PinguApps.Appwrite.Client.Internals; using PinguApps.Appwrite.Client.Utils; using PinguApps.Appwrite.Shared; @@ -17,9 +16,9 @@ public class TeamsClient : SessionAwareClientBase, ITeamsClient private readonly ITeamsApi _teamsApi; private readonly Config _config; - public TeamsClient(IServiceProvider services, Config config) + internal TeamsClient(ITeamsApi teamsApi, Config config) { - _teamsApi = services.GetRequiredService(); + _teamsApi = teamsApi; _config = config; } diff --git a/src/PinguApps.Appwrite.Client/ServiceCollectionExtensions.cs b/src/PinguApps.Appwrite.Client/ServiceCollectionExtensions.cs index d2b79611..8c7d39c2 100644 --- a/src/PinguApps.Appwrite.Client/ServiceCollectionExtensions.cs +++ b/src/PinguApps.Appwrite.Client/ServiceCollectionExtensions.cs @@ -49,9 +49,23 @@ public static IServiceCollection AddAppwriteClient(this IServiceCollection servi .AddHttpMessageHandler() .AddHttpMessageHandler(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(sp => + { + var api = sp.GetRequiredService(); + var config = sp.GetRequiredService(); + return new AccountClient(api, config); + }); + services.AddSingleton(sp => + { + var api = sp.GetRequiredService(); + var config = sp.GetRequiredService(); + return new TeamsClient(api, config); + }); + services.AddSingleton(sp => + { + var api = sp.GetRequiredService(); + return new DatabasesClient(api); + }); services.AddSingleton(); services.AddSingleton(x => new Lazy(() => x.GetRequiredService())); @@ -88,9 +102,23 @@ public static IServiceCollection AddAppwriteClientForServer(this IServiceCollect .AddHttpMessageHandler() .ConfigurePrimaryHttpMessageHandler(ConfigurePrimaryHttpMessageHandler); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(sp => + { + var api = sp.GetRequiredService(); + var config = sp.GetRequiredService(); + return new AccountClient(api, config); + }); + services.AddSingleton(sp => + { + var api = sp.GetRequiredService(); + var config = sp.GetRequiredService(); + return new TeamsClient(api, config); + }); + services.AddSingleton(sp => + { + var api = sp.GetRequiredService(); + return new DatabasesClient(api); + }); services.AddSingleton(); return services; diff --git a/src/PinguApps.Appwrite.Server/Clients/AccountClient.cs b/src/PinguApps.Appwrite.Server/Clients/AccountClient.cs index 40efa473..f2847a6e 100644 --- a/src/PinguApps.Appwrite.Server/Clients/AccountClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/AccountClient.cs @@ -1,6 +1,5 @@ using System; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using PinguApps.Appwrite.Server.Internals; using PinguApps.Appwrite.Server.Utils; using PinguApps.Appwrite.Shared; @@ -16,9 +15,9 @@ public class AccountClient : IAccountClient private readonly Config _config; - public AccountClient(IServiceProvider services, Config config) + internal AccountClient(IAccountApi accountApi, Config config) { - _accountApi = services.GetRequiredService(); + _accountApi = accountApi; _config = config; } diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index aeeff636..01db438c 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using PinguApps.Appwrite.Server.Internals; using PinguApps.Appwrite.Shared; using PinguApps.Appwrite.Shared.Requests.Databases; @@ -16,9 +15,9 @@ public class DatabasesClient : IDatabasesClient { private readonly IDatabasesApi _databasesApi; - public DatabasesClient(IServiceProvider services) + internal DatabasesClient(IDatabasesApi databasesApi) { - _databasesApi = services.GetRequiredService(); + _databasesApi = databasesApi; } [ExcludeFromCodeCoverage] diff --git a/src/PinguApps.Appwrite.Server/Clients/TeamsClient.cs b/src/PinguApps.Appwrite.Server/Clients/TeamsClient.cs index c23acbab..b5786391 100644 --- a/src/PinguApps.Appwrite.Server/Clients/TeamsClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/TeamsClient.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using PinguApps.Appwrite.Server.Internals; using PinguApps.Appwrite.Server.Utils; using PinguApps.Appwrite.Shared; @@ -17,9 +16,9 @@ public class TeamsClient : ITeamsClient private readonly ITeamsApi _teamsApi; private readonly Config _config; - public TeamsClient(IServiceProvider services, Config config) + internal TeamsClient(ITeamsApi teamsApi, Config config) { - _teamsApi = services.GetRequiredService(); + _teamsApi = teamsApi; _config = config; } diff --git a/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs b/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs index 7822d28e..c0c1f7f5 100644 --- a/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/UsersClient.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using PinguApps.Appwrite.Server.Internals; using PinguApps.Appwrite.Server.Utils; using PinguApps.Appwrite.Shared; @@ -16,9 +15,9 @@ public class UsersClient : IUsersClient private readonly IUsersApi _usersApi; private readonly Config _config; - public UsersClient(IServiceProvider services, Config config) + internal UsersClient(IUsersApi usersApi, Config config) { - _usersApi = services.GetRequiredService(); + _usersApi = usersApi; _config = config; } diff --git a/src/PinguApps.Appwrite.Server/ServiceCollectionExtensions.cs b/src/PinguApps.Appwrite.Server/ServiceCollectionExtensions.cs index b40bb81e..c1f39084 100644 --- a/src/PinguApps.Appwrite.Server/ServiceCollectionExtensions.cs +++ b/src/PinguApps.Appwrite.Server/ServiceCollectionExtensions.cs @@ -54,10 +54,29 @@ public static IServiceCollection AddAppwriteServer(this IServiceCollection servi .AddHttpMessageHandler() .ConfigurePrimaryHttpMessageHandler(ConfigurePrimaryHttpMessageHandler); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(sp => + { + var api = sp.GetRequiredService(); + var config = sp.GetRequiredService(); + return new AccountClient(api, config); + }); + services.AddSingleton(sp => + { + var api = sp.GetRequiredService(); + var config = sp.GetRequiredService(); + return new UsersClient(api, config); + }); + services.AddSingleton(sp => + { + var api = sp.GetRequiredService(); + var config = sp.GetRequiredService(); + return new TeamsClient(api, config); + }); + services.AddSingleton(sp => + { + var api = sp.GetRequiredService(); + return new DatabasesClient(api); + }); services.AddSingleton(); return services; diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.cs index 6b41629f..1221fce2 100644 --- a/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.cs +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Account/AccountClientTests.cs @@ -46,9 +46,7 @@ public void SetSession_UpdatesSession() // Arrange var sc = new ServiceCollection(); var mockAccountApi = new Mock(); - sc.AddSingleton(mockAccountApi.Object); - var sp = sc.BuildServiceProvider(); - var accountClient = new AccountClient(sp, new Config(TestConstants.Endpoint, TestConstants.ProjectId)); + var accountClient = new AccountClient(mockAccountApi.Object, new Config(TestConstants.Endpoint, TestConstants.ProjectId)); var sessionAware = accountClient as ISessionAware; // Act From 0568812e8b440bf860057a9125552acb340f1c45 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 21:27:50 +0000 Subject: [PATCH 197/303] Remove blank line Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com> --- src/PinguApps.Appwrite.Shared/Utils/Permission.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PinguApps.Appwrite.Shared/Utils/Permission.cs b/src/PinguApps.Appwrite.Shared/Utils/Permission.cs index cb8a2c83..17f11b86 100644 --- a/src/PinguApps.Appwrite.Shared/Utils/Permission.cs +++ b/src/PinguApps.Appwrite.Shared/Utils/Permission.cs @@ -136,5 +136,4 @@ internal PermissionBuilder(PermissionType permissionType) /// public Permission Label(string label) => new(_permissionType, RoleType.Label, label: label); } - } From ab8e0db5f412440cd398e7c1787b6e80ffe3afe1 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 21:28:03 +0000 Subject: [PATCH 198/303] Remove Blank Line Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com> --- .../Converters/AlwaysWriteNullableDateTimeConverterTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/AlwaysWriteNullableDateTimeConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AlwaysWriteNullableDateTimeConverterTests.cs index d89c0cbb..0f28e78a 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/AlwaysWriteNullableDateTimeConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/AlwaysWriteNullableDateTimeConverterTests.cs @@ -91,5 +91,4 @@ public void Serialize_WhenPropertyHasValue_SerializesDateTime() Assert.NotNull(deserialized?.Date); Assert.Equal(testDate, deserialized.Date.Value); } - } From b8f7fcd61aa93220fd214f1c6bc40d75bcadca51 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 21:28:11 +0000 Subject: [PATCH 199/303] Remove Blank Line Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com> --- .../Converters/DocumentConverterTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs index de87ea57..e5fa062a 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs @@ -460,5 +460,4 @@ public void Write_ObjectValue_SerializesCorrectly() Assert.Contains("\"objectField\":{}}", json); } - } From 88a4c716021e699541b8466316cb9bce59096763 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 23 Nov 2024 21:28:18 +0000 Subject: [PATCH 200/303] Remove Blank Line Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com> --- .../Requests/Databases/UpdateDocumentRequestBuilderTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestBuilderTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestBuilderTests.cs index 11feb85c..ed67a3dc 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestBuilderTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateDocumentRequestBuilderTests.cs @@ -345,5 +345,4 @@ public void WithChanges_PropertyChangedFromValueToNull_AddsToData() Assert.True(request.Data.ContainsKey("RegularProperty")); Assert.Null(request.Data["RegularProperty"]); } - } From 273f1fae303fd6ddfd952ed59f2c338891665fbc Mon Sep 17 00:00:00 2001 From: codefactor-io Date: Sat, 23 Nov 2024 21:29:00 +0000 Subject: [PATCH 201/303] [CodeFactor] Apply fixes --- .../Converters/DocumentConverterTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs index e5fa062a..676f37f4 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs @@ -1,4 +1,4 @@ -using System.Text; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using PinguApps.Appwrite.Shared.Converters; @@ -459,5 +459,4 @@ public void Write_ObjectValue_SerializesCorrectly() Assert.Contains("\"objectField\":{}}", json); } - } From 07549d40e95e6ba32c9f7019989bf7c82b433e36 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 17:18:53 +0000 Subject: [PATCH 202/303] Update alstr/todo-to-issue-action digest to 3ac81cb --- .github/workflows/dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 06d266d3..38f6be3d 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -30,7 +30,7 @@ jobs: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: "TODO to Issue" - uses: "alstr/todo-to-issue-action@7b4aab71051f6c80dbb899b9c3458c49507eea6e" # v5 + uses: "alstr/todo-to-issue-action@3ac81cb1658124fa226ebe4b329fb3025b46732c" # v5 with: INSERT_ISSUE_URLS: true AUTO_ASSIGN: true From 231485d0182e6a61054d24b7bc333a51f52719e6 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 26 Nov 2024 19:25:14 +0000 Subject: [PATCH 203/303] Partial implementation of GenericDocument response object - Requires JsonConverter to be completed --- .../Converters/DocumentGenericConverter.cs | 229 ++++++++++++++++++ .../Responses/Document.cs | 14 +- .../Responses/DocumentBase.cs | 26 ++ .../Responses/DoocumentGeneric.cs | 28 +++ 4 files changed, 290 insertions(+), 7 deletions(-) create mode 100644 src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/DocumentBase.cs create mode 100644 src/PinguApps.Appwrite.Shared/Responses/DoocumentGeneric.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs new file mode 100644 index 00000000..d4361b09 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Converters; + +public class DocumentGenericConverter : JsonConverter> + where TData : class, new() +{ + public override Doocument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + string? id = null; + string? collectionId = null; + string? databaseId = null; + DateTime? createdAt = null; + DateTime? updatedAt = null; + List? permissions = null; + TData data = new(); + + var dataProperties = new Dictionary(); + + var dateTimeConverter = new MultiFormatDateTimeConverter(); + var permissionListConverter = new PermissionListConverter(); + + if (reader.TokenType is not JsonTokenType.StartObject) + { + throw new JsonException("Expected StartObject token"); + } + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + string propertyName = reader.GetString()!; + + reader.Read(); + + switch (propertyName) + { + case "$id": + id = reader.GetString(); + break; + case "$collectionId": + collectionId = reader.GetString(); + break; + case "$databaseId": + databaseId = reader.GetString(); + break; + case "$createdAt": + createdAt = dateTimeConverter.Read(ref reader, typeof(DateTime), options); + break; + case "$updatedAt": + updatedAt = dateTimeConverter.Read(ref reader, typeof(DateTime), options); + break; + case "$permissions": + permissions = permissionListConverter.Read(ref reader, typeof(List), options); + break; + default: + var value = ReadValue(ref reader, options); + dataProperties[propertyName] = value; + break; + } + } + + if (id is null) + { + throw new JsonException("Unable to find a value for Id"); + } + + if (collectionId is null) + { + throw new JsonException("Unable to find a value for CollectionId"); + } + + if (databaseId is null) + { + throw new JsonException("Unable to find a value for DatabaseId"); + } + + if (createdAt is null) + { + throw new JsonException("Unable to find a value for CreatedAt"); + } + + if (updatedAt is null) + { + throw new JsonException("Unable to find a value for UpdatedAt"); + } + + if (permissions is null) + { + throw new JsonException("Unable to find a value for Permissions"); + } + + // Deserialize the remaining properties into TData + var dataJson = JsonSerializer.Serialize(dataProperties, options); + data = JsonSerializer.Deserialize(dataJson, options) ?? new TData(); + + return new Doocument(id, collectionId, databaseId, createdAt.Value, updatedAt.Value, permissions, data); + } + + internal object? ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var str = reader.GetString(); + + if ((DateTime.TryParse(str, out var dateTime))) + { + return dateTime; + } + return str; + + case JsonTokenType.Number: + if (reader.TryGetInt64(out var longValue)) + { + return longValue; + } + return reader.GetSingle(); + + case JsonTokenType.True: + case JsonTokenType.False: + return reader.GetBoolean(); + + case JsonTokenType.Null: + return null; + + case JsonTokenType.StartArray: + return ReadArray(ref reader, options); + + case JsonTokenType.StartObject: + return ReadObject(ref reader, options); + + default: + throw new JsonException($"Unsupported token type: {reader.TokenType}"); + } + } + + private IReadOnlyCollection ReadArray(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + var list = new List(); + + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndArray) + { + break; + } + + var item = ReadValue(ref reader, options); + list.Add(item); + } + + return list; + } + + private Dictionary ReadObject(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + var dict = new Dictionary(); + + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndObject) + { + break; + } + + var propertyName = reader.GetString()!; + + reader.Read(); + + var value = ReadValue(ref reader, options); + + dict[propertyName] = value; + } + + return dict; + } + + public override void Write(Utf8JsonWriter writer, Doocument value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + writer.WriteString("$id", value.Id); + writer.WriteString("$collectionId", value.CollectionId); + writer.WriteString("$databaseId", value.DatabaseId); + + // Use MultiFormatDateTimeConverter for DateTime properties + var dateTimeConverter = new MultiFormatDateTimeConverter(); + + writer.WritePropertyName("$createdAt"); + dateTimeConverter.Write(writer, value.CreatedAt, options); + + writer.WritePropertyName("$updatedAt"); + dateTimeConverter.Write(writer, value.UpdatedAt, options); + + writer.WritePropertyName("$permissions"); + JsonSerializer.Serialize(writer, value.Permissions, options); + + // Serialize the data object into individual properties. + var dataProperties = JsonSerializer.SerializeToElement(value.Data, options); + foreach (var property in dataProperties.EnumerateObject()) + { + writer.WritePropertyName(property.Name); + WriteValue(writer, property, options); + } + + writer.WriteEndObject(); + } + + private void WriteValue(Utf8JsonWriter writer, JsonProperty property, JsonSerializerOptions options) + { + // Handle null values + if (property.Value.ValueKind is JsonValueKind.Null) + { + writer.WriteNullValue(); + return; + } + + // TODO - Complete the implementation using DocumentConverter as a guide + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/Document.cs b/src/PinguApps.Appwrite.Shared/Responses/Document.cs index 5e822dd3..0723c37b 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Document.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Document.cs @@ -19,14 +19,14 @@ namespace PinguApps.Appwrite.Shared.Responses; /// Document data [JsonConverter(typeof(DocumentConverter))] public record Document( - [property: JsonPropertyName("$id")] string Id, - [property: JsonPropertyName("$collectionId")] string CollectionId, - [property: JsonPropertyName("$databaseId")] string DatabaseId, - [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, - [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, - [property: JsonPropertyName("$permissions"), JsonConverter(typeof(PermissionReadOnlyListConverter))] IReadOnlyList Permissions, + string Id, + string CollectionId, + string DatabaseId, + DateTime CreatedAt, + DateTime UpdatedAt, + IReadOnlyList Permissions, [property: JsonExtensionData] Dictionary Data -) +) : DocumentBase(Id, CollectionId, DatabaseId, CreatedAt, UpdatedAt, Permissions) { /// /// Extract document data by key diff --git a/src/PinguApps.Appwrite.Shared/Responses/DocumentBase.cs b/src/PinguApps.Appwrite.Shared/Responses/DocumentBase.cs new file mode 100644 index 00000000..15ea69c7 --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/DocumentBase.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite Document object +/// +/// Document ID +/// Collection ID +/// Database ID +/// Document creation date in ISO 8601 format +/// Document update date in ISO 8601 format +/// Document permissions. Learn more about permissions +[JsonConverter(typeof(DocumentConverter))] +public abstract record DocumentBase( + [property: JsonPropertyName("$id")] string Id, + [property: JsonPropertyName("$collectionId")] string CollectionId, + [property: JsonPropertyName("$databaseId")] string DatabaseId, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, + [property: JsonPropertyName("$permissions"), JsonConverter(typeof(PermissionReadOnlyListConverter))] IReadOnlyList Permissions +); diff --git a/src/PinguApps.Appwrite.Shared/Responses/DoocumentGeneric.cs b/src/PinguApps.Appwrite.Shared/Responses/DoocumentGeneric.cs new file mode 100644 index 00000000..a5c7542a --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Responses/DoocumentGeneric.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Responses; + +/// +/// An Appwrite Document object +/// +/// Document ID +/// Collection ID +/// Database ID +/// Document creation date in ISO 8601 format +/// Document update date in ISO 8601 format +/// Document permissions. Learn more about permissions +/// Document data +[JsonConverter(typeof(DocumentGenericConverter))] +public record Doocument( + string Id, + string CollectionId, + string DatabaseId, + DateTime CreatedAt, + DateTime UpdatedAt, + IReadOnlyList Permissions, + TData Data +) : DocumentBase(Id, CollectionId, DatabaseId, CreatedAt, UpdatedAt, Permissions) + where TData : class, new(); From 7f732fb10e4b578ebf323bc97c790a61d7416b43 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 27 Nov 2024 23:04:20 +0000 Subject: [PATCH 204/303] Added tests for the new converters etc --- .../Converters/DocumentGenericConverter.cs | 80 +- .../DocumentGenericConverterFactory.cs | 24 + ...DoocumentGeneric.cs => DocumentGeneric.cs} | 5 +- .../DocumentGenericConverterFactoryTests.cs | 99 +++ .../DocumentGenericConverterTests.cs | 687 ++++++++++++++++++ 5 files changed, 876 insertions(+), 19 deletions(-) create mode 100644 src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverterFactory.cs rename src/PinguApps.Appwrite.Shared/Responses/{DoocumentGeneric.cs => DocumentGeneric.cs} (88%) create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterFactoryTests.cs create mode 100644 tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs diff --git a/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs index d4361b09..b11a585d 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs @@ -7,10 +7,10 @@ namespace PinguApps.Appwrite.Shared.Converters; -public class DocumentGenericConverter : JsonConverter> +public class DocumentGenericConverter : JsonConverter> where TData : class, new() { - public override Doocument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override Document Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { string? id = null; string? collectionId = null; @@ -37,7 +37,7 @@ public override Doocument Read(ref Utf8JsonReader reader, Type typeToConv break; } - string propertyName = reader.GetString()!; + var propertyName = reader.GetString()!; reader.Read(); @@ -102,7 +102,7 @@ public override Doocument Read(ref Utf8JsonReader reader, Type typeToConv var dataJson = JsonSerializer.Serialize(dataProperties, options); data = JsonSerializer.Deserialize(dataJson, options) ?? new TData(); - return new Doocument(id, collectionId, databaseId, createdAt.Value, updatedAt.Value, permissions, data); + return new Document(id, collectionId, databaseId, createdAt.Value, updatedAt.Value, permissions, data); } internal object? ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) @@ -184,7 +184,7 @@ public override Doocument Read(ref Utf8JsonReader reader, Type typeToConv return dict; } - public override void Write(Utf8JsonWriter writer, Doocument value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, Document value, JsonSerializerOptions options) { writer.WriteStartObject(); @@ -204,26 +204,72 @@ public override void Write(Utf8JsonWriter writer, Doocument value, JsonSe writer.WritePropertyName("$permissions"); JsonSerializer.Serialize(writer, value.Permissions, options); - // Serialize the data object into individual properties. - var dataProperties = JsonSerializer.SerializeToElement(value.Data, options); - foreach (var property in dataProperties.EnumerateObject()) + // Serialize the Data property + if (value.Data is not null) { - writer.WritePropertyName(property.Name); - WriteValue(writer, property, options); + var dataProperties = JsonSerializer.SerializeToElement(value.Data, options); + foreach (var property in dataProperties.EnumerateObject()) + { + writer.WritePropertyName(property.Name); + WriteValue(writer, property.Value, options); + } } writer.WriteEndObject(); } - private void WriteValue(Utf8JsonWriter writer, JsonProperty property, JsonSerializerOptions options) + internal void WriteValue(Utf8JsonWriter writer, JsonElement element, JsonSerializerOptions options) { - // Handle null values - if (property.Value.ValueKind is JsonValueKind.Null) + var dateTimeConverter = new MultiFormatDateTimeConverter(); + + switch (element.ValueKind) { - writer.WriteNullValue(); - return; + case JsonValueKind.String: + var stringValue = element.GetString(); + if (DateTime.TryParse(stringValue, out var dateTimeValue)) + { + // Write DateTime using the MultiFormatDateTimeConverter + dateTimeConverter.Write(writer, dateTimeValue, options); + } + else + { + writer.WriteStringValue(stringValue); + } + break; + case JsonValueKind.Number: + if (element.TryGetInt32(out var intValue)) + writer.WriteNumberValue(intValue); + else if (element.TryGetInt64(out var longValue)) + writer.WriteNumberValue(longValue); + else if (element.TryGetDouble(out var doubleValue)) + writer.WriteNumberValue(doubleValue); + break; + case JsonValueKind.True: + case JsonValueKind.False: + writer.WriteBooleanValue(element.GetBoolean()); + break; + case JsonValueKind.Null: + writer.WriteNullValue(); + break; + case JsonValueKind.Array: + writer.WriteStartArray(); + foreach (var item in element.EnumerateArray()) + { + WriteValue(writer, item, options); + } + writer.WriteEndArray(); + break; + case JsonValueKind.Object: + writer.WriteStartObject(); + foreach (var property in element.EnumerateObject()) + { + writer.WritePropertyName(property.Name); + WriteValue(writer, property.Value, options); + } + writer.WriteEndObject(); + break; + case JsonValueKind.Undefined: + throw new JsonException("Cannot serialize undefined JsonElement"); } - - // TODO - Complete the implementation using DocumentConverter as a guide } } diff --git a/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverterFactory.cs b/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverterFactory.cs new file mode 100644 index 00000000..da178a5f --- /dev/null +++ b/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverterFactory.cs @@ -0,0 +1,24 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Shared.Converters; +public class DocumentGenericConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) + { + // Ensure the type is a generic type of Document<> + return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(Document<>); + } + + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + // Extract the TData type from Doocument + Type dataType = typeToConvert.GetGenericArguments()[0]; + + // Create a specific generic converter for Doocument + var converterType = typeof(DocumentGenericConverter<>).MakeGenericType(dataType); + return (JsonConverter?)Activator.CreateInstance(converterType); + } +} diff --git a/src/PinguApps.Appwrite.Shared/Responses/DoocumentGeneric.cs b/src/PinguApps.Appwrite.Shared/Responses/DocumentGeneric.cs similarity index 88% rename from src/PinguApps.Appwrite.Shared/Responses/DoocumentGeneric.cs rename to src/PinguApps.Appwrite.Shared/Responses/DocumentGeneric.cs index a5c7542a..e5465118 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/DoocumentGeneric.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/DocumentGeneric.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Responses; @@ -15,8 +16,8 @@ namespace PinguApps.Appwrite.Shared.Responses; /// Document update date in ISO 8601 format /// Document permissions. Learn more about permissions /// Document data -[JsonConverter(typeof(DocumentGenericConverter))] -public record Doocument( +[JsonConverter(typeof(DocumentGenericConverterFactory))] +public record Document( string Id, string CollectionId, string DatabaseId, diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterFactoryTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterFactoryTests.cs new file mode 100644 index 00000000..2110449e --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterFactoryTests.cs @@ -0,0 +1,99 @@ +using System.Text.Json; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Responses; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class DocumentGenericConverterFactoryTests +{ + private readonly DocumentGenericConverterFactory _factory; + + public DocumentGenericConverterFactoryTests() + { + _factory = new DocumentGenericConverterFactory(); + } + + public class TestData + { + public string? Field1 { get; set; } + } + + [Fact] + public void CanConvert_DocumentGenericType_ReturnsTrue() + { + // Arrange + var typeToConvert = typeof(Document); + + // Act + var result = _factory.CanConvert(typeToConvert); + + // Assert + Assert.True(result); + } + + [Fact] + public void CanConvert_NonGenericType_ReturnsFalse() + { + // Arrange + var typeToConvert = typeof(TestData); + + // Act + var result = _factory.CanConvert(typeToConvert); + + // Assert + Assert.False(result); + } + + [Fact] + public void CanConvert_DifferentGenericType_ReturnsFalse() + { + // Arrange + var typeToConvert = typeof(List); + + // Act + var result = _factory.CanConvert(typeToConvert); + + // Assert + Assert.False(result); + } + + [Fact] + public void CreateConverter_DocumentGenericType_ReturnsConverter() + { + // Arrange + var typeToConvert = typeof(Document); + var options = new JsonSerializerOptions(); + + // Act + var converter = _factory.CreateConverter(typeToConvert, options); + + // Assert + Assert.NotNull(converter); + Assert.IsType>(converter); + } + + [Fact] + public void CreateConverter_NonGenericType_ThrowsException() + { + // Arrange + var typeToConvert = typeof(TestData); + var options = new JsonSerializerOptions(); + + // Act & Assert + Assert.Throws(() => _factory.CreateConverter(typeToConvert, options)); + } + + [Fact] + public void CreateConverter_DifferentGenericType_ReturnsConverterWithFirstGenericArgument() + { + // Arrange + var typeToConvert = typeof(List); + var options = new JsonSerializerOptions(); + + // Act + var converter = _factory.CreateConverter(typeToConvert, options); + + // Assert + Assert.NotNull(converter); + Assert.IsType>(converter); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs new file mode 100644 index 00000000..08579d4f --- /dev/null +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs @@ -0,0 +1,687 @@ +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Responses; +using PinguApps.Appwrite.Shared.Utils; + +namespace PinguApps.Appwrite.Shared.Tests.Converters; +public class DocumentGenericConverterTests +{ + private readonly JsonSerializerOptions _options; + + public DocumentGenericConverterTests() + { + _options = new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Converters = { new DocumentGenericConverter() } + }; + } + + public class TestData + { + public string? Field1 { get; set; } + public int Field2 { get; set; } + public bool Field3 { get; set; } + public DateTime? Field4 { get; set; } + public List? Field5 { get; set; } + public Dictionary? Field6 { get; set; } + public float? FloatField { get; set; } + public long? LongField { get; set; } + public double? DoubleField { get; set; } + } + + [Fact] + public void Read_ValidJson_ReturnsDocument() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""Field1"": ""value1"", + ""Field2"": 42, + ""Field3"": true, + ""Field4"": ""2020-10-15T06:38:00.000+00:00"", + ""Field5"": [""item1"", ""item2""], + ""Field6"": { ""key1"": ""value1"", ""key2"": 2 } + }"; + + var document = JsonSerializer.Deserialize>(json, _options); + + Assert.NotNull(document); + Assert.Equal("1", document.Id); + Assert.Equal("col1", document.CollectionId); + Assert.Equal("db1", document.DatabaseId); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00"), document.CreatedAt); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00"), document.UpdatedAt); + Assert.Single(document.Permissions); + Assert.Equal(PermissionType.Read, document.Permissions[0].PermissionType); + Assert.Equal(RoleType.Any, document.Permissions[0].RoleType); + + Assert.NotNull(document.Data); + Assert.Equal("value1", document.Data.Field1); + Assert.Equal(42, document.Data.Field2); + Assert.True(document.Data.Field3); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00"), document.Data.Field4); + Assert.Equal(new List { "item1", "item2" }, document.Data.Field5); + + Assert.NotNull(document.Data.Field6); + var field6 = document.Data.Field6!; + Assert.Equal(2, field6.Count); + + // Extract values from JsonElement + Assert.True(field6.ContainsKey("key1")); + Assert.Equal("value1", field6["key1"]?.ToString()); + + Assert.True(field6.ContainsKey("key2")); + Assert.Equal(2, Convert.ToInt32(field6["key2"]?.ToString())); + } + + [Fact] + public void Read_InvalidJson_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""invalid-date"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""Field1"": ""value1"" + }"; + + Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Read_MissingRequiredFields_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""Field1"": ""value1"" + }"; + + Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Write_ValidDocument_WritesJson() + { + var testData = new TestData + { + Field1 = "value1", + Field2 = 42, + Field3 = true, + Field4 = DateTime.Parse("2020-10-15T06:38:00.000+00:00"), + Field5 = ["item1", "item2"], + Field6 = new Dictionary { { "key1", "value1" }, { "key2", 2 } } + }; + + var document = new Document( + "1", + "col1", + "db1", + DateTime.Parse("2020-10-15T06:38:00.000+00:00"), + DateTime.Parse("2020-10-15T06:38:00.000+00:00"), + [Permission.Read().Any()], + testData + ); + + var json = JsonSerializer.Serialize(document, _options); + + var expectedJson = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""Field1"": ""value1"", + ""Field2"": 42, + ""Field3"": true, + ""Field4"": ""2020-10-15T06:38:00.000+00:00"", + ""Field5"": [""item1"", ""item2""], + ""Field6"": { ""key1"": ""value1"", ""key2"": 2 }, + ""FloatField"": null, + ""LongField"": null, + ""DoubleField"": null + }".ReplaceLineEndings("").Replace(" ", ""); + + Assert.Equal(JsonDocument.Parse(expectedJson).RootElement.ToString(), JsonDocument.Parse(json).RootElement.ToString()); + } + + [Fact] + public void Write_NullData_WritesJsonWithNoDataProperties() + { + var document = new Document( + "1", + "col1", + "db1", + DateTime.UtcNow, + DateTime.UtcNow, + [Permission.Read().Any()], + null! + ); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"$id\"", json); + Assert.DoesNotContain("\"Field1\"", json); + } + + [Fact] + public void Read_NullProperty_InsertedIntoData() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""Field1"": null + }"; + + var document = JsonSerializer.Deserialize>(json, _options); + + Assert.NotNull(document); + Assert.NotNull(document.Data); + Assert.Null(document.Data.Field1); + } + + [Fact] + public void Write_NullValue_SerializesCorrectly() + { + var testData = new TestData + { + Field1 = null + }; + + var document = new Document( + "1", + "col1", + "db1", + DateTime.UtcNow, + DateTime.UtcNow, + [Permission.Read().Any()], + testData + ); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"Field1\":null", json); + } + + //[Fact] + //public void ReadValue_UnsupportedTokenType_ThrowsJsonException() + //{ + // var json = @" + // { + // ""$id"": ""1"", + // ""$collectionId"": ""col1"", + // ""$databaseId"": ""db1"", + // ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + // ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + // ""$permissions"": [""read(\""any\"")""], + // ""unsupported"": /** comment */ + // }"; + + // Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); + //} + + [Fact] + public void Write_CustomObject_SerializesUsingJsonSerializer() + { + var testData = new TestData + { + Field6 = new Dictionary + { + { "nestedObject", new { Prop1 = "value1", Prop2 = 2 } } + } + }; + + var document = new Document( + "1", + "col1", + "db1", + DateTime.UtcNow, + DateTime.UtcNow, + [Permission.Read().Any()], + testData + ); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"Prop1\":\"value1\"", json); + Assert.Contains("\"Prop2\":2", json); + } + + [Fact] + public void Read_InvalidJsonTokenType_ThrowsJsonException() + { + var json = @" + [ + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""Field1"": ""value1"" + } + ]"; + + Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Write_DateTimeValue_SerializesCorrectly() + { + var testData = new TestData + { + Field4 = DateTime.Parse("2020-10-15T06:38:00.000+00:00") + }; + + var document = new Document( + "1", + "col1", + "db1", + DateTime.UtcNow, + DateTime.UtcNow, + [], + testData + ); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"Field4\":\"2020-10-15T06:38:00.000+00:00\"", json); + } + + [Fact] + public void Write_NullDataProperty_WritesNull() + { + var testData = new TestData + { + Field5 = null + }; + + var document = new Document( + "1", + "col1", + "db1", + DateTime.UtcNow, + DateTime.UtcNow, + [], + testData + ); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"Field5\":null", json); + } + + [Fact] + public void Write_BooleanValue_SerializesCorrectly() + { + var testData = new TestData + { + Field3 = true + }; + + var document = new Document( + "1", + "col1", + "db1", + DateTime.UtcNow, + DateTime.UtcNow, + [], + testData + ); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"Field3\":true", json); + } + + [Fact] + public void Write_NumberValues_SerializesCorrectly() + { + var testData = new TestData + { + Field2 = 123 + }; + + var document = new Document( + "1", + "col1", + "db1", + DateTime.UtcNow, + DateTime.UtcNow, + [], + testData + ); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"Field2\":123", json); + } + + [Fact] + public void Read_MissingId_ThrowsJsonException() + { + var json = @" + { + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""Field1"": ""value1"" + }"; + + Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Read_MissingCollectionId_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""Field1"": ""value1"" + }"; + + Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Read_MissingDatabaseId_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""Field1"": ""value1"" + }"; + + Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Read_MissingCreatedAt_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""Field1"": ""value1"" + }"; + + Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Read_MissingUpdatedAt_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""Field1"": ""value1"" + }"; + + Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Read_MissingPermissions_ThrowsJsonException() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""Field1"": ""value1"" + }"; + + Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); + } + + [Fact] + public void Write_NullDocumentData_SerializesCorrectly() + { + var document = new Document( + "1", + "col1", + "db1", + DateTime.UtcNow, + DateTime.UtcNow, + [], + null! + ); + + var json = JsonSerializer.Serialize(document, _options); + + Assert.Contains("\"$id\"", json); + Assert.DoesNotContain("\"Field1\"", json); + } + + // Custom converter that returns null during deserialization + public class NullReturningConverter : JsonConverter where T : class + { + public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // Always return null + reader.Skip(); // Skip the current value to avoid infinite loops + return null; + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + // Write null + writer.WriteNullValue(); + } + } + + [Fact] + public void Read_DataDeserializationReturnsNull_DataSetToNewInstance() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""Field1"": ""value1"", + ""Field2"": 42 + }"; + + // Create new options with the NullReturningConverter for TestData + var optionsWithNullConverter = new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Converters = + { + new NullReturningConverter(), + new DocumentGenericConverter(), + new MultiFormatDateTimeConverter(), + new PermissionListConverter() + } + }; + + var document = JsonSerializer.Deserialize>(json, optionsWithNullConverter); + + Assert.NotNull(document); + Assert.NotNull(document.Data); + // Since data deserialization returned null, Data should be set to new TData() + // So Data's properties should have default values + Assert.Null(document.Data.Field1); + Assert.Equal(0, document.Data.Field2); + } + + [Fact] + public void ReadValue_UnsupportedTokenType_ThrowsJsonException() + { + var json = @"{ + ""Field1"": /* Comment */ ""value1"" + }"; + + var readerOptions = new JsonReaderOptions + { + CommentHandling = JsonCommentHandling.Allow + }; + + var bytes = Encoding.UTF8.GetBytes(json); + + var reader = new Utf8JsonReader(bytes, readerOptions); + + var converter = new DocumentGenericConverter(); + + // Read the StartObject token + reader.Read(); // JsonTokenType.StartObject + + // Read the PropertyName token + reader.Read(); // JsonTokenType.PropertyName + + var propertyName = reader.GetString()!; + + // Read the Comment token + reader.Read(); // JsonTokenType.Comment + + // At this point, reader.TokenType is Comment, which is not handled in ReadValue + // Calling ReadValue should now hit the default case and throw JsonException + try + { + converter.ReadValue(ref reader, _options); + Assert.Fail("Did not throw JsonException"); + } + catch (JsonException) + { + } + } + + [Fact] + public void ReadValue_FloatNumber_ReturnsSingle() + { + var json = @" + { + ""$id"": ""1"", + ""$collectionId"": ""col1"", + ""$databaseId"": ""db1"", + ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", + ""$permissions"": [""read(\""any\"")""], + ""FloatField"": 1.23 + }"; + + var document = JsonSerializer.Deserialize>(json, _options); + + Assert.NotNull(document); + Assert.NotNull(document.Data); + Assert.Equal(1.23f, document.Data.FloatField); + } + + [Fact] + public void WriteValue_UndefinedValueKind_CallsJsonSerializer() + { + var converter = new DocumentGenericConverter(); + + // Create a default-initialized JsonElement (ValueKind is Undefined) + JsonElement undefinedElement = default; + + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + // Since JsonSerializer.Serialize will throw an exception when trying to serialize an undefined JsonElement, + // we can expect an InvalidOperationException + Assert.Throws(() => converter.WriteValue(writer, undefinedElement, _options)); + } + + [Fact] + public void WriteValue_LongNumber_WritesLongValue() + { + var testData = new TestData + { + LongField = (long)int.MaxValue + 1 // Value larger than int.MaxValue + }; + + var document = new Document( + "1", + "col1", + "db1", + DateTime.UtcNow, + DateTime.UtcNow, + [], + testData + ); + + var json = JsonSerializer.Serialize(document, _options); + + // Verify that the LongField is serialized correctly + var jsonDoc = JsonDocument.Parse(json); + Assert.True(jsonDoc.RootElement.TryGetProperty("LongField", out var longFieldElement)); + Assert.Equal(JsonValueKind.Number, longFieldElement.ValueKind); + Assert.Equal((long)int.MaxValue + 1, longFieldElement.GetInt64()); + } + + [Fact] + public void WriteValue_DoubleNumber_WritesDoubleValue() + { + var testData = new TestData + { + DoubleField = 1.23e20 // A large double value + }; + + var document = new Document( + "1", + "col1", + "db1", + DateTime.UtcNow, + DateTime.UtcNow, + [], + testData + ); + + var json = JsonSerializer.Serialize(document, _options); + + // Verify that the DoubleField is serialized correctly + var jsonDoc = JsonDocument.Parse(json); + Assert.True(jsonDoc.RootElement.TryGetProperty("DoubleField", out var doubleFieldElement)); + Assert.Equal(JsonValueKind.Number, doubleFieldElement.ValueKind); + Assert.Equal(1.23e20, doubleFieldElement.GetDouble()); + } + +} From 4de2e4e8310e6dadba92d6411d3ab1af948190f3 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 27 Nov 2024 23:06:49 +0000 Subject: [PATCH 205/303] Update tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs Co-authored-by: codefactor-io[bot] <47775046+codefactor-io[bot]@users.noreply.github.com> --- .../Converters/DocumentGenericConverterTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs index 08579d4f..9a18580c 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs @@ -683,5 +683,4 @@ public void WriteValue_DoubleNumber_WritesDoubleValue() Assert.Equal(JsonValueKind.Number, doubleFieldElement.ValueKind); Assert.Equal(1.23e20, doubleFieldElement.GetDouble()); } - } From 4d490d8e3576e81f08de6e07dca1d7629609b5e5 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 17:47:13 +0000 Subject: [PATCH 206/303] Implemented ListDatabases --- src/PinguApps.Appwrite.Playground/App.cs | 58 +++---------------- .../Clients/DatabasesClient.cs | 18 +++++- .../Clients/IDatabasesClient.cs | 11 +++- 3 files changed, 32 insertions(+), 55 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 785b44a0..0c1eb713 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -1,10 +1,5 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using Microsoft.Extensions.Configuration; -using PinguApps.Appwrite.Shared.Converters; -using PinguApps.Appwrite.Shared.Responses; -using PinguApps.Appwrite.Shared.Responses.Interfaces; -using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; +using Microsoft.Extensions.Configuration; +using PinguApps.Appwrite.Shared.Requests.Databases; namespace PinguApps.Appwrite.Playground; internal class App @@ -22,51 +17,14 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var attributes = new List - { - new AttributeBoolean("a", "boolean", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null), - new AttributeDatetime("b", "datetime", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "datetime", null), - new AttributeEmail("c", "string", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "email", null), - new AttributeEnum("d", "string", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, ["some", "elements"], "enum", null), - new AttributeFloat("e", "float", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null, null, null), - new AttributeInteger("f", "integer", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, null, null, null), - new AttributeIp("g", "string", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "ip", null), - new AttributeRelationship("h", "string", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "collection", Shared.Enums.RelationType.OneToMany, false, "a string", Shared.Enums.OnDelete.Restrict, Shared.Enums.RelationshipSide.Parent), - new AttributeString("i", "string", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, 128, null), - new AttributeUrl("j", "string", Shared.Enums.DatabaseElementStatus.Available, null, false, false, DateTime.UtcNow, DateTime.UtcNow, "url", null) - }; + var request = new ListDatabasesRequest(); - var visitor = new AttributeVisitor(); + var response = await _server.Databases.ListDatabases(request); - foreach (var attribute in attributes) - { - attribute.Accept(visitor); - } + Console.WriteLine(response.Result.Match( + result => result.ToString(), + appwriteError => appwriteError.Message, + internalError => internalError.Message)); - var options = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - }; - - options.Converters.Add(new IgnoreSdkExcludedPropertiesConverterFactory()); - - var json = JsonSerializer.Serialize(attributes[0]); - - Console.WriteLine(json); } } - -internal class AttributeVisitor : IAttributeVisitor -{ - public void Visit(AttributeBoolean attribute) => Console.WriteLine($"Boolean attribute Key: {attribute.Key}"); - public void Visit(AttributeInteger attribute) => Console.WriteLine($"Integer attribute Key: {attribute.Key}"); - public void Visit(AttributeFloat attribute) => Console.WriteLine($"Float attribute Key: {attribute.Key}"); - public void Visit(AttributeString attribute) => Console.WriteLine($"String attribute Key: {attribute.Key}"); - public void Visit(AttributeEmail attribute) => Console.WriteLine($"Email attribute Key: {attribute.Key}"); - public void Visit(AttributeUrl attribute) => Console.WriteLine($"Url attribute Key: {attribute.Key}"); - public void Visit(AttributeIp attribute) => Console.WriteLine($"Ip attribute Key: {attribute.Key}"); - public void Visit(AttributeDatetime attribute) => Console.WriteLine($"Datetime attribute Key: {attribute.Key}"); - public void Visit(AttributeEnum attribute) => Console.WriteLine($"Enum attribute Key: {attribute.Key}"); - public void Visit(AttributeRelationship attribute) => Console.WriteLine($"Relationship attribute Key: {attribute.Key}"); -} diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 01db438c..d735c47c 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using PinguApps.Appwrite.Server.Internals; +using PinguApps.Appwrite.Server.Utils; using PinguApps.Appwrite.Shared; using PinguApps.Appwrite.Shared.Requests.Databases; using PinguApps.Appwrite.Shared.Responses; @@ -20,9 +21,22 @@ internal DatabasesClient(IDatabasesApi databasesApi) _databasesApi = databasesApi; } - [ExcludeFromCodeCoverage] /// - public Task> ListDatabases(ListDatabasesRequest request) => throw new NotImplementedException(); + public async Task> ListDatabases(ListDatabasesRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.ListDatabases(RequestUtils.GetQueryStrings(request.Queries), request.Search); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 89066b24..cc7249bb 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -17,6 +17,14 @@ namespace PinguApps.Appwrite.Server.Clients; /// public interface IDatabasesClient { + /// + /// Get a list of all databases from the current Appwrite project. You can use the search parameter to filter your results. + /// Appwrite Docs + /// + /// The request content + /// The databases list + Task> ListDatabases(ListDatabasesRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); @@ -95,9 +103,6 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task> ListCollections(ListCollectionsRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> ListDatabases(ListDatabasesRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> ListDocuments(ListDocumentsRequest request); From e195f46f1375492fe6507de7bf7fd0938854dd99 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 17:58:10 +0000 Subject: [PATCH 207/303] Added tests for ListDatabases --- src/PinguApps.Appwrite.Playground/App.cs | 1 - .../DatabasesClientTests.ListDatabases.cs | 93 +++++++++++++++++++ .../Clients/Databases/DatabasesClientTests.cs | 39 ++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListDatabases.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.cs diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 0c1eb713..8812c4c3 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -25,6 +25,5 @@ public async Task Run(string[] args) result => result.ToString(), appwriteError => appwriteError.Message, internalError => internalError.Message)); - } } diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListDatabases.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListDatabases.cs new file mode 100644 index 00000000..390a9942 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListDatabases.cs @@ -0,0 +1,93 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task ListDatabases_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new ListDatabasesRequest(); + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.DatabasesListResponse); + + // Act + var result = await _appwriteClient.Databases.ListDatabases(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListDatabases_ShouldProvideQueryParams_WhenQueriesAndSearchProvided() + { + // Arrange + var query = Query.Limit(5); + var search = "SearchString"; + var request = new ListDatabasesRequest + { + Queries = new List { query }, + Search = search + }; + + var expectedQueryParams = new Dictionary + { + { "queries[]", query.GetQueryString() }, + { "search", search } + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases") + .ExpectedHeaders() + .WithQueryString($"queries[]={query.GetQueryString()}&search={search}") + .Respond(TestConstants.AppJson, TestConstants.DatabasesListResponse); + + // Act + var result = await _appwriteClient.Databases.ListDatabases(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListDatabases_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new ListDatabasesRequest(); + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.ListDatabases(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task ListDatabases_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new ListDatabasesRequest(); + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.ListDatabases(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.cs new file mode 100644 index 00000000..bbf4016e --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.cs @@ -0,0 +1,39 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.Extensions.DependencyInjection; +using PinguApps.Appwrite.Server.Clients; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Tests; +using Refit; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + private readonly MockHttpMessageHandler _mockHttp; + private readonly IAppwriteClient _appwriteClient; + private readonly JsonSerializerOptions _jsonSerializerOptions; + + public DatabasesClientTests() + { + _mockHttp = new MockHttpMessageHandler(); + var services = new ServiceCollection(); + + services.AddAppwriteServer(TestConstants.ProjectId, TestConstants.ApiKey, TestConstants.Endpoint, new RefitSettings + { + HttpMessageHandlerFactory = () => _mockHttp + }); + + var serviceProvider = services.BuildServiceProvider(); + + _appwriteClient = serviceProvider.GetRequiredService(); + + _jsonSerializerOptions = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + _jsonSerializerOptions.Converters.Add(new IgnoreSdkExcludedPropertiesConverterFactory()); + } +} From f0812f799c5bdff53e792ee897383f7024cea052 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:00:13 +0000 Subject: [PATCH 208/303] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 332e94d7..676875d4 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress -![Server & Client - 125 / 317](https://img.shields.io/badge/Server_&_Client-125%20%2F%20317-gold?style=for-the-badge) +![Server & Client - 126 / 317](https://img.shields.io/badge/Server_&_Client-126%20%2F%20317-gold?style=for-the-badge) -![Server - 65 / 224](https://img.shields.io/badge/Server-65%20%2F%20224-red?style=for-the-badge) +![Server - 66 / 224](https://img.shields.io/badge/Server-66%20%2F%20224-red?style=for-the-badge) ![Client - 60 / 93](https://img.shields.io/badge/Client-60%20%2F%2093-gold?style=for-the-badge) @@ -275,11 +275,11 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Update Preferences](https://appwrite.io/docs/references/1.6.x/client-rest/teams#updatePrefs) | ✅ | ✅ | ### Databases -![Databases - 0 / 47](https://img.shields.io/badge/Databases-0%20%2F%2047-red?style=for-the-badge) +![Databases - 1 / 47](https://img.shields.io/badge/Databases-1%20%2F%2047-red?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| -| [List Databases](https://appwrite.io/docs/references/1.6.x/server-rest/databases#list) | ❌ | ⬛ | +| [List Databases](https://appwrite.io/docs/references/1.6.x/server-rest/databases#list) | ❌ | ✅ | | [Create Databases](https://appwrite.io/docs/references/1.6.x/server-rest/databases#create) | ❌ | ⬛ | | [Get Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#get) | ❌ | ⬛ | | [Update Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#update) | ❌ | ⬛ | From c6c9854fb7736f0d16cc8a20dd21655639429925 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:09:39 +0000 Subject: [PATCH 209/303] implemented create database --- src/PinguApps.Appwrite.Playground/App.cs | 7 +++++-- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 11 ++++++++--- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 8812c4c3..5026951e 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,9 +17,12 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new ListDatabasesRequest(); + var request = new CreateDatabaseRequest() + { + Name = "My Database" + }; - var response = await _server.Databases.ListDatabases(request); + var response = await _server.Databases.CreateDatabase(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index d735c47c..a880bf05 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -38,9 +38,22 @@ public async Task> ListDatabases(ListDatabasesRequ } } - [ExcludeFromCodeCoverage] /// - public Task> CreateDatabase(CreateDatabaseRequest request) => throw new NotImplementedException(); + public async Task> CreateDatabase(CreateDatabaseRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateDatabase(request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index cc7249bb..99625fbd 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -25,15 +25,20 @@ public interface IDatabasesClient /// The databases list Task> ListDatabases(ListDatabasesRequest request); + /// + /// Create a new Database. + /// Appwrite Docs + /// + /// The request content + /// The database + Task> CreateDatabase(CreateDatabaseRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); [Obsolete("Endpoint not yet implemented.")] Task> CreateCollection(CreateCollectionRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> CreateDatabase(CreateDatabaseRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> CreateDatetimeAttribute(CreateDatetimeAttributeRequest request); From 9d4ea4a9e7568a264a352b6c187af0c191d7ec84 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:13:57 +0000 Subject: [PATCH 210/303] added tests for create database --- .../DatabasesClientTests.CreateDatabase.cs | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatabase.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatabase.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatabase.cs new file mode 100644 index 00000000..8fc8adbb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatabase.cs @@ -0,0 +1,71 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task CreateDatabase_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateDatabaseRequest + { + Name = "Pingu's Database" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.DatabaseResponse); + + // Act + var result = await _appwriteClient.Databases.CreateDatabase(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateDatabase_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateDatabaseRequest + { + Name = "Pingu's Database" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateDatabase(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateDatabase_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateDatabaseRequest + { + Name = "Pingu's Database" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateDatabase(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From e16a59ffdfa2c1ca1be4ed4be84386b2bd09a982 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:14:35 +0000 Subject: [PATCH 211/303] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 676875d4..b3cedfb4 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress -![Server & Client - 126 / 317](https://img.shields.io/badge/Server_&_Client-126%20%2F%20317-gold?style=for-the-badge) +![Server & Client - 127 / 317](https://img.shields.io/badge/Server_&_Client-127%20%2F%20317-gold?style=for-the-badge) -![Server - 66 / 224](https://img.shields.io/badge/Server-66%20%2F%20224-red?style=for-the-badge) +![Server - 67 / 224](https://img.shields.io/badge/Server-67%20%2F%20224-red?style=for-the-badge) ![Client - 60 / 93](https://img.shields.io/badge/Client-60%20%2F%2093-gold?style=for-the-badge) @@ -275,12 +275,12 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Update Preferences](https://appwrite.io/docs/references/1.6.x/client-rest/teams#updatePrefs) | ✅ | ✅ | ### Databases -![Databases - 1 / 47](https://img.shields.io/badge/Databases-1%20%2F%2047-red?style=for-the-badge) +![Databases - 2 / 47](https://img.shields.io/badge/Databases-2%20%2F%2047-red?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| | [List Databases](https://appwrite.io/docs/references/1.6.x/server-rest/databases#list) | ❌ | ✅ | -| [Create Databases](https://appwrite.io/docs/references/1.6.x/server-rest/databases#create) | ❌ | ⬛ | +| [Create Databases](https://appwrite.io/docs/references/1.6.x/server-rest/databases#create) | ❌ | ✅ | | [Get Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#get) | ❌ | ⬛ | | [Update Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#update) | ❌ | ⬛ | | [Delete Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#delete) | ❌ | ⬛ | From 976d71def413c0583348350edbaddf953b68b45e Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:20:15 +0000 Subject: [PATCH 212/303] implemented delete database --- src/PinguApps.Appwrite.Playground/App.cs | 6 +++--- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 11 ++++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 5026951e..146e2fe5 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,12 +17,12 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new CreateDatabaseRequest() + var request = new DeleteDatabaseRequest() { - Name = "My Database" + DatabaseId = "6748b1cb000b8513a348" }; - var response = await _server.Databases.CreateDatabase(request); + var response = await _server.Databases.DeleteDatabase(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index a880bf05..a486541d 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -55,9 +55,22 @@ public async Task> CreateDatabase(CreateDatabaseRequest } } - [ExcludeFromCodeCoverage] /// - public Task DeleteDatabase(DeleteDatabaseRequest request) => throw new NotImplementedException(); + public async Task DeleteDatabase(DeleteDatabaseRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.DeleteDatabase(request.DatabaseId); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 99625fbd..acdd5cd4 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -33,6 +33,14 @@ public interface IDatabasesClient /// The database Task> CreateDatabase(CreateDatabaseRequest request); + /// + /// Delete a database by its unique ID. Only API keys with with databases.write scope can delete a database. + /// Appwrite Docs + /// + /// The request content + /// 200 Success Response + Task DeleteDatabase(DeleteDatabaseRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); @@ -78,9 +86,6 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task DeleteCollection(DeleteCollectionRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task DeleteDatabase(DeleteDatabaseRequest request); - [Obsolete("Endpoint not yet implemented.")] Task DeleteDocument(DeleteDocumentRequest request); From 0a926d1c5757cdddbdd3adf49c98958d40019b66 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:23:21 +0000 Subject: [PATCH 213/303] added tests for delete database --- .../DatabasesClientTests.DeleteDatabase.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteDatabase.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteDatabase.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteDatabase.cs new file mode 100644 index 00000000..16de8bf8 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteDatabase.cs @@ -0,0 +1,72 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task DeleteDatabase_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new DeleteDatabaseRequest + { + DatabaseId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.NoContent); + + // Act + var result = await _appwriteClient.Databases.DeleteDatabase(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task DeleteDatabase_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new DeleteDatabaseRequest + { + DatabaseId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.DeleteDatabase(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task DeleteDatabase_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new DeleteDatabaseRequest + { + DatabaseId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.DeleteDatabase(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From d8a063a4bb6800a18c77bfae47c83574717a6e23 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:24:13 +0000 Subject: [PATCH 214/303] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b3cedfb4..63b459d2 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress -![Server & Client - 127 / 317](https://img.shields.io/badge/Server_&_Client-127%20%2F%20317-gold?style=for-the-badge) +![Server & Client - 128 / 317](https://img.shields.io/badge/Server_&_Client-128%20%2F%20317-gold?style=for-the-badge) -![Server - 67 / 224](https://img.shields.io/badge/Server-67%20%2F%20224-red?style=for-the-badge) +![Server - 68 / 224](https://img.shields.io/badge/Server-68%20%2F%20224-red?style=for-the-badge) ![Client - 60 / 93](https://img.shields.io/badge/Client-60%20%2F%2093-gold?style=for-the-badge) @@ -275,7 +275,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Update Preferences](https://appwrite.io/docs/references/1.6.x/client-rest/teams#updatePrefs) | ✅ | ✅ | ### Databases -![Databases - 2 / 47](https://img.shields.io/badge/Databases-2%20%2F%2047-red?style=for-the-badge) +![Databases - 3 / 47](https://img.shields.io/badge/Databases-3%20%2F%2047-red?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -283,7 +283,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Create Databases](https://appwrite.io/docs/references/1.6.x/server-rest/databases#create) | ❌ | ✅ | | [Get Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#get) | ❌ | ⬛ | | [Update Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#update) | ❌ | ⬛ | -| [Delete Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#delete) | ❌ | ⬛ | +| [Delete Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#delete) | ❌ | ✅ | | [List Collections](https://appwrite.io/docs/references/1.6.x/server-rest/databases#listCollections) | ❌ | ⬛ | | [Create Collection](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createCollection) | ❌ | ⬛ | | [Get Collections](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getCollection) | ❌ | ⬛ | From 25a78e2a300fd316f0fa67b573046380edea9ab2 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:31:08 +0000 Subject: [PATCH 215/303] implemented get database --- src/PinguApps.Appwrite.Playground/App.cs | 6 +++--- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 11 ++++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 146e2fe5..d805a54e 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,12 +17,12 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new DeleteDatabaseRequest() + var request = new GetDatabaseRequest() { - DatabaseId = "6748b1cb000b8513a348" + DatabaseId = "6748b44d000b2b0e73ac" }; - var response = await _server.Databases.DeleteDatabase(request); + var response = await _server.Databases.GetDatabase(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index a486541d..cb0c435c 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -72,9 +72,22 @@ public async Task DeleteDatabase(DeleteDatabaseRequest request) } } - [ExcludeFromCodeCoverage] /// - public Task> GetDatabase(GetDatabaseRequest request) => throw new NotImplementedException(); + public async Task> GetDatabase(GetDatabaseRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.GetDatabase(request.DatabaseId); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index acdd5cd4..de076a54 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -41,6 +41,14 @@ public interface IDatabasesClient /// 200 Success Response Task DeleteDatabase(DeleteDatabaseRequest request); + /// + /// Get a database by its unique ID. This endpoint response returns a JSON object with the database metadata. + /// Appwrite Docs + /// + /// The request content + /// The database + Task> GetDatabase(GetDatabaseRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); @@ -98,9 +106,6 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task> GetCollection(GetCollectionRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> GetDatabase(GetDatabaseRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> GetDocument(GetDocumentRequest request); From 23003d87a55f43b1b94059418f4bac368e67effb Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:31:16 +0000 Subject: [PATCH 216/303] added tests for get database --- .../DatabasesClientTests.GetDatabase.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetDatabase.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetDatabase.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetDatabase.cs new file mode 100644 index 00000000..7e5a022f --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetDatabase.cs @@ -0,0 +1,72 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task GetDatabase_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new GetDatabaseRequest + { + DatabaseId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.DatabaseResponse); + + // Act + var result = await _appwriteClient.Databases.GetDatabase(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task GetDatabase_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new GetDatabaseRequest + { + DatabaseId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.GetDatabase(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task GetDatabase_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new GetDatabaseRequest + { + DatabaseId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.GetDatabase(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 5995bae999b4e6364d38a5b9393d008d1d76547d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:32:02 +0000 Subject: [PATCH 217/303] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 63b459d2..4f376af8 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress -![Server & Client - 128 / 317](https://img.shields.io/badge/Server_&_Client-128%20%2F%20317-gold?style=for-the-badge) +![Server & Client - 129 / 317](https://img.shields.io/badge/Server_&_Client-129%20%2F%20317-gold?style=for-the-badge) -![Server - 68 / 224](https://img.shields.io/badge/Server-68%20%2F%20224-red?style=for-the-badge) +![Server - 69 / 224](https://img.shields.io/badge/Server-69%20%2F%20224-red?style=for-the-badge) ![Client - 60 / 93](https://img.shields.io/badge/Client-60%20%2F%2093-gold?style=for-the-badge) @@ -275,13 +275,13 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Update Preferences](https://appwrite.io/docs/references/1.6.x/client-rest/teams#updatePrefs) | ✅ | ✅ | ### Databases -![Databases - 3 / 47](https://img.shields.io/badge/Databases-3%20%2F%2047-red?style=for-the-badge) +![Databases - 4 / 47](https://img.shields.io/badge/Databases-4%20%2F%2047-red?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| | [List Databases](https://appwrite.io/docs/references/1.6.x/server-rest/databases#list) | ❌ | ✅ | | [Create Databases](https://appwrite.io/docs/references/1.6.x/server-rest/databases#create) | ❌ | ✅ | -| [Get Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#get) | ❌ | ⬛ | +| [Get Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#get) | ❌ | ✅ | | [Update Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#update) | ❌ | ⬛ | | [Delete Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#delete) | ❌ | ✅ | | [List Collections](https://appwrite.io/docs/references/1.6.x/server-rest/databases#listCollections) | ❌ | ⬛ | From 30bae7acf99ead9939c2a0fb9804a2c47d660ac7 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:36:36 +0000 Subject: [PATCH 218/303] implemented update database --- src/PinguApps.Appwrite.Playground/App.cs | 7 ++++--- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 11 ++++++++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index d805a54e..55e9ce99 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,12 +17,13 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new GetDatabaseRequest() + var request = new UpdateDatabaseRequest() { - DatabaseId = "6748b44d000b2b0e73ac" + DatabaseId = "6748b44d000b2b0e73ac", + Name = "New Name" }; - var response = await _server.Databases.GetDatabase(request); + var response = await _server.Databases.UpdateDatabase(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index cb0c435c..f81b8ae0 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -89,9 +89,22 @@ public async Task> GetDatabase(GetDatabaseRequest reque } } - [ExcludeFromCodeCoverage] /// - public Task> UpdateDatabase(UpdateDatabaseRequest request) => throw new NotImplementedException(); + public async Task> UpdateDatabase(UpdateDatabaseRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateDatabase(request.DatabaseId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index de076a54..7e1f41eb 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -49,6 +49,14 @@ public interface IDatabasesClient /// The database Task> GetDatabase(GetDatabaseRequest request); + /// + /// Update a database by its unique ID. + /// Appwrite Docs + /// + /// The request content + /// The database + Task> UpdateDatabase(UpdateDatabaseRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); @@ -130,9 +138,6 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task> UpdateCollection(UpdateCollectionRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> UpdateDatabase(UpdateDatabaseRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> UpdateDatetimeAttribute(UpdateDatetimeAttributeRequest request); From 174ca082536cef323abbf71339bd77ae827796b5 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:39:43 +0000 Subject: [PATCH 219/303] added tests for update database --- .../DatabasesClientTests.UpdateDatabase.cs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatabase.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatabase.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatabase.cs new file mode 100644 index 00000000..c1ea6931 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatabase.cs @@ -0,0 +1,75 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task UpdateDatabase_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateDatabaseRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "New Name" + }; + + _mockHttp.Expect(HttpMethod.Put, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.DatabaseResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateDatabase(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateDatabase_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateDatabaseRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "New Name" + }; + + _mockHttp.Expect(HttpMethod.Put, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateDatabase(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateDatabase_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateDatabaseRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "New Name" + }; + + _mockHttp.Expect(HttpMethod.Put, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateDatabase(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 772e30e444fbe5fdd051930f0812361ffd2e62d3 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:44:24 +0000 Subject: [PATCH 220/303] implemented list collections --- src/PinguApps.Appwrite.Playground/App.cs | 7 +++---- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 11 ++++++++--- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 55e9ce99..2c0e9033 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,13 +17,12 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new UpdateDatabaseRequest() + var request = new ListCollectionsRequest() { - DatabaseId = "6748b44d000b2b0e73ac", - Name = "New Name" + DatabaseId = "6748b44d000b2b0e73ac" }; - var response = await _server.Databases.UpdateDatabase(request); + var response = await _server.Databases.ListCollections(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index f81b8ae0..66fe6ac9 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -106,9 +106,22 @@ public async Task> UpdateDatabase(UpdateDatabaseRequest } } - [ExcludeFromCodeCoverage] /// - public Task> ListCollections(ListCollectionsRequest request) => throw new NotImplementedException(); + public async Task> ListCollections(ListCollectionsRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.ListCollections(request.DatabaseId, RequestUtils.GetQueryStrings(request.Queries), request.Search); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 7e1f41eb..8c8adc59 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -57,6 +57,14 @@ public interface IDatabasesClient /// The database Task> UpdateDatabase(UpdateDatabaseRequest request); + /// + /// Get a list of all collections that belong to the provided databaseId. You can use the search parameter to filter your results. + /// Appwrite Docs + /// + /// The request content + /// The collections list + Task> ListCollections(ListCollectionsRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); @@ -123,9 +131,6 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task> ListAttributes(ListAttributesRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> ListCollections(ListCollectionsRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> ListDocuments(ListDocumentsRequest request); From f3bc48d59f8d2d352a60b7a90ca250b4eb2c3500 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:45:32 +0000 Subject: [PATCH 221/303] added tests for list collections --- .../DatabasesClientTests.ListCollections.cs | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListCollections.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListCollections.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListCollections.cs new file mode 100644 index 00000000..1b51cc02 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListCollections.cs @@ -0,0 +1,103 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task ListCollections_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new ListCollectionsRequest + { + DatabaseId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.CollectionsListResponse); + + // Act + var result = await _appwriteClient.Databases.ListCollections(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListCollections_ShouldProvideQueryParams_WhenQueriesAndSearchProvided() + { + // Arrange + var query = Query.Limit(5); + var search = "SearchString"; + var request = new ListCollectionsRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + Queries = new List { query }, + Search = search + }; + + var expectedQueryParams = new Dictionary + { + { "queries[]", query.GetQueryString() }, + { "search", search } + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections") + .ExpectedHeaders() + .WithQueryString($"queries[]={query.GetQueryString()}&search={search}") + .Respond(TestConstants.AppJson, TestConstants.CollectionsListResponse); + + // Act + var result = await _appwriteClient.Databases.ListCollections(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListCollections_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new ListCollectionsRequest + { + DatabaseId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.ListCollections(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task ListCollections_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new ListCollectionsRequest + { + DatabaseId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.ListCollections(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 5eb4301b3708e6d5e7c4c0a62417ef95a91c11cb Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:49:56 +0000 Subject: [PATCH 222/303] implemented create collection --- src/PinguApps.Appwrite.Playground/App.cs | 7 ++++--- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 11 ++++++++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 2c0e9033..3a7ef12c 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,12 +17,13 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new ListCollectionsRequest() + var request = new CreateCollectionRequest() { - DatabaseId = "6748b44d000b2b0e73ac" + DatabaseId = "6748b44d000b2b0e73ac", + Name = "My Collection" }; - var response = await _server.Databases.ListCollections(request); + var response = await _server.Databases.CreateCollection(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 66fe6ac9..82c0d034 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -123,9 +123,22 @@ public async Task> ListCollections(ListCollectio } } - [ExcludeFromCodeCoverage] /// - public Task> CreateCollection(CreateCollectionRequest request) => throw new NotImplementedException(); + public async Task> CreateCollection(CreateCollectionRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateCollection(request.DatabaseId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 8c8adc59..2e0a70cb 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -65,11 +65,16 @@ public interface IDatabasesClient /// The collections list Task> ListCollections(ListCollectionsRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); + /// + /// Create a new Collection. Before using this route, you should create a new database resource using either or directly from your database console. + /// Appwrite Docs + /// + /// The request content + /// The collection + Task> CreateCollection(CreateCollectionRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> CreateCollection(CreateCollectionRequest request); + Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); [Obsolete("Endpoint not yet implemented.")] Task> CreateDatetimeAttribute(CreateDatetimeAttributeRequest request); From b760f3160cbf182a8f9f83893c7a8ee7d6eaca2c Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:52:50 +0000 Subject: [PATCH 223/303] added tests for create collection --- .../DatabasesClientTests.CreateCollection.cs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateCollection.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateCollection.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateCollection.cs new file mode 100644 index 00000000..90789f44 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateCollection.cs @@ -0,0 +1,75 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task CreateCollection_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "My Collection" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.CollectionResponse); + + // Act + var result = await _appwriteClient.Databases.CreateCollection(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateCollection_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "My Collection" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateCollection(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateCollection_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + Name = "My Collection" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateCollection(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 4bdc16fae766c12e2b583a23dd7f7fcfd3449050 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:56:51 +0000 Subject: [PATCH 224/303] implemented delete collection --- src/PinguApps.Appwrite.Playground/App.cs | 6 +++--- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 11 ++++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 3a7ef12c..c7ebf8be 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,13 +17,13 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new CreateCollectionRequest() + var request = new DeleteCollectionRequest() { DatabaseId = "6748b44d000b2b0e73ac", - Name = "My Collection" + CollectionId = "6748b9ac000f0ce5a4fd" }; - var response = await _server.Databases.CreateCollection(request); + var response = await _server.Databases.DeleteCollection(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 82c0d034..6328bbc4 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -140,9 +140,22 @@ public async Task> CreateCollection(CreateCollectionR } } - [ExcludeFromCodeCoverage] /// - public Task DeleteCollection(DeleteCollectionRequest request) => throw new NotImplementedException(); + public async Task DeleteCollection(DeleteCollectionRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.DeleteCollection(request.DatabaseId, request.CollectionId); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 2e0a70cb..4cb4e423 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -73,6 +73,14 @@ public interface IDatabasesClient /// The collection Task> CreateCollection(CreateCollectionRequest request); + /// + /// Delete a collection by its unique ID. Only users with write permissions have access to delete this resource. + /// Appwrite Docs + /// + /// The request content + /// 200 Success Response + Task DeleteCollection(DeleteCollectionRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); @@ -112,9 +120,6 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task DeleteAttribute(DeleteAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task DeleteCollection(DeleteCollectionRequest request); - [Obsolete("Endpoint not yet implemented.")] Task DeleteDocument(DeleteDocumentRequest request); From 7f12baa86f547b40d6f17df4462e424f8b09ccf7 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 18:58:18 +0000 Subject: [PATCH 225/303] added tests for delete collection --- .../DatabasesClientTests.DeleteCollection.cs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteCollection.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteCollection.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteCollection.cs new file mode 100644 index 00000000..ae8da736 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteCollection.cs @@ -0,0 +1,75 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task DeleteCollection_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new DeleteCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.NoContent); + + // Act + var result = await _appwriteClient.Databases.DeleteCollection(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task DeleteCollection_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new DeleteCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.DeleteCollection(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task DeleteCollection_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new DeleteCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.DeleteCollection(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From b311bfbb716fd7670123a7eda2bfff2cf1d26506 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 19:01:54 +0000 Subject: [PATCH 226/303] implemented get collection --- src/PinguApps.Appwrite.Playground/App.cs | 6 +++--- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 13 +++++++++---- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index c7ebf8be..d327b883 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,13 +17,13 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new DeleteCollectionRequest() + var request = new GetCollectionRequest() { DatabaseId = "6748b44d000b2b0e73ac", - CollectionId = "6748b9ac000f0ce5a4fd" + CollectionId = "6748bb30002a12d4708f" }; - var response = await _server.Databases.DeleteCollection(request); + var response = await _server.Databases.GetCollection(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 6328bbc4..04d2a5e3 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -157,9 +157,22 @@ public async Task DeleteCollection(DeleteCollectionRequest reque } } - [ExcludeFromCodeCoverage] /// - public Task> GetCollection(GetCollectionRequest request) => throw new NotImplementedException(); + public async Task> GetCollection(GetCollectionRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.GetCollection(request.DatabaseId, request.CollectionId); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 4cb4e423..c7b9e2db 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -78,9 +78,17 @@ public interface IDatabasesClient /// Appwrite Docs /// /// The request content - /// 200 Success Response + /// 204 Success Response Task DeleteCollection(DeleteCollectionRequest request); + /// + /// Get a collection by its unique ID. This endpoint response returns a JSON object with the collection metadata. + /// Appwrite Docs + /// + /// The request content + /// The collection + Task> GetCollection(GetCollectionRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); @@ -129,9 +137,6 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task> GetAttribute(GetAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> GetCollection(GetCollectionRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> GetDocument(GetDocumentRequest request); From 91a3b3e772904937abd61769913b6da33fe77f33 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 19:03:33 +0000 Subject: [PATCH 227/303] added tests for get collection --- .../DatabasesClientTests.GetCollection.cs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetCollection.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetCollection.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetCollection.cs new file mode 100644 index 00000000..f50e13d2 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetCollection.cs @@ -0,0 +1,75 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task GetCollection_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new GetCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.CollectionResponse); + + // Act + var result = await _appwriteClient.Databases.GetCollection(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task GetCollection_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new GetCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.GetCollection(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task GetCollection_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new GetCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.GetCollection(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From f9559dce09b849a7f3667d4ddae8a7f2c7e4db48 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 19:06:37 +0000 Subject: [PATCH 228/303] implemented update collection --- src/PinguApps.Appwrite.Playground/App.cs | 7 ++++--- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 11 ++++++++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index d327b883..aad7cbef 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,13 +17,14 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new GetCollectionRequest() + var request = new UpdateCollectionRequest() { DatabaseId = "6748b44d000b2b0e73ac", - CollectionId = "6748bb30002a12d4708f" + CollectionId = "6748bb30002a12d4708f", + Name = "New Name" }; - var response = await _server.Databases.GetCollection(request); + var response = await _server.Databases.UpdateCollection(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 04d2a5e3..3e5ff123 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -174,9 +174,22 @@ public async Task> GetCollection(GetCollectionRequest } } - [ExcludeFromCodeCoverage] /// - public Task> UpdateCollection(UpdateCollectionRequest request) => throw new NotImplementedException(); + public async Task> UpdateCollection(UpdateCollectionRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateCollection(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index c7b9e2db..b2073152 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -89,6 +89,14 @@ public interface IDatabasesClient /// The collection Task> GetCollection(GetCollectionRequest request); + /// + /// Update a collection by its unique ID. + /// Appwrite Docs + /// + /// The request content + /// The collection + Task> UpdateCollection(UpdateCollectionRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); @@ -155,9 +163,6 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task> UpdateBooleanAttribute(UpdateBooleanAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> UpdateCollection(UpdateCollectionRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> UpdateDatetimeAttribute(UpdateDatetimeAttributeRequest request); From dc93fb4395c91f67b20a0b9ff666f56d60cb422b Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 19:08:59 +0000 Subject: [PATCH 229/303] added tests for update collection --- .../DatabasesClientTests.UpdateCollection.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateCollection.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateCollection.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateCollection.cs new file mode 100644 index 00000000..93643e21 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateCollection.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task UpdateCollection_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = "New Name" + }; + + _mockHttp.Expect(HttpMethod.Put, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.CollectionResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateCollection(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateCollection_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = "New Name" + }; + + _mockHttp.Expect(HttpMethod.Put, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateCollection(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateCollection_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateCollectionRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Name = "New Name" + }; + + _mockHttp.Expect(HttpMethod.Put, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateCollection(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From d1589b5922a671772c6aaa5b55a04c4271362f9a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 19:12:42 +0000 Subject: [PATCH 230/303] implemented list attributes --- src/PinguApps.Appwrite.Playground/App.cs | 7 +++---- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 11 ++++++++--- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index aad7cbef..048064e1 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,14 +17,13 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new UpdateCollectionRequest() + var request = new ListAttributesRequest() { DatabaseId = "6748b44d000b2b0e73ac", - CollectionId = "6748bb30002a12d4708f", - Name = "New Name" + CollectionId = "6748bb30002a12d4708f" }; - var response = await _server.Databases.UpdateCollection(request); + var response = await _server.Databases.ListAttributes(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 3e5ff123..296c20a7 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -191,9 +191,22 @@ public async Task> UpdateCollection(UpdateCollectionR } } - [ExcludeFromCodeCoverage] /// - public Task> ListAttributes(ListAttributesRequest request) => throw new NotImplementedException(); + public async Task> ListAttributes(ListAttributesRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.ListAttributes(request.DatabaseId, request.CollectionId, RequestUtils.GetQueryStrings(request.Queries)); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index b2073152..cb3aee4b 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -97,6 +97,14 @@ public interface IDatabasesClient /// The collection Task> UpdateCollection(UpdateCollectionRequest request); + /// + /// List attributes in the collection. + /// Appwrite Docs + /// + /// The request content + /// The attributes list + Task> ListAttributes(ListAttributesRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); @@ -151,9 +159,6 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task> GetIndex(GetIndexRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> ListAttributes(ListAttributesRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> ListDocuments(ListDocumentsRequest request); From eb89f1f32e7e3525c4c44d8ded90baa05ce48df8 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 19:14:59 +0000 Subject: [PATCH 231/303] added tests for list attributes --- .../DatabasesClientTests.ListAttributes.cs | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListAttributes.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListAttributes.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListAttributes.cs new file mode 100644 index 00000000..23ad8386 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListAttributes.cs @@ -0,0 +1,104 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task ListAttributes_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new ListAttributesRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributesListResponse); + + // Act + var result = await _appwriteClient.Databases.ListAttributes(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListAttributes_ShouldProvideQueryParams_WhenQueriesProvided() + { + // Arrange + var query = Query.Limit(5); + var request = new ListAttributesRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Queries = new List { query } + }; + + var expectedQueryParams = new Dictionary + { + { "queries[]", query.GetQueryString() } + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes") + .ExpectedHeaders() + .WithQueryString($"queries[]={query.GetQueryString()}") + .Respond(TestConstants.AppJson, TestConstants.AttributesListResponse); + + // Act + var result = await _appwriteClient.Databases.ListAttributes(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListAttributes_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new ListAttributesRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.ListAttributes(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task ListAttributes_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new ListAttributesRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.ListAttributes(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 72ba4e60ca563a79ef71c19b070c6f03f5a93954 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 19:18:10 +0000 Subject: [PATCH 232/303] implemented create boolean attribute --- src/PinguApps.Appwrite.Playground/App.cs | 7 ++++--- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 7 ++++++- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 048064e1..0b7bacbf 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,13 +17,14 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new ListAttributesRequest() + var request = new CreateBooleanAttributeRequest() { DatabaseId = "6748b44d000b2b0e73ac", - CollectionId = "6748bb30002a12d4708f" + CollectionId = "6748bb30002a12d4708f", + Key = "isHuman" }; - var response = await _server.Databases.ListAttributes(request); + var response = await _server.Databases.CreateBooleanAttribute(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 296c20a7..028643bd 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -208,9 +208,22 @@ public async Task> ListAttributes(ListAttributesR } } - [ExcludeFromCodeCoverage] /// - public Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request) => throw new NotImplementedException(); + public async Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateBooleanAttribute(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index cb3aee4b..f2d723a8 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -105,7 +105,12 @@ public interface IDatabasesClient /// The attributes list Task> ListAttributes(ListAttributesRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Create a boolean attribute. + /// Appwrite Docs + /// + /// The request content + /// The boolean attribute Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); [Obsolete("Endpoint not yet implemented.")] From 05ec6e35bbd378b277a8cf0f6782f4b776f91904 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 19:21:01 +0000 Subject: [PATCH 233/303] Added tests for create boolean attribute --- ...basesClientTests.CreateBooleanAttribute.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateBooleanAttribute.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateBooleanAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateBooleanAttribute.cs new file mode 100644 index 00000000..db32ba61 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateBooleanAttribute.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task CreateBooleanAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateBooleanAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "myKey" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/boolean") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeBooleanResponse); + + // Act + var result = await _appwriteClient.Databases.CreateBooleanAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateBooleanAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateBooleanAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "myKey" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/boolean") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateBooleanAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateBooleanAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateBooleanAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "myKey" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/boolean") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateBooleanAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 3d7e0d0d848193ed8828b864708f43216b99c19a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Thu, 28 Nov 2024 19:23:47 +0000 Subject: [PATCH 234/303] Update README.md --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 4f376af8..032b48e2 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress -![Server & Client - 129 / 317](https://img.shields.io/badge/Server_&_Client-129%20%2F%20317-gold?style=for-the-badge) +![Server & Client - 137 / 317](https://img.shields.io/badge/Server_&_Client-137%20%2F%20317-gold?style=for-the-badge) -![Server - 69 / 224](https://img.shields.io/badge/Server-69%20%2F%20224-red?style=for-the-badge) +![Server - 77 / 224](https://img.shields.io/badge/Server-77%20%2F%20224-gold?style=for-the-badge) ![Client - 60 / 93](https://img.shields.io/badge/Client-60%20%2F%2093-gold?style=for-the-badge) @@ -275,22 +275,22 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Update Preferences](https://appwrite.io/docs/references/1.6.x/client-rest/teams#updatePrefs) | ✅ | ✅ | ### Databases -![Databases - 4 / 47](https://img.shields.io/badge/Databases-4%20%2F%2047-red?style=for-the-badge) +![Databases - 12 / 47](https://img.shields.io/badge/Databases-12%20%2F%2047-red?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| | [List Databases](https://appwrite.io/docs/references/1.6.x/server-rest/databases#list) | ❌ | ✅ | | [Create Databases](https://appwrite.io/docs/references/1.6.x/server-rest/databases#create) | ❌ | ✅ | | [Get Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#get) | ❌ | ✅ | -| [Update Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#update) | ❌ | ⬛ | +| [Update Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#update) | ❌ | ✅ | | [Delete Database](https://appwrite.io/docs/references/1.6.x/server-rest/databases#delete) | ❌ | ✅ | -| [List Collections](https://appwrite.io/docs/references/1.6.x/server-rest/databases#listCollections) | ❌ | ⬛ | -| [Create Collection](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createCollection) | ❌ | ⬛ | -| [Get Collections](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getCollection) | ❌ | ⬛ | -| [Update Collection](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateCollection) | ❌ | ⬛ | -| [Delete Collection](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteCollection) | ❌ | ⬛ | -| [List Attributes](https://appwrite.io/docs/references/1.6.x/server-rest/databases#listAttributes) | ❌ | ⬛ | -| [Create Boolean Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createBooleanAttribute) | ❌ | ⬛ | +| [List Collections](https://appwrite.io/docs/references/1.6.x/server-rest/databases#listCollections) | ❌ | ✅ | +| [Create Collection](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createCollection) | ❌ | ✅ | +| [Get Collections](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getCollection) | ❌ | ✅ | +| [Update Collection](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateCollection) | ❌ | ✅ | +| [Delete Collection](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteCollection) | ❌ | ✅ | +| [List Attributes](https://appwrite.io/docs/references/1.6.x/server-rest/databases#listAttributes) | ❌ | ✅ | +| [Create Boolean Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createBooleanAttribute) | ❌ | ✅ | | [Update Boolean Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateBooleanAttribute) | ❌ | ⬛ | | [Create Datatime Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createDatetimeAttribute) | ❌ | ⬛ | | [Update Datetime Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateDatetimeAttribute) | ❌ | ⬛ | From 0e952382f81414c2bddb1b6899cd28843bd589d5 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Mon, 2 Dec 2024 02:22:52 +0000 Subject: [PATCH 235/303] Implemented Update attribute --- src/PinguApps.Appwrite.Playground/App.cs | 7 ++++--- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 11 ++++++++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 0b7bacbf..74e9593f 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,14 +17,15 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new CreateBooleanAttributeRequest() + var request = new UpdateBooleanAttributeRequest() { DatabaseId = "6748b44d000b2b0e73ac", CollectionId = "6748bb30002a12d4708f", - Key = "isHuman" + Key = "isHuman", + NewKey = "isRobot" }; - var response = await _server.Databases.CreateBooleanAttribute(request); + var response = await _server.Databases.UpdateBooleanAttribute(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 028643bd..91c4c191 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -225,9 +225,22 @@ public async Task> CreateBooleanAttribute(Creat } } - [ExcludeFromCodeCoverage] /// - public Task> UpdateBooleanAttribute(UpdateBooleanAttributeRequest request) => throw new NotImplementedException(); + public async Task> UpdateBooleanAttribute(UpdateBooleanAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateBooleanAttribute(request.DatabaseId, request.CollectionId, request.Key, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index f2d723a8..124e2175 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -113,6 +113,14 @@ public interface IDatabasesClient /// The boolean attribute Task> CreateBooleanAttribute(CreateBooleanAttributeRequest request); + /// + /// Update a boolean attribute. Changing the default value will not update already existing documents. + /// Appwrite Docs + /// + /// The request content + /// The boolean attribute + Task> UpdateBooleanAttribute(UpdateBooleanAttributeRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateDatetimeAttribute(CreateDatetimeAttributeRequest request); @@ -170,9 +178,6 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task> ListIndexes(ListIndexesRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> UpdateBooleanAttribute(UpdateBooleanAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> UpdateDatetimeAttribute(UpdateDatetimeAttributeRequest request); From 6ef81f8b66973a68e218298d8a08356fd547c563 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Mon, 2 Dec 2024 02:32:52 +0000 Subject: [PATCH 236/303] Added tests for Update attribute --- ...basesClientTests.UpdateBooleanAttribute.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateBooleanAttribute.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateBooleanAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateBooleanAttribute.cs new file mode 100644 index 00000000..ce3d356d --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateBooleanAttribute.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task UpdateBooleanAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateBooleanAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "myKey" + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/boolean/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeBooleanResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateBooleanAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateBooleanAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateBooleanAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "myKey" + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/boolean/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateBooleanAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateBooleanAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateBooleanAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "myKey" + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/boolean/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateBooleanAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 3b3d85b3ccb6a2cf3dfa3f096d6a738e770b1acc Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 3 Dec 2024 17:51:56 +0000 Subject: [PATCH 237/303] #358 Implemented create date time attribute --- src/PinguApps.Appwrite.Playground/App.cs | 7 +++---- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 7 ++++++- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 74e9593f..1feccfac 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,15 +17,14 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new UpdateBooleanAttributeRequest() + var request = new CreateDatetimeAttributeRequest() { DatabaseId = "6748b44d000b2b0e73ac", CollectionId = "6748bb30002a12d4708f", - Key = "isHuman", - NewKey = "isRobot" + Key = "created" }; - var response = await _server.Databases.UpdateBooleanAttribute(request); + var response = await _server.Databases.CreateDatetimeAttribute(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 91c4c191..01e3101e 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -242,9 +242,22 @@ public async Task> UpdateBooleanAttribute(Updat } } - [ExcludeFromCodeCoverage] /// - public Task> CreateDatetimeAttribute(CreateDatetimeAttributeRequest request) => throw new NotImplementedException(); + public async Task> CreateDatetimeAttribute(CreateDatetimeAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateDatetimeAttribute(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 124e2175..03fa4f55 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -121,7 +121,12 @@ public interface IDatabasesClient /// The boolean attribute Task> UpdateBooleanAttribute(UpdateBooleanAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Create a date time attribute according to the ISO 8601 standard. + /// Appwrite Docs + /// + /// The request content + /// The datetime attribute attribute Task> CreateDatetimeAttribute(CreateDatetimeAttributeRequest request); [Obsolete("Endpoint not yet implemented.")] From 26195c6583203b61de84e677f46c96d88177ac82 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 3 Dec 2024 17:56:25 +0000 Subject: [PATCH 238/303] added tests for create datetime attribute --- ...asesClientTests.CreateDatetimeAttribute.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatetimeAttribute.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatetimeAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatetimeAttribute.cs new file mode 100644 index 00000000..c015c652 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatetimeAttribute.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task CreateDatetimeAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateDatetimeAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/datetime") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeDatetimeResponse); + + // Act + var result = await _appwriteClient.Databases.CreateDatetimeAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateDatetimeAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateDatetimeAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/datetime") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateDatetimeAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateDatetimeAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateDatetimeAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/datetime") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateDatetimeAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From e83afbe160dc4bf440cf1e5fc1d6790924360ecd Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 3 Dec 2024 18:48:04 +0000 Subject: [PATCH 239/303] Updated float attribute to use double over float --- .../Databases/CreateFloatAttributeRequest.cs | 6 +++--- .../Databases/UpdateFloatAttributeRequest.cs | 6 +++--- .../UpdateFloatAttributeRequestValidator.cs | 6 +++--- .../Databases/UpdateFloatAttributeRequestTests.cs | 14 +++----------- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateFloatAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateFloatAttributeRequest.cs index b7bc6374..83061828 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateFloatAttributeRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateFloatAttributeRequest.cs @@ -12,17 +12,17 @@ public class CreateFloatAttributeRequest : CreateAttributeBaseRequest [JsonPropertyName("default")] - public float? Default { get; set; } + public double? Default { get; set; } /// /// Minimum value to enforce on new documents /// [JsonPropertyName("min")] - public float? Min { get; set; } + public double? Min { get; set; } /// /// Maximum value to enforce on new documents /// [JsonPropertyName("max")] - public float? Max { get; set; } + public double? Max { get; set; } } diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateFloatAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateFloatAttributeRequest.cs index 6483a089..bcc7f799 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateFloatAttributeRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateFloatAttributeRequest.cs @@ -13,19 +13,19 @@ public class UpdateFloatAttributeRequest : UpdateAttributeBaseRequest [JsonPropertyName("default")] [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public float? Default { get; set; } + public double? Default { get; set; } /// /// Minimum value to enforce on new documents /// [JsonPropertyName("min")] [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public float? Min { get; set; } + public double Min { get; set; } = double.MinValue; /// /// Maximum value to enforce on new documents /// [JsonPropertyName("max")] [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public float? Max { get; set; } + public double Max { get; set; } = double.MaxValue; } diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateFloatAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateFloatAttributeRequestValidator.cs index 52bfd65d..647885a5 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateFloatAttributeRequestValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateFloatAttributeRequestValidator.cs @@ -10,13 +10,13 @@ public UpdateFloatAttributeRequestValidator() RuleFor(x => x.Default) .Must((request, defaultValue) => !request.Required || defaultValue is null) .WithMessage("Default value cannot be set when attribute is required.") - .Must((request, defaultValue) => (request.Min is null || defaultValue is null) || defaultValue >= request.Min) + .Must((request, defaultValue) => defaultValue is null || defaultValue >= request.Min) .WithMessage("Default cannot be a smaller value than Min.") - .Must((request, defaultValue) => (request.Max is null || defaultValue is null) || defaultValue <= request.Max) + .Must((request, defaultValue) => defaultValue is null || defaultValue <= request.Max) .WithMessage("Default cannot be a larger value than Max."); RuleFor(x => x.Max) - .Must((request, max) => (request.Min is null || max is null) || max >= request.Min) + .Must((request, max) => max >= request.Min) .WithMessage("Max can not be a lower value than Min."); } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateFloatAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateFloatAttributeRequestTests.cs index 8b649a25..0e401aa6 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateFloatAttributeRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateFloatAttributeRequestTests.cs @@ -16,8 +16,8 @@ public void Constructor_InitializesWithExpectedValues() // Assert Assert.Null(request.Default); - Assert.Null(request.Min); - Assert.Null(request.Max); + Assert.Equal(double.MinValue, request.Min); + Assert.Equal(double.MaxValue, request.Max); } [Fact] @@ -49,8 +49,6 @@ public void Properties_CanBeSet() CollectionId = IdUtils.GenerateUniqueId(), Key = IdUtils.GenerateUniqueId(), Default = null, - Min = null, - Max = null, Required = true }, new() @@ -69,8 +67,6 @@ public void Properties_CanBeSet() CollectionId = IdUtils.GenerateUniqueId(), Key = IdUtils.GenerateUniqueId(), Default = -10, - Min = null, - Max = null, Required = false } ]; @@ -94,8 +90,6 @@ public void IsValid_WithValidData_ReturnsTrue(UpdateFloatAttributeRequest reques CollectionId = IdUtils.GenerateUniqueId(), Key = IdUtils.GenerateUniqueId(), Default = 0f, - Min = null, - Max = null, Required = true }, new() @@ -104,8 +98,7 @@ public void IsValid_WithValidData_ReturnsTrue(UpdateFloatAttributeRequest reques CollectionId = IdUtils.GenerateUniqueId(), Key = IdUtils.GenerateUniqueId(), Default = 0f, - Min = 1f, - Max = null, + Min = 1f }, new() { @@ -113,7 +106,6 @@ public void IsValid_WithValidData_ReturnsTrue(UpdateFloatAttributeRequest reques CollectionId = IdUtils.GenerateUniqueId(), Key = IdUtils.GenerateUniqueId(), Default = 1f, - Min = null, Max = 0f, }, new() From 426fa9dc2355f786a10dea09a9edf331904cb967 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 3 Dec 2024 19:02:02 +0000 Subject: [PATCH 240/303] Fixed tests and converted int to long for int attribute --- .../Databases/CreateIntegerAttributeRequest.cs | 6 +++--- .../Databases/UpdateIntegerAttributeRequest.cs | 6 +++--- .../UpdateIntegerAttributeRequestValidator.cs | 6 +++--- .../Responses/AttributeFloat.cs | 6 +++--- .../Responses/AttributeInteger.cs | 6 +++--- .../Databases/UpdateIntegerAttributeRequestTests.cs | 12 ++---------- 6 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIntegerAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIntegerAttributeRequest.cs index ea8c1d91..e4fe056e 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIntegerAttributeRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateIntegerAttributeRequest.cs @@ -12,17 +12,17 @@ public class CreateIntegerAttributeRequest : CreateAttributeBaseRequest [JsonPropertyName("default")] - public int? Default { get; set; } + public long? Default { get; set; } /// /// Minimum value to enforce on new documents /// [JsonPropertyName("min")] - public int? Min { get; set; } + public long? Min { get; set; } /// /// Maximum value to enforce on new documents /// [JsonPropertyName("max")] - public int? Max { get; set; } + public long? Max { get; set; } } diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIntegerAttributeRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIntegerAttributeRequest.cs index a722f19b..0e86313b 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIntegerAttributeRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/UpdateIntegerAttributeRequest.cs @@ -13,19 +13,19 @@ public class UpdateIntegerAttributeRequest : UpdateAttributeBaseRequest [JsonPropertyName("default")] [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public int? Default { get; set; } + public long? Default { get; set; } /// /// Minimum value to enforce on new documents /// [JsonPropertyName("min")] [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public int? Min { get; set; } + public long Min { get; set; } = long.MinValue; /// /// Maximum value to enforce on new documents /// [JsonPropertyName("max")] [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public int? Max { get; set; } + public long Max { get; set; } = long.MaxValue; } diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIntegerAttributeRequestValidator.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIntegerAttributeRequestValidator.cs index 37c0ac7a..684c14d3 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIntegerAttributeRequestValidator.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/Validators/UpdateIntegerAttributeRequestValidator.cs @@ -10,13 +10,13 @@ public UpdateIntegerAttributeRequestValidator() RuleFor(x => x.Default) .Must((request, defaultValue) => !request.Required || defaultValue is null) .WithMessage("Default value cannot be set when attribute is required.") - .Must((request, defaultValue) => (request.Min is null || defaultValue is null) || defaultValue >= request.Min) + .Must((request, defaultValue) => defaultValue is null || defaultValue >= request.Min) .WithMessage("Default cannot be a smaller value than Min.") - .Must((request, defaultValue) => (request.Max is null || defaultValue is null) || defaultValue <= request.Max) + .Must((request, defaultValue) => defaultValue is null || defaultValue <= request.Max) .WithMessage("Default cannot be a larger value than Max."); RuleFor(x => x.Max) - .Must((request, max) => (request.Min is null || max is null) || max >= request.Min) + .Must((request, max) => max >= request.Min) .WithMessage("Max can not be a lower value than Min."); } } diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs index 0dbab8e2..2fb3cfa5 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeFloat.cs @@ -29,9 +29,9 @@ public record AttributeFloat( DateTime CreatedAt, DateTime UpdatedAt, - [property: JsonPropertyName("min")] float? Min, - [property: JsonPropertyName("max")] float? Max, - [property: JsonPropertyName("default")] float? Default + [property: JsonPropertyName("min")] double? Min, + [property: JsonPropertyName("max")] double? Max, + [property: JsonPropertyName("default")] double? Default ) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) { /// diff --git a/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs b/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs index db324627..e00a90db 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/AttributeInteger.cs @@ -29,9 +29,9 @@ public record AttributeInteger( DateTime CreatedAt, DateTime UpdatedAt, - [property: JsonPropertyName("min")] int? Min, - [property: JsonPropertyName("max")] int? Max, - [property: JsonPropertyName("default")] int? Default + [property: JsonPropertyName("min")] long? Min, + [property: JsonPropertyName("max")] long? Max, + [property: JsonPropertyName("default")] long? Default ) : Attribute(Key, Type, Status, Error, Required, Array, CreatedAt, UpdatedAt) { /// diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIntegerAttributeRequestTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIntegerAttributeRequestTests.cs index 0d7ef447..5c7a9dc7 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIntegerAttributeRequestTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/UpdateIntegerAttributeRequestTests.cs @@ -16,8 +16,8 @@ public void Constructor_InitializesWithExpectedValues() // Assert Assert.Null(request.Default); - Assert.Null(request.Min); - Assert.Null(request.Max); + Assert.Equal(long.MinValue, request.Min); + Assert.Equal(long.MaxValue, request.Max); } [Fact] @@ -49,8 +49,6 @@ public void Properties_CanBeSet() CollectionId = IdUtils.GenerateUniqueId(), Key = IdUtils.GenerateUniqueId(), Default = null, - Min = null, - Max = null, Required = true }, new() @@ -69,8 +67,6 @@ public void Properties_CanBeSet() CollectionId = IdUtils.GenerateUniqueId(), Key = IdUtils.GenerateUniqueId(), Default = -10, - Min = null, - Max = null, Required = false } ]; @@ -94,8 +90,6 @@ public void IsValid_WithValidData_ReturnsTrue(UpdateIntegerAttributeRequest requ CollectionId = IdUtils.GenerateUniqueId(), Key = IdUtils.GenerateUniqueId(), Default = 0, - Min = null, - Max = null, Required = true }, new() @@ -105,7 +99,6 @@ public void IsValid_WithValidData_ReturnsTrue(UpdateIntegerAttributeRequest requ Key = IdUtils.GenerateUniqueId(), Default = 0, Min = 1, - Max = null, }, new() { @@ -113,7 +106,6 @@ public void IsValid_WithValidData_ReturnsTrue(UpdateIntegerAttributeRequest requ CollectionId = IdUtils.GenerateUniqueId(), Key = IdUtils.GenerateUniqueId(), Default = 1, - Min = null, Max = 0, }, new() From b0ea7323e2a461e8e095a17ce02776fd34a94f67 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 3 Dec 2024 19:13:49 +0000 Subject: [PATCH 241/303] #359 #360 #361 #362 #363 #364 #365 #366 #367 #368 #369 #370 #371 #372 #373 #374 #377 Implemented all attribute endpoints --- src/PinguApps.Appwrite.Playground/App.cs | 30 +- .../Clients/DatabasesClient.cs | 289 +++++++++++++++--- .../Clients/IDatabasesClient.cs | 169 +++++++--- 3 files changed, 408 insertions(+), 80 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 1feccfac..90251f27 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,16 +17,38 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new CreateDatetimeAttributeRequest() + var key = "url"; + var newKey = $"new_{key}"; + + var createRequest = new CreateRelationshipAttributeRequest() + { + DatabaseId = "6748b44d000b2b0e73ac", + CollectionId = "6748bb30002a12d4708f", + Key = key, + RelatedCollectionId = "674f57a30011310c0b3c" + }; + + var createResponse = await _server.Databases.CreateRelationshipAttribute(createRequest); + + Console.WriteLine(createResponse.Result.Match( + result => result.ToString(), + appwriteError => appwriteError.Message, + internalError => internalError.Message)); + + Console.WriteLine("#############################################################################################"); + Console.ReadKey(); + + var updateRequest = new UpdateRelationshipAttributeRequest() { DatabaseId = "6748b44d000b2b0e73ac", CollectionId = "6748bb30002a12d4708f", - Key = "created" + Key = key, + NewKey = newKey }; - var response = await _server.Databases.CreateDatetimeAttribute(request); + var updateResponse = await _server.Databases.UpdateRelationshipAttribute(updateRequest); - Console.WriteLine(response.Result.Match( + Console.WriteLine(updateResponse.Result.Match( result => result.ToString(), appwriteError => appwriteError.Message, internalError => internalError.Message)); diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 01e3101e..88ee2213 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -259,69 +259,277 @@ public async Task> CreateDatetimeAttribute(Cre } } - [ExcludeFromCodeCoverage] /// - public Task> UpdateDatetimeAttribute(UpdateDatetimeAttributeRequest request) => throw new NotImplementedException(); + public async Task> UpdateDatetimeAttribute(UpdateDatetimeAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateDatetimeAttribute(request.DatabaseId, request.CollectionId, request.Key, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> CreateEmailAttribute(CreateEmailAttributeRequest request) => throw new NotImplementedException(); + public async Task> CreateEmailAttribute(CreateEmailAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateEmailAttribute(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> UpdateEmailAttribute(UpdateEmailAttributeRequest request) => throw new NotImplementedException(); + public async Task> UpdateEmailAttribute(UpdateEmailAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateEmailAttribute(request.DatabaseId, request.CollectionId, request.Key, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> CreateEnumAttribute(CreateEnumAttributeRequest request) => throw new NotImplementedException(); + public async Task> CreateEnumAttribute(CreateEnumAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateEnumAttribute(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> UpdateEnumAttribute(UpdateEnumAttributeRequest request) => throw new NotImplementedException(); + public async Task> UpdateEnumAttribute(UpdateEnumAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateEnumAttribute(request.DatabaseId, request.CollectionId, request.Key, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> CreateFloatAttribute(CreateFloatAttributeRequest request) => throw new NotImplementedException(); + public async Task> CreateFloatAttribute(CreateFloatAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateFloatAttribute(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> UpdateFloatAttribute(UpdateFloatAttributeRequest request) => throw new NotImplementedException(); + public async Task> UpdateFloatAttribute(UpdateFloatAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateFloatAttribute(request.DatabaseId, request.CollectionId, request.Key, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> CreateIntegerAttribute(CreateIntegerAttributeRequest request) => throw new NotImplementedException(); + public async Task> CreateIntegerAttribute(CreateIntegerAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateIntegerAttribute(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> UpdateIntegerAttribute(UpdateIntegerAttributeRequest request) => throw new NotImplementedException(); + public async Task> UpdateIntegerAttribute(UpdateIntegerAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateIntegerAttribute(request.DatabaseId, request.CollectionId, request.Key, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> CreateIpAttribute(CreateIPAttributeRequest request) => throw new NotImplementedException(); + public async Task> CreateIpAttribute(CreateIPAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateIpAttribute(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> UpdateIpAttribute(UpdateIPAttributeRequest request) => throw new NotImplementedException(); + public async Task> UpdateIpAttribute(UpdateIPAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateIpAttribute(request.DatabaseId, request.CollectionId, request.Key, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> CreateRelationshipAttribute(CreateRelationshipAttributeRequest request) => throw new NotImplementedException(); + public async Task> CreateRelationshipAttribute(CreateRelationshipAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateRelationshipAttribute(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> CreateStringAttribute(CreateStringAttributeRequest request) => throw new NotImplementedException(); + public async Task> CreateStringAttribute(CreateStringAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateStringAttribute(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> UpdateStringAttribute(UpdateStringAttributeRequest request) => throw new NotImplementedException(); + public async Task> UpdateStringAttribute(UpdateStringAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateStringAttribute(request.DatabaseId, request.CollectionId, request.Key, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> CreateUrlAttribute(CreateURLAttributeRequest request) => throw new NotImplementedException(); + public async Task> CreateUrlAttribute(CreateURLAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateUrlAttribute(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } - [ExcludeFromCodeCoverage] /// - public Task> UpdateUrlAttribute(UpdateURLAttributeRequest request) => throw new NotImplementedException(); + public async Task> UpdateUrlAttribute(UpdateURLAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateUrlAttribute(request.DatabaseId, request.CollectionId, request.Key, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// @@ -331,9 +539,22 @@ public async Task> CreateDatetimeAttribute(Cre /// public Task> GetAttribute(GetAttributeRequest request) => throw new NotImplementedException(); - [ExcludeFromCodeCoverage] /// - public Task> UpdateRelationshipAttribute(UpdateRelationshipAttributeRequest request) => throw new NotImplementedException(); + public async Task> UpdateRelationshipAttribute(UpdateRelationshipAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateRelationshipAttribute(request.DatabaseId, request.CollectionId, request.Key, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 03fa4f55..990699de 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -126,90 +126,175 @@ public interface IDatabasesClient /// Appwrite Docs /// /// The request content - /// The datetime attribute attribute + /// The datetime attribute Task> CreateDatetimeAttribute(CreateDatetimeAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> CreateDocument(CreateDocumentRequest request); + /// + /// Update a date time attribute. Changing the default value will not update already existing documents. + /// Appwrite Docs + /// + /// The request content + /// The datetime attribute + Task> UpdateDatetimeAttribute(UpdateDatetimeAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Create an email attribute. + /// Appwrite Docs + /// + /// The request content + /// The email attribute Task> CreateEmailAttribute(CreateEmailAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Update an email attribute. Changing the default value will not update already existing documents. + /// Appwrite Docs + /// + /// The request content + /// The email attribute + Task> UpdateEmailAttribute(UpdateEmailAttributeRequest request); + + /// + /// Create an enumeration attribute. The elements param acts as a white-list of accepted values for this attribute. + /// Appwrite Docs + /// + /// The request content + /// The enum attribute Task> CreateEnumAttribute(CreateEnumAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Update an enumeration attribute. Changing the default value will not update already existing documents. + /// Appwrite Docs + /// + /// The request content + /// The enum attribute + Task> UpdateEnumAttribute(UpdateEnumAttributeRequest request); + + /// + /// Create a float attribute. Optionally, minimum and maximum values can be provided. + /// Appwrite Docs + /// + /// The request content + /// The float attribute Task> CreateFloatAttribute(CreateFloatAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> CreateIndex(CreateIndexRequest request); + /// + /// Update a float attribute. Changing the default value will not update already existing documents. + /// Appwrite Docs + /// + /// The request content + /// The float attribute + Task> UpdateFloatAttribute(UpdateFloatAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Create an integer attribute. Optionally, minimum and maximum values can be provided. + /// Appwrite Docs + /// + /// The request content + /// The integer attribute Task> CreateIntegerAttribute(CreateIntegerAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Update an integer attribute. Changing the default value will not update already existing documents. + /// Appwrite Docs + /// + /// The request content + /// The integer attribute + Task> UpdateIntegerAttribute(UpdateIntegerAttributeRequest request); + + /// + /// Create an IP attribute. + /// Appwrite Docs + /// + /// The request content + /// The ip address attribute Task> CreateIpAttribute(CreateIPAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Update an IP attribute. Changing the default value will not update already existing documents. + /// Appwrite Docs + /// + /// The request content + /// The ip address attribute + Task> UpdateIpAttribute(UpdateIPAttributeRequest request); + + /// + /// Create relationship attribute. Learn more about relationship attributes. + /// Appwrite Docs + /// + /// The request content + /// The relationship attribute Task> CreateRelationshipAttribute(CreateRelationshipAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Create a string attribute. + /// Appwrite Docs + /// + /// The request content + /// The string attribute Task> CreateStringAttribute(CreateStringAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> CreateUrlAttribute(CreateURLAttributeRequest request); + /// + /// Update a string attribute. Changing the default value will not update already existing documents. + /// Appwrite Docs + /// + /// The request content + /// The string attribute + Task> UpdateStringAttribute(UpdateStringAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task DeleteAttribute(DeleteAttributeRequest request); + /// + /// Create a URL attribute. + /// Appwrite Docs + /// + /// The request content + /// The url attribute + Task> CreateUrlAttribute(CreateURLAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task DeleteDocument(DeleteDocumentRequest request); + /// + /// Update a URL attribute. Changing the default value will not update already existing documents. + /// Appwrite Docs + /// + /// The request content + /// The url attribute + Task> UpdateUrlAttribute(UpdateURLAttributeRequest request); [Obsolete("Endpoint not yet implemented.")] - Task DeleteIndex(DeleteIndexRequest request); + Task DeleteAttribute(DeleteAttributeRequest request); [Obsolete("Endpoint not yet implemented.")] Task> GetAttribute(GetAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> GetDocument(GetDocumentRequest request); - - [Obsolete("Endpoint not yet implemented.")] - Task> GetIndex(GetIndexRequest request); - - [Obsolete("Endpoint not yet implemented.")] - Task> ListDocuments(ListDocumentsRequest request); - - [Obsolete("Endpoint not yet implemented.")] - Task> ListIndexes(ListIndexesRequest request); - - [Obsolete("Endpoint not yet implemented.")] - Task> UpdateDatetimeAttribute(UpdateDatetimeAttributeRequest request); + /// + /// Update relationship attribute. Learn more about relationship attributes. + /// Appwrite Docs + /// + /// The request content + /// The relationship attribute + Task> UpdateRelationshipAttribute(UpdateRelationshipAttributeRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> UpdateDocument(UpdateDocumentRequest request); + Task> CreateDocument(CreateDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> UpdateEmailAttribute(UpdateEmailAttributeRequest request); + Task> CreateIndex(CreateIndexRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> UpdateEnumAttribute(UpdateEnumAttributeRequest request); + Task DeleteDocument(DeleteDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> UpdateFloatAttribute(UpdateFloatAttributeRequest request); + Task DeleteIndex(DeleteIndexRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> UpdateIntegerAttribute(UpdateIntegerAttributeRequest request); + Task> GetDocument(GetDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> UpdateIpAttribute(UpdateIPAttributeRequest request); + Task> GetIndex(GetIndexRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> UpdateRelationshipAttribute(UpdateRelationshipAttributeRequest request); + Task> ListDocuments(ListDocumentsRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> UpdateStringAttribute(UpdateStringAttributeRequest request); + Task> ListIndexes(ListIndexesRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> UpdateUrlAttribute(UpdateURLAttributeRequest request); + Task> UpdateDocument(UpdateDocumentRequest request); } From fab3f5b8182f81323ec0ad2ccc765548fc97967e Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 3 Dec 2024 19:22:36 +0000 Subject: [PATCH 242/303] Added empty test classes where tests are required --- .../Databases/DatabasesClientTests.CreateEmailAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.CreateEnumAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.CreateFloatAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.CreateIntegerAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.CreateIpAttribute.cs | 4 ++++ .../DatabasesClientTests.CreateRelationshipAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.CreateStringAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.CreateUrlAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.UpdateEmailAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.UpdateEnumAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.UpdateFloatAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.UpdateIntegerAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.UpdateIpAttribute.cs | 4 ++++ .../DatabasesClientTests.UpdateRelationshipAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.UpdateStringAttribute.cs | 4 ++++ .../Databases/DatabasesClientTests.UpdateUrlAttribute.cs | 4 ++++ 17 files changed, 68 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEmailAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEnumAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateFloatAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIntegerAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIpAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateRelationshipAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateStringAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateUrlAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEmailAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEnumAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateFloatAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIntegerAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIpAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateRelationshipAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateStringAttribute.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateUrlAttribute.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEmailAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEmailAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEmailAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEnumAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEnumAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEnumAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateFloatAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateFloatAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateFloatAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIntegerAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIntegerAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIntegerAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIpAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIpAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIpAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateRelationshipAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateRelationshipAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateRelationshipAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateStringAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateStringAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateStringAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateUrlAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateUrlAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateUrlAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEmailAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEmailAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEmailAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEnumAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEnumAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEnumAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateFloatAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateFloatAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateFloatAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIntegerAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIntegerAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIntegerAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIpAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIpAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIpAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateRelationshipAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateRelationshipAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateRelationshipAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateStringAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateStringAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateStringAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateUrlAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateUrlAttribute.cs new file mode 100644 index 00000000..47e7affb --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateUrlAttribute.cs @@ -0,0 +1,4 @@ +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ +} From 975b495e5f14eddbc8b7bf16a9e17475aeee8f1e Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 3 Dec 2024 19:24:21 +0000 Subject: [PATCH 243/303] added tests for update datetime attribute --- ...asesClientTests.UpdateDatetimeAttribute.cs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs index 47e7affb..179d7b73 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs @@ -1,4 +1,81 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task UpdateDatetimeAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateDatetimeAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/datetime/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeDatetimeResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateDatetimeAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateDatetimeAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateDatetimeAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/datetime/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateDatetimeAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateDatetimeAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateDatetimeAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/datetime/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateDatetimeAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From f2c203424de31d3585e92e249a198ecae1c29654 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 3 Dec 2024 19:26:25 +0000 Subject: [PATCH 244/303] added tests for create email attribute --- ...tabasesClientTests.CreateEmailAttribute.cs | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEmailAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEmailAttribute.cs index 47e7affb..882dd613 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEmailAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEmailAttribute.cs @@ -1,4 +1,78 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task CreateEmailAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateEmailAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "email" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/email") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeEmailResponse); + + // Act + var result = await _appwriteClient.Databases.CreateEmailAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateEmailAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateEmailAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "email" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/email") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateEmailAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateEmailAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateEmailAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "email" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/email") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateEmailAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From 4ed499475cb337a24da86c067b73996f884dba22 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 00:43:49 +0000 Subject: [PATCH 245/303] added tests for update email attribute --- ...tabasesClientTests.UpdateEmailAttribute.cs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEmailAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEmailAttribute.cs index 47e7affb..13b7d976 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEmailAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEmailAttribute.cs @@ -1,4 +1,81 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task UpdateEmailAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateEmailAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/email/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeEmailResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateEmailAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateEmailAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateEmailAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/email/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateEmailAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateEmailAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateEmailAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/email/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateEmailAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From 1198040ae184b1d87a8d47385724a0e77b53355b Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 00:45:47 +0000 Subject: [PATCH 246/303] added tests for create enum attribute --- ...atabasesClientTests.CreateEnumAttribute.cs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEnumAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEnumAttribute.cs index 47e7affb..49554880 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEnumAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEnumAttribute.cs @@ -1,4 +1,81 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task CreateEnumAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateEnumAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "enum", + Elements = ["one", "two", "three"] + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/enum") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeEnumResponse); + + // Act + var result = await _appwriteClient.Databases.CreateEnumAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateEnumAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateEnumAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "enum", + Elements = ["one", "two", "three"] + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/enum") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateEnumAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateEnumAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateEnumAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "enum", + Elements = ["one", "two", "three"] + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/enum") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateEnumAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From 3970d3f1fd6c1e93260b3399386d46ef79255f18 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 00:51:50 +0000 Subject: [PATCH 247/303] added tests for update enum attribute --- ...atabasesClientTests.UpdateEnumAttribute.cs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEnumAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEnumAttribute.cs index 47e7affb..e2c9afaa 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEnumAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEnumAttribute.cs @@ -1,4 +1,81 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task UpdateEnumAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateEnumAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + Elements = ["element1", "element2", "element3"] + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/enum/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeEnumResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateEnumAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateEnumAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateEnumAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + Elements = ["element1", "element2", "element3"] + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/enum/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateEnumAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateEnumAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateEnumAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + Elements = ["element1", "element2", "element3"] + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/enum/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateEnumAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From 7182a3de119a10ba6c9e25e71bde4955a768400b Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 00:51:58 +0000 Subject: [PATCH 248/303] added tests for create float attribute --- ...tabasesClientTests.CreateFloatAttribute.cs | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateFloatAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateFloatAttribute.cs index 47e7affb..dc30c08c 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateFloatAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateFloatAttribute.cs @@ -1,4 +1,78 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task CreateFloatAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateFloatAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/float") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeFloatResponse); + + // Act + var result = await _appwriteClient.Databases.CreateFloatAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateFloatAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateFloatAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/float") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateFloatAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateFloatAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateFloatAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/float") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateFloatAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From 0fd9781715be422e9d62d68202eb7228d5a249af Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 00:53:11 +0000 Subject: [PATCH 249/303] added tests for update float attribute --- ...tabasesClientTests.UpdateFloatAttribute.cs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateFloatAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateFloatAttribute.cs index 47e7affb..5add0254 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateFloatAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateFloatAttribute.cs @@ -1,4 +1,81 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task UpdateFloatAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateFloatAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/float/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeFloatResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateFloatAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateFloatAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateFloatAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/float/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateFloatAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateFloatAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateFloatAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/float/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateFloatAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From c53a1accb658ebeb6db73f8e90ce0d71f3efe674 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 00:54:15 +0000 Subject: [PATCH 250/303] added tests for create int attribute --- ...basesClientTests.CreateIntegerAttribute.cs | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIntegerAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIntegerAttribute.cs index 47e7affb..634f723f 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIntegerAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIntegerAttribute.cs @@ -1,4 +1,78 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task CreateIntegerAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateIntegerAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/integer") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeIntegerResponse); + + // Act + var result = await _appwriteClient.Databases.CreateIntegerAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateIntegerAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateIntegerAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/integer") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateIntegerAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateIntegerAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateIntegerAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/integer") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateIntegerAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From 4262aa5ff70d3cc7f8f54bb2e3127d369fcbe9ae Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 00:55:37 +0000 Subject: [PATCH 251/303] added tests for update int attribute --- ...basesClientTests.UpdateIntegerAttribute.cs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIntegerAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIntegerAttribute.cs index 47e7affb..438e9658 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIntegerAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIntegerAttribute.cs @@ -1,4 +1,81 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task UpdateIntegerAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateIntegerAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/integer/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeIntegerResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateIntegerAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateIntegerAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateIntegerAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/integer/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateIntegerAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateIntegerAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateIntegerAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/integer/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateIntegerAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From d6417d8517e6cd7da145ab34b93edeb40b858fa8 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 00:56:39 +0000 Subject: [PATCH 252/303] added tests for create ip attribute --- .../DatabasesClientTests.CreateIpAttribute.cs | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIpAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIpAttribute.cs index 47e7affb..c0bd4966 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIpAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIpAttribute.cs @@ -1,4 +1,78 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task CreateIpAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateIPAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/ip") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeIpResponse); + + // Act + var result = await _appwriteClient.Databases.CreateIpAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateIpAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateIPAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/ip") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateIpAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateIpAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateIPAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/ip") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateIpAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From c0e644bd676b1144abe524898ec44c5e9ef3e8b3 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 00:57:41 +0000 Subject: [PATCH 253/303] added tests for update ip attribute --- .../DatabasesClientTests.UpdateIpAttribute.cs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIpAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIpAttribute.cs index 47e7affb..8e41f535 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIpAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIpAttribute.cs @@ -1,4 +1,81 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task UpdateIpAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateIPAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/ip/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeIpResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateIpAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateIpAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateIPAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/ip/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateIpAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateIpAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateIPAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/ip/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateIpAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From 5d742834b1a2bb18a7533cfa89c2e8b6bb1018ac Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 00:59:13 +0000 Subject: [PATCH 254/303] added tests for create relationship attribute --- ...ClientTests.CreateRelationshipAttribute.cs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateRelationshipAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateRelationshipAttribute.cs index 47e7affb..6f95a3a0 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateRelationshipAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateRelationshipAttribute.cs @@ -1,4 +1,81 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task CreateRelationshipAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateRelationshipAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created", + RelatedCollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/relationship") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeRelationshipResponse); + + // Act + var result = await _appwriteClient.Databases.CreateRelationshipAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateRelationshipAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateRelationshipAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created", + RelatedCollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/relationship") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateRelationshipAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateRelationshipAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateRelationshipAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created", + RelatedCollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/relationship") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateRelationshipAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From 72fb9a9f3b664b1e770c9fcc5f61198273f6762e Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 01:01:31 +0000 Subject: [PATCH 255/303] added tests for create string attribute --- ...abasesClientTests.CreateStringAttribute.cs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateStringAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateStringAttribute.cs index 47e7affb..fe5713b2 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateStringAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateStringAttribute.cs @@ -1,4 +1,81 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task CreateStringAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateStringAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created", + Size = 100 + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/string") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeStringResponse); + + // Act + var result = await _appwriteClient.Databases.CreateStringAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateStringAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateStringAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created", + Size = 100 + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/string") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateStringAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateStringAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateStringAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created", + Size = 100 + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/string") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateStringAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From fc06fd0277c4fa724d03603ecb15e1d28460e410 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 01:04:30 +0000 Subject: [PATCH 256/303] added tests for update string attribute --- ...abasesClientTests.UpdateStringAttribute.cs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateStringAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateStringAttribute.cs index 47e7affb..806f35fb 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateStringAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateStringAttribute.cs @@ -1,4 +1,81 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task UpdateStringAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateStringAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/string/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeStringResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateStringAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateStringAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateStringAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/string/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateStringAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateStringAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateStringAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/string/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateStringAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From 021f588394dba87c9758e038abf09e35b3a8fb32 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 01:06:25 +0000 Subject: [PATCH 257/303] added tests for create url attribute --- ...DatabasesClientTests.CreateUrlAttribute.cs | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateUrlAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateUrlAttribute.cs index 47e7affb..1dab125b 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateUrlAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateUrlAttribute.cs @@ -1,4 +1,78 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task CreateUrlAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateURLAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/url") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeUrlResponse); + + // Act + var result = await _appwriteClient.Databases.CreateUrlAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateUrlAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateURLAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/url") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateUrlAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateUrlAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateURLAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "created" + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/url") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateUrlAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From 0c245dad0b1d88a0cdd655b3f0dd2eebf2054694 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 01:07:52 +0000 Subject: [PATCH 258/303] added tests for update url attribute --- ...DatabasesClientTests.UpdateUrlAttribute.cs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateUrlAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateUrlAttribute.cs index 47e7affb..40fc3de3 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateUrlAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateUrlAttribute.cs @@ -1,4 +1,81 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task UpdateUrlAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateURLAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/url/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeUrlResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateUrlAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateUrlAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateURLAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/url/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateUrlAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateUrlAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateURLAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/url/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateUrlAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From f53f099e016656c0c0d2f75db950a416b8991a50 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 01:09:04 +0000 Subject: [PATCH 259/303] added tests for update relationship attribute --- ...ClientTests.UpdateRelationshipAttribute.cs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateRelationshipAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateRelationshipAttribute.cs index 47e7affb..30a2f89c 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateRelationshipAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateRelationshipAttribute.cs @@ -1,4 +1,81 @@ -namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; public partial class DatabasesClientTests { + [Fact] + public async Task UpdateRelationshipAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new UpdateRelationshipAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}/relationship") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeRelationshipResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateRelationshipAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateRelationshipAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new UpdateRelationshipAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}/relationship") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateRelationshipAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateRelationshipAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new UpdateRelationshipAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey", + // Populate other request properties as needed + }; + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}/relationship") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateRelationshipAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } } From f02db4a74e4a32dafbcde1f01fcbfdbf0be57361 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 01:16:16 +0000 Subject: [PATCH 260/303] Update README.md --- README.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 032b48e2..15c46a85 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress -![Server & Client - 137 / 317](https://img.shields.io/badge/Server_&_Client-137%20%2F%20317-gold?style=for-the-badge) +![Server & Client - 156 / 317](https://img.shields.io/badge/Server_&_Client-156%20%2F%20317-gold?style=for-the-badge) -![Server - 77 / 224](https://img.shields.io/badge/Server-77%20%2F%20224-gold?style=for-the-badge) +![Server - 96 / 224](https://img.shields.io/badge/Server-96%20%2F%20224-gold?style=for-the-badge) ![Client - 60 / 93](https://img.shields.io/badge/Client-60%20%2F%2093-gold?style=for-the-badge) @@ -275,7 +275,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Update Preferences](https://appwrite.io/docs/references/1.6.x/client-rest/teams#updatePrefs) | ✅ | ✅ | ### Databases -![Databases - 12 / 47](https://img.shields.io/badge/Databases-12%20%2F%2047-red?style=for-the-badge) +![Databases - 31 / 47](https://img.shields.io/badge/Databases-31%20%2F%2047-gold?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -291,27 +291,27 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Delete Collection](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteCollection) | ❌ | ✅ | | [List Attributes](https://appwrite.io/docs/references/1.6.x/server-rest/databases#listAttributes) | ❌ | ✅ | | [Create Boolean Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createBooleanAttribute) | ❌ | ✅ | -| [Update Boolean Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateBooleanAttribute) | ❌ | ⬛ | -| [Create Datatime Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createDatetimeAttribute) | ❌ | ⬛ | -| [Update Datetime Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateDatetimeAttribute) | ❌ | ⬛ | -| [Create Email Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createEmailAttribute) | ❌ | ⬛ | -| [Update Email Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateEmailAttribute) | ❌ | ⬛ | -| [Create Enum Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createEnumAttribute) | ❌ | ⬛ | -| [Update Enum Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateEnumAttribute) | ❌ | ⬛ | -| [Create Float Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createFloatAttribute) | ❌ | ⬛ | -| [Update Float Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateFloatAttribute) | ❌ | ⬛ | -| [Create Integer Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createIntegerAttribute) | ❌ | ⬛ | -| [Update Integer attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateIntegerAttribute) | ❌ | ⬛ | -| [Create IP Address Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createIpAttribute) | ❌ | ⬛ | -| [Update IP Address Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateIpAttribute) | ❌ | ⬛ | -| [Create Relationship Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createRelationshipAttribute) | ❌ | ⬛ | -| [Create String Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createStringAttribute) | ❌ | ⬛ | -| [Update String Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateStringAttribute) | ❌ | ⬛ | -| [Create URL Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createUrlAttribute) | ❌ | ⬛ | -| [Update URL Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateUrlAttribute) | ❌ | ⬛ | +| [Update Boolean Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateBooleanAttribute) | ❌ | ✅ | +| [Create Datetime Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createDatetimeAttribute) | ❌ | ✅ | +| [Update Datetime Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateDatetimeAttribute) | ❌ | ✅ | +| [Create Email Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createEmailAttribute) | ❌ | ✅ | +| [Update Email Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateEmailAttribute) | ❌ | ✅ | +| [Create Enum Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createEnumAttribute) | ❌ | ✅ | +| [Update Enum Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateEnumAttribute) | ❌ | ✅ | +| [Create Float Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createFloatAttribute) | ❌ | ✅ | +| [Update Float Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateFloatAttribute) | ❌ | ✅ | +| [Create Integer Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createIntegerAttribute) | ❌ | ✅ | +| [Update Integer attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateIntegerAttribute) | ❌ | ✅ | +| [Create IP Address Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createIpAttribute) | ❌ | ✅ | +| [Update IP Address Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateIpAttribute) | ❌ | ✅ | +| [Create Relationship Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createRelationshipAttribute) | ❌ | ✅ | +| [Create String Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createStringAttribute) | ❌ | ✅ | +| [Update String Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateStringAttribute) | ❌ | ✅ | +| [Create URL Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createUrlAttribute) | ❌ | ✅ | +| [Update URL Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateUrlAttribute) | ❌ | ✅ | | [Get Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getAttribute) | ❌ | ⬛ | | [Delete Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteAttribute) | ❌ | ⬛ | -| [Update Relationship Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateRelationshipAttribute) | ❌ | ⬛ | +| [Update Relationship Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateRelationshipAttribute) | ❌ | ✅ | | [List Documents](https://appwrite.io/docs/references/1.6.x/client-rest/databases#listDocuments) | ⬛ | ⬛ | | [Create Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#createDocument) | ⬛ | ⬛ | | [Get Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#getDocument) | ⬛ | ⬛ | From 8a05123630584e7ed93a670a5ab37c546ca046a3 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 4 Dec 2024 10:05:21 +0000 Subject: [PATCH 261/303] Implemented get attribute --- src/PinguApps.Appwrite.Playground/App.cs | 30 +++---------------- .../Clients/DatabasesClient.cs | 17 +++++++++-- .../Clients/IDatabasesClient.cs | 7 ++++- .../Responses/Attribute.cs | 1 + 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 90251f27..e8c1d8e9 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,38 +17,16 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var key = "url"; - var newKey = $"new_{key}"; - - var createRequest = new CreateRelationshipAttributeRequest() - { - DatabaseId = "6748b44d000b2b0e73ac", - CollectionId = "6748bb30002a12d4708f", - Key = key, - RelatedCollectionId = "674f57a30011310c0b3c" - }; - - var createResponse = await _server.Databases.CreateRelationshipAttribute(createRequest); - - Console.WriteLine(createResponse.Result.Match( - result => result.ToString(), - appwriteError => appwriteError.Message, - internalError => internalError.Message)); - - Console.WriteLine("#############################################################################################"); - Console.ReadKey(); - - var updateRequest = new UpdateRelationshipAttributeRequest() + var request = new GetAttributeRequest() { DatabaseId = "6748b44d000b2b0e73ac", CollectionId = "6748bb30002a12d4708f", - Key = key, - NewKey = newKey + Key = "isRobot" }; - var updateResponse = await _server.Databases.UpdateRelationshipAttribute(updateRequest); + var response = await _server.Databases.GetAttribute(request); - Console.WriteLine(updateResponse.Result.Match( + Console.WriteLine(response.Result.Match( result => result.ToString(), appwriteError => appwriteError.Message, internalError => internalError.Message)); diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 88ee2213..8cb3c98e 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -535,9 +535,22 @@ public async Task> UpdateUrlAttribute(UpdateURLAttr /// public Task DeleteAttribute(DeleteAttributeRequest request) => throw new NotImplementedException(); - [ExcludeFromCodeCoverage] /// - public Task> GetAttribute(GetAttributeRequest request) => throw new NotImplementedException(); + public async Task> GetAttribute(GetAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.GetAttribute(request.DatabaseId, request.CollectionId, request.Key); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } /// public async Task> UpdateRelationshipAttribute(UpdateRelationshipAttributeRequest request) diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 990699de..5ab777b7 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -260,7 +260,12 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task DeleteAttribute(DeleteAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Get attribute by ID. + /// Appwrite Docs + /// + /// The request content + /// The attribute Task> GetAttribute(GetAttributeRequest request); /// diff --git a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs index cc4a44b5..d8cb83c1 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Attribute.cs @@ -17,6 +17,7 @@ namespace PinguApps.Appwrite.Shared.Responses; /// Is attribute an array? /// Attribute creation date in ISO 8601 format /// Attribute update date in ISO 8601 format +[JsonConverter(typeof(AttributeJsonConverter))] public abstract record Attribute( [property: JsonPropertyName("key")] string Key, [property: JsonPropertyName("type")] string Type, From bd326bd0f14e3ac5b831955fb60c6de81319cc06 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 18:03:20 +0000 Subject: [PATCH 262/303] Update actions/cache digest to 1bd1e32 --- .github/workflows/_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index 9404811d..1ec4fddb 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -25,7 +25,7 @@ jobs: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Nuget Cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} From 2f744e2ac1570e583102b6422740ee87aed18edc Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 17:59:03 +0000 Subject: [PATCH 263/303] added tests for get attribute --- .../DatabasesClientTests.GetAttribute.cs | 78 +++++++++++++++++++ .../TestConstants.cs | 14 ++++ 2 files changed, 92 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetAttribute.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetAttribute.cs new file mode 100644 index 00000000..af2e7396 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetAttribute.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task GetAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new GetAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey" + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.AttributeResponse); + + // Act + var result = await _appwriteClient.Databases.GetAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task GetAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new GetAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey" + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.GetAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task GetAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new GetAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey" + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.GetAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} diff --git a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs index 7fa034f2..6a663f5b 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/TestConstants.cs @@ -609,6 +609,20 @@ public static class TestConstants } """; + public const string AttributeResponse = """ + { + "key": "isEnabled", + "type": "boolean", + "status": "available", + "error": "string", + "required": true, + "array": false, + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "default": false + } + """; + public const string IndexResponse = """ { "key": "index1", From 026a375cc2bfd02fa34da862203195221d624145 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 18:03:08 +0000 Subject: [PATCH 264/303] Implemented delete attribute --- src/PinguApps.Appwrite.Playground/App.cs | 6 +++--- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 7 ++++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index e8c1d8e9..198259c1 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,14 +17,14 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new GetAttributeRequest() + var request = new DeleteAttributeRequest() { DatabaseId = "6748b44d000b2b0e73ac", CollectionId = "6748bb30002a12d4708f", - Key = "isRobot" + Key = "test23" }; - var response = await _server.Databases.GetAttribute(request); + var response = await _server.Databases.DeleteAttribute(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 8cb3c98e..08ac332b 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -531,9 +531,22 @@ public async Task> UpdateUrlAttribute(UpdateURLAttr } } - [ExcludeFromCodeCoverage] /// - public Task DeleteAttribute(DeleteAttributeRequest request) => throw new NotImplementedException(); + public async Task DeleteAttribute(DeleteAttributeRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.DeleteAttribute(request.DatabaseId, request.CollectionId, request.Key); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } /// public async Task> GetAttribute(GetAttributeRequest request) diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 5ab777b7..11bbdf02 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -257,7 +257,12 @@ public interface IDatabasesClient /// The url attribute Task> UpdateUrlAttribute(UpdateURLAttributeRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Deletes an attribute. + /// Appwrite Docs + /// + /// The request content + /// 204 success code Task DeleteAttribute(DeleteAttributeRequest request); /// From 891b93a192ad436ca433f81e8cb95a3e4f92be2f Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 18:05:05 +0000 Subject: [PATCH 265/303] added tests for delete attribute --- .../DatabasesClientTests.DeleteAttribute.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteAttribute.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteAttribute.cs new file mode 100644 index 00000000..71a11f06 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteAttribute.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task DeleteAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new DeleteAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey" + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.NoContent); + + // Act + var result = await _appwriteClient.Databases.DeleteAttribute(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task DeleteAttribute_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new DeleteAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey" + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.DeleteAttribute(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task DeleteAttribute_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new DeleteAttributeRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "attributeKey" + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.DeleteAttribute(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 906e588712db8ccc3792bf6c5e25ad301ea7909b Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 18:08:08 +0000 Subject: [PATCH 266/303] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 15c46a85..3b40edc7 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress -![Server & Client - 156 / 317](https://img.shields.io/badge/Server_&_Client-156%20%2F%20317-gold?style=for-the-badge) +![Server & Client - 158 / 317](https://img.shields.io/badge/Server_&_Client-158%20%2F%20317-gold?style=for-the-badge) -![Server - 96 / 224](https://img.shields.io/badge/Server-96%20%2F%20224-gold?style=for-the-badge) +![Server - 98 / 224](https://img.shields.io/badge/Server-98%20%2F%20224-gold?style=for-the-badge) ![Client - 60 / 93](https://img.shields.io/badge/Client-60%20%2F%2093-gold?style=for-the-badge) @@ -275,7 +275,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Update Preferences](https://appwrite.io/docs/references/1.6.x/client-rest/teams#updatePrefs) | ✅ | ✅ | ### Databases -![Databases - 31 / 47](https://img.shields.io/badge/Databases-31%20%2F%2047-gold?style=for-the-badge) +![Databases - 33 / 47](https://img.shields.io/badge/Databases-33%20%2F%2047-forestgreen?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -309,8 +309,8 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Update String Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateStringAttribute) | ❌ | ✅ | | [Create URL Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createUrlAttribute) | ❌ | ✅ | | [Update URL Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateUrlAttribute) | ❌ | ✅ | -| [Get Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getAttribute) | ❌ | ⬛ | -| [Delete Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteAttribute) | ❌ | ⬛ | +| [Get Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getAttribute) | ❌ | ✅ | +| [Delete Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteAttribute) | ❌ | ✅ | | [Update Relationship Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateRelationshipAttribute) | ❌ | ✅ | | [List Documents](https://appwrite.io/docs/references/1.6.x/client-rest/databases#listDocuments) | ⬛ | ⬛ | | [Create Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#createDocument) | ⬛ | ⬛ | From b44d450dd8454d32d7558fc028880e0aea58097e Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 18:19:29 +0000 Subject: [PATCH 267/303] Implemented list indexes --- src/PinguApps.Appwrite.Playground/App.cs | 7 +++--- .../Clients/DatabasesClient.cs | 17 ++++++++++++-- .../Clients/IDatabasesClient.cs | 23 +++++++++++-------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 198259c1..38ead25d 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,14 +17,13 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new DeleteAttributeRequest() + var request = new ListIndexesRequest() { DatabaseId = "6748b44d000b2b0e73ac", - CollectionId = "6748bb30002a12d4708f", - Key = "test23" + CollectionId = "6748bb30002a12d4708f" }; - var response = await _server.Databases.DeleteAttribute(request); + var response = await _server.Databases.ListIndexes(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 08ac332b..2007d1e0 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -602,9 +602,22 @@ public async Task> UpdateRelationshipAttri /// public Task> UpdateDocument(UpdateDocumentRequest request) => throw new NotImplementedException(); - [ExcludeFromCodeCoverage] /// - public Task> ListIndexes(ListIndexesRequest request) => throw new NotImplementedException(); + public async Task> ListIndexes(ListIndexesRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.ListIndexes(request.DatabaseId, request.CollectionId, RequestUtils.GetQueryStrings(request.Queries)); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 11bbdf02..c6c16df8 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -285,26 +285,31 @@ public interface IDatabasesClient Task> CreateDocument(CreateDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> CreateIndex(CreateIndexRequest request); + Task DeleteIndex(DeleteIndexRequest request); [Obsolete("Endpoint not yet implemented.")] - Task DeleteDocument(DeleteDocumentRequest request); + Task> GetDocument(GetDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] - Task DeleteIndex(DeleteIndexRequest request); + Task> ListDocuments(ListDocumentsRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> GetDocument(GetDocumentRequest request); + Task> UpdateDocument(UpdateDocumentRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> GetIndex(GetIndexRequest request); + /// + /// List indexes in the collection. + /// Appwrite Docs + /// + /// The request content + /// The indexes list + Task> ListIndexes(ListIndexesRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> ListDocuments(ListDocumentsRequest request); + Task> CreateIndex(CreateIndexRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> ListIndexes(ListIndexesRequest request); + Task DeleteDocument(DeleteDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] - Task> UpdateDocument(UpdateDocumentRequest request); + Task> GetIndex(GetIndexRequest request); } From 519b1764c33259f9fc8b7e3a44158638c49542e4 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 18:23:53 +0000 Subject: [PATCH 268/303] added tests for list indexes --- .../DatabasesClientTests.ListIndexes.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListIndexes.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListIndexes.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListIndexes.cs new file mode 100644 index 00000000..2fbef873 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListIndexes.cs @@ -0,0 +1,99 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task ListIndexes_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new ListIndexesRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.IndexesListResponse); + + // Act + var result = await _appwriteClient.Databases.ListIndexes(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListIndexes_ShouldProvideQueryParams_WhenQueriesProvided() + { + // Arrange + var query = Query.Limit(5); + var request = new ListIndexesRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Queries = [query] + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .WithQueryString($"queries[]={query.GetQueryString()}") + .Respond(TestConstants.AppJson, TestConstants.IndexesListResponse); + + // Act + var result = await _appwriteClient.Databases.ListIndexes(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListIndexes_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new ListIndexesRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.ListIndexes(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task ListIndexes_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new ListIndexesRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.ListIndexes(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From b0320c757b0d521ad2731f1dd64b1b5cf277c611 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 18:30:16 +0000 Subject: [PATCH 269/303] implemented create index --- src/PinguApps.Appwrite.Playground/App.cs | 11 ++++++++--- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 8 +++++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 38ead25d..dbeb6538 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Requests.Databases; namespace PinguApps.Appwrite.Playground; @@ -17,13 +18,17 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new ListIndexesRequest() + var request = new CreateIndexRequest() { DatabaseId = "6748b44d000b2b0e73ac", - CollectionId = "6748bb30002a12d4708f" + CollectionId = "6748bb30002a12d4708f", + Key = "myKey", + IndexType = IndexType.Key, + Attributes = ["new_int"], + Orders = [SortDirection.Asc] }; - var response = await _server.Databases.ListIndexes(request); + var response = await _server.Databases.CreateIndex(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 2007d1e0..23600b94 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -619,9 +619,22 @@ public async Task> ListIndexes(ListIndexesRequest re } } - [ExcludeFromCodeCoverage] /// - public Task> CreateIndex(CreateIndexRequest request) => throw new NotImplementedException(); + public async Task> CreateIndex(CreateIndexRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateIndex(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index c6c16df8..2dfd0ece 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using PinguApps.Appwrite.Shared; +using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Requests.Databases; using PinguApps.Appwrite.Shared.Responses; using Attribute = PinguApps.Appwrite.Shared.Responses.Attribute; @@ -304,7 +305,12 @@ public interface IDatabasesClient /// The indexes list Task> ListIndexes(ListIndexesRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request. Attributes can be , and . + /// Appwrite Docs + /// + /// The request content + /// The index Task> CreateIndex(CreateIndexRequest request); [Obsolete("Endpoint not yet implemented.")] From 2a3faa2c550aef5c8c9d1981d4c6e217f1b06efe Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 18:34:09 +0000 Subject: [PATCH 270/303] added tests for create index --- .../DatabasesClientTests.CreateIndex.cs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIndex.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIndex.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIndex.cs new file mode 100644 index 00000000..b6cf11e6 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIndex.cs @@ -0,0 +1,88 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Enums; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task CreateIndex_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new CreateIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "myIndex", + IndexType = IndexType.Unique, + Attributes = ["new_int"], + Orders = [SortDirection.Asc] + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.IndexResponse); + + // Act + var result = await _appwriteClient.Databases.CreateIndex(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateIndex_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new CreateIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "myIndex", + IndexType = IndexType.Unique, + Attributes = ["new_int"], + Orders = [SortDirection.Asc] + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateIndex(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateIndex_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new CreateIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "myIndex", + IndexType = IndexType.Unique, + Attributes = ["new_int"], + Orders = [SortDirection.Asc] + }; + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateIndex(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 168c183b72a590249bf0f213dcc4d2158349f944 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 18:37:02 +0000 Subject: [PATCH 271/303] implemented delete index --- src/PinguApps.Appwrite.Playground/App.cs | 10 +++------- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 11 ++++++++--- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index dbeb6538..e6447454 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Configuration; -using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Requests.Databases; namespace PinguApps.Appwrite.Playground; @@ -18,17 +17,14 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new CreateIndexRequest() + var request = new DeleteIndexRequest() { DatabaseId = "6748b44d000b2b0e73ac", CollectionId = "6748bb30002a12d4708f", - Key = "myKey", - IndexType = IndexType.Key, - Attributes = ["new_int"], - Orders = [SortDirection.Asc] + Key = "myKey" }; - var response = await _server.Databases.CreateIndex(request); + var response = await _server.Databases.DeleteIndex(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 23600b94..8d63bed8 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -636,9 +636,22 @@ public async Task> CreateIndex(CreateIndexRequest request) } } - [ExcludeFromCodeCoverage] /// - public Task DeleteIndex(DeleteIndexRequest request) => throw new NotImplementedException(); + public async Task DeleteIndex(DeleteIndexRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.DeleteIndex(request.DatabaseId, request.CollectionId, request.Key); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 2dfd0ece..5f63a201 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -286,7 +286,7 @@ public interface IDatabasesClient Task> CreateDocument(CreateDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] - Task DeleteIndex(DeleteIndexRequest request); + Task DeleteDocument(DeleteDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] Task> GetDocument(GetDocumentRequest request); @@ -313,8 +313,13 @@ public interface IDatabasesClient /// The index Task> CreateIndex(CreateIndexRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task DeleteDocument(DeleteDocumentRequest request); + /// + /// Delete an index. + /// Appwrite Docs + /// + /// The request content + /// 204 success code + Task DeleteIndex(DeleteIndexRequest request); [Obsolete("Endpoint not yet implemented.")] Task> GetIndex(GetIndexRequest request); From 506f44513e12934557c62cbf7fdc497e1fda091d Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 18:39:00 +0000 Subject: [PATCH 272/303] added tests for delete index --- .../DatabasesClientTests.DeleteIndex.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteIndex.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteIndex.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteIndex.cs new file mode 100644 index 00000000..13a0fcba --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteIndex.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task DeleteIndex_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new DeleteIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "indexKey" + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.NoContent); + + // Act + var result = await _appwriteClient.Databases.DeleteIndex(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task DeleteIndex_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new DeleteIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "indexKey" + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.DeleteIndex(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task DeleteIndex_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new DeleteIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "indexKey" + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.DeleteIndex(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From e2546957fe7283156e1917995a0b9c372a4307ac Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 18:41:02 +0000 Subject: [PATCH 273/303] implemented get index --- src/PinguApps.Appwrite.Playground/App.cs | 6 +++--- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 7 ++++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index e6447454..20e50e11 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,14 +17,14 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new DeleteIndexRequest() + var request = new GetIndexRequest() { DatabaseId = "6748b44d000b2b0e73ac", CollectionId = "6748bb30002a12d4708f", - Key = "myKey" + Key = "index_1" }; - var response = await _server.Databases.DeleteIndex(request); + var response = await _server.Databases.GetIndex(request); Console.WriteLine(response.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 8d63bed8..58df7c69 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -653,7 +653,20 @@ public async Task DeleteIndex(DeleteIndexRequest request) } } - [ExcludeFromCodeCoverage] /// - public Task> GetIndex(GetIndexRequest request) => throw new NotImplementedException(); + public async Task> GetIndex(GetIndexRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.GetIndex(request.DatabaseId, request.CollectionId, request.Key); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } } diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 5f63a201..2351eeec 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -321,6 +321,11 @@ public interface IDatabasesClient /// 204 success code Task DeleteIndex(DeleteIndexRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Get index by ID. + /// Appwrite Docs + /// + /// The request content + /// The index Task> GetIndex(GetIndexRequest request); } From b77d8536d41bddec67336b817ef9ae50f7f01849 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 18:43:04 +0000 Subject: [PATCH 274/303] added tests for get index --- .../DatabasesClientTests.GetIndex.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetIndex.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetIndex.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetIndex.cs new file mode 100644 index 00000000..383471ed --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetIndex.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task GetIndex_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new GetIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "indexKey" + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes/{request.Key}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.IndexResponse); + + // Act + var result = await _appwriteClient.Databases.GetIndex(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task GetIndex_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new GetIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "indexKey" + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes/{request.Key}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.GetIndex(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task GetIndex_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new GetIndexRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Key = "indexKey" + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes/{request.Key}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.GetIndex(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 2243fe700449e3a350682d452d72bfe5c40fe4ad Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Fri, 6 Dec 2024 18:44:48 +0000 Subject: [PATCH 275/303] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3b40edc7..13cb326a 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress -![Server & Client - 158 / 317](https://img.shields.io/badge/Server_&_Client-158%20%2F%20317-gold?style=for-the-badge) +![Server & Client - 162 / 317](https://img.shields.io/badge/Server_&_Client-162%20%2F%20317-gold?style=for-the-badge) -![Server - 98 / 224](https://img.shields.io/badge/Server-98%20%2F%20224-gold?style=for-the-badge) +![Server - 102 / 224](https://img.shields.io/badge/Server-102%20%2F%20224-gold?style=for-the-badge) ![Client - 60 / 93](https://img.shields.io/badge/Client-60%20%2F%2093-gold?style=for-the-badge) @@ -275,7 +275,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Update Preferences](https://appwrite.io/docs/references/1.6.x/client-rest/teams#updatePrefs) | ✅ | ✅ | ### Databases -![Databases - 33 / 47](https://img.shields.io/badge/Databases-33%20%2F%2047-forestgreen?style=for-the-badge) +![Databases - 37 / 47](https://img.shields.io/badge/Databases-37%20%2F%2047-forestgreen?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -317,10 +317,10 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Get Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#getDocument) | ⬛ | ⬛ | | [Update Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#updateDocument) | ⬛ | ⬛ | | [Delete Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#deleteDocument) | ⬛ | ⬛ | -| [List Indexes](https://appwrite.io/docs/references/1.6.x/server-rest/databases#listIndexes) | ❌ | ⬛ | -| [Create Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createIndex) | ❌ | ⬛ | -| [Get Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getIndex) | ❌ | ⬛ | -| [Delete Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteIndex) | ❌ | ⬛ | +| [List Indexes](https://appwrite.io/docs/references/1.6.x/server-rest/databases#listIndexes) | ❌ | ✅ | +| [Create Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createIndex) | ❌ | ✅ | +| [Get Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getIndex) | ❌ | ✅ | +| [Delete Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteIndex) | ❌ | ✅ | ### Storage ![storage - 0 / 21](https://img.shields.io/badge/Storage-0%20%2F%2021-red?style=for-the-badge) From 75f48f2b45c325f4ec520362f11593a8da21e2a6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 18:49:21 +0000 Subject: [PATCH 276/303] Update alstr/todo-to-issue-action digest to f357c40 --- .github/workflows/dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 38f6be3d..60464879 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -30,7 +30,7 @@ jobs: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: "TODO to Issue" - uses: "alstr/todo-to-issue-action@3ac81cb1658124fa226ebe4b329fb3025b46732c" # v5 + uses: "alstr/todo-to-issue-action@f357c404fe00f5a17cd96696eaf6f8ce5fd19cf7" # v5 with: INSERT_ISSUE_URLS: true AUTO_ASSIGN: true From 64a05e48c81e47d122ee54f79c33cafa310df843 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sat, 7 Dec 2024 09:54:24 +0000 Subject: [PATCH 277/303] Implemented List Documents --- .../Clients/DatabasesClient.cs | 18 +++++++++++++-- .../Clients/IDatabasesClient.cs | 13 +++++++---- .../Internals/IDatabasesApi.cs | 10 ++++----- src/PinguApps.Appwrite.Playground/App.cs | 22 ++++++++++++++----- .../Clients/DatabasesClient.cs | 17 ++++++++++++-- .../Clients/IDatabasesClient.cs | 11 +++++++--- 6 files changed, 69 insertions(+), 22 deletions(-) diff --git a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs index 06f0cb89..aa5789cc 100644 --- a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using PinguApps.Appwrite.Client.Internals; +using PinguApps.Appwrite.Client.Utils; using PinguApps.Appwrite.Shared; using PinguApps.Appwrite.Shared.Requests.Databases; using PinguApps.Appwrite.Shared.Responses; @@ -18,9 +19,22 @@ internal DatabasesClient(IDatabasesApi databasesApi) _databasesApi = databasesApi; } - [ExcludeFromCodeCoverage] /// - public Task> ListDocuments(ListDocumentsRequest request) => throw new NotImplementedException(); + public async Task> ListDocuments(ListDocumentsRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.ListDocuments(GetCurrentSessionOrThrow(), request.DatabaseId, request.CollectionId, RequestUtils.GetQueryStrings(request.Queries)); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs index 9b337035..e2e4baee 100644 --- a/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs @@ -11,10 +11,18 @@ namespace PinguApps.Appwrite.Client.Clients; /// All data returned by the Databases service are represented as structured JSON documents. /// The Databases service can contain multiple databases, each database can contain multiple collections. A collection is a group of similarly structured documents. The accepted structure of documents is defined by collection attributes. The collection attributes help you ensure all your user-submitted data is validated and stored according to the collection structure. /// Using Appwrite permissions architecture, you can assign read or write access to each collection or document in your project for either a specific user, team, user role, or even grant it with public access (any). You can learn more about how Appwrite handles permissions and access control. -/// Appwrite Docs +/// Appwrite Docs /// public interface IDatabasesClient { + /// + /// Get a list of all the user's documents in a given collection. You can use the query params to filter your results. + /// Appwrite Docs + /// + /// The request content + /// The documents list + Task> ListDocuments(ListDocumentsRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateDocument(CreateDocumentRequest request); @@ -24,9 +32,6 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task> GetDocument(GetDocumentRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> ListDocuments(ListDocumentsRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> UpdateDocument(UpdateDocumentRequest request); } diff --git a/src/PinguApps.Appwrite.Client/Internals/IDatabasesApi.cs b/src/PinguApps.Appwrite.Client/Internals/IDatabasesApi.cs index 408af82a..86d937eb 100644 --- a/src/PinguApps.Appwrite.Client/Internals/IDatabasesApi.cs +++ b/src/PinguApps.Appwrite.Client/Internals/IDatabasesApi.cs @@ -10,18 +10,18 @@ internal interface IDatabasesApi : IBaseApi { [Get("/databases/{databaseId}/collections/{collectionId}/documents")] [QueryUriFormat(UriFormat.Unescaped)] - Task> ListDocuments(string databaseId, string collectionId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); + Task> ListDocuments([Header("x-appwrite-session")] string session, string databaseId, string collectionId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); [Post("/databases/{databaseId}/collections/{collectionId}/documents")] - Task> CreateDocument(string databaseId, string collectionId, CreateDocumentRequest request); + Task> CreateDocument([Header("x-appwrite-session")] string session, string databaseId, string collectionId, CreateDocumentRequest request); [Delete("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] - Task DeleteDocument(string databaseId, string collectionId, string documentId); + Task DeleteDocument([Header("x-appwrite-session")] string session, string databaseId, string collectionId, string documentId); [Get("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] [QueryUriFormat(UriFormat.Unescaped)] - Task> GetDocument(string databaseId, string collectionId, string documentId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); + Task> GetDocument([Header("x-appwrite-session")] string session, string databaseId, string collectionId, string documentId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); [Patch("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] - Task> UpdateDocument(string databaseId, string collectionId, string documentId, UpdateDocumentRequest request); + Task> UpdateDocument([Header("x-appwrite-session")] string session, string databaseId, string collectionId, string documentId, UpdateDocumentRequest request); } diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 20e50e11..0bcff66c 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -17,16 +17,26 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new GetIndexRequest() + var request = new ListDocumentsRequest() { - DatabaseId = "6748b44d000b2b0e73ac", - CollectionId = "6748bb30002a12d4708f", - Key = "index_1" + DatabaseId = "67541a2800221703e717", + CollectionId = "67541a37001514b81821" }; - var response = await _server.Databases.GetIndex(request); + var serverResponse = await _server.Databases.ListDocuments(request); - Console.WriteLine(response.Result.Match( + Console.WriteLine(serverResponse.Result.Match( + result => result.ToString(), + appwriteError => appwriteError.Message, + internalError => internalError.Message)); + + Console.WriteLine("###############################################################################"); + + _client.SetSession(_session); + + var clientResponse = await _client.Databases.ListDocuments(request); + + Console.WriteLine(clientResponse.Result.Match( result => result.ToString(), appwriteError => appwriteError.Message, internalError => internalError.Message)); diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 58df7c69..b0d92d1d 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -582,9 +582,22 @@ public async Task> UpdateRelationshipAttri } } - [ExcludeFromCodeCoverage] /// - public Task> ListDocuments(ListDocumentsRequest request) => throw new NotImplementedException(); + public async Task> ListDocuments(ListDocumentsRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.ListDocuments(request.DatabaseId, request.CollectionId, RequestUtils.GetQueryStrings(request.Queries)); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 2351eeec..fc689c8a 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -282,6 +282,14 @@ public interface IDatabasesClient /// The relationship attribute Task> UpdateRelationshipAttribute(UpdateRelationshipAttributeRequest request); + /// + /// Get a list of all the user's documents in a given collection. You can use the query params to filter your results. + /// Appwrite Docs + /// + /// The request content + /// The documents list + Task> ListDocuments(ListDocumentsRequest request); + [Obsolete("Endpoint not yet implemented.")] Task> CreateDocument(CreateDocumentRequest request); @@ -291,9 +299,6 @@ public interface IDatabasesClient [Obsolete("Endpoint not yet implemented.")] Task> GetDocument(GetDocumentRequest request); - [Obsolete("Endpoint not yet implemented.")] - Task> ListDocuments(ListDocumentsRequest request); - [Obsolete("Endpoint not yet implemented.")] Task> UpdateDocument(UpdateDocumentRequest request); From d79b5eecbbfd5944f40b6cb9affbd8b14b16f327 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 8 Dec 2024 00:24:01 +0000 Subject: [PATCH 278/303] added server tests for list documents --- .../DatabasesClientTests.ListDocuments.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListDocuments.cs diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListDocuments.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListDocuments.cs new file mode 100644 index 00000000..c7099d4f --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListDocuments.cs @@ -0,0 +1,99 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task ListDocuments_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new ListDocumentsRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.DocumentsListResponse); + + // Act + var result = await _appwriteClient.Databases.ListDocuments(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListDocuments_ShouldProvideQueryParams_WhenQueriesProvided() + { + // Arrange + var query = Query.Limit(5); + var request = new ListDocumentsRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Queries = [query] + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .WithQueryString($"queries[]={query.GetQueryString()}") + .Respond(TestConstants.AppJson, TestConstants.DocumentsListResponse); + + // Act + var result = await _appwriteClient.Databases.ListDocuments(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListDocuments_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new ListDocumentsRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.ListDocuments(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task ListDocuments_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new ListDocumentsRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.ListDocuments(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From fe431be07f7d374afb256546b772f71e1bff7daa Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 8 Dec 2024 00:38:02 +0000 Subject: [PATCH 279/303] Ensured that client operations don't require a login, but will pass it when needed --- .../Clients/DatabasesClient.cs | 2 +- .../Internals/IDatabasesApi.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs index aa5789cc..d1f3833b 100644 --- a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs @@ -26,7 +26,7 @@ public async Task> ListDocuments(ListDocumentsRequ { request.Validate(true); - var result = await _databasesApi.ListDocuments(GetCurrentSessionOrThrow(), request.DatabaseId, request.CollectionId, RequestUtils.GetQueryStrings(request.Queries)); + var result = await _databasesApi.ListDocuments(GetCurrentSession(), request.DatabaseId, request.CollectionId, RequestUtils.GetQueryStrings(request.Queries)); return result.GetApiResponse(); } diff --git a/src/PinguApps.Appwrite.Client/Internals/IDatabasesApi.cs b/src/PinguApps.Appwrite.Client/Internals/IDatabasesApi.cs index 86d937eb..cc723517 100644 --- a/src/PinguApps.Appwrite.Client/Internals/IDatabasesApi.cs +++ b/src/PinguApps.Appwrite.Client/Internals/IDatabasesApi.cs @@ -10,18 +10,18 @@ internal interface IDatabasesApi : IBaseApi { [Get("/databases/{databaseId}/collections/{collectionId}/documents")] [QueryUriFormat(UriFormat.Unescaped)] - Task> ListDocuments([Header("x-appwrite-session")] string session, string databaseId, string collectionId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); + Task> ListDocuments([Header("x-appwrite-session")] string? session, string databaseId, string collectionId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); [Post("/databases/{databaseId}/collections/{collectionId}/documents")] - Task> CreateDocument([Header("x-appwrite-session")] string session, string databaseId, string collectionId, CreateDocumentRequest request); + Task> CreateDocument([Header("x-appwrite-session")] string? session, string databaseId, string collectionId, CreateDocumentRequest request); [Delete("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] - Task DeleteDocument([Header("x-appwrite-session")] string session, string databaseId, string collectionId, string documentId); + Task DeleteDocument([Header("x-appwrite-session")] string? session, string databaseId, string collectionId, string documentId); [Get("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] [QueryUriFormat(UriFormat.Unescaped)] - Task> GetDocument([Header("x-appwrite-session")] string session, string databaseId, string collectionId, string documentId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); + Task> GetDocument([Header("x-appwrite-session")] string? session, string databaseId, string collectionId, string documentId, [Query(CollectionFormat.Multi), AliasAs("queries[]")] IEnumerable queries); [Patch("/databases/{databaseId}/collections/{collectionId}/documents/{documentId}")] - Task> UpdateDocument([Header("x-appwrite-session")] string session, string databaseId, string collectionId, string documentId, UpdateDocumentRequest request); + Task> UpdateDocument([Header("x-appwrite-session")] string? session, string databaseId, string collectionId, string documentId, UpdateDocumentRequest request); } From 3321755fd4f08d9e5936713dfdfece3f63723716 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Sun, 8 Dec 2024 00:38:19 +0000 Subject: [PATCH 280/303] added tests for client List Documents --- .../DatabasesClientTests.ListDocuments.cs | 99 +++++++++++++++++++ .../Clients/Databases/DatabasesClientTests.cs | 38 +++++++ 2 files changed, 137 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.ListDocuments.cs create mode 100644 tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.cs diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.ListDocuments.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.ListDocuments.cs new file mode 100644 index 00000000..9b7aab76 --- /dev/null +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.ListDocuments.cs @@ -0,0 +1,99 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Client.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task ListDocuments_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new ListDocumentsRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.DocumentsListResponse); + + // Act + var result = await _appwriteClient.Databases.ListDocuments(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListDocuments_ShouldProvideQueryParams_WhenQueriesProvided() + { + // Arrange + var query = Query.Limit(5); + var request = new ListDocumentsRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + Queries = [query] + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .WithQueryString($"queries[]={query.GetQueryString()}") + .Respond(TestConstants.AppJson, TestConstants.DocumentsListResponse); + + // Act + var result = await _appwriteClient.Databases.ListDocuments(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task ListDocuments_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new ListDocumentsRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.ListDocuments(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task ListDocuments_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new ListDocumentsRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.ListDocuments(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.cs new file mode 100644 index 00000000..c2b3d5d2 --- /dev/null +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.cs @@ -0,0 +1,38 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.Extensions.DependencyInjection; +using PinguApps.Appwrite.Shared.Converters; +using PinguApps.Appwrite.Shared.Tests; +using Refit; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Client.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + private readonly MockHttpMessageHandler _mockHttp; + private readonly IAppwriteClient _appwriteClient; + private readonly JsonSerializerOptions _jsonSerializerOptions; + + public DatabasesClientTests() + { + _mockHttp = new MockHttpMessageHandler(); + var services = new ServiceCollection(); + + services.AddAppwriteClientForServer(TestConstants.ProjectId, TestConstants.Endpoint, new RefitSettings + { + HttpMessageHandlerFactory = () => _mockHttp + }); + + var serviceProvider = services.BuildServiceProvider(); + + _appwriteClient = serviceProvider.GetRequiredService(); + + _jsonSerializerOptions = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + _jsonSerializerOptions.Converters.Add(new IgnoreSdkExcludedPropertiesConverterFactory()); + } +} From dfebe3276684756fa353757c8045de7d7af4a173 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Mon, 9 Dec 2024 00:05:35 +0000 Subject: [PATCH 281/303] Added WithData to CreateDocumentRequiestBuilder --- .../Databases/CreateDocumentRequestBuilder.cs | 56 ++++++- .../ICreateDocumentRequestBuilder.cs | 29 +++- .../CreateDocumentRequestBuilderTests.cs | 146 +++++++++++++++++- 3 files changed, 228 insertions(+), 3 deletions(-) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestBuilder.cs index 108ce1ff..09a5e2ac 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestBuilder.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequestBuilder.cs @@ -1,5 +1,12 @@ -using System.Collections.Generic; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Utils; +using static PinguApps.Appwrite.Shared.Requests.Databases.ICreateDocumentRequestBuilder; namespace PinguApps.Appwrite.Shared.Requests.Databases; internal class CreateDocumentRequestBuilder : ICreateDocumentRequestBuilder @@ -7,6 +14,14 @@ internal class CreateDocumentRequestBuilder : ICreateDocumentRequestBuilder private readonly CreateDocumentRequest _request = new(); private readonly Dictionary _data = []; + private static readonly JsonSerializerOptions _jsonOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + private static readonly ConcurrentDictionary _propertyCache = new(); + public ICreateDocumentRequestBuilder WithDatabaseId(string databaseId) { _request.DatabaseId = databaseId; @@ -43,6 +58,45 @@ public ICreateDocumentRequestBuilder AddField(string name, object? value) return this; } + public ICreateDocumentRequestBuilder WithData(T? data, Action? options = null) where T : class + { + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } + + var withDataOptions = new WithDataOptions(); + options?.Invoke(withDataOptions); + + var properties = _propertyCache.GetOrAdd(typeof(T), x => x.GetProperties(BindingFlags.Public | BindingFlags.Instance)); + + foreach (var property in properties) + { + if (!withDataOptions.ShouldIncludeProperty(property)) + { + continue; + } + + var jsonPropertyName = GetJsonPropertyName(property); + + var value = property.GetValue(data); + if (value is not null || !withDataOptions.IgnoreNullValues) + { + AddField(jsonPropertyName, value); + } + } + + return this; + } + + [ExcludeFromCodeCoverage] + private static string GetJsonPropertyName(PropertyInfo property) + { + return property.GetCustomAttribute()?.Name + ?? _jsonOptions.PropertyNamingPolicy?.ConvertName(property.Name) + ?? property.Name; + } + public CreateDocumentRequest Build() { _request.Data = _data; diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs index 165071b6..bb3c171c 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Shared.Requests.Databases; @@ -42,4 +45,28 @@ public interface ICreateDocumentRequestBuilder /// Builds the document request /// CreateDocumentRequest Build(); + + /// + /// Adds your given data do the document data + /// + /// The type of your data object + /// The data + /// Options + ICreateDocumentRequestBuilder WithData(T? data, Action? options = null) where T : class; + + public class WithDataOptions + { + public bool IgnoreNullValues { get; set; } = true; + public Func? PropertyFilter { get; set; } + + public bool ShouldIncludeProperty(PropertyInfo property) + { + if (property.GetCustomAttribute() != null) + { + return false; + } + + return PropertyFilter?.Invoke(property) ?? true; + } + } } diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestBuilderTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestBuilderTests.cs index 4c684fa5..3701a9c2 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestBuilderTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestBuilderTests.cs @@ -1,5 +1,7 @@ -using PinguApps.Appwrite.Shared.Requests.Databases; +using System.Text.Json.Serialization; +using PinguApps.Appwrite.Shared.Requests.Databases; using PinguApps.Appwrite.Shared.Utils; +using static PinguApps.Appwrite.Shared.Requests.Databases.ICreateDocumentRequestBuilder; namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; public class CreateDocumentRequestBuilderTests @@ -192,4 +194,146 @@ public void Build_WithNoFieldsAdded_CreatesEmptyDataDictionary() Assert.NotNull(request.Data); Assert.Empty(request.Data); } + + public class TestClass + { + public string? StringProp { get; set; } + public int IntProp { get; set; } + [JsonIgnore] + public string? IgnoredProp { get; set; } + [JsonPropertyName("custom")] + public string? CustomNameProp { get; set; } + } + + [Fact] + public void WithData_NullData_ThrowsArgumentNullException() + { + var builder = CreateDocumentRequest.CreateBuilder(); + + TestClass? nullData = null; + var ex = Assert.Throws(() => builder.WithData(nullData)); + Assert.Equal("data", ex.ParamName); + } + + [Fact] + public void WithData_NullOptions_UsesDefaultOptions() + { + var builder = CreateDocumentRequest.CreateBuilder(); + + var data = new TestClass { StringProp = "test" }; + builder.WithData(data, options: null); + // Assert AddField was called with "stringProp" and "test" + // You'll need to mock/verify this based on your implementation + } + + [Fact] + public void WithData_CustomOptions_AppliesOptions() + { + var builder = CreateDocumentRequest.CreateBuilder(); + + var data = new TestClass { StringProp = "test" }; + builder.WithData(data, options => options.IgnoreNullValues = false); + // Assert all properties were added, including nulls + } + + [Fact] + public void WithData_PropertyFilter_AppliesFilter() + { + var builder = CreateDocumentRequest.CreateBuilder(); + + var data = new TestClass { StringProp = "test", IntProp = 42 }; + builder.WithData(data, options => + options.PropertyFilter = prop => prop.PropertyType == typeof(string)); + // Assert only string properties were added + } + + [Fact] + public void WithData_JsonIgnoreAttribute_SkipsProperty() + { + var builder = CreateDocumentRequest.CreateBuilder(); + + var data = new TestClass { IgnoredProp = "ignored" }; + builder.WithData(data); + // Assert IgnoredProp was not added + } + + [Fact] + public void WithData_JsonPropertyNameAttribute_UsesCustomName() + { + var builder = CreateDocumentRequest.CreateBuilder(); + + var data = new TestClass { CustomNameProp = "test" }; + builder.WithData(data); + // Assert AddField was called with "custom" instead of "customNameProp" + } + + [Fact] + public void WithData_NullValue_IgnoredByDefault() + { + var builder = CreateDocumentRequest.CreateBuilder(); + + var data = new TestClass { StringProp = null }; + builder.WithData(data); + // Assert AddField was not called for StringProp + } + + [Fact] + public void WithData_NullValue_IncludedWhenIgnoreNullValuesFalse() + { + var builder = CreateDocumentRequest.CreateBuilder(); + + var data = new TestClass { StringProp = null }; + builder.WithData(data, options => options.IgnoreNullValues = false); + // Assert AddField was called with null value + } + + [Fact] + public void WithData_PropertyCacheReuse_CachesProperties() + { + var builder = CreateDocumentRequest.CreateBuilder(); + + var data1 = new TestClass(); + var data2 = new TestClass(); + builder.WithData(data1); + builder.WithData(data2); + // Assert properties were cached (you'll need to expose cache or use reflection to verify) + } + + [Fact] + public void ShouldIncludeProperty_NoAttributeNoFilter_ReturnsTrue() + { + var options = new WithDataOptions(); + var prop = typeof(TestClass).GetProperty(nameof(TestClass.StringProp))!; + Assert.True(options.ShouldIncludeProperty(prop)); + } + + [Fact] + public void ShouldIncludeProperty_JsonIgnoreAttribute_ReturnsFalse() + { + var options = new WithDataOptions(); + var prop = typeof(TestClass).GetProperty(nameof(TestClass.IgnoredProp))!; + Assert.False(options.ShouldIncludeProperty(prop)); + } + + [Fact] + public void ShouldIncludeProperty_CustomFilterTrue_ReturnsTrue() + { + var options = new WithDataOptions + { + PropertyFilter = _ => true + }; + var prop = typeof(TestClass).GetProperty(nameof(TestClass.StringProp))!; + Assert.True(options.ShouldIncludeProperty(prop)); + } + + [Fact] + public void ShouldIncludeProperty_CustomFilterFalse_ReturnsFalse() + { + var options = new WithDataOptions + { + PropertyFilter = _ => false + }; + var prop = typeof(TestClass).GetProperty(nameof(TestClass.StringProp))!; + Assert.False(options.ShouldIncludeProperty(prop)); + } } From 6a73ee8e6224551fffd62de44986757c99776d41 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Mon, 9 Dec 2024 00:05:58 +0000 Subject: [PATCH 282/303] Implemented CreateDocument (untested) --- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 7 ++++++- .../Clients/DatabasesClient.cs | 17 +++++++++++++++-- .../Clients/IDatabasesClient.cs | 7 ++++++- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs index d1f3833b..6dbdfcac 100644 --- a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs @@ -36,9 +36,22 @@ public async Task> ListDocuments(ListDocumentsRequ } } - [ExcludeFromCodeCoverage] /// - public Task> CreateDocument(CreateDocumentRequest request) => throw new NotImplementedException(); + public async Task> CreateDocument(CreateDocumentRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateDocument(GetCurrentSession(), request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs index e2e4baee..8bd83d51 100644 --- a/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs @@ -23,7 +23,12 @@ public interface IDatabasesClient /// The documents list Task> ListDocuments(ListDocumentsRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Create a new Document. Before using this route, you should create a new collection resource using either a server integration API or directly from your database console. + /// Appwrite Docs + /// + /// The request content + /// The document Task> CreateDocument(CreateDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index b0d92d1d..72953a1c 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -599,9 +599,22 @@ public async Task> ListDocuments(ListDocumentsRequ } } - [ExcludeFromCodeCoverage] /// - public Task> CreateDocument(CreateDocumentRequest request) => throw new NotImplementedException(); + public async Task> CreateDocument(CreateDocumentRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.CreateDocument(request.DatabaseId, request.CollectionId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index fc689c8a..e5d43285 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -290,7 +290,12 @@ public interface IDatabasesClient /// The documents list Task> ListDocuments(ListDocumentsRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Create a new Document. Before using this route, you should create a new collection resource using either or directly from your database console. + /// Appwrite Docs + /// + /// The request content + /// The document Task> CreateDocument(CreateDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] From 663ca32f1169455799f5274433c8e93721072c6a Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 16:39:11 +0000 Subject: [PATCH 283/303] Update App.cs --- src/PinguApps.Appwrite.Playground/App.cs | 34 +++++++++++------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 0bcff66c..ccc0cb9f 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Configuration; -using PinguApps.Appwrite.Shared.Requests.Databases; namespace PinguApps.Appwrite.Playground; internal class App @@ -17,28 +16,27 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new ListDocumentsRequest() - { - DatabaseId = "67541a2800221703e717", - CollectionId = "67541a37001514b81821" - }; + //var request = CreateDocumentRequest.CreateBuilder() + // .WithDatabaseId("") + // .WithCollectionId("") + // .AddField - var serverResponse = await _server.Databases.ListDocuments(request); + //var serverResponse = await _server.Databases.ListDocuments(request); - Console.WriteLine(serverResponse.Result.Match( - result => result.ToString(), - appwriteError => appwriteError.Message, - internalError => internalError.Message)); + //Console.WriteLine(serverResponse.Result.Match( + // result => result.ToString(), + // appwriteError => appwriteError.Message, + // internalError => internalError.Message)); - Console.WriteLine("###############################################################################"); + //Console.WriteLine("###############################################################################"); - _client.SetSession(_session); + //_client.SetSession(_session); - var clientResponse = await _client.Databases.ListDocuments(request); + //var clientResponse = await _client.Databases.ListDocuments(request); - Console.WriteLine(clientResponse.Result.Match( - result => result.ToString(), - appwriteError => appwriteError.Message, - internalError => internalError.Message)); + //Console.WriteLine(clientResponse.Result.Match( + // result => result.ToString(), + // appwriteError => appwriteError.Message, + // internalError => internalError.Message)); } } From 2c07a4f9f3c983ad7e8f25cf5ba1ce7afea1b30c Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 17:54:01 +0000 Subject: [PATCH 284/303] Completed manual testing of CreateDocument --- src/PinguApps.Appwrite.Playground/App.cs | 39 +++++++++++-------- .../ICreateDocumentRequestBuilder.cs | 12 +++++- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index ccc0cb9f..9cd1b4ad 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -1,4 +1,6 @@ using Microsoft.Extensions.Configuration; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Playground; internal class App @@ -16,27 +18,32 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - //var request = CreateDocumentRequest.CreateBuilder() - // .WithDatabaseId("") - // .WithCollectionId("") - // .AddField + var request = CreateDocumentRequest.CreateBuilder() + .WithDatabaseId("67541a2800221703e717") + .WithCollectionId("67541a37001514b81821") + .WithData(new + { + test = "TEST", + boolAttribute = false + }) + .Build(); - //var serverResponse = await _server.Databases.ListDocuments(request); + var serverResponse = await _server.Databases.CreateDocument(request); - //Console.WriteLine(serverResponse.Result.Match( - // result => result.ToString(), - // appwriteError => appwriteError.Message, - // internalError => internalError.Message)); + Console.WriteLine(serverResponse.Result.Match( + result => result.ToString(), + appwriteError => appwriteError.Message, + internalError => internalError.Message)); - //Console.WriteLine("###############################################################################"); + Console.WriteLine("###############################################################################"); - //_client.SetSession(_session); + request.DocumentId = IdUtils.GenerateUniqueId(); - //var clientResponse = await _client.Databases.ListDocuments(request); + var clientResponse = await _client.Databases.CreateDocument(request); - //Console.WriteLine(clientResponse.Result.Match( - // result => result.ToString(), - // appwriteError => appwriteError.Message, - // internalError => internalError.Message)); + Console.WriteLine(clientResponse.Result.Match( + result => result.ToString(), + appwriteError => appwriteError.Message, + internalError => internalError.Message)); } } diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs index bb3c171c..e0a73873 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/ICreateDocumentRequestBuilder.cs @@ -54,12 +54,22 @@ public interface ICreateDocumentRequestBuilder /// Options ICreateDocumentRequestBuilder WithData(T? data, Action? options = null) where T : class; + /// + /// Options for adding data to the document + /// public class WithDataOptions { + /// + /// Whether to ignore null values + /// public bool IgnoreNullValues { get; set; } = true; + + /// + /// A filter for properties + /// public Func? PropertyFilter { get; set; } - public bool ShouldIncludeProperty(PropertyInfo property) + internal bool ShouldIncludeProperty(PropertyInfo property) { if (property.GetCustomAttribute() != null) { From 33ab94ab28355d95f0064c314ae539f47c3f7018 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 17:55:56 +0000 Subject: [PATCH 285/303] Added extra xml docs to instruct user on how to create a create document request --- .../Requests/Databases/CreateDocumentRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs index afc131f1..2038f46d 100644 --- a/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs +++ b/src/PinguApps.Appwrite.Shared/Requests/Databases/CreateDocumentRequest.cs @@ -3,7 +3,7 @@ namespace PinguApps.Appwrite.Shared.Requests.Databases; /// -/// The request to create a document +/// The request to create a document. To instantiate, use /// public class CreateDocumentRequest : CreateDocumentRequest> { From 596a77e6f7a15d27c18f0b91546bf80d7e4bb6a4 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 18:09:37 +0000 Subject: [PATCH 286/303] Added tests for create document --- .../DatabasesClientTests.CreateDocument.cs | 78 +++++++++++++++++++ .../DatabasesClientTests.CreateDocument.cs | 78 +++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.CreateDocument.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDocument.cs diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.CreateDocument.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.CreateDocument.cs new file mode 100644 index 00000000..7fda4022 --- /dev/null +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.CreateDocument.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Client.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task CreateDocument_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = CreateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .AddField("AttributeName", "MyValue") + .Build(); + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) + .Respond(TestConstants.AppJson, TestConstants.DocumentResponse); + + // Act + var result = await _appwriteClient.Databases.CreateDocument(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateDocument_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = CreateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .AddField("AttributeName", "MyValue") + .Build(); + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateDocument(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateDocument_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = CreateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .AddField("AttributeName", "MyValue") + .Build(); + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateDocument(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDocument.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDocument.cs new file mode 100644 index 00000000..35cfa091 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDocument.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task CreateDocument_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = CreateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .AddField("AttributeName", "MyValue") + .Build(); + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) + .Respond(TestConstants.AppJson, TestConstants.DocumentResponse); + + // Act + var result = await _appwriteClient.Databases.CreateDocument(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task CreateDocument_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = CreateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .AddField("AttributeName", "MyValue") + .Build(); + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.CreateDocument(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task CreateDocument_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = CreateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .AddField("AttributeName", "MyValue") + .Build(); + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.CreateDocument(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From f301803073115e64cbc183525c688a8944c41b86 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 18:19:25 +0000 Subject: [PATCH 287/303] Updated tests that were missing WithJsonContent --- .../Databases/DatabasesClientTests.CreateBooleanAttribute.cs | 3 +++ .../Clients/Databases/DatabasesClientTests.CreateCollection.cs | 3 +++ .../Clients/Databases/DatabasesClientTests.CreateDatabase.cs | 3 +++ .../Databases/DatabasesClientTests.CreateDatetimeAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.CreateEmailAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.CreateEnumAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.CreateFloatAttribute.cs | 3 +++ .../Clients/Databases/DatabasesClientTests.CreateIndex.cs | 3 +++ .../Databases/DatabasesClientTests.CreateIntegerAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.CreateIpAttribute.cs | 3 +++ .../DatabasesClientTests.CreateRelationshipAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.CreateStringAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.CreateUrlAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.UpdateBooleanAttribute.cs | 3 +++ .../Clients/Databases/DatabasesClientTests.UpdateCollection.cs | 3 +++ .../Clients/Databases/DatabasesClientTests.UpdateDatabase.cs | 3 +++ .../Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.UpdateEmailAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.UpdateEnumAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.UpdateFloatAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.UpdateIntegerAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.UpdateIpAttribute.cs | 3 +++ .../DatabasesClientTests.UpdateRelationshipAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.UpdateStringAttribute.cs | 3 +++ .../Databases/DatabasesClientTests.UpdateUrlAttribute.cs | 3 +++ 25 files changed, 75 insertions(+) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateBooleanAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateBooleanAttribute.cs index db32ba61..01638421 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateBooleanAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateBooleanAttribute.cs @@ -20,6 +20,7 @@ public async Task CreateBooleanAttribute_ShouldReturnSuccess_WhenApiCallSucceeds _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/boolean") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeBooleanResponse); // Act @@ -42,6 +43,7 @@ public async Task CreateBooleanAttribute_ShouldHandleException_WhenApiCallFails( _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/boolean") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -65,6 +67,7 @@ public async Task CreateBooleanAttribute_ShouldReturnErrorResponse_WhenException _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/boolean") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateCollection.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateCollection.cs index 90789f44..5fcbcf3b 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateCollection.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateCollection.cs @@ -19,6 +19,7 @@ public async Task CreateCollection_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.CollectionResponse); // Act @@ -40,6 +41,7 @@ public async Task CreateCollection_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -62,6 +64,7 @@ public async Task CreateCollection_ShouldReturnErrorResponse_WhenExceptionOccurs _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatabase.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatabase.cs index 8fc8adbb..0b53cba2 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatabase.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatabase.cs @@ -17,6 +17,7 @@ public async Task CreateDatabase_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.DatabaseResponse); // Act @@ -37,6 +38,7 @@ public async Task CreateDatabase_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -58,6 +60,7 @@ public async Task CreateDatabase_ShouldReturnErrorResponse_WhenExceptionOccurs() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatetimeAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatetimeAttribute.cs index c015c652..95c3f667 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatetimeAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateDatetimeAttribute.cs @@ -20,6 +20,7 @@ public async Task CreateDatetimeAttribute_ShouldReturnSuccess_WhenApiCallSucceed _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/datetime") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeDatetimeResponse); // Act @@ -42,6 +43,7 @@ public async Task CreateDatetimeAttribute_ShouldHandleException_WhenApiCallFails _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/datetime") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -65,6 +67,7 @@ public async Task CreateDatetimeAttribute_ShouldReturnErrorResponse_WhenExceptio _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/datetime") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEmailAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEmailAttribute.cs index 882dd613..b7a8ec75 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEmailAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEmailAttribute.cs @@ -20,6 +20,7 @@ public async Task CreateEmailAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/email") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeEmailResponse); // Act @@ -42,6 +43,7 @@ public async Task CreateEmailAttribute_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/email") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -65,6 +67,7 @@ public async Task CreateEmailAttribute_ShouldReturnErrorResponse_WhenExceptionOc _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/email") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEnumAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEnumAttribute.cs index 49554880..d0e59d3e 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEnumAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateEnumAttribute.cs @@ -21,6 +21,7 @@ public async Task CreateEnumAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/enum") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeEnumResponse); // Act @@ -44,6 +45,7 @@ public async Task CreateEnumAttribute_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/enum") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -68,6 +70,7 @@ public async Task CreateEnumAttribute_ShouldReturnErrorResponse_WhenExceptionOcc _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/enum") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateFloatAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateFloatAttribute.cs index dc30c08c..e1a7a4d2 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateFloatAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateFloatAttribute.cs @@ -20,6 +20,7 @@ public async Task CreateFloatAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/float") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeFloatResponse); // Act @@ -42,6 +43,7 @@ public async Task CreateFloatAttribute_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/float") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -65,6 +67,7 @@ public async Task CreateFloatAttribute_ShouldReturnErrorResponse_WhenExceptionOc _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/float") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIndex.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIndex.cs index b6cf11e6..91a582fd 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIndex.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIndex.cs @@ -24,6 +24,7 @@ public async Task CreateIndex_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.IndexResponse); // Act @@ -49,6 +50,7 @@ public async Task CreateIndex_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -75,6 +77,7 @@ public async Task CreateIndex_ShouldReturnErrorResponse_WhenExceptionOccurs() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/indexes") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIntegerAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIntegerAttribute.cs index 634f723f..8aa904a6 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIntegerAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIntegerAttribute.cs @@ -20,6 +20,7 @@ public async Task CreateIntegerAttribute_ShouldReturnSuccess_WhenApiCallSucceeds _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/integer") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeIntegerResponse); // Act @@ -42,6 +43,7 @@ public async Task CreateIntegerAttribute_ShouldHandleException_WhenApiCallFails( _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/integer") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -65,6 +67,7 @@ public async Task CreateIntegerAttribute_ShouldReturnErrorResponse_WhenException _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/integer") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIpAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIpAttribute.cs index c0bd4966..d2b21fb9 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIpAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateIpAttribute.cs @@ -20,6 +20,7 @@ public async Task CreateIpAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/ip") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeIpResponse); // Act @@ -42,6 +43,7 @@ public async Task CreateIpAttribute_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/ip") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -65,6 +67,7 @@ public async Task CreateIpAttribute_ShouldReturnErrorResponse_WhenExceptionOccur _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/ip") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateRelationshipAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateRelationshipAttribute.cs index 6f95a3a0..9779c068 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateRelationshipAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateRelationshipAttribute.cs @@ -21,6 +21,7 @@ public async Task CreateRelationshipAttribute_ShouldReturnSuccess_WhenApiCallSuc _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/relationship") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeRelationshipResponse); // Act @@ -44,6 +45,7 @@ public async Task CreateRelationshipAttribute_ShouldHandleException_WhenApiCallF _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/relationship") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -68,6 +70,7 @@ public async Task CreateRelationshipAttribute_ShouldReturnErrorResponse_WhenExce _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/relationship") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateStringAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateStringAttribute.cs index fe5713b2..b456a3cf 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateStringAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateStringAttribute.cs @@ -21,6 +21,7 @@ public async Task CreateStringAttribute_ShouldReturnSuccess_WhenApiCallSucceeds( _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/string") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeStringResponse); // Act @@ -44,6 +45,7 @@ public async Task CreateStringAttribute_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/string") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -68,6 +70,7 @@ public async Task CreateStringAttribute_ShouldReturnErrorResponse_WhenExceptionO _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/string") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateUrlAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateUrlAttribute.cs index 1dab125b..14d2aa97 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateUrlAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.CreateUrlAttribute.cs @@ -20,6 +20,7 @@ public async Task CreateUrlAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/url") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeUrlResponse); // Act @@ -42,6 +43,7 @@ public async Task CreateUrlAttribute_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/url") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -65,6 +67,7 @@ public async Task CreateUrlAttribute_ShouldReturnErrorResponse_WhenExceptionOccu _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/url") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateBooleanAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateBooleanAttribute.cs index ce3d356d..b0ba2cba 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateBooleanAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateBooleanAttribute.cs @@ -20,6 +20,7 @@ public async Task UpdateBooleanAttribute_ShouldReturnSuccess_WhenApiCallSucceeds _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/boolean/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeBooleanResponse); // Act @@ -42,6 +43,7 @@ public async Task UpdateBooleanAttribute_ShouldHandleException_WhenApiCallFails( _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/boolean/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -65,6 +67,7 @@ public async Task UpdateBooleanAttribute_ShouldReturnErrorResponse_WhenException _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/boolean/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateCollection.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateCollection.cs index 93643e21..c5061c86 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateCollection.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateCollection.cs @@ -20,6 +20,7 @@ public async Task UpdateCollection_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Put, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.CollectionResponse); // Act @@ -42,6 +43,7 @@ public async Task UpdateCollection_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Put, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -65,6 +67,7 @@ public async Task UpdateCollection_ShouldReturnErrorResponse_WhenExceptionOccurs _mockHttp.Expect(HttpMethod.Put, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatabase.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatabase.cs index c1ea6931..79a5b6d8 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatabase.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatabase.cs @@ -19,6 +19,7 @@ public async Task UpdateDatabase_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Put, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.DatabaseResponse); // Act @@ -40,6 +41,7 @@ public async Task UpdateDatabase_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Put, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -62,6 +64,7 @@ public async Task UpdateDatabase_ShouldReturnErrorResponse_WhenExceptionOccurs() _mockHttp.Expect(HttpMethod.Put, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs index 179d7b73..b8f4c021 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDatetimeAttribute.cs @@ -21,6 +21,7 @@ public async Task UpdateDatetimeAttribute_ShouldReturnSuccess_WhenApiCallSucceed _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/datetime/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeDatetimeResponse); // Act @@ -44,6 +45,7 @@ public async Task UpdateDatetimeAttribute_ShouldHandleException_WhenApiCallFails _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/datetime/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -68,6 +70,7 @@ public async Task UpdateDatetimeAttribute_ShouldReturnErrorResponse_WhenExceptio _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/datetime/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEmailAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEmailAttribute.cs index 13b7d976..1029b6fc 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEmailAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEmailAttribute.cs @@ -21,6 +21,7 @@ public async Task UpdateEmailAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/email/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeEmailResponse); // Act @@ -44,6 +45,7 @@ public async Task UpdateEmailAttribute_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/email/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -68,6 +70,7 @@ public async Task UpdateEmailAttribute_ShouldReturnErrorResponse_WhenExceptionOc _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/email/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEnumAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEnumAttribute.cs index e2c9afaa..50a7207a 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEnumAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateEnumAttribute.cs @@ -21,6 +21,7 @@ public async Task UpdateEnumAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/enum/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeEnumResponse); // Act @@ -44,6 +45,7 @@ public async Task UpdateEnumAttribute_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/enum/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -68,6 +70,7 @@ public async Task UpdateEnumAttribute_ShouldReturnErrorResponse_WhenExceptionOcc _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/enum/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateFloatAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateFloatAttribute.cs index 5add0254..16715f46 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateFloatAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateFloatAttribute.cs @@ -21,6 +21,7 @@ public async Task UpdateFloatAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/float/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeFloatResponse); // Act @@ -44,6 +45,7 @@ public async Task UpdateFloatAttribute_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/float/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -68,6 +70,7 @@ public async Task UpdateFloatAttribute_ShouldReturnErrorResponse_WhenExceptionOc _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/float/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIntegerAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIntegerAttribute.cs index 438e9658..c8e3f8ef 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIntegerAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIntegerAttribute.cs @@ -21,6 +21,7 @@ public async Task UpdateIntegerAttribute_ShouldReturnSuccess_WhenApiCallSucceeds _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/integer/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeIntegerResponse); // Act @@ -44,6 +45,7 @@ public async Task UpdateIntegerAttribute_ShouldHandleException_WhenApiCallFails( _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/integer/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -68,6 +70,7 @@ public async Task UpdateIntegerAttribute_ShouldReturnErrorResponse_WhenException _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/integer/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIpAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIpAttribute.cs index 8e41f535..36672906 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIpAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateIpAttribute.cs @@ -21,6 +21,7 @@ public async Task UpdateIpAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/ip/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeIpResponse); // Act @@ -44,6 +45,7 @@ public async Task UpdateIpAttribute_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/ip/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -68,6 +70,7 @@ public async Task UpdateIpAttribute_ShouldReturnErrorResponse_WhenExceptionOccur _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/ip/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateRelationshipAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateRelationshipAttribute.cs index 30a2f89c..1f9becf0 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateRelationshipAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateRelationshipAttribute.cs @@ -21,6 +21,7 @@ public async Task UpdateRelationshipAttribute_ShouldReturnSuccess_WhenApiCallSuc _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}/relationship") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeRelationshipResponse); // Act @@ -44,6 +45,7 @@ public async Task UpdateRelationshipAttribute_ShouldHandleException_WhenApiCallF _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}/relationship") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -68,6 +70,7 @@ public async Task UpdateRelationshipAttribute_ShouldReturnErrorResponse_WhenExce _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/{request.Key}/relationship") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateStringAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateStringAttribute.cs index 806f35fb..bedaeedc 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateStringAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateStringAttribute.cs @@ -21,6 +21,7 @@ public async Task UpdateStringAttribute_ShouldReturnSuccess_WhenApiCallSucceeds( _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/string/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeStringResponse); // Act @@ -44,6 +45,7 @@ public async Task UpdateStringAttribute_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/string/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -68,6 +70,7 @@ public async Task UpdateStringAttribute_ShouldReturnErrorResponse_WhenExceptionO _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/string/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateUrlAttribute.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateUrlAttribute.cs index 40fc3de3..a9738398 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateUrlAttribute.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateUrlAttribute.cs @@ -21,6 +21,7 @@ public async Task UpdateUrlAttribute_ShouldReturnSuccess_WhenApiCallSucceeds() _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/url/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(TestConstants.AppJson, TestConstants.AttributeUrlResponse); // Act @@ -44,6 +45,7 @@ public async Task UpdateUrlAttribute_ShouldHandleException_WhenApiCallFails() _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/url/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); // Act @@ -68,6 +70,7 @@ public async Task UpdateUrlAttribute_ShouldReturnErrorResponse_WhenExceptionOccu _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes/url/{request.Key}") .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) .Throw(new HttpRequestException("An error occurred")); // Act From 3735cd4e764dc409f0ccfe8b483116fcd57b41c3 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 18:21:08 +0000 Subject: [PATCH 288/303] Implemented delete document --- .../Clients/DatabasesClient.cs | 17 +++++++++++-- .../Clients/IDatabasesClient.cs | 7 +++++- src/PinguApps.Appwrite.Playground/App.cs | 24 ++++++++----------- .../Clients/DatabasesClient.cs | 17 +++++++++++-- .../Clients/IDatabasesClient.cs | 7 +++++- 5 files changed, 52 insertions(+), 20 deletions(-) diff --git a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs index 6dbdfcac..c9de8048 100644 --- a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs @@ -53,9 +53,22 @@ public async Task> CreateDocument(CreateDocumentRequest } } - [ExcludeFromCodeCoverage] /// - public Task DeleteDocument(DeleteDocumentRequest request) => throw new NotImplementedException(); + public async Task DeleteDocument(DeleteDocumentRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.DeleteDocument(GetCurrentSession(), request.DatabaseId, request.CollectionId, request.DocumentId); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs index 8bd83d51..c6217d38 100644 --- a/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs @@ -31,7 +31,12 @@ public interface IDatabasesClient /// The document Task> CreateDocument(CreateDocumentRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Delete a document by its unique ID. + /// Appwrite Docs + /// + /// The request content + /// 204 Success Code Task DeleteDocument(DeleteDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 9cd1b4ad..e6ee200e 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.Configuration; using PinguApps.Appwrite.Shared.Requests.Databases; -using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Playground; internal class App @@ -18,17 +17,14 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = CreateDocumentRequest.CreateBuilder() - .WithDatabaseId("67541a2800221703e717") - .WithCollectionId("67541a37001514b81821") - .WithData(new - { - test = "TEST", - boolAttribute = false - }) - .Build(); - - var serverResponse = await _server.Databases.CreateDocument(request); + var request = new DeleteDocumentRequest() + { + DatabaseId = "67541a2800221703e717", + CollectionId = "67541a37001514b81821", + DocumentId = "67587ff00029d437c8f3" + }; + + var serverResponse = await _server.Databases.DeleteDocument(request); Console.WriteLine(serverResponse.Result.Match( result => result.ToString(), @@ -37,9 +33,9 @@ public async Task Run(string[] args) Console.WriteLine("###############################################################################"); - request.DocumentId = IdUtils.GenerateUniqueId(); + request.DocumentId = "67587ff000057509d0ec"; - var clientResponse = await _client.Databases.CreateDocument(request); + var clientResponse = await _client.Databases.DeleteDocument(request); Console.WriteLine(clientResponse.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 72953a1c..0cb5636d 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -616,9 +616,22 @@ public async Task> CreateDocument(CreateDocumentRequest } } - [ExcludeFromCodeCoverage] /// - public Task DeleteDocument(DeleteDocumentRequest request) => throw new NotImplementedException(); + public async Task DeleteDocument(DeleteDocumentRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.DeleteDocument(request.DatabaseId, request.CollectionId, request.DocumentId); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index e5d43285..1c4f474e 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -298,7 +298,12 @@ public interface IDatabasesClient /// The document Task> CreateDocument(CreateDocumentRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Delete a document by its unique ID. + /// Appwrite Docs + /// + /// The request content + /// 204 Success Code Task DeleteDocument(DeleteDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] From 766d1173f703011a164c6cf0a86adb9285ab172b Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 18:24:44 +0000 Subject: [PATCH 289/303] added tests for delete document --- .../DatabasesClientTests.DeleteDocument.cs | 78 +++++++++++++++++++ .../DatabasesClientTests.DeleteDocument.cs | 78 +++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.DeleteDocument.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteDocument.cs diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.DeleteDocument.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.DeleteDocument.cs new file mode 100644 index 00000000..e713626b --- /dev/null +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.DeleteDocument.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Client.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task DeleteDocument_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new DeleteDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.NoContent); + + // Act + var result = await _appwriteClient.Databases.DeleteDocument(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task DeleteDocument_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new DeleteDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.DeleteDocument(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task DeleteDocument_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new DeleteDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.DeleteDocument(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteDocument.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteDocument.cs new file mode 100644 index 00000000..916b3cf9 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.DeleteDocument.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task DeleteDocument_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new DeleteDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.NoContent); + + // Act + var result = await _appwriteClient.Databases.DeleteDocument(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task DeleteDocument_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new DeleteDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.DeleteDocument(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task DeleteDocument_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new DeleteDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.DeleteDocument(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 599eb45f3e5bf464b2e03324cb7b84e24b97e87e Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 18:47:46 +0000 Subject: [PATCH 290/303] Implemented GetDocument and made chances to support query limiting the document data --- .../Clients/DatabasesClient.cs | 17 ++++++++++++++-- .../Clients/IDatabasesClient.cs | 7 ++++++- src/PinguApps.Appwrite.Playground/App.cs | 11 +++++----- .../Clients/DatabasesClient.cs | 17 ++++++++++++++-- .../Clients/IDatabasesClient.cs | 9 +++++++-- .../Converters/DocumentConverter.cs | 20 +++++++++---------- .../Converters/DocumentGenericConverter.cs | 2 +- .../Responses/Document.cs | 4 ++-- .../Responses/DocumentBase.cs | 4 ++-- .../Responses/DocumentGeneric.cs | 4 ++-- 10 files changed, 66 insertions(+), 29 deletions(-) diff --git a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs index c9de8048..9072af30 100644 --- a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs @@ -70,9 +70,22 @@ public async Task DeleteDocument(DeleteDocumentRequest request) } } - [ExcludeFromCodeCoverage] /// - public Task> GetDocument(GetDocumentRequest request) => throw new NotImplementedException(); + public async Task> GetDocument(GetDocumentRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.GetDocument(GetCurrentSession(), request.DatabaseId, request.CollectionId, request.DocumentId, RequestUtils.GetQueryStrings(request.Queries)); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs index c6217d38..890bf8d6 100644 --- a/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs @@ -39,7 +39,12 @@ public interface IDatabasesClient /// 204 Success Code Task DeleteDocument(DeleteDocumentRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Get a document by its unique ID. This endpoint response returns a JSON object with the document data. You can return select columns by passing in a Select query. + /// Appwrite Docs + /// + /// The request content + /// The document Task> GetDocument(GetDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index e6ee200e..4473909a 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Configuration; using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Playground; internal class App @@ -17,14 +18,14 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new DeleteDocumentRequest() + var request = new GetDocumentRequest() { DatabaseId = "67541a2800221703e717", CollectionId = "67541a37001514b81821", - DocumentId = "67587ff00029d437c8f3" + DocumentId = "67541af9000055e59e59" }; - var serverResponse = await _server.Databases.DeleteDocument(request); + var serverResponse = await _server.Databases.GetDocument(request); Console.WriteLine(serverResponse.Result.Match( result => result.ToString(), @@ -33,9 +34,9 @@ public async Task Run(string[] args) Console.WriteLine("###############################################################################"); - request.DocumentId = "67587ff000057509d0ec"; + request.Queries = [Query.Select(["test"])]; - var clientResponse = await _client.Databases.DeleteDocument(request); + var clientResponse = await _client.Databases.GetDocument(request); Console.WriteLine(clientResponse.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index 0cb5636d..bcf9acfe 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -633,9 +633,22 @@ public async Task DeleteDocument(DeleteDocumentRequest request) } } - [ExcludeFromCodeCoverage] /// - public Task> GetDocument(GetDocumentRequest request) => throw new NotImplementedException(); + public async Task> GetDocument(GetDocumentRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.GetDocument(request.DatabaseId, request.CollectionId, request.DocumentId, RequestUtils.GetQueryStrings(request.Queries)); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } [ExcludeFromCodeCoverage] /// diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index 1c4f474e..ff687c2b 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -303,10 +303,15 @@ public interface IDatabasesClient /// Appwrite Docs /// /// The request content - /// 204 Success Code + /// 204 success code Task DeleteDocument(DeleteDocumentRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Get a document by its unique ID. This endpoint response returns a JSON object with the document data. You can return select columns by passing in a Select query. + /// Appwrite Docs + /// + /// The request content + /// The document Task> GetDocument(GetDocumentRequest request); [Obsolete("Endpoint not yet implemented.")] diff --git a/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs index 0051db28..464b9e75 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs @@ -79,22 +79,22 @@ public class DocumentConverter : JsonConverter throw new JsonException("Unable to find a value for DatabaseId"); } - if (createdAt is null) - { - throw new JsonException("Unable to find a value for CreatedAt"); - } + //if (createdAt is null) + //{ + // throw new JsonException("Unable to find a value for CreatedAt"); + //} - if (updatedAt is null) - { - throw new JsonException("Unable to find a value for UpdatedAt"); - } + //if (updatedAt is null) + //{ + // throw new JsonException("Unable to find a value for UpdatedAt"); + //} if (permissions is null) { throw new JsonException("Unable to find a value for Permissions"); } - return new Document(id, collectionId, databaseId, createdAt.Value, updatedAt.Value, permissions, data); + return new Document(id, collectionId, databaseId, createdAt, updatedAt, permissions, data); } internal object? ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) @@ -186,7 +186,7 @@ public override void Write(Utf8JsonWriter writer, Document value, JsonSerializer writer.WriteString("$databaseId", value.DatabaseId); // Use MultiFormatDateTimeConverter for DateTime properties - var dateTimeConverter = new MultiFormatDateTimeConverter(); + var dateTimeConverter = new NullableDateTimeConverter(); writer.WritePropertyName("$createdAt"); dateTimeConverter.Write(writer, value.CreatedAt, options); diff --git a/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs index b11a585d..ade68935 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs @@ -193,7 +193,7 @@ public override void Write(Utf8JsonWriter writer, Document value, JsonSer writer.WriteString("$databaseId", value.DatabaseId); // Use MultiFormatDateTimeConverter for DateTime properties - var dateTimeConverter = new MultiFormatDateTimeConverter(); + var dateTimeConverter = new NullableDateTimeConverter(); writer.WritePropertyName("$createdAt"); dateTimeConverter.Write(writer, value.CreatedAt, options); diff --git a/src/PinguApps.Appwrite.Shared/Responses/Document.cs b/src/PinguApps.Appwrite.Shared/Responses/Document.cs index 0723c37b..ad18c6c5 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/Document.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/Document.cs @@ -22,8 +22,8 @@ public record Document( string Id, string CollectionId, string DatabaseId, - DateTime CreatedAt, - DateTime UpdatedAt, + DateTime? CreatedAt, + DateTime? UpdatedAt, IReadOnlyList Permissions, [property: JsonExtensionData] Dictionary Data ) : DocumentBase(Id, CollectionId, DatabaseId, CreatedAt, UpdatedAt, Permissions) diff --git a/src/PinguApps.Appwrite.Shared/Responses/DocumentBase.cs b/src/PinguApps.Appwrite.Shared/Responses/DocumentBase.cs index 15ea69c7..d48a5395 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/DocumentBase.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/DocumentBase.cs @@ -20,7 +20,7 @@ public abstract record DocumentBase( [property: JsonPropertyName("$id")] string Id, [property: JsonPropertyName("$collectionId")] string CollectionId, [property: JsonPropertyName("$databaseId")] string DatabaseId, - [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime CreatedAt, - [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(MultiFormatDateTimeConverter))] DateTime UpdatedAt, + [property: JsonPropertyName("$createdAt"), JsonConverter(typeof(NullableDateTimeConverter))] DateTime? CreatedAt, + [property: JsonPropertyName("$updatedAt"), JsonConverter(typeof(NullableDateTimeConverter))] DateTime? UpdatedAt, [property: JsonPropertyName("$permissions"), JsonConverter(typeof(PermissionReadOnlyListConverter))] IReadOnlyList Permissions ); diff --git a/src/PinguApps.Appwrite.Shared/Responses/DocumentGeneric.cs b/src/PinguApps.Appwrite.Shared/Responses/DocumentGeneric.cs index e5465118..09dbe6be 100644 --- a/src/PinguApps.Appwrite.Shared/Responses/DocumentGeneric.cs +++ b/src/PinguApps.Appwrite.Shared/Responses/DocumentGeneric.cs @@ -21,8 +21,8 @@ public record Document( string Id, string CollectionId, string DatabaseId, - DateTime CreatedAt, - DateTime UpdatedAt, + DateTime? CreatedAt, + DateTime? UpdatedAt, IReadOnlyList Permissions, TData Data ) : DocumentBase(Id, CollectionId, DatabaseId, CreatedAt, UpdatedAt, Permissions) From 7c5b7614d55050b9fc54ca3159a770f6abb6d577 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 18:50:36 +0000 Subject: [PATCH 291/303] Fixed failing tests for Document --- .../Converters/DocumentConverter.cs | 10 ------- .../Converters/DocumentConverterTests.cs | 30 ------------------- .../Responses/DocumentTests.cs | 4 +-- .../Responses/DocumentsListTests.cs | 4 +-- 4 files changed, 4 insertions(+), 44 deletions(-) diff --git a/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs index 464b9e75..f7e40cac 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/DocumentConverter.cs @@ -79,16 +79,6 @@ public class DocumentConverter : JsonConverter throw new JsonException("Unable to find a value for DatabaseId"); } - //if (createdAt is null) - //{ - // throw new JsonException("Unable to find a value for CreatedAt"); - //} - - //if (updatedAt is null) - //{ - // throw new JsonException("Unable to find a value for UpdatedAt"); - //} - if (permissions is null) { throw new JsonException("Unable to find a value for Permissions"); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs index 676f37f4..c8cbdc18 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentConverterTests.cs @@ -243,36 +243,6 @@ public void Read_MissingDatabaseId_ThrowsJsonException() Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); } - [Fact] - public void Read_MissingCreatedAt_ThrowsJsonException() - { - var json = @" - { - ""$id"": ""1"", - ""$collectionId"": ""col1"", - ""$databaseId"": ""db1"", - ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read(\""any\"")""] - }"; - - Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); - } - - [Fact] - public void Read_MissingUpdatedAt_ThrowsJsonException() - { - var json = @" - { - ""$id"": ""1"", - ""$collectionId"": ""col1"", - ""$databaseId"": ""db1"", - ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read(\""any\"")""] - }"; - - Assert.Throws(() => JsonSerializer.Deserialize(json, _options)); - } - [Fact] public void Read_MissingPermissions_ThrowsJsonException() { diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs index 2ccae015..555f5bb7 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentTests.cs @@ -14,8 +14,8 @@ public void Document_ShouldBeDeserialized_FromJson() Assert.Equal("5e5ea5c16897e", document.Id); Assert.Equal("5e5ea5c15117e", document.CollectionId); Assert.Equal("5e5ea5c15117e", document.DatabaseId); - Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.CreatedAt.ToUniversalTime()); - Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.UpdatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.CreatedAt?.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.UpdatedAt?.ToUniversalTime()); Assert.Single(document.Permissions); Assert.Equal(PermissionType.Read, document.Permissions[0].PermissionType); Assert.Equal(RoleType.Any, document.Permissions[0].RoleType); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs index b8f38dbc..a9e5e297 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Responses/DocumentsListTests.cs @@ -42,8 +42,8 @@ public void CanBeDeserialized_FromJson() Assert.Equal("5e5ea5c16897e", document.Id); Assert.Equal("5e5ea5c15117e", document.CollectionId); Assert.Equal("5e5ea5c15117e", document.DatabaseId); - Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.CreatedAt.ToUniversalTime()); - Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.UpdatedAt.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.CreatedAt?.ToUniversalTime()); + Assert.Equal(DateTime.Parse("2020-10-15T06:38:00.000+00:00").ToUniversalTime(), document.UpdatedAt?.ToUniversalTime()); Assert.Single(document.Permissions); Assert.Equal(PermissionType.Read, document.Permissions[0].PermissionType); Assert.Equal(RoleType.Any, document.Permissions[0].RoleType); From dc6d9cd4dab6a9f7c41f501f4b8b85f5acc7bd99 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 18:59:09 +0000 Subject: [PATCH 292/303] added tests for get document --- .../DatabasesClientTests.GetDocument.cs | 103 ++++++++++++++++++ .../DatabasesClientTests.GetDocument.cs | 103 ++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.GetDocument.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetDocument.cs diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.GetDocument.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.GetDocument.cs new file mode 100644 index 00000000..131e41eb --- /dev/null +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.GetDocument.cs @@ -0,0 +1,103 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Client.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task GetDocument_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new GetDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.DocumentResponse); + + // Act + var result = await _appwriteClient.Databases.GetDocument(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task GetDocument_ShouldProvideQueryParams_WhenQueriesProvided() + { + // Arrange + var query = Query.Select(["col1", "col2"]); + var request = new GetDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + Queries = [query] + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .WithQueryString($"queries[]={query.GetQueryString()}") + .Respond(TestConstants.AppJson, TestConstants.DocumentResponse); + + // Act + var result = await _appwriteClient.Databases.GetDocument(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task GetDocument_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new GetDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.GetDocument(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task GetDocument_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new GetDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.GetDocument(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetDocument.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetDocument.cs new file mode 100644 index 00000000..4a030cf8 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.GetDocument.cs @@ -0,0 +1,103 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task GetDocument_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = new GetDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.DocumentResponse); + + // Act + var result = await _appwriteClient.Databases.GetDocument(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task GetDocument_ShouldProvideQueryParams_WhenQueriesProvided() + { + // Arrange + var query = Query.Select(["col1", "col2"]); + var request = new GetDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + Queries = [query] + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .WithQueryString($"queries[]={query.GetQueryString()}") + .Respond(TestConstants.AppJson, TestConstants.DocumentResponse); + + // Act + var result = await _appwriteClient.Databases.GetDocument(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task GetDocument_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = new GetDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.GetDocument(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task GetDocument_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = new GetDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.GetDocument(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From 0041e2b4b0de6144078f3e763864d7adac160d6b Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 19:00:51 +0000 Subject: [PATCH 293/303] Removed unused variable from some tests --- .../Databases/DatabasesClientTests.ListAttributes.cs | 5 ----- .../Databases/DatabasesClientTests.ListCollections.cs | 6 ------ .../Clients/Databases/DatabasesClientTests.ListDatabases.cs | 6 ------ 3 files changed, 17 deletions(-) diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListAttributes.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListAttributes.cs index 23ad8386..fa60a459 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListAttributes.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListAttributes.cs @@ -40,11 +40,6 @@ public async Task ListAttributes_ShouldProvideQueryParams_WhenQueriesProvided() Queries = new List { query } }; - var expectedQueryParams = new Dictionary - { - { "queries[]", query.GetQueryString() } - }; - _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/attributes") .ExpectedHeaders() .WithQueryString($"queries[]={query.GetQueryString()}") diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListCollections.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListCollections.cs index 1b51cc02..27410f0d 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListCollections.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListCollections.cs @@ -40,12 +40,6 @@ public async Task ListCollections_ShouldProvideQueryParams_WhenQueriesAndSearchP Search = search }; - var expectedQueryParams = new Dictionary - { - { "queries[]", query.GetQueryString() }, - { "search", search } - }; - _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections") .ExpectedHeaders() .WithQueryString($"queries[]={query.GetQueryString()}&search={search}") diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListDatabases.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListDatabases.cs index 390a9942..67bcf65a 100644 --- a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListDatabases.cs +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.ListDatabases.cs @@ -36,12 +36,6 @@ public async Task ListDatabases_ShouldProvideQueryParams_WhenQueriesAndSearchPro Search = search }; - var expectedQueryParams = new Dictionary - { - { "queries[]", query.GetQueryString() }, - { "search", search } - }; - _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases") .ExpectedHeaders() .WithQueryString($"queries[]={query.GetQueryString()}&search={search}") From 8a282db3ce3e880d88697af9342a91d10215afdc Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 19:06:18 +0000 Subject: [PATCH 294/303] Implemented Update Document --- .../Clients/DatabasesClient.cs | 18 ++++++++++++++--- .../Clients/IDatabasesClient.cs | 10 +++++++--- src/PinguApps.Appwrite.Playground/App.cs | 20 +++++++++---------- .../Clients/DatabasesClient.cs | 18 ++++++++++++++--- .../Clients/IDatabasesClient.cs | 10 +++++++--- 5 files changed, 54 insertions(+), 22 deletions(-) diff --git a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs index 9072af30..ba81c4df 100644 --- a/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/DatabasesClient.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using PinguApps.Appwrite.Client.Internals; using PinguApps.Appwrite.Client.Utils; @@ -87,7 +86,20 @@ public async Task> GetDocument(GetDocumentRequest reque } } - [ExcludeFromCodeCoverage] /// - public Task> UpdateDocument(UpdateDocumentRequest request) => throw new NotImplementedException(); + public async Task> UpdateDocument(UpdateDocumentRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateDocument(GetCurrentSession(), request.DatabaseId, request.CollectionId, request.DocumentId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } } diff --git a/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs index 890bf8d6..bf5797fc 100644 --- a/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Client/Clients/IDatabasesClient.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using PinguApps.Appwrite.Shared; using PinguApps.Appwrite.Shared.Requests.Databases; using PinguApps.Appwrite.Shared.Responses; @@ -47,6 +46,11 @@ public interface IDatabasesClient /// The document Task> GetDocument(GetDocumentRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Update a document by its unique ID. Using the patch method you can pass only specific fields that will get updated. + /// Appwrite Docs + /// + /// The request content + /// The document Task> UpdateDocument(UpdateDocumentRequest request); } diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index 4473909a..ff32aba6 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.Configuration; using PinguApps.Appwrite.Shared.Requests.Databases; -using PinguApps.Appwrite.Shared.Utils; namespace PinguApps.Appwrite.Playground; internal class App @@ -18,14 +17,14 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, public async Task Run(string[] args) { - var request = new GetDocumentRequest() - { - DatabaseId = "67541a2800221703e717", - CollectionId = "67541a37001514b81821", - DocumentId = "67541af9000055e59e59" - }; + var request = UpdateDocumentRequest.CreateBuilder() + .WithDatabaseId("67541a2800221703e717") + .WithCollectionId("67541a37001514b81821") + .WithDocumentId("67541af9000055e59e59") + .AddField("test", "Server Update") + .Build(); - var serverResponse = await _server.Databases.GetDocument(request); + var serverResponse = await _server.Databases.UpdateDocument(request); Console.WriteLine(serverResponse.Result.Match( result => result.ToString(), @@ -34,9 +33,10 @@ public async Task Run(string[] args) Console.WriteLine("###############################################################################"); - request.Queries = [Query.Select(["test"])]; + Console.ReadKey(); + request.Data["test"] = "Client Update"; - var clientResponse = await _client.Databases.GetDocument(request); + var clientResponse = await _client.Databases.UpdateDocument(request); Console.WriteLine(clientResponse.Result.Match( result => result.ToString(), diff --git a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs index bcf9acfe..1a47a2ca 100644 --- a/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/DatabasesClient.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using PinguApps.Appwrite.Server.Internals; using PinguApps.Appwrite.Server.Utils; @@ -650,9 +649,22 @@ public async Task> GetDocument(GetDocumentRequest reque } } - [ExcludeFromCodeCoverage] /// - public Task> UpdateDocument(UpdateDocumentRequest request) => throw new NotImplementedException(); + public async Task> UpdateDocument(UpdateDocumentRequest request) + { + try + { + request.Validate(true); + + var result = await _databasesApi.UpdateDocument(request.DatabaseId, request.CollectionId, request.DocumentId, request); + + return result.GetApiResponse(); + } + catch (Exception e) + { + return e.GetExceptionResponse(); + } + } /// public async Task> ListIndexes(ListIndexesRequest request) diff --git a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs index ff687c2b..9cc432c9 100644 --- a/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs +++ b/src/PinguApps.Appwrite.Server/Clients/IDatabasesClient.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using PinguApps.Appwrite.Shared; using PinguApps.Appwrite.Shared.Enums; using PinguApps.Appwrite.Shared.Requests.Databases; @@ -314,7 +313,12 @@ public interface IDatabasesClient /// The document Task> GetDocument(GetDocumentRequest request); - [Obsolete("Endpoint not yet implemented.")] + /// + /// Update a document by its unique ID. Using the patch method you can pass only specific fields that will get updated. + /// Appwrite Docs + /// + /// The request content + /// The document Task> UpdateDocument(UpdateDocumentRequest request); /// From 0c869087b58ae8e26572cee4401d8451f6450cf2 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 19:15:16 +0000 Subject: [PATCH 295/303] Testing the before / after builder pattern for update document --- src/PinguApps.Appwrite.Playground/App.cs | 31 +++++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/PinguApps.Appwrite.Playground/App.cs b/src/PinguApps.Appwrite.Playground/App.cs index ff32aba6..6bcb8df0 100644 --- a/src/PinguApps.Appwrite.Playground/App.cs +++ b/src/PinguApps.Appwrite.Playground/App.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Configuration; +using System.Text.Json.Serialization; +using Microsoft.Extensions.Configuration; using PinguApps.Appwrite.Shared.Requests.Databases; namespace PinguApps.Appwrite.Playground; @@ -15,13 +16,25 @@ public App(Client.IAppwriteClient client, Server.Clients.IAppwriteClient server, _session = config.GetValue("Session"); } + private class Rec + { + [JsonPropertyName("test")] + public string Test { get; set; } = string.Empty; + + [JsonPropertyName("boolAttribute")] + public bool BoolAttribute { get; set; } + } + public async Task Run(string[] args) { + var before = new Rec { Test = "test", BoolAttribute = false }; + var after = new Rec { Test = "test", BoolAttribute = true }; + var request = UpdateDocumentRequest.CreateBuilder() .WithDatabaseId("67541a2800221703e717") .WithCollectionId("67541a37001514b81821") .WithDocumentId("67541af9000055e59e59") - .AddField("test", "Server Update") + .WithChanges(before, after) .Build(); var serverResponse = await _server.Databases.UpdateDocument(request); @@ -33,14 +46,14 @@ public async Task Run(string[] args) Console.WriteLine("###############################################################################"); - Console.ReadKey(); - request.Data["test"] = "Client Update"; + //Console.ReadKey(); + //request.Data["test"] = "Client Update"; - var clientResponse = await _client.Databases.UpdateDocument(request); + //var clientResponse = await _client.Databases.UpdateDocument(request); - Console.WriteLine(clientResponse.Result.Match( - result => result.ToString(), - appwriteError => appwriteError.Message, - internalError => internalError.Message)); + //Console.WriteLine(clientResponse.Result.Match( + // result => result.ToString(), + // appwriteError => appwriteError.Message, + // internalError => internalError.Message)); } } From 0f600e8181bacfbfab40f98118475bd2ad760822 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 19:20:15 +0000 Subject: [PATCH 296/303] added tests for update document --- .../DatabasesClientTests.UpdateDocument.cs | 78 +++++++++++++++++++ .../DatabasesClientTests.UpdateDocument.cs | 78 +++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.UpdateDocument.cs create mode 100644 tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDocument.cs diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.UpdateDocument.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.UpdateDocument.cs new file mode 100644 index 00000000..7cd92fac --- /dev/null +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.UpdateDocument.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Client.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task UpdateDocument_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = UpdateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .AddField("test", "test") + .Build(); + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.DocumentResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateDocument(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateDocument_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = UpdateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .AddField("test", "test") + .Build(); + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateDocument(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateDocument_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = UpdateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .AddField("test", "test") + .Build(); + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateDocument(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} diff --git a/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDocument.cs b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDocument.cs new file mode 100644 index 00000000..7eaf2f65 --- /dev/null +++ b/tests/PinguApps.Appwrite.Server.Tests/Clients/Databases/DatabasesClientTests.UpdateDocument.cs @@ -0,0 +1,78 @@ +using System.Net; +using PinguApps.Appwrite.Shared.Requests.Databases; +using PinguApps.Appwrite.Shared.Tests; +using PinguApps.Appwrite.Shared.Utils; +using RichardSzalay.MockHttp; + +namespace PinguApps.Appwrite.Server.Tests.Clients.Databases; +public partial class DatabasesClientTests +{ + [Fact] + public async Task UpdateDocument_ShouldReturnSuccess_WhenApiCallSucceeds() + { + // Arrange + var request = UpdateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .AddField("test", "test") + .Build(); + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.DocumentResponse); + + // Act + var result = await _appwriteClient.Databases.UpdateDocument(request); + + // Assert + Assert.True(result.Success); + } + + [Fact] + public async Task UpdateDocument_ShouldHandleException_WhenApiCallFails() + { + // Arrange + var request = UpdateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .AddField("test", "test") + .Build(); + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.BadRequest, TestConstants.AppJson, TestConstants.AppwriteError); + + // Act + var result = await _appwriteClient.Databases.UpdateDocument(request); + + // Assert + Assert.True(result.IsError); + Assert.True(result.IsAppwriteError); + } + + [Fact] + public async Task UpdateDocument_ShouldReturnErrorResponse_WhenExceptionOccurs() + { + // Arrange + var request = UpdateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .AddField("test", "test") + .Build(); + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Throw(new HttpRequestException("An error occurred")); + + // Act + var result = await _appwriteClient.Databases.UpdateDocument(request); + + // Assert + Assert.False(result.Success); + Assert.True(result.IsInternalError); + Assert.Equal("An error occurred", result.Result.AsT2.Message); + } +} From c3202a74ad8e294467e75171043eeb8450bc3a4b Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 22:05:56 +0000 Subject: [PATCH 297/303] Added tests to each documents client method, to check that when needed, session is added --- .../DatabasesClientTests.CreateDocument.cs | 24 +++++++++++++++++++ .../DatabasesClientTests.DeleteDocument.cs | 24 +++++++++++++++++++ .../DatabasesClientTests.GetDocument.cs | 24 +++++++++++++++++++ .../DatabasesClientTests.ListDocuments.cs | 23 ++++++++++++++++++ .../DatabasesClientTests.UpdateDocument.cs | 24 +++++++++++++++++++ 5 files changed, 119 insertions(+) diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.CreateDocument.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.CreateDocument.cs index 7fda4022..1f44e84f 100644 --- a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.CreateDocument.cs +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.CreateDocument.cs @@ -29,6 +29,30 @@ public async Task CreateDocument_ShouldReturnSuccess_WhenApiCallSucceeds() Assert.True(result.Success); } + [Fact] + public async Task CreateDocument_ShouldIncludeSessionHeaders_WhenProvided() + { + // Arrange + var request = CreateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .AddField("AttributeName", "MyValue") + .Build(); + + _mockHttp.Expect(HttpMethod.Post, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders() + .WithJsonContent(request, _jsonSerializerOptions) + .Respond(TestConstants.AppJson, TestConstants.DocumentResponse); + + _appwriteClient.SetSession(TestConstants.Session); + + // Act + var result = await _appwriteClient.Databases.CreateDocument(request); + + // Assert + _mockHttp.VerifyNoOutstandingExpectation(); + } + [Fact] public async Task CreateDocument_ShouldHandleException_WhenApiCallFails() { diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.DeleteDocument.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.DeleteDocument.cs index e713626b..ec2df9df 100644 --- a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.DeleteDocument.cs +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.DeleteDocument.cs @@ -29,6 +29,30 @@ public async Task DeleteDocument_ShouldReturnSuccess_WhenApiCallSucceeds() Assert.True(result.Success); } + [Fact] + public async Task DeleteDocument_ShouldIncludeSessionHeaders_WhenProvided() + { + // Arrange + var request = new DeleteDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Delete, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(HttpStatusCode.NoContent); + + _appwriteClient.SetSession(TestConstants.Session); + + // Act + var result = await _appwriteClient.Databases.DeleteDocument(request); + + // Assert + _mockHttp.VerifyNoOutstandingExpectation(); + } + [Fact] public async Task DeleteDocument_ShouldHandleException_WhenApiCallFails() { diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.GetDocument.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.GetDocument.cs index 131e41eb..29af6f14 100644 --- a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.GetDocument.cs +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.GetDocument.cs @@ -29,6 +29,30 @@ public async Task GetDocument_ShouldReturnSuccess_WhenApiCallSucceeds() Assert.True(result.Success); } + [Fact] + public async Task GetDocument_ShouldIncludeSessionHeaders_WhenProvided() + { + // Arrange + var request = new GetDocumentRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId(), + DocumentId = IdUtils.GenerateUniqueId(), + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders() + .Respond(TestConstants.AppJson, TestConstants.DocumentResponse); + + _appwriteClient.SetSession(TestConstants.Session); + + // Act + var result = await _appwriteClient.Databases.GetDocument(request); + + // Assert + _mockHttp.VerifyNoOutstandingExpectation(); + } + [Fact] public async Task GetDocument_ShouldProvideQueryParams_WhenQueriesProvided() { diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.ListDocuments.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.ListDocuments.cs index 9b7aab76..1107abe5 100644 --- a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.ListDocuments.cs +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.ListDocuments.cs @@ -28,6 +28,29 @@ public async Task ListDocuments_ShouldReturnSuccess_WhenApiCallSucceeds() Assert.True(result.Success); } + [Fact] + public async Task ListDocuments_ShouldIncludeSessionHeaders_WhenProvided() + { + // Arrange + var request = new ListDocumentsRequest + { + DatabaseId = IdUtils.GenerateUniqueId(), + CollectionId = IdUtils.GenerateUniqueId() + }; + + _mockHttp.Expect(HttpMethod.Get, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents") + .ExpectedHeaders(true) + .Respond(TestConstants.AppJson, TestConstants.DocumentsListResponse); + + _appwriteClient.SetSession(TestConstants.Session); + + // Act + var result = await _appwriteClient.Databases.ListDocuments(request); + + // Assert + _mockHttp.VerifyNoOutstandingExpectation(); + } + [Fact] public async Task ListDocuments_ShouldProvideQueryParams_WhenQueriesProvided() { diff --git a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.UpdateDocument.cs b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.UpdateDocument.cs index 7cd92fac..05e95f9a 100644 --- a/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.UpdateDocument.cs +++ b/tests/PinguApps.Appwrite.Client.Tests/Clients/Databases/DatabasesClientTests.UpdateDocument.cs @@ -29,6 +29,30 @@ public async Task UpdateDocument_ShouldReturnSuccess_WhenApiCallSucceeds() Assert.True(result.Success); } + [Fact] + public async Task UpdateDocument_ShouldIncludeSessionHeaders_WhenProvided() + { + // Arrange + var request = UpdateDocumentRequest.CreateBuilder() + .WithDatabaseId(IdUtils.GenerateUniqueId()) + .WithCollectionId(IdUtils.GenerateUniqueId()) + .WithDocumentId(IdUtils.GenerateUniqueId()) + .AddField("test", "test") + .Build(); + + _mockHttp.Expect(HttpMethod.Patch, $"{TestConstants.Endpoint}/databases/{request.DatabaseId}/collections/{request.CollectionId}/documents/{request.DocumentId}") + .ExpectedHeaders(true) + .Respond(TestConstants.AppJson, TestConstants.DocumentResponse); + + _appwriteClient.SetSession(TestConstants.Session); + + // Act + var result = await _appwriteClient.Databases.UpdateDocument(request); + + // Assert + _mockHttp.VerifyNoOutstandingExpectation(); + } + [Fact] public async Task UpdateDocument_ShouldHandleException_WhenApiCallFails() { From 7cb674eb3e11be23c8d87c73b86dae29d7569ddc Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 22:16:19 +0000 Subject: [PATCH 298/303] Update README.md --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 13cb326a..8700f46a 100644 --- a/README.md +++ b/README.md @@ -141,11 +141,11 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( ## ⌛ Progress -![Server & Client - 162 / 317](https://img.shields.io/badge/Server_&_Client-162%20%2F%20317-gold?style=for-the-badge) +![Server & Client - 172 / 317](https://img.shields.io/badge/Server_&_Client-172%20%2F%20317-gold?style=for-the-badge) -![Server - 102 / 224](https://img.shields.io/badge/Server-102%20%2F%20224-gold?style=for-the-badge) +![Server - 107 / 224](https://img.shields.io/badge/Server-107%20%2F%20224-gold?style=for-the-badge) -![Client - 60 / 93](https://img.shields.io/badge/Client-60%20%2F%2093-gold?style=for-the-badge) +![Client - 65 / 93](https://img.shields.io/badge/Client-65%20%2F%2093-forestgreen?style=for-the-badge) ### 🔑 Key | Icon | Definition | @@ -275,7 +275,7 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Update Preferences](https://appwrite.io/docs/references/1.6.x/client-rest/teams#updatePrefs) | ✅ | ✅ | ### Databases -![Databases - 37 / 47](https://img.shields.io/badge/Databases-37%20%2F%2047-forestgreen?style=for-the-badge) +![Databases - 47 / 47](https://img.shields.io/badge/Databases-47%20%2F%2047-blue?style=for-the-badge) | Endpoint | Client | Server | |:-:|:-:|:-:| @@ -312,11 +312,11 @@ string emailAddressOrErrorMessage = userResponse.Result.Match( | [Get Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getAttribute) | ❌ | ✅ | | [Delete Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#deleteAttribute) | ❌ | ✅ | | [Update Relationship Attribute](https://appwrite.io/docs/references/1.6.x/server-rest/databases#updateRelationshipAttribute) | ❌ | ✅ | -| [List Documents](https://appwrite.io/docs/references/1.6.x/client-rest/databases#listDocuments) | ⬛ | ⬛ | -| [Create Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#createDocument) | ⬛ | ⬛ | -| [Get Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#getDocument) | ⬛ | ⬛ | -| [Update Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#updateDocument) | ⬛ | ⬛ | -| [Delete Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#deleteDocument) | ⬛ | ⬛ | +| [List Documents](https://appwrite.io/docs/references/1.6.x/client-rest/databases#listDocuments) | ✅ | ✅ | +| [Create Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#createDocument) | ✅ | ✅ | +| [Get Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#getDocument) | ✅ | ✅ | +| [Update Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#updateDocument) | ✅ | ✅ | +| [Delete Document](https://appwrite.io/docs/references/1.6.x/client-rest/databases#deleteDocument) | ✅ | ✅ | | [List Indexes](https://appwrite.io/docs/references/1.6.x/server-rest/databases#listIndexes) | ❌ | ✅ | | [Create Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#createIndex) | ❌ | ✅ | | [Get Index](https://appwrite.io/docs/references/1.6.x/server-rest/databases#getIndex) | ❌ | ✅ | From fe7d004fec1d2204163d6ee7d458e8540d9f1201 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Tue, 10 Dec 2024 22:40:01 +0000 Subject: [PATCH 299/303] fixed code quality issue for multiple classes per file --- .../CreateDocumentRequestGenericTests.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestGenericTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestGenericTests.cs index e14d0911..ef83e4a3 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestGenericTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Requests/Databases/CreateDocumentRequestGenericTests.cs @@ -2,18 +2,19 @@ using PinguApps.Appwrite.Shared.Requests.Databases; using PinguApps.Appwrite.Shared.Requests.Databases.Validators; using PinguApps.Appwrite.Shared.Utils; +using static PinguApps.Appwrite.Shared.Tests.Requests.Databases.CreateDocumentRequestGenericTests; namespace PinguApps.Appwrite.Shared.Tests.Requests.Databases; -// Test model for our generic type -public class CreateDocumentTestData -{ - public string Name { get; set; } = string.Empty; - public int Age { get; set; } -} - public class CreateDocumentRequestGenericTests : DatabaseCollectionIdBaseRequestTests, CreateDocumentRequestValidator> { + // Test model for our generic type + public class CreateDocumentTestData + { + public string Name { get; set; } = string.Empty; + public int Age { get; set; } + } + protected override CreateDocumentRequest CreateValidDatabaseCollectionIdRequest => new() { DocumentId = IdUtils.GenerateUniqueId(), From 303682910b57d43cccbee481be19120dffce6271 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 11 Dec 2024 00:17:57 +0000 Subject: [PATCH 300/303] Split up read method of Doc Generic Converter as it was too complex --- .../Converters/DocumentGenericConverter.cs | 155 ++++++++++-------- .../DocumentGenericConverterTests.cs | 36 +--- 2 files changed, 84 insertions(+), 107 deletions(-) diff --git a/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs index ade68935..3aa27074 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/DocumentGenericConverter.cs @@ -10,102 +10,92 @@ namespace PinguApps.Appwrite.Shared.Converters; public class DocumentGenericConverter : JsonConverter> where TData : class, new() { + private class DocumentFields + { + public string? Id { get; set; } + public string? CollectionId { get; set; } + public string? DatabaseId { get; set; } + public DateTime? CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + public List? Permissions { get; set; } + public Dictionary DataProperties { get; set; } = new(); + } + public override Document Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - string? id = null; - string? collectionId = null; - string? databaseId = null; - DateTime? createdAt = null; - DateTime? updatedAt = null; - List? permissions = null; - TData data = new(); + ValidateStartObject(ref reader); - var dataProperties = new Dictionary(); + var documentFields = ReadDocumentFields(ref reader, options); - var dateTimeConverter = new MultiFormatDateTimeConverter(); - var permissionListConverter = new PermissionListConverter(); + ValidateRequiredFields(documentFields); + + var data = DeserializeCustomData(documentFields.DataProperties, options); + + return new Document(documentFields.Id!, documentFields.CollectionId!, documentFields.DatabaseId!, documentFields.CreatedAt, + documentFields.UpdatedAt, documentFields.Permissions!, data); + } + private static void ValidateStartObject(ref Utf8JsonReader reader) + { if (reader.TokenType is not JsonTokenType.StartObject) { throw new JsonException("Expected StartObject token"); } + } + + private DocumentFields ReadDocumentFields(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + var dateTimeConverter = new MultiFormatDateTimeConverter(); + var permissionListConverter = new PermissionListConverter(); + var fields = new DocumentFields(); while (reader.Read()) { - if (reader.TokenType == JsonTokenType.EndObject) + if (reader.TokenType is JsonTokenType.EndObject) { break; } var propertyName = reader.GetString()!; - reader.Read(); - switch (propertyName) - { - case "$id": - id = reader.GetString(); - break; - case "$collectionId": - collectionId = reader.GetString(); - break; - case "$databaseId": - databaseId = reader.GetString(); - break; - case "$createdAt": - createdAt = dateTimeConverter.Read(ref reader, typeof(DateTime), options); - break; - case "$updatedAt": - updatedAt = dateTimeConverter.Read(ref reader, typeof(DateTime), options); - break; - case "$permissions": - permissions = permissionListConverter.Read(ref reader, typeof(List), options); - break; - default: - var value = ReadValue(ref reader, options); - dataProperties[propertyName] = value; - break; - } - } - - if (id is null) - { - throw new JsonException("Unable to find a value for Id"); - } - - if (collectionId is null) - { - throw new JsonException("Unable to find a value for CollectionId"); - } - - if (databaseId is null) - { - throw new JsonException("Unable to find a value for DatabaseId"); + ProcessProperty(ref reader, propertyName, fields, dateTimeConverter, permissionListConverter, options); } - if (createdAt is null) - { - throw new JsonException("Unable to find a value for CreatedAt"); - } - - if (updatedAt is null) - { - throw new JsonException("Unable to find a value for UpdatedAt"); - } + return fields; + } - if (permissions is null) + private static void ProcessProperty(ref Utf8JsonReader reader, string propertyName, DocumentFields fields, + MultiFormatDateTimeConverter dateTimeConverter, PermissionListConverter permissionListConverter, JsonSerializerOptions options) + { + switch (propertyName) { - throw new JsonException("Unable to find a value for Permissions"); + case "$id": + fields.Id = reader.GetString(); + break; + case "$collectionId": + fields.CollectionId = reader.GetString(); + break; + case "$databaseId": + fields.DatabaseId = reader.GetString(); + break; + case "$createdAt": + fields.CreatedAt = dateTimeConverter.Read(ref reader, typeof(DateTime), options); + break; + case "$updatedAt": + fields.UpdatedAt = dateTimeConverter.Read(ref reader, typeof(DateTime), options); + break; + case "$permissions": + fields.Permissions = permissionListConverter.Read(ref reader, typeof(List), options); + break; + default: + var value = ReadValue(ref reader, options); + fields.DataProperties[propertyName] = value; + break; } - - // Deserialize the remaining properties into TData - var dataJson = JsonSerializer.Serialize(dataProperties, options); - data = JsonSerializer.Deserialize(dataJson, options) ?? new TData(); - - return new Document(id, collectionId, databaseId, createdAt.Value, updatedAt.Value, permissions, data); } - internal object? ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) + internal static object? ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options) { switch (reader.TokenType) { @@ -143,7 +133,7 @@ public override Document Read(ref Utf8JsonReader reader, Type typeToConve } } - private IReadOnlyCollection ReadArray(ref Utf8JsonReader reader, JsonSerializerOptions options) + private static IReadOnlyCollection ReadArray(ref Utf8JsonReader reader, JsonSerializerOptions options) { var list = new List(); @@ -161,7 +151,7 @@ public override Document Read(ref Utf8JsonReader reader, Type typeToConve return list; } - private Dictionary ReadObject(ref Utf8JsonReader reader, JsonSerializerOptions options) + private static Dictionary ReadObject(ref Utf8JsonReader reader, JsonSerializerOptions options) { var dict = new Dictionary(); @@ -184,6 +174,27 @@ public override Document Read(ref Utf8JsonReader reader, Type typeToConve return dict; } + private static void ValidateRequiredFields(DocumentFields fields) + { + if (fields.Id is null) + throw new JsonException("Unable to find a value for Id"); + + if (fields.CollectionId is null) + throw new JsonException("Unable to find a value for CollectionId"); + + if (fields.DatabaseId is null) + throw new JsonException("Unable to find a value for DatabaseId"); + + if (fields.Permissions is null) + throw new JsonException("Unable to find a value for Permissions"); + } + + private static TData DeserializeCustomData(Dictionary dataProperties, JsonSerializerOptions options) + { + var dataJson = JsonSerializer.Serialize(dataProperties, options); + return JsonSerializer.Deserialize(dataJson, options) ?? new TData(); + } + public override void Write(Utf8JsonWriter writer, Document value, JsonSerializerOptions options) { writer.WriteStartObject(); diff --git a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs index 9a18580c..129dd713 100644 --- a/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs +++ b/tests/PinguApps.Appwrite.Shared.Tests/Converters/DocumentGenericConverterTests.cs @@ -430,38 +430,6 @@ public void Read_MissingDatabaseId_ThrowsJsonException() Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); } - [Fact] - public void Read_MissingCreatedAt_ThrowsJsonException() - { - var json = @" - { - ""$id"": ""1"", - ""$collectionId"": ""col1"", - ""$databaseId"": ""db1"", - ""$updatedAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read(\""any\"")""], - ""Field1"": ""value1"" - }"; - - Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); - } - - [Fact] - public void Read_MissingUpdatedAt_ThrowsJsonException() - { - var json = @" - { - ""$id"": ""1"", - ""$collectionId"": ""col1"", - ""$databaseId"": ""db1"", - ""$createdAt"": ""2020-10-15T06:38:00.000+00:00"", - ""$permissions"": [""read(\""any\"")""], - ""Field1"": ""value1"" - }"; - - Assert.Throws(() => JsonSerializer.Deserialize>(json, _options)); - } - [Fact] public void Read_MissingPermissions_ThrowsJsonException() { @@ -568,8 +536,6 @@ public void ReadValue_UnsupportedTokenType_ThrowsJsonException() var reader = new Utf8JsonReader(bytes, readerOptions); - var converter = new DocumentGenericConverter(); - // Read the StartObject token reader.Read(); // JsonTokenType.StartObject @@ -585,7 +551,7 @@ public void ReadValue_UnsupportedTokenType_ThrowsJsonException() // Calling ReadValue should now hit the default case and throw JsonException try { - converter.ReadValue(ref reader, _options); + DocumentGenericConverter.ReadValue(ref reader, _options); Assert.Fail("Did not throw JsonException"); } catch (JsonException) From 98e38c3dfb237d09185e0599952e7f8c5f4bbfc1 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 11 Dec 2024 00:40:08 +0000 Subject: [PATCH 301/303] Spit up complex method in permission json converter --- .../Converters/PermissionJsonConverter.cs | 70 +++++++++++++++---- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/src/PinguApps.Appwrite.Shared/Converters/PermissionJsonConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/PermissionJsonConverter.cs index 0cc00eab..004c1cbc 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/PermissionJsonConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/PermissionJsonConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; using PinguApps.Appwrite.Shared.Enums; @@ -8,15 +9,27 @@ namespace PinguApps.Appwrite.Shared.Converters; public class PermissionJsonConverter : JsonConverter { public override Permission? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ValidateTokenType(ref reader); + + var value = reader.GetString()!; + + var (permissionType, roleString) = ParsePermissionString(value); + var permissionBuilder = CreatePermissionBuilder(permissionType); + + return ResolveRole(permissionBuilder, roleString); + } + + private static void ValidateTokenType(ref Utf8JsonReader reader) { if (reader.TokenType is not JsonTokenType.String) { throw new JsonException("Expected string value for Permission"); } + } - var value = reader.GetString()!; - - // Format is "permissionType(\"roleString\")" + private static (string permissionType, string roleString) ParsePermissionString(string value) + { var openParenIndex = value.IndexOf('('); var closeParenIndex = value.LastIndexOf(')'); @@ -25,31 +38,58 @@ public class PermissionJsonConverter : JsonConverter throw new JsonException("Invalid Permission format"); } - var permissionTypeStr = value[..openParenIndex].ToLower(); + var permissionType = value[..openParenIndex].ToLower(); // Remove the quotes from the role string var roleString = value[(openParenIndex + 2)..(closeParenIndex - 1)]; - var permissionBuilder = permissionTypeStr switch + return (permissionType, roleString); + } + + private static Permission.PermissionBuilder CreatePermissionBuilder(string permissionType) + { + return permissionType switch { "read" => Permission.Read(), "write" => Permission.Write(), "create" => Permission.Create(), "update" => Permission.Update(), "delete" => Permission.Delete(), - _ => throw new JsonException($"Unknown permission type: {permissionTypeStr}") + _ => throw new JsonException($"Unknown permission type: {permissionType}") }; + } + + private static Permission ResolveRole(Permission.PermissionBuilder builder, string roleString) + { + if (IsSimpleRole(roleString, builder, out var simplePermission)) + { + return simplePermission; + } + + return ResolveComplexRole(builder, roleString); + } - // Parse the role string + private static bool IsSimpleRole(string roleString, Permission.PermissionBuilder builder, [NotNullWhen(true)] out Permission? permission) + { + permission = roleString switch + { + "any" => builder.Any(), + "users" => builder.Users(), + "guests" => builder.Guests(), + _ => null + }; + + return permission != null; + } + + private static Permission ResolveComplexRole(Permission.PermissionBuilder builder, string roleString) + { return roleString switch { - "any" => permissionBuilder.Any(), - "users" => permissionBuilder.Users(), - "guests" => permissionBuilder.Guests(), - var s when s.StartsWith("user:") => ParseUserRole(permissionBuilder, s[5..]), - var s when s.StartsWith("users/") => ParseUsersRole(permissionBuilder, s[6..]), - var s when s.StartsWith("team:") => ParseTeamRole(permissionBuilder, s[5..]), - var s when s.StartsWith("member:") => permissionBuilder.Member(s[7..]), - var s when s.StartsWith("label:") => permissionBuilder.Label(s[6..]), + var s when s.StartsWith("user:") => ParseUserRole(builder, s[5..]), + var s when s.StartsWith("users/") => ParseUsersRole(builder, s[6..]), + var s when s.StartsWith("team:") => ParseTeamRole(builder, s[5..]), + var s when s.StartsWith("member:") => builder.Member(s[7..]), + var s when s.StartsWith("label:") => builder.Label(s[6..]), _ => throw new JsonException($"Unknown role format: {roleString}") }; } From 5c5a16721e64f8a1db0fc02cbf8f0409fadc2dec Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 11 Dec 2024 00:54:03 +0000 Subject: [PATCH 302/303] Simplified complex read method in attribute json converter --- .../Converters/AttributeJsonConverter.cs | 63 +++++++++++++++---- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/src/PinguApps.Appwrite.Shared/Converters/AttributeJsonConverter.cs b/src/PinguApps.Appwrite.Shared/Converters/AttributeJsonConverter.cs index 8845814b..118e385e 100644 --- a/src/PinguApps.Appwrite.Shared/Converters/AttributeJsonConverter.cs +++ b/src/PinguApps.Appwrite.Shared/Converters/AttributeJsonConverter.cs @@ -10,36 +10,73 @@ public class AttributeJsonConverter : JsonConverter public override Attribute? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using var jsonDoc = JsonDocument.ParseValue(ref reader); + var jsonObject = jsonDoc.RootElement; + var attributeType = ResolveAttributeType(jsonObject); + + return DeserializeAttribute(jsonObject, attributeType, options); + } + + private static Type ResolveAttributeType(JsonElement jsonObject) + { + var type = GetRequiredTypeProperty(jsonObject); + var format = GetOptionalFormatProperty(jsonObject); + + return DetermineAttributeType(type, format, jsonObject); + } + + private static string GetRequiredTypeProperty(JsonElement jsonObject) + { if (!jsonObject.TryGetProperty("type", out var typeProperty)) { throw new JsonException("Missing `Type` property"); } - var type = typeProperty.GetString(); + return typeProperty.GetString()!; + } + + private static string? GetOptionalFormatProperty(JsonElement jsonObject) + { jsonObject.TryGetProperty("format", out var formatProperty); - var format = formatProperty.ValueKind == JsonValueKind.String ? formatProperty.GetString() : null; - var derivedType = type switch + return formatProperty.ValueKind is JsonValueKind.String ? formatProperty.GetString() : null; + } + + private static Type DetermineAttributeType(string type, string? format, JsonElement jsonObject) + { + return type switch { "boolean" => typeof(AttributeBoolean), "integer" => typeof(AttributeInteger), "double" => typeof(AttributeFloat), "datetime" => typeof(AttributeDatetime), - "string" => format switch - { - "email" => typeof(AttributeEmail), - "url" => typeof(AttributeUrl), - "ip" => typeof(AttributeIp), - "enum" => typeof(AttributeEnum), - null or "" => jsonObject.TryGetProperty("relatedCollection", out _) ? typeof(AttributeRelationship) : typeof(AttributeString), - _ => throw new JsonException($"Unknown format: {format}") - }, + "string" => ResolveStringAttributeType(format, jsonObject), _ => throw new JsonException($"Unknown type: {type}") }; + } + + private static Type ResolveStringAttributeType(string? format, JsonElement jsonObject) + { + return format switch + { + "email" => typeof(AttributeEmail), + "url" => typeof(AttributeUrl), + "ip" => typeof(AttributeIp), + "enum" => typeof(AttributeEnum), + null or "" => ResolveBasicStringAttributeType(jsonObject), + _ => throw new JsonException($"Unknown format: {format}") + }; + } + + private static Type ResolveBasicStringAttributeType(JsonElement jsonObject) + { + return jsonObject.TryGetProperty("relatedCollection", out _) ? typeof(AttributeRelationship) : typeof(AttributeString); + } - return (Attribute?)JsonSerializer.Deserialize(jsonObject.GetRawText(), derivedType, options); + private static Attribute? DeserializeAttribute(JsonElement jsonObject, Type attributeType, JsonSerializerOptions options) + { + return (Attribute?)JsonSerializer.Deserialize(jsonObject.GetRawText(), attributeType, options); } public override void Write(Utf8JsonWriter writer, Attribute value, JsonSerializerOptions options) From 5a70108d6e1852a29e731fdbbec63751b3124b90 Mon Sep 17 00:00:00 2001 From: Matthew Parker Date: Wed, 11 Dec 2024 01:06:56 +0000 Subject: [PATCH 303/303] Bump Version --- src/PinguApps.Appwrite.Shared/Constants.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PinguApps.Appwrite.Shared/Constants.cs b/src/PinguApps.Appwrite.Shared/Constants.cs index 552fe9fd..b6fffe82 100644 --- a/src/PinguApps.Appwrite.Shared/Constants.cs +++ b/src/PinguApps.Appwrite.Shared/Constants.cs @@ -1,5 +1,5 @@ namespace PinguApps.Appwrite.Shared; public static class Constants { - public const string Version = "0.3.0"; + public const string Version = "0.4.0"; }