Files
mic-check/docs/api/06_admin_api_output.md
James Wampler 6887d09f9c version-0.1 (#1)
Squash and merge of version-0.1

Co-authored-by: James Wampler <james@wamp.dev>
Co-committed-by: James Wampler <james@wamp.dev>
2026-07-01 13:30:07 -07:00

8.8 KiB
Executable File

Step 6 Output: Admin API

Summary

Implemented the Admin API — authenticated management endpoints for organizations, projects, environments, features, segments, identities, tags, webhooks, and audit logs. Authenticated via Authorization: Api-Key <TOKEN> or JWT bearer token.


What Was Built

Shared Utilities

MicCheck.Api/Common/DomainException.cs

  • Custom exception for business rule violations (feature limit, segment limit, etc.)
  • Caught in controllers and returned as 400 BadRequest

MicCheck.Api/Common/PaginatedResponse.cs

  • Generic PaginatedResponse<T>(Count, Next, Previous, Results) record used by all list endpoints

Audit

MicCheck.Api/Audit/AuditService.cs

  • virtual LogAsync(resourceType, resourceId, action, organizationId, projectId?, environmentId?, changes?, ct) — persists audit log entry, extracts actor user ID from UserId claim via IHttpContextAccessor

MicCheck.Api/Audit/AuditLogQueryService.cs

  • ListByOrganizationAsync, ListByProjectAsync, ListByEnvironmentAsync — ordered by descending CreatedAt

MicCheck.Api/Audit/AuditLogResponse.cs

  • Static From(AuditLog) factory

MicCheck.Api/Audit/AuditLogsController.cs

  • GET /api/v1/organisations/{id}/audit-logs
  • GET /api/v1/projects/{projectId}/audit-logs
  • (Environment audit logs handled in EnvironmentsController)

Organizations

MicCheck.Api/Organizations/OrganizationService.cs

  • ListForUserAsync(userId) — returns orgs where user is a member
  • CreateAsync(name, creatorUserId) — auto-adds creator as Admin member
  • UpdateAsync, DeleteAsync, FindByIdAsync
  • ListMembersAsync, InviteUserAsync (upsert), RemoveMemberAsync

MicCheck.Api/Organizations/OrganizationsController.cs Routes:

  • GET /api/v1/organisations/ — paginated, filtered to calling user's orgs
  • POST /api/v1/organisations/
  • GET /api/v1/organisations/{id}/
  • PUT /api/v1/organisations/{id}/
  • DELETE /api/v1/organisations/{id}/
  • GET /api/v1/organisations/{id}/users/
  • POST /api/v1/organisations/{id}/users/invite/
  • DELETE /api/v1/organisations/{id}/users/{userId}/

Projects

MicCheck.Api/Projects/ProjectService.cs

  • ListByOrganizationAsync, FindByIdAsync, CreateAsync, UpdateAsync, DeleteAsync
  • ListUserPermissionsAsync, SetUserPermissionsAsync (upsert), RemoveUserPermissionsAsync

MicCheck.Api/Projects/ProjectsController.cs Routes:

  • GET /api/v1/projects/?organizationId=
  • POST /api/v1/projects/
  • GET /api/v1/projects/{id}/
  • PUT /api/v1/projects/{id}/
  • DELETE /api/v1/projects/{id}/
  • GET /api/v1/projects/{id}/user-permissions/
  • POST /api/v1/projects/{id}/user-permissions/
  • PUT /api/v1/projects/{id}/user-permissions/{userId}/
  • DELETE /api/v1/projects/{id}/user-permissions/{userId}/

Environments

MicCheck.Api/Environments/EnvironmentService.cs

  • CreateAsync — generates a random API key via ApiKeyHasher.GenerateKey(), auto-creates FeatureState for all existing features
  • CloneAsync — duplicates environment-level FeatureState records only (not identity or segment overrides)
  • UpdateAsync, DeleteAsync, FindByApiKeyAsync, ListByProjectAsync

MicCheck.Api/Environments/EnvironmentsController.cs Routes:

  • GET /api/v1/environments/?projectId=
  • POST /api/v1/environments/
  • GET /api/v1/environments/{apiKey}/
  • PUT /api/v1/environments/{apiKey}/
  • DELETE /api/v1/environments/{apiKey}/
  • POST /api/v1/environments/{apiKey}/clone/
  • GET /api/v1/environments/{apiKey}/webhooks/
  • POST /api/v1/environments/{apiKey}/webhooks/
  • PUT /api/v1/environments/{apiKey}/webhooks/{id}/
  • DELETE /api/v1/environments/{apiKey}/webhooks/{id}/
  • GET /api/v1/environments/{apiKey}/audit-logs/
  • GET /api/v1/environments/{apiKey}/identities/ (paginated)
  • GET /api/v1/environments/{apiKey}/identities/{id}/
  • DELETE /api/v1/environments/{apiKey}/identities/{id}/
  • GET /api/v1/environments/{apiKey}/identities/{id}/featurestates/
  • PUT /api/v1/environments/{apiKey}/identities/{id}/featurestates/{featureId}/
  • DELETE /api/v1/environments/{apiKey}/identities/{id}/featurestates/{featureId}/
  • GET /api/v1/environments/{apiKey}/featurestates/
  • GET /api/v1/environments/{apiKey}/featurestates/{id}/
  • PUT /api/v1/environments/{apiKey}/featurestates/{id}/
  • PATCH /api/v1/environments/{apiKey}/featurestates/{id}/

Features

MicCheck.Api/Features/FeatureService.cs

  • CreateAsync — enforces 400-feature limit (DomainException), auto-creates FeatureState for all project environments
  • UpdateAsync, DeleteAsync, FindByIdAsync, ListByProjectAsync

MicCheck.Api/Features/FeatureStateService.cs

  • ListByEnvironmentAsync — environment-level states only (no identity/segment overrides)
  • FindByIdAsync, UpdateAsync (full replace), PatchAsync (partial)

MicCheck.Api/Features/FeaturesController.cs Routes under api/v1/projects/{projectId}/features/:

  • GET, POST, GET {id}, PUT {id}, PATCH {id}, DELETE {id}

Segments

MicCheck.Api/Segments/SegmentService.cs

  • CreateAsync — enforces 100-segment limit, 100-condition limit (across all rules recursively)
  • UpdateAsync — replaces all rules and conditions (full replace semantics)
  • DeleteAsync, FindByIdAsync, ListByProjectAsync

MicCheck.Api/Segments/SegmentsController.cs Routes under api/v1/projects/{projectId}/segments/:

  • GET, POST, GET {id}, PUT {id}, DELETE {id}

Admin Identities

MicCheck.Api/Identities/AdminIdentityService.cs

  • ListAsync (paginated), FindByIdAsync, DeleteAsync
  • GetFeatureStatesAsync, SetFeatureStateAsync (upsert), DeleteFeatureStateAsync

Tags

MicCheck.Api/Features/TagService.cs — list, create, delete

MicCheck.Api/Features/TagsController.cs Routes under api/v1/projects/{projectId}/tags/:

  • GET, POST, DELETE {id}

Webhooks

MicCheck.Api/Webhooks/WebhookService.cs — list by environment, create, update, delete


Request Validation

All incoming DTOs have FluentValidation validators in the same folder. Validation errors return 422 Unprocessable Entity with field-level detail (configured via ApiBehaviorOptions).

Key validation rules:

  • Feature names: ^[a-zA-Z0-9_-]+$, max 150 chars
  • Feature InitialValue: max 20,000 chars
  • Webhook URLs: must be absolute URI
  • Tag colors: hex format (#RGB or #RRGGBB)
  • Segment rule Type: All, Any, or None
  • Segment condition Operator: any of the 17 SegmentConditionOperator enum values

Program.cs Changes

  • Added JsonStringEnumConverter to MVC JSON options (allows "Standard" instead of 0 in API requests)
  • Added Configure<ApiBehaviorOptions> to return 422 with field-level errors for validation failures
  • Registered new services: AuditService, AuditLogQueryService, OrganizationService, ProjectService, EnvironmentService, FeatureService, FeatureStateService, SegmentService, TagService, WebhookService, AdminIdentityService

Deviations from Plan

  • Identity admin endpoints are nested under /api/v1/environments/{apiKey}/ (as specified in the plan) and handled in EnvironmentsController via [FromServices] to avoid constructor over-injection
  • Feature state endpoints (/api/v1/environments/{apiKey}/featurestates/) are also in EnvironmentsController for the same reason
  • AuditService.LogAsync is marked virtual to support Moq in service unit tests

Tests Added

FeatureServiceTests (6 tests, in-memory DB)

  • Creating a feature auto-creates FeatureState for each environment
  • InitialValue is propagated to FeatureState.Value
  • 400-feature limit throws DomainException
  • Update changes name and description
  • Delete removes the feature
  • List returns all project features

EnvironmentServiceTests (5 tests, in-memory DB)

  • Creating an environment auto-creates FeatureState for each existing feature
  • Each created environment gets a unique API key
  • Clone copies environment-level feature states
  • Clone does NOT copy identity overrides
  • Clone generates a different API key from source

SegmentServiceTests (6 tests, in-memory DB)

  • Create persists segment, rules, and conditions
  • 100-segment limit throws DomainException
  • 100-condition limit throws DomainException
  • Update replaces old rules with new rules
  • Delete removes segment
  • List returns all project segments

AdminApiIntegrationTests (7 tests, WebApplicationFactory)

  • Creating a project returns 201
  • Creating a feature returns 201
  • Creating a feature auto-creates FeatureState for environments
  • Creating an environment auto-creates FeatureState for existing features
  • Missing API key returns 401
  • Invalid feature name (with spaces) returns 422
  • Creating a segment returns 201

Test Results

Passed! - Failed: 0, Passed: 166, Skipped: 0, Total: 166