Skip to content

Individual account approval resources (dotnet) - #46

Open
lucasmiranda-stark wants to merge 1 commit into
masterfrom
feature/account-approval
Open

Individual account approval resources (dotnet)#46
lucasmiranda-stark wants to merge 1 commit into
masterfrom
feature/account-approval

Conversation

@lucasmiranda-stark

Copy link
Copy Markdown
Contributor

Context

The account-approval feature — IndividualAccountRequest and IndividualAccountAttachment (each with a read-only .Log) — for the dotnet infra SDK, generated from contracts individual-account-request.md (v3) and individual-account-attachment.md (v3). Run-type: generate. Run artifacts: runs/20260528-182857-account-approval/dotnet/.

What changed

  • Add IndividualAccountRequest (create/get/query/page/update + read-only .Log) with a structured Address sub-resource (nested object, never flattened).
  • Add IndividualAccountAttachment (create/get/query/page/cancel + read-only .Log); the constructor encodes content+contentType into a data:<contentType>;base64,… URL client-side; contentType is input-only; cancel → status deleted (idempotent).
  • CHANGELOG entry + per-resource README usage sections.

Test plan

  • 37 passed passing locally against the infra sandbox (Phase 6 — see runs/20260528-182857-account-approval/dotnet/test-run-success.md)
  • Sandbox smoke check (manual after merge)

Risk & Rollback

  • Risk: new resources, additive only — no changes to existing public types.
  • Rollback: revert this PR; no data migration.

Contract review (Phase 7)

Contract Review

Checklist

IndividualAccountRequest — mandatory points (M1–M12)

Mandatory point Status Implementation Test
[M1] create accepts a list of IndividualAccountRequest and returns the same shape with server-assigned id, status, accountType, created, updated populated covered IndividualAccountRequest.cs:111–120 (static Create(List<IndividualAccountRequest>)) CreateGet @ IndividualAccountRequest.cs:19 — asserts NotNull(request.ID), Equal("individual", request.AccountType), NotNull(request.Status)
[M2] address is an object (not a string), with required sub-fields street, number, neighborhood, city, state, zipCode; serialized as nested JSON, never flattened covered Address.cs:23–53 (struct), IndividualAccountRequest.cs:38 (Address Address), ResourceMaker:330 (Address.Parse(json.address)) CreateWithStructuredAddress @ IndividualAccountRequest.cs:35 — asserts request.Address.Street, .Number, .Neighborhood not null/value-equal after round-trip
[M3] get(id) returns a single IndividualAccountRequest by id covered IndividualAccountRequest.cs:173–182 (static Get(string id)) CreateGet @ IndividualAccountRequest.cs:27 — calls Get(id: request.ID), asserts getRequest.ID == request.ID
[M4] query returns an iterable of IndividualAccountRequest, accepting limit, after, before, status, tags, ids covered IndividualAccountRequest.cs:205–222 (IEnumerable<IndividualAccountRequest> Query(…)) Query @ IndividualAccountRequest.cs:52; QueryIds @ :64; QueryParams @ :89 — all filter params exercised
[M5] page returns (items, cursor) and accepts same params as query plus cursor covered IndividualAccountRequest.cs:248–273 ((List<IndividualAccountRequest> page, string pageCursor) Page(…)) Page @ IndividualAccountRequest.cs:105; PageParams @ :127 — cursor returned, items/cursor unpacked, all params exercised
[M6] update(id, …) PATCHes the request; accepts any subset of name, taxId, address, income, status, tags; replaces address as whole object covered IndividualAccountRequest.cs:301–319 (Update(string id, string name, string taxID, Address address, long? income, string status, List<string> tags)) Update @ IndividualAccountRequest.cs:147 — patches name and address as whole object, asserts updated values
[M7] status enum is exactly approved|created|denied|processing|updated; every fetched status is a member of this set covered IndividualAccountRequest.cs:27 (docstring lists all five values) StatusEnum @ IndividualAccountRequest.cs:173 — queries 20 records, asserts every .Status is in the allowed list
[M8] IndividualAccountRequest.Log is read-only, exposed under <resource>.Log, provides get, query, page; Log Request field is the parent type covered Log.cs:26–188 (nested public class Log : Resource, with IndividualAccountRequest Request field at :28, static Get, Query, Page at :73, :104, :145) QueryAndGet @ IndividualAccountRequestLog.cs:17 — asserts log.Request != null and log.Request.ID != null; also calls Log.Get; Page @ :41
[M9] Log.query and Log.page accept limit, after, before, types, accountRequestIds covered Log.cs:104–120 (Query(…, List<string> types, List<string> accountRequestIds)); Log.cs:145–168 (Page(…, List<string> types, List<string> accountRequestIds)) QueryParams @ IndividualAccountRequestLog.cs:63 — all five params including accountRequestIds serialized; PageParams @ :78
[M10] DateTime fields (created, updated) are parsed to native DateTime on the way out covered IndividualAccountRequest.cs:338–340 (StarkCore.Utils.Checks.CheckDateTime(createdString); same for updated); Log.cs:182–183 (same pattern) OutputOnlyFieldsIgnoredOnCreate @ IndividualAccountRequest.cs:190 — asserts request.Created != null and request.Updated != null after API round-trip
[M11] accountType, flags, id, status, created, updated are output-only — passing to constructor populates field but API ignores on POST covered IndividualAccountRequest.cs:73–89 (constructor accepts output-only as optional params with null defaults; not included in POST payload serialization) OutputOnlyFieldsIgnoredOnCreate @ IndividualAccountRequest.cs:190 — constructs without output-only fields, verifies API populates them on response
[M12] Each error case has a test asserting InputErrors is raised (not a specific code string): name empty, taxId invalid, address incomplete, income < 0, status transition invalid, unknown id covered IndividualAccountRequest.cs:111–120 (all ops wired to Core REST which maps 400/404 to StarkCore.Error.InputErrors) CreateWithEmptyNameRaises @ IndividualAccountRequest.cs:211; CreateWithInvalidTaxIdRaises @ :227; CreateWithIncompleteAddressRaises @ :244; CreateWithNegativeIncomeRaises @ :265; UpdateWithInvalidStatusRaises @ :281; GetUnknownIdRaises @ :295 — all use Assert.Throws<StarkCore.Error.InputErrors>

