diff --git a/Metron Comicbook Database.yaml b/Metron Comicbook Database.yaml index 519da8c..a20c057 100644 --- a/Metron Comicbook Database.yaml +++ b/Metron Comicbook Database.yaml @@ -463,6 +463,10 @@ paths: Update the rating of a specific collection item (must belong to authenticated user). Read-tracking fields (is_read/date_read) are not editable here; use scrobble instead. Requires authentication. + + destroy: + Remove a specific collection item (must belong to authenticated user). + Requires authentication. parameters: - in: query name: book_format @@ -667,6 +671,10 @@ paths: Update the rating of a specific collection item (must belong to authenticated user). Read-tracking fields (is_read/date_read) are not editable here; use scrobble instead. Requires authentication. + + destroy: + Remove a specific collection item (must belong to authenticated user). + Requires authentication. parameters: - in: path name: id @@ -707,6 +715,10 @@ paths: Update the rating of a specific collection item (must belong to authenticated user). Read-tracking fields (is_read/date_read) are not editable here; use scrobble instead. Requires authentication. + + destroy: + Remove a specific collection item (must belong to authenticated user). + Requires authentication. parameters: - in: path name: id @@ -758,6 +770,10 @@ paths: Update the rating of a specific collection item (must belong to authenticated user). Read-tracking fields (is_read/date_read) are not editable here; use scrobble instead. Requires authentication. + + destroy: + Remove a specific collection item (must belong to authenticated user). + Requires authentication. parameters: - in: path name: id @@ -789,6 +805,81 @@ paths: schema: $ref: '#/components/schemas/CollectionRatingUpdate' description: '' + delete: + operationId: api_collection_destroy + description: |- + list: + Returns authenticated user's collection items. + Requires authentication. + + retrieve: + Returns details of a specific collection item (must belong to authenticated user). + Requires authentication. + + partial_update: + Update the rating of a specific collection item (must belong to authenticated user). + Read-tracking fields (is_read/date_read) are not editable here; use scrobble instead. + Requires authentication. + + update: + Update the rating of a specific collection item (must belong to authenticated user). + Read-tracking fields (is_read/date_read) are not editable here; use scrobble instead. + Requires authentication. + + destroy: + Remove a specific collection item (must belong to authenticated user). + Requires authentication. + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this Collection Item. + required: true + tags: + - api + security: + - basicAuth: [] + - cookieAuth: [] + - knoxApiToken: [] + responses: + '204': + description: No response body + /api/collection/add/: + post: + operationId: api_collection_add_create + description: Add an issue to the authenticated user's collection. + tags: + - api + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CollectionAddItem' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/CollectionAddItem' + multipart/form-data: + schema: + $ref: '#/components/schemas/CollectionAddItem' + required: true + security: + - basicAuth: [] + - cookieAuth: [] + - knoxApiToken: [] + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/CollectionRead' + description: '' + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/CollectionRead' + description: '' /api/collection/missing_issues/{series_id}/: get: operationId: api_collection_missing_issues_list @@ -3655,6 +3746,16 @@ components: BlankEnum: enum: - '' + BookFormatEnum: + enum: + - PRINT + - DIGITAL + - BOTH + type: string + description: |- + * `PRINT` - Print + * `DIGITAL` - Digital + * `BOTH` - Both Character: type: object properties: @@ -3790,6 +3891,54 @@ components: - resource_url - teams - universes + CollectionAddItem: + type: object + description: Serializer for adding an issue to the authenticated user's collection. + properties: + issue_id: + type: integer + quantity: + type: integer + minimum: 1 + default: 1 + book_format: + allOf: + - $ref: '#/components/schemas/BookFormatEnum' + default: PRINT + grade: + type: string + format: decimal + pattern: ^-?\d{0,2}(?:\.\d{0,1})?$ + nullable: true + grading_company: + default: '' + oneOf: + - $ref: '#/components/schemas/GradingCompanyEnum' + - $ref: '#/components/schemas/BlankEnum' + purchase_date: + type: string + format: date + nullable: true + purchase_price: + type: string + format: decimal + pattern: ^-?\d{0,5}(?:\.\d{0,2})?$ + nullable: true + purchase_price_currency: + allOf: + - $ref: '#/components/schemas/CurrencyEnum' + default: USD + purchase_store: + type: string + default: '' + storage_location: + type: string + default: '' + notes: + type: string + default: '' + required: + - issue_id CollectionIssue: type: object description: Serializer for issues in collection (without image and cover_hash). @@ -4787,6 +4936,16 @@ components: required: - id - name + GradingCompanyEnum: + enum: + - CGC + - CBCS + - PGX + type: string + description: |- + * `CGC` - CGC (Certified Guaranty Company) + * `CBCS` - CBCS (Comic Book Certification Service) + * `PGX` - PGX (Professional Grading Experts) Imprint: type: object properties: diff --git a/src/Metron.Api/Models/Generated/CollectionAddItem.cs b/src/Metron.Api/Models/Generated/CollectionAddItem.cs new file mode 100644 index 0000000..d694efd --- /dev/null +++ b/src/Metron.Api/Models/Generated/CollectionAddItem.cs @@ -0,0 +1,53 @@ +// +// Generated by tools/codegen/generate_models.py from "Metron Comicbook Database.yaml". +// Do not edit by hand -- re-run the script instead. +// +#nullable enable + +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Metron.Api.Serialization; + +namespace Metron.Api.Models; + +/// +/// Serializer for adding an issue to the authenticated user's collection. +/// +public sealed class CollectionAddItem +{ + [JsonPropertyName("issue_id")] + public int? IssueId { get; set; } + + [JsonPropertyName("quantity")] + public int? Quantity { get; set; } + + [JsonPropertyName("book_format")] + public BookFormatEnum? BookFormat { get; set; } + + [JsonPropertyName("grade")] + [JsonConverter(typeof(DecimalStringConverter))] + public decimal? Grade { get; set; } + + [JsonPropertyName("grading_company")] + public GradingCompanyEnum? GradingCompany { get; set; } + + [JsonPropertyName("purchase_date")] + public DateOnly? PurchaseDate { get; set; } + + [JsonPropertyName("purchase_price")] + [JsonConverter(typeof(DecimalStringConverter))] + public decimal? PurchasePrice { get; set; } + + [JsonPropertyName("purchase_price_currency")] + public CurrencyEnum? PurchasePriceCurrency { get; set; } + + [JsonPropertyName("purchase_store")] + public string? PurchaseStore { get; set; } + + [JsonPropertyName("storage_location")] + public string? StorageLocation { get; set; } + + [JsonPropertyName("notes")] + public string? Notes { get; set; } +} diff --git a/src/Metron.Api/Models/Generated/Enums.cs b/src/Metron.Api/Models/Generated/Enums.cs index 37363bb..45ad0b1 100644 --- a/src/Metron.Api/Models/Generated/Enums.cs +++ b/src/Metron.Api/Models/Generated/Enums.cs @@ -31,6 +31,19 @@ public enum AttributionSourceEnum OTHER, } +/// +/// * `PRINT` - Print +/// * `DIGITAL` - Digital +/// * `BOTH` - Both +/// +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum BookFormatEnum +{ + PRINT, + DIGITAL, + BOTH, +} + /// /// * `AF` - Afghanistan /// * `AX` - Ă…land Islands @@ -547,6 +560,19 @@ public enum CurrencyEnum GBP, } +/// +/// * `CGC` - CGC (Certified Guaranty Company) +/// * `CBCS` - CBCS (Comic Book Certification Service) +/// * `PGX` - PGX (Professional Grading Experts) +/// +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum GradingCompanyEnum +{ + CGC, + CBCS, + PGX, +} + public enum StatusEnum { Cancelled = 1, diff --git a/src/Metron.Api/Resources/CollectionClient.cs b/src/Metron.Api/Resources/CollectionClient.cs index 68e84e9..f9c0089 100644 --- a/src/Metron.Api/Resources/CollectionClient.cs +++ b/src/Metron.Api/Resources/CollectionClient.cs @@ -28,6 +28,14 @@ public Task UpdateAsync(int id, CollectionRatingUpdate r public Task PartialUpdateAsync(int id, PatchedCollectionRatingUpdate rating, CancellationToken cancellationToken = default) => SendFormAsync(HttpMethod.Patch, $"collection/{id}/", rating, cancellationToken); + /// Removes an item from the collection. + public Task RemoveAsync(int id, CancellationToken cancellationToken = default) => + DeleteAsync($"collection/{id}/", cancellationToken); + + /// Adds an issue to the collection. + public Task AddAsync(CollectionAddItem item, CancellationToken cancellationToken = default) => + SendFormAsync(HttpMethod.Post, "collection/add/", item, cancellationToken); + /// Returns a single page of issues from a series that are missing from the collection. public Task> GetMissingIssuesAsync(int seriesId, CollectionMissingIssuesFilter? filter = null, CancellationToken cancellationToken = default) => GetAsync>($"collection/missing_issues/{seriesId}/" + QueryStringBuilder.Build(filter), cancellationToken); diff --git a/tests/Metron.Api.Tests/ClientIntegrationTests.cs b/tests/Metron.Api.Tests/ClientIntegrationTests.cs index 2ab0bde..3be0f38 100644 --- a/tests/Metron.Api.Tests/ClientIntegrationTests.cs +++ b/tests/Metron.Api.Tests/ClientIntegrationTests.cs @@ -103,4 +103,43 @@ public async Task Arc_CreateAsync_SendsMultipartFormData() Assert.Equal(HttpMethod.Post, request.Method); Assert.IsType(request.Content); } + + [Fact] + public async Task Collection_AddAsync_SendsMultipartFormData_AndDeserializesResponse() + { + var fake = new FakeHttpMessageHandler(); + fake.Enqueue(FakeHttpMessageHandler.Json(HttpStatusCode.Created, """ + {"id": 99, "quantity": 2, "book_format": "PRINT"} + """)); + + using var client = CreateClient(fake); + var added = await client.Collection.AddAsync(new Models.CollectionAddItem + { + IssueId = 123, + Quantity = 2, + BookFormat = Models.BookFormatEnum.PRINT, + }); + + Assert.Equal(99, added.Id); + Assert.Equal(2, added.Quantity); + + var request = Assert.Single(fake.Requests); + Assert.Equal(HttpMethod.Post, request.Method); + Assert.IsType(request.Content); + Assert.Equal("https://metron.cloud/api/collection/add/", request.RequestUri!.ToString()); + } + + [Fact] + public async Task Collection_RemoveAsync_SendsDeleteRequest() + { + var fake = new FakeHttpMessageHandler(); + fake.Enqueue(new HttpResponseMessage(HttpStatusCode.NoContent)); + + using var client = CreateClient(fake); + await client.Collection.RemoveAsync(7); + + var request = Assert.Single(fake.Requests); + Assert.Equal(HttpMethod.Delete, request.Method); + Assert.Equal("https://metron.cloud/api/collection/7/", request.RequestUri!.ToString()); + } }