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());
+ }
}