Compare commits

..

2 commits

Author SHA1 Message Date
powermaker450 9843459d22 Basic API docs 2024-09-16 12:14:50 -04:00
powermaker450 7b59fc5e4e Return different errors for invalid ID length and no review being found 2024-09-15 10:20:06 -04:00
6 changed files with 168 additions and 8 deletions

View file

@ -22,4 +22,4 @@ pnpm build
pnpm start pnpm start
``` ```
To post a review to the server, use [Simple Review Client](https://git.povario.com/powermaker450/simple-review-client) To post a review to the server, use [Simple Review Client](https://git.povario.com/powermaker450/simple-review-client), or consult the [API Docs](https://git.povario.com/powermaker450/simple-review-server/src/branch/main/docs/API.md)

4
docs/API.md Normal file
View file

@ -0,0 +1,4 @@
# API Docs
### [POST](https://git.povario.com/powermaker450/simple-review-server/src/branch/main/docs/POST.md)
### [GET](https://git.povario.com/powermaker450/simple-review-server/src/branch/main/docs/GET.md)

75
docs/GET.md Normal file
View file

@ -0,0 +1,75 @@
# GET
## `/api/reviews/`
**Response Codes:** `200` \
**Response Format:** `JSON`
### Example response:
```
[
{
"username": "bob",
"rating": 4.5,
"title": "All fields",
"content": "This user review contains all required and allowed fields!",
"id": "a1b2c3",
"timestamp": "2024-09-12T03a:55:23.830Z"
},
{
"username": "sarah",
"rating": 4.5,
"title": "Only a title",
"content": null,
"id": "e1f2g3",
"timestamp": "2024-09-12T03a:56:23.830Z"
},
{
"username": "phillip",
"rating": 4.5,
"title": null,
"content": null,
"id": "h1i2j3",
"timestamp": "2024-09-12T03a:57:23.830Z"
}
]
```
---
## `/api/reviews/:id`
**Response Codes:** `200`, `404`, `400` \
**Response Format:** `JSON`
## Example requests and responses:
### Good Request
`GET /api/reviews/a1b2c3`
**Server Response (`200`):**
```
{
"username": "bob",
"rating": 4.5,
"title": "All fields",
"content": "This user review contains all required and allowed fields!",
"timestamp": "2024-09-12T03a:55:23.830Z"
}
```
### Bad Request
`GET /api/reviews/a`
**Server Response (`400`)**:
```
{
error: {
"type": "requestError",
"message": "review id must be 6 characters"
}
}
```
---

59
docs/POST.md Normal file
View file

@ -0,0 +1,59 @@
# POST
## `/api/post`
**Encoding:** `JSON` \
**Response Codes:** `201`, `400`, `500` \
**Response Format:** `JSON`
**Schema:**
| **Field** | **Type** | **Info** | **Required** |
| --------- | -------- | ---------------- | ------------ |
| username | string | 2-30 characters | `true` |
| rating | number | from 0.5-5, only .5 increments | `true` |
| title | string | 0-50 characters | `false` |
| content | string | 0-2000 characters | `false` |
## Example Objects (being submitted by a client):
### Good Request
```
{
"username": "bob",
"rating": 4.5,
"title": "All fields",
"content": "This user review contains all required and allowed fields!"
}
```
**Server Response (`200`):**
```
{
"message": "review was sent"
}
```
### Bad Request
```
{
"username": "hacker",
"rating": -3
}
```
**Server Response (`400`):**
```
{
"error": {
"type": "ValidationError",
"message": "rating must be a positive number"
}
}
```
**Additional info:** When submitted, the server will add two more fields to your review and store this info server side:
| **Field** | **Type** | **Info** |
| --------- | -------- | -------- |
| id | string | 6 randomly-generated characters |
| timestamp | string | ISO timestamp |
---

View file

@ -22,20 +22,33 @@ export class MessagesResponder extends ApiRoute {
}); });
this.server.get(`${this.routeName}/:id`, (req: IdRequest, res: Response) => { this.server.get(`${this.routeName}/:id`, (req: IdRequest, res: Response) => {
// "req.params.id" is the review ID that was receieved
// "result" is the review that is identified by that ID, if any
const receiver = req.headers["user-agent"]; const receiver = req.headers["user-agent"];
const result = data.getReviewById(req.params.id); const result = data.getReviewById(req.params.id);
let err = false;
if (Object.keys(result).length) { if (req.params.id.length === 6) {
res.writeHead(200, typeJson); if (Object.keys(result).length) {
res.write(JSON.stringify(result)); res.writeHead(200, typeJson);
res.end(); res.write(JSON.stringify(result));
res.end();
} else {
res.writeHead(404, typeJson);
res.write(Responder.notFoundError("review not found"));
res.end();
err = true;
}
} else { } else {
res.writeHead(404, typeJson); res.writeHead(400, typeJson);
res.write(Responder.requestError("review not found")); res.write(Responder.requestError("review id must be 6 characters"));
res.end(); res.end();
err = true;
} }
this.logger.log(`${Logger.emp(receiver)} <~ "${req.path}"`); // If an error was returned to the client, mark their user agent red in the logs
this.logger.log(`${err ? Logger.err(receiver) : Logger.emp(receiver)} <~ "${req.path}"`);
}); });
this.complete(); this.complete();

View file

@ -42,4 +42,13 @@ export class Responder {
}, },
}); });
} }
public static notFoundError(errorMessage: string): string {
return JSON.stringify({
error: {
type: "notFoundError",
message: errorMessage
}
});
}
} }