Skip to content

jdmadriz/InterviewAssessment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CMS Event Processor API

A .NET 9 service that ingests events from a CMS via webhook, processes them, stores them in a database, and exposes them via a secure REST API.

Project Structure

CmsEventProcessor/
├── src/
│   ├── CmsEventProcessor.Api/            # Web API, Controllers, Authentication
│   ├── CmsEventProcessor.Application/    # Business Logic, DTOs, Validators
│   ├── CmsEventProcessor.Domain/         # Entities, Interfaces
│   └── CmsEventProcessor.Infrastructure/ # EF Core, Repositories
├── tests/
│   └── CmsEventProcessor.Tests/          # Unit and Integration Tests

Getting Started

1. Clone the Repository

git clone https://github.com/jdmadriz/InterviewAssessment.git
cd CmsEventProcessor

2. Restore Dependencies

dotnet restore

3. Build the Solution

dotnet build

4. Run the Application

dotnet run --project src/CmsEventProcessor.Api

The API will start at http://localhost:5000

5. Access Swagger UI

Open your browser and navigate to: http://localhost:5000/swagger

Authentication

The API uses Basic Authentication with two separate schemes:

CMS Webhook User (for /cms/events endpoint)

Field Value
Username webhook
Password 550e8400-e29b-41d4-a716-446655440000
Base64 Token d2ViaG9vazo1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDA=

API Users (for /api/entities endpoints)

User Username Password Role Base64 Token
Admin admin 7c9e6679-7425-40de-944b-e07fc1f90ae7 Admin YWRtaW46N2M5ZTY2NzktNzQyNS00MGRlLTk0NGItZTA3ZmMxZjkwYWU3
User user a3bb189e-8bf9-3888-9912-ace4e6543002 User dXNlcjphM2JiMTg5ZS04YmY5LTM4ODgtOTkxMi1hY2U0ZTY1NDMwMDI=

API Endpoints

Method Endpoint Auth Description
POST /cms/events CMS User Receive batch events from CMS
GET /api/entities API Users List all entities
GET /api/entities/{id} API Users Get entity by ID
POST /api/entities/{id}/disable Admin only Disable entity (admin override)
POST /api/entities/{id}/enable Admin only Enable entity (admin override)

Testing with cURL

Make sure the API is running (dotnet run --project src/CmsEventProcessor.Api)

1. Create Entities (Publish)

curl -X POST http://localhost:5000/cms/events \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic d2ViaG9vazo1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDA=" \
  -d '[
    {"type": "publish", "id": "entity-1", "payload": {"title": "First Article", "content": "Content here"}, "version": 1, "timestamp": "2024-01-01T00:00:00Z"},
    {"type": "publish", "id": "entity-2", "payload": {"title": "Second Article", "content": "More content"}, "version": 1, "timestamp": "2024-01-01T00:00:00Z"},
    {"type": "publish", "id": "entity-3", "payload": {"title": "Third Article", "content": "Third content"}, "version": 1, "timestamp": "2024-01-01T00:00:00Z"}
  ]'

Expected Response: {"message":"Processed 3 events"}

2. Update Entity (New Version)

curl -X POST http://localhost:5000/cms/events \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic d2ViaG9vazo1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDA=" \
  -d '[
    {"type": "publish", "id": "entity-1", "payload": {"title": "First Article UPDATED", "content": "Updated content"}, "version": 2, "timestamp": "2024-01-02T00:00:00Z"}
  ]'

3. Ignore Older Version (Should Not Update)

curl -X POST http://localhost:5000/cms/events \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic d2ViaG9vazo1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDA=" \
  -d '[
    {"type": "publish", "id": "entity-1", "payload": {"title": "OLD VERSION - SHOULD BE IGNORED"}, "version": 1, "timestamp": "2024-01-01T00:00:00Z"}
  ]'

4. Unpublish Entity (Soft-Delete, Keeps Data)

curl -X POST http://localhost:5000/cms/events \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic d2ViaG9vazo1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDA=" \
  -d '[
    {"type": "unpublish", "id": "entity-2", "payload": {"title": "Second Article"}, "version": 1, "timestamp": "2024-01-03T00:00:00Z"}
  ]'

5. Delete Entity (Hard-Delete, Removes from DB)

curl -X POST http://localhost:5000/cms/events \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic d2ViaG9vazo1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDA=" \
  -d '[
    {"type": "delete", "id": "entity-3", "timestamp": "2024-01-04T00:00:00Z"}
  ]'

6. Corner Case: Unpublish Without Previously Published Version