IndividualAccountAttachment — mandatory points (M1–M14)

Mandatory point Status Implementation Test
[M1] create accepts a list of IndividualAccountAttachment and returns the same shape with server-assigned id, status, created populated covered IndividualAccountAttachment.cs:129–138 (static Create(List<IndividualAccountAttachment>)) CreateGet @ IndividualAccountAttachment.cs:22 — asserts NotNull(attachment.ID) after create round-trip
[M2] Constructor encodes content + contentType into data:<contentType>;base64,<payload> URL client-side; callers pass raw bytes + MIME type covered IndividualAccountAttachment.cs:63–74 (byte[] constructor: Content = "data:" + contentType + ";base64," + Convert.ToBase64String(content)) CreateGet @ IndividualAccountAttachment.cs:22 — uses Example() which calls byte-array constructor with SampleImageBytes() + "image/png"
[M3] contentType is input-only: after constructor builds data URL, contentType is not serialized as its own wire field; response object has no populated ContentType covered IndividualAccountAttachment.cs:36 (public string ContentType { get; } — declared but never assigned in byte[] constructor or ResourceMaker; ResourceMaker at :329–343 does not set ContentType) ContentTypeIsInputOnly @ IndividualAccountAttachment.cs:40 — asserts attachment.ContentType == null on response from create
[M4] get(id) returns a single IndividualAccountAttachment by id covered IndividualAccountAttachment.cs:191–200 (static Get(string id)) CreateGet @ IndividualAccountAttachment.cs:31 — calls Get(id: attachment.ID), asserts IDs equal
[M5] query returns an iterable of IndividualAccountAttachment, accepting limit, after, before, status, tags, ids covered IndividualAccountAttachment.cs:223–240 (IEnumerable<IndividualAccountAttachment> Query(…)) Query @ IndividualAccountAttachment.cs:55; QueryIds @ :69; QueryParams @ :94
[M6] page returns (items, cursor) and accepts same params as query plus cursor covered IndividualAccountAttachment.cs:266–291 ((List<IndividualAccountAttachment> page, string pageCursor) Page(…)) Page @ IndividualAccountAttachment.cs:109; PageParams @ :133
[M7] cancel(id) maps to DELETE /individual-account-attachment/{id}, returns resource with status = deleted; cancel is idempotent covered IndividualAccountAttachment.cs:313–322 (static Cancel(string id) calls Rest.DeleteId(…)) GetAndCancel @ IndividualAccountAttachment.cs:153 — asserts canceled.Status == "deleted"; calls Cancel twice, second invocation asserts no exception and returns same ID
[M8] Resource exposed as IndividualAccountAttachment (not accountRequestAttachment) covered IndividualAccountAttachment.cs:32 (public partial class IndividualAccountAttachment : Resource); Resource() at :324 returns "IndividualAccountAttachment" CreateGet @ IndividualAccountAttachment.cs:22 — calls IndividualAccountAttachment.Create, IndividualAccountAttachment.Get by class name
[M9] IndividualAccountAttachment.Log is read-only, exposed under <resource>.Log, provides get, query, page; Log attachment field is the parent type covered IndividualAccountAttachment Log.cs:26–189 (nested public class Log : Resource, IndividualAccountAttachment Attachment at :28, static Get/Query/Page at :73/:104/:145) QueryAndGet @ IndividualAccountAttachmentLog.cs:12 — asserts log.Attachment != null and log.Attachment.ID != null; calls Log.Get; Page @ :40
[M10] Log.query and Log.page accept limit, after, before, types, attachmentIds (not accountRequestIds) covered IndividualAccountAttachment Log.cs:104–119 (Query(…, List<string> types, List<string> attachmentIds)); :145–168 (Page(…, List<string> attachmentIds)) QueryParams @ IndividualAccountAttachmentLog.cs:65 — uses attachmentIds param; PageParams @ :83 — same; param is attachmentIds not accountRequestIds
[M11] type enum is exactly drivers-license-front|drivers-license-back|identity-front|identity-back; selfie not valid covered IndividualAccountAttachment.cs:47 (docstring lists the four valid types, excluding selfie) CreateWithInvalidTypeRaises @ IndividualAccountAttachment.cs:177 — uses "not-a-real-type" (comment notes selfie is also invalid); asserts InputErrors raised
[M12] DateTime field created is parsed to native DateTime covered IndividualAccountAttachment.cs:338 (DateTime created = StarkCore.Utils.Checks.CheckDateTime(createdString)); Log.cs:182–183 (same) CreateGet @ IndividualAccountAttachment.cs:22 — creates attachment, which exercises ResourceMaker; Created is typed DateTime? at scaffold line 40
[M13] Each error case has a test asserting InputErrors is raised: type invalid, content empty, contentType missing, accountRequestId not found, unknown id covered IndividualAccountAttachment.cs:129–138 (Create wired to Core REST mapping 400 → StarkCore.Error.InputErrors); :191–200 (Get same) CreateWithInvalidTypeRaises @ IndividualAccountAttachment.cs:179; CreateWithEmptyContentRaises @ :194; CreateWithMissingContentTypeRaises @ :213; CreateWithUnknownAccountRequestIdRaises @ :229; GetUnknownIdRaises @ :243 — all use Assert.Throws<StarkCore.Error.InputErrors>
[M14] Previous AccountRequestAttachment resource (route /account-request-attachment, SDK id accountRequestAttachment) is removed in same change set not applicable n/a n/a — run-type is generate; no prior AccountRequestAttachment exists in sdk-infra/dotnet (grep confirms zero hits); M14 applies only to migrate run-types where the legacy resource pre-exists

Summary

  • Total mandatory points: 26
  • covered: 25
  • not covered: 0
  • partial: 0
  • not applicable: 1
  • VERDICT: PASS (0 not-covered, 0 partial)

If FAIL — routing recommendation

N/A — PASS

Metadata

  • Contracts: contracts/individual-account-request.md (v3), contracts/individual-account-attachment.md (v3)
  • Run artifacts: runs/20260528-182857-account-approval/dotnet/
  • Branch: feature/account-approval
  • Base SHA: 7d02cd06e9b2a2865c3cbdc46ce32e2e8e642e25

@lucasmiranda-stark
lucasmiranda-stark requested a review from a team May 29, 2026 23:42
@lucasmiranda-stark
lucasmiranda-stark force-pushed the feature/account-approval branch from 5e2bbc3 to f841482 Compare May 30, 2026 00:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant