Skip to content

Commit 2f85cdb

Browse files
committed
chore: update return type for send and batch-send to also return the error
1 parent 6fb0177 commit 2f85cdb

9 files changed

Lines changed: 80 additions & 27 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ diesel migration run
8080
- [x] Templating engine
8181
- [ ] Component system
8282
- [ ] i18n
83-
- [ ] simple if and for logic
83+
- [x] simple if and for logic
8484
- API Routes
8585
- [x] Sending mail
8686
- [x] Send to mailing list

backend/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "meel"
3-
version = "0.0.5"
3+
version = "0.0.6"
44
edition = "2021"
55

66
[dependencies]

backend/src/routes/mails.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use std::sync::Arc;
99
use std::time::SystemTime;
1010

1111
use crate::database::models::{Mail, NewMail};
12+
use crate::templating::TemplateDataMap;
1213
use crate::utils::api_error::{ApiError, ApiErrorCode};
1314
use crate::{database, templating, utils};
14-
use crate::templating::TemplateDataMap;
1515

1616
#[derive(Deserialize)]
1717
pub struct SendMailRequest {
@@ -126,7 +126,8 @@ pub async fn send_mail(
126126
// Setting allow_html to true here is a bit of a hack, as if we don't it will replace spaces
127127
// and special characters with html equivalents, which we don't want.
128128
true,
129-
).map_err(|err| {
129+
)
130+
.map_err(|err| {
130131
ApiError::new(
131132
StatusCode::BAD_REQUEST,
132133
ApiErrorCode::Unknown,
@@ -180,15 +181,23 @@ pub async fn send_mail(
180181
pub async fn send_mails(
181182
pool: Extension<Arc<database::ConnectionPool>>,
182183
Json(payload): Json<Vec<SendMailRequest>>,
183-
) -> Result<Json<Vec<SendMailResponse>>, ApiError> {
184-
let mut mails: Vec<Mail> = vec![];
184+
) -> Result<Json<Vec<Result<SendMailResponse, ApiError>>>, ApiError> {
185+
let mut mails: Vec<Result<Mail, ApiError>> = vec![];
185186

186187
for mail_payload in payload {
187-
let created_mail = send_mail(pool.clone(), mail_payload).await?;
188+
let created_mail = send_mail(pool.clone(), mail_payload).await;
188189
mails.push(created_mail);
189190
}
190191

191-
Ok(Json(mails.into_iter().map(SendMailResponse::new).collect()))
192+
Ok(Json(
193+
mails
194+
.into_iter()
195+
.map(|mail| match mail {
196+
Ok(mail) => Ok(SendMailResponse::new(mail)),
197+
Err(err) => Err(err),
198+
})
199+
.collect(),
200+
))
192201
}
193202

194203
pub async fn get_mail_status(

backend/src/utils/api_error.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ pub enum ApiErrorCode {
1111
NotFound,
1212
}
1313

14-
#[derive(Debug)]
14+
#[derive(Debug, Serialize)]
1515
pub struct ApiError {
16-
pub status_code: StatusCode,
16+
pub status_code: u16,
1717
pub error_code: ApiErrorCode,
1818
pub message: String,
1919
pub details: HashMap<String, String>,
@@ -22,7 +22,7 @@ pub struct ApiError {
2222
impl ApiError {
2323
pub fn new(status_code: StatusCode, error_code: ApiErrorCode, message: String, details: HashMap<String, String>) -> Self {
2424
Self {
25-
status_code,
25+
status_code: status_code.as_u16(),
2626
error_code,
2727
message,
2828
details,
@@ -33,12 +33,13 @@ impl ApiError {
3333
impl IntoResponse for ApiError {
3434
fn into_response(self) -> Response {
3535
let body = json!({
36-
"status_code": self.status_code.as_u16(),
36+
"status_code": self.status_code,
3737
"error_code": self.error_code,
3838
"message": self.message,
3939
"details": self.details
4040
});
4141

42-
(self.status_code, body.to_string()).into_response()
42+
// this unwrap should be safe, as we have a valid status code
43+
(StatusCode::from_u16(self.status_code).unwrap(), body.to_string()).into_response()
4344
}
4445
}

sdk/node/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "meel",
33
"description": "Typescript wrapper for the Meel project",
4-
"version": "0.0.12",
4+
"version": "0.0.13",
55
"author": {
66
"name": "borisnliscool",
77
"url": "https://github.com/borisnliscool"
@@ -20,7 +20,8 @@
2020
"build": "rimraf dist && bun build.ts",
2121
"prepublish": "bun run build",
2222
"lint": "bun run prettier src/ --check && bun run eslint",
23-
"format": "bun run prettier . --write"
23+
"format": "bun run prettier . --write",
24+
"test": "bun test"
2425
},
2526
"peerDependencies": {
2627
"typescript": "^5.0.0"

sdk/node/src/classes/meel-error.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1+
export interface MeelErrorConstructor {
2+
status_code: number;
3+
error_code: number;
4+
message: string;
5+
details: object;
6+
}
7+
18
export class MeelError extends Error {
29
public readonly message: string;
310
public readonly code: number;
11+
public readonly details: object;
12+
public readonly error_code: number;
413

5-
public constructor(message: string, code: number) {
6-
super(message);
7-
this.message = message;
8-
this.code = code;
14+
public constructor(data: MeelErrorConstructor) {
15+
super(data.message);
16+
this.message = data.message;
17+
this.code = data.status_code;
18+
this.details = data.details;
19+
this.error_code = data.error_code;
920
}
1021

1122
public toString(): string {

sdk/node/src/classes/meel-sender.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import Try from '@borisnl/tried';
1+
import { Try } from '@borisnl/tried';
22
import ky from 'ky';
3-
import { Meel, MeelError, SentMeel, SentMeelConstructor } from '.';
3+
import { Meel, MeelError, MeelErrorConstructor, SentMeel, SentMeelConstructor } from '.';
44

55
/**
66
* MeelSender is a class that sends Meel instances to a specified base url.
@@ -36,7 +36,7 @@ export class MeelSender {
3636
* @return {Promise<SentMeel>} SentMeel instance
3737
* @throws {MeelError} If the mail could not be sent
3838
*/
39-
public async send(mail: Meel): Promise<SentMeel> {
39+
public async send(mail: Meel): Promise<(SentMeel | MeelError)> {
4040
return this.batchSend([mail]).then(data => data[0]);
4141
}
4242

@@ -47,10 +47,14 @@ export class MeelSender {
4747
* @returns {Promise<SentMeel>} SentMeel instance
4848
* @throws {MeelError} If the mail could not be sent
4949
*/
50-
public async batchSend(mails: Meel[]): Promise<SentMeel[]> {
50+
public async batchSend(mails: Meel[]): Promise<(SentMeel | MeelError)[]> {
5151
const response = await Try(() =>
5252
ky
53-
.post<SentMeelConstructor[]>(`${this.baseUrl}/mails/send`, {
53+
.post<({
54+
Ok: SentMeelConstructor
55+
} | {
56+
Err: MeelErrorConstructor
57+
})[]>(`${this.baseUrl}/mails/send`, {
5458
body: JSON.stringify(
5559
mails.map(mail => mail.toPlainObject()),
5660
),
@@ -62,9 +66,14 @@ export class MeelSender {
6266
);
6367

6468
if (!response) {
65-
throw new MeelError('Failed to send mail', 500);
69+
throw new MeelError({
70+
status_code: 500,
71+
error_code: 0,
72+
message: 'Failed to send mail',
73+
details: {},
74+
});
6675
}
6776

68-
return response.map((data: SentMeelConstructor) => new SentMeel(data));
77+
return response.map((data) => ('Ok' in data) ? new SentMeel(data.Ok) : new MeelError(data.Err));
6978
}
7079
}

sdk/node/test/test.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { test, expect } from "bun:test";
2+
3+
import { Meel, MeelSender, SentMeel } from '../src';
4+
5+
const baseUrl = 'http://localhost:8080'
6+
7+
test('send email', async () => {
8+
const sender = new MeelSender({ baseUrl });
9+
10+
const meel = new Meel({
11+
subject: "Hello world",
12+
recipient: "Boris <boris@example.com>",
13+
sender: "me@example.com",
14+
template: "test",
15+
data: {
16+
names: ["john doe", "jane doe"],
17+
redacted: false
18+
},
19+
});
20+
21+
expect(await sender.send(meel)).toBeInstanceOf(SentMeel)
22+
})

0 commit comments

Comments
 (0)