curl -X POST http://localhost:5000/cms/events \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic d2ViaG9vazo1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDA=" \
  -d '[
    {"type": "unpublish", "id": "never-existed", "payload": {"title": "Never Published"}, "version": 5, "timestamp": "2024-01-05T00:00:00Z"}
  ]'

7. List Entities (Normal User)

curl -X GET http://localhost:5000/api/entities \
  -H "Authorization: Basic dXNlcjphM2JiMTg5ZS04YmY5LTM4ODgtOTkxMi1hY2U0ZTY1NDMwMDI="

Note: Normal users only see published and non-disabled entities.

8. List Entities (Admin User)

curl -X GET http://localhost:5000/api/entities \
  -H "Authorization: Basic YWRtaW46N2M5ZTY2NzktNzQyNS00MGRlLTk0NGItZTA3ZmMxZjkwYWU3"

Note: Admin users see all entities including unpublished and disabled ones.

9. Get Entity by ID

curl -X GET http://localhost:5000/api/entities/entity-1 \
  -H "Authorization: Basic YWRtaW46N2M5ZTY2NzktNzQyNS00MGRlLTk0NGItZTA3ZmMxZjkwYWU3"

10. Disable Entity (Admin Override)

curl -X POST http://localhost:5000/api/entities/entity-1/disable \
  -H "Authorization: Basic YWRtaW46N2M5ZTY2NzktNzQyNS00MGRlLTk0NGItZTA3ZmMxZjkwYWU3"

Expected Response: {"message":"Entity entity-1 disabled"}

11. Verify Normal User No Longer Sees entity-1

curl -X GET http://localhost:5000/api/entities \
  -H "Authorization: Basic dXNlcjphM2JiMTg5ZS04YmY5LTM4ODgtOTkxMi1hY2U0ZTY1NDMwMDI="

Expected Result: Empty list (entity-1 was disabled by admin).

12. Enable Entity (Admin Override)

curl -X POST http://localhost:5000/api/entities/entity-1/enable \
  -H "Authorization: Basic YWRtaW46N2M5ZTY2NzktNzQyNS00MGRlLTk0NGItZTA3ZmMxZjkwYWU3"

Expected Response: {"message":"Entity entity-1 enabled"}

13. Normal User Tries to Disable (Should Fail with 403)

curl -X POST http://localhost:5000/api/entities/entity-1/disable \
  -H "Authorization: Basic dXNlcjphM2JiMTg5ZS04YmY5LTM4ODgtOTkxMi1hY2U0ZTY1NDMwMDI="

14. Invalid Credentials (Should Fail with 401)

curl -X GET http://localhost:5000/api/entities \
  -H "Authorization: Basic aW52YWxpZDppbnZhbGlk"

15. No Credentials (Should Fail with 401)

curl -X GET http://localhost:5000/api/entities

16. API User Tries to Use CMS Webhook (Should Fail with 401)

curl -X POST http://localhost:5000/cms/events \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic YWRtaW46N2M5ZTY2NzktNzQyNS00MGRlLTk0NGItZTA3ZmMxZjkwYWU3" \
  -d '[{"type": "publish", "id": "test", "payload": {"title": "Test"}, "version": 1, "timestamp": "2024-01-01T00:00:00Z"}]'

Running Tests

Run All Tests

dotnet test

Run Specific Test Project

dotnet test tests/CmsEventProcessor.Tests

Test Coverage

The test suite includes:

Unit Tests (Unit/EventProcessorServiceTests.cs):

  • Publish new entity
  • Publish existing entity (version update)
  • Publish with lower version (should ignore)
  • Unpublish existing entity
  • Unpublish non-existing entity (corner case)
  • Delete entity
  • Invalid event type validation
  • Missing required fields validation

Integration Tests (Integration/AuthenticationTests.cs):

  • CMS webhook with valid credentials
  • CMS webhook with invalid credentials
  • CMS webhook with no credentials
  • API with valid admin credentials
  • API with valid user credentials
  • API with invalid credentials
  • User trying admin action (forbidden)
  • API user trying CMS endpoint (unauthorized)

Event Processing Rules

Event Types

Type Action Payload Required Version Required
publish Create or update entity Yes Yes
unpublish Mark entity as unpublished (soft-delete) Yes Yes
delete Remove entity permanently (hard-delete) No No

Version Handling

  • Events with a version lower than or equal to the current version are ignored
  • Unpublish events for non-existing entities are ignored (corner case)

Visibility Rules

User Type Sees
Normal User Published entities that are not disabled by admin
Admin User All entities (published, unpublished, disabled)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages