-
Notifications
You must be signed in to change notification settings - Fork 6
R. Yusup #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Yusuprozimemet
wants to merge
9
commits into
HackYourAssignment:main
Choose a base branch
from
Yusuprozimemet:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
R. Yusup #2
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
4063ddc
Project setup is ok
Yusuprozimemet 5267fb8
AnalyticsRecord model; add empty class stubs for remaining layers
Yusuprozimemet b9571f1
dto and exception are compiled well
Yusuprozimemet 5ddf923
sercice is the business logic layer, it receives plain ava objects, a…
Yusuprozimemet a807f2a
api layer works well with create, retrieve records
Yusuprozimemet 1dd6de3
filter, pagination limit, sorting by timestamp are added
Yusuprozimemet 0cd6c86
delete and replace are added
Yusuprozimemet 9adb541
added summary endpoint
Yusuprozimemet d524baf
add analytics summary endpoint and API design documentation
Yusuprozimemet File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| /mvnw text eol=lf | ||
| *.cmd text eol=crlf |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| HELP.md | ||
| target/ | ||
| .mvn/wrapper/maven-wrapper.jar | ||
| !**/src/main/**/target/ | ||
| !**/src/test/**/target/ | ||
|
|
||
| ### STS ### | ||
| .apt_generated | ||
| .classpath | ||
| .factorypath | ||
| .project | ||
| .settings | ||
| .springBeans | ||
| .sts4-cache | ||
|
|
||
| ### IntelliJ IDEA ### | ||
| .idea | ||
| *.iws | ||
| *.iml | ||
| *.ipr | ||
|
|
||
| ### NetBeans ### | ||
| /nbproject/private/ | ||
| /nbbuild/ | ||
| /dist/ | ||
| /nbdist/ | ||
| /.nb-gradle/ | ||
| build/ | ||
| !**/src/main/**/build/ | ||
| !**/src/test/**/build/ | ||
|
|
||
| ### VS Code ### | ||
| .vscode/ |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| wrapperVersion=3.3.4 | ||
| distributionType=only-script | ||
| distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,311 @@ | ||
| # API Design - Analytics API | ||
|
|
||
| ## Base URL | ||
|
|
||
| `/api` | ||
|
|
||
| ## Endpoint overview | ||
|
|
||
| ```mermaid | ||
| graph TD | ||
| Client([Client]) | ||
|
|
||
| subgraph Records ["Resource: /api/analytics-records"] | ||
| POST["POST /api/analytics-records | ||
| Body: timestamp, eventType, eventSource, sessionId | ||
| 201 Created, 400 Bad Request"] | ||
|
|
||
| GET_LIST["GET /api/analytics-records | ||
| Filters: from, to, eventType, eventSource, sessionId | ||
| Pagination: limit, offset | ||
| 200 OK"] | ||
|
|
||
| subgraph ByID ["By ID: /api/analytics-records/{id}"] | ||
| GET_ONE["GET /{id} | ||
| 200 OK, 404 Not Found"] | ||
|
|
||
| PUT["PUT /{id} | ||
| Body: timestamp, eventType, eventSource, sessionId | ||
| 200 OK, 404 Not Found, 400 Bad Request"] | ||
|
|
||
| DEL["DELETE /{id} | ||
| 204 No Content, 404 Not Found"] | ||
| end | ||
| end | ||
|
|
||
| subgraph Summary ["Resource: /api/analytics-summary"] | ||
| GET_SUM["GET /api/analytics-summary | ||
| Filters: from, to, eventType, eventSource, sessionId | ||
| 200 OK: totalRecords, totalsByEventType, uniqueSessions"] | ||
| end | ||
|
|
||
| Client -->|create| POST | ||
| Client -->|list + filter| GET_LIST | ||
| Client -->|fetch one| GET_ONE | ||
| Client -->|replace one| PUT | ||
| Client -->|delete one| DEL | ||
| Client -->|get summary| GET_SUM | ||
| ``` | ||
|
|
||
| ## Resource: analytics records | ||
|
|
||
| ### Data model | ||
|
|
||
| AnalyticsRecord | ||
|
|
||
| ``` | ||
| { | ||
| "id": "uuid", | ||
| "timestamp": "2026-05-28T12:34:56Z", | ||
| "eventType": "page_view", | ||
| "eventSource": "web", | ||
| "sessionId": "anon-123" | ||
| } | ||
| ``` | ||
|
|
||
| Notes: | ||
| - `id` is server-generated and acts as the trace ID. | ||
| - `timestamp` uses ISO-8601 in UTC (e.g., `2026-05-28T12:34:56Z`). | ||
| - `sessionId` must be anonymized. No personal data is allowed. | ||
|
|
||
| ```mermaid | ||
| classDiagram | ||
| class AnalyticsRecord { | ||
| +String id | ||
| +Instant timestamp | ||
| +String eventType | ||
| +String eventSource | ||
| +String sessionId | ||
| } | ||
|
|
||
| class AnalyticsRecordCreateRequest { | ||
| +Instant timestamp | ||
| +String eventType | ||
| +String eventSource | ||
| +String sessionId | ||
| } | ||
|
|
||
| class AnalyticsRecordReplaceRequest { | ||
| +Instant timestamp | ||
| +String eventType | ||
| +String eventSource | ||
| +String sessionId | ||
| } | ||
|
|
||
| class AnalyticsSummary { | ||
| +long totalRecords | ||
| +Map~String, Long~ totalsByEventType | ||
| +long uniqueSessions | ||
| } | ||
|
|
||
| class ErrorResponse { | ||
| +Instant timestamp | ||
| +int status | ||
| +String error | ||
| +String message | ||
| +String path | ||
| +List~FieldError~ details | ||
| } | ||
|
|
||
| AnalyticsRecordCreateRequest ..> AnalyticsRecord : server adds id | ||
| AnalyticsRecordReplaceRequest ..> AnalyticsRecord : server keeps id | ||
| ``` | ||
|
|
||
| ## Endpoints | ||
|
|
||
| ### Create record | ||
|
|
||
| `POST /api/analytics-records` | ||
|
|
||
| Request body (AnalyticsRecordCreateRequest) | ||
|
|
||
| ``` | ||
| { | ||
| "timestamp": "2026-05-28T12:34:56Z", | ||
| "eventType": "page_view", | ||
| "eventSource": "web", | ||
| "sessionId": "anon-123" | ||
| } | ||
| ``` | ||
|
|
||
| Validation rules | ||
| - `timestamp`: required, valid ISO-8601. | ||
| - `eventType`: required, non-blank, length 1-64. | ||
| - `eventSource`: required, non-blank, length 1-64. | ||
| - `sessionId`: required, non-blank, length 1-64. | ||
|
|
||
| Response | ||
| - `201 Created` with the created record. | ||
| - `Location` header points to `/api/analytics-records/{id}`. | ||
|
|
||
| ```mermaid | ||
| sequenceDiagram | ||
| participant Client | ||
| participant API as API Layer (Controller) | ||
| participant Service as Business Logic (Service) | ||
| participant DB as Database Layer (Repository) | ||
|
|
||
| Client->>API: POST /api/analytics-records<br/>{ timestamp, eventType, eventSource, sessionId } | ||
| API->>API: deserialize JSON into AnalyticsRecordCreateRequest DTO | ||
| API->>API: validate fields (not blank, length 1-64, ISO-8601) | ||
| API-->>Client: 400 Bad Request + error details (if validation fails) | ||
| API->>Service: create(request) | ||
| Service->>Service: generate UUID as trace ID | ||
| Service->>Service: build AnalyticsRecord model | ||
| Service->>DB: save(record) | ||
| DB-->>Service: saved AnalyticsRecord | ||
| Service-->>API: AnalyticsRecord | ||
| API->>API: set Location header to /api/analytics-records/{id} | ||
| API-->>Client: 201 Created + { id, timestamp, eventType, eventSource, sessionId } | ||
| ``` | ||
|
|
||
| ### List records (with filters) | ||
|
|
||
| `GET /api/analytics-records` | ||
|
|
||
| Query parameters (all optional) | ||
| - `from`: ISO-8601 start time, inclusive. | ||
| - `to`: ISO-8601 end time, inclusive. | ||
| - `eventType`: filter by event type. | ||
| - `eventSource`: filter by event source. | ||
| - `sessionId`: filter by anonymized session ID. | ||
| - `limit`: default 100. | ||
| - `offset`: default 0. | ||
|
|
||
| Behavior | ||
| - Filters are combined with AND. | ||
| - If only `from` is provided, return records on or after `from`. | ||
| - If only `to` is provided, return records on or before `to`. | ||
| - If both are provided and `from` is after `to`, an empty list is returned. | ||
| - Results are sorted by `timestamp` descending. | ||
|
|
||
| Response | ||
| - `200 OK` with a JSON array of records. | ||
|
|
||
| ### Get record by ID | ||
|
|
||
| `GET /api/analytics-records/{id}` | ||
|
|
||
| Response | ||
| - `200 OK` with the record. | ||
| - `404 Not Found` if the ID does not exist. | ||
|
|
||
| ### Replace record by ID | ||
|
|
||
| `PUT /api/analytics-records/{id}` | ||
|
|
||
| Request body (AnalyticsRecordReplaceRequest) | ||
|
|
||
| ``` | ||
| { | ||
| "timestamp": "2026-05-28T12:34:56Z", | ||
| "eventType": "page_view", | ||
| "eventSource": "web", | ||
| "sessionId": "anon-123" | ||
| } | ||
| ``` | ||
|
|
||
| Validation rules | ||
| - Same as create. | ||
| - Full replace: all fields are required. | ||
|
|
||
| Response | ||
| - `200 OK` with the updated record. | ||
| - `404 Not Found` if the ID does not exist. | ||
|
|
||
| ### Delete record by ID | ||
|
|
||
| `DELETE /api/analytics-records/{id}` | ||
|
|
||
| Response | ||
| - `204 No Content` if deleted. | ||
| - `404 Not Found` if the ID does not exist. | ||
|
|
||
| ### Summary | ||
|
|
||
| `GET /api/analytics-summary` | ||
|
|
||
| Query parameters (optional, same as list) | ||
| - `from`, `to`, `eventType`, `eventSource`, `sessionId` | ||
|
|
||
| Response body | ||
|
|
||
| ``` | ||
| { | ||
| "totalRecords": 123, | ||
| "totalsByEventType": { | ||
| "page_view": 100, | ||
| "signup": 23 | ||
| }, | ||
| "uniqueSessions": 17 | ||
| } | ||
| ``` | ||
|
|
||
| Response | ||
| - `200 OK` with the summary of records that match the filters. | ||
|
|
||
| ## Error responses | ||
|
|
||
| All error responses use the following shape: | ||
|
|
||
| ``` | ||
| { | ||
| "timestamp": "2026-05-28T12:34:56Z", | ||
| "status": 400, | ||
| "error": "Bad Request", | ||
| "message": "Validation failed", | ||
| "path": "/api/analytics-records", | ||
| "details": [ | ||
| { "field": "eventType", "message": "must not be blank" } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| Common errors | ||
| - `400 Bad Request` for validation failures (missing or blank required fields, invalid JSON). | ||
| - `404 Not Found` when a record ID does not exist. | ||
|
|
||
| ## Architecture | ||
|
|
||
| ```mermaid | ||
| graph LR | ||
| Client([Client]) | ||
|
|
||
| subgraph "API Layer" | ||
| C1["AnalyticsController"] | ||
| C2["AnalyticsSummaryController"] | ||
| C3["GlobalExceptionHandler"] | ||
| end | ||
|
|
||
| subgraph "Business Logic Layer" | ||
| S1["AnalyticsService"] | ||
| end | ||
|
|
||
| subgraph "DTOs" | ||
| D1["AnalyticsRecordCreateRequest"] | ||
| D2["AnalyticsRecordReplaceRequest"] | ||
| D3["AnalyticsSummary"] | ||
| D4["ErrorResponse"] | ||
| end | ||
|
|
||
| subgraph "Model" | ||
| M1["AnalyticsRecord"] | ||
| end | ||
|
|
||
| subgraph "Database Layer" | ||
| R1["AnalyticsRepository<br/>(in-memory List)"] | ||
| end | ||
|
|
||
| Client -->|HTTP| C1 | ||
| Client -->|HTTP| C2 | ||
| C1 --> S1 | ||
| C2 --> S1 | ||
| S1 --> R1 | ||
| C1 -.->|uses| D1 | ||
| C1 -.->|uses| D2 | ||
| C1 -.->|uses| D4 | ||
| C2 -.->|uses| D3 | ||
| C3 -.->|produces| D4 | ||
| S1 -.->|uses| M1 | ||
| R1 -.->|stores| M1 | ||
| ``` | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice mermaid graph! Love it 👍