Skip to content

Commit

Permalink
feat: add api structs + "create" route (#162)
Browse files Browse the repository at this point in the history
* feat: add events groups

* chore: add generate type

* feat: start adding infra & endpoints for groups

* chore: remove unused files for now

* test: add basic infra tests

* feat: add api structs + "create" route
  • Loading branch information
GuillaumeDecMeetsMore authored Dec 12, 2024
1 parent 48af2a8 commit 3aab60e
Show file tree
Hide file tree
Showing 14 changed files with 478 additions and 0 deletions.
22 changes: 22 additions & 0 deletions clients/javascript/lib/gen_types/CreateEventGroupRequestBody.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ID } from './ID'

/**
* Request body for creating an event
*/
export type CreateEventGroupRequestBody = {
/**
* UUID of the calendar where the event group will be created
*/
calendarId: ID
/**
* Optional parent event ID
* This is useful for external applications that need to link Nittei's events to a wider data model (e.g. a project, an order, etc.)
*/
parentId?: string
/**
* Optional external event ID
* This is useful for external applications that need to link Nittei's events to their own data models
*/
externalId?: string
}
23 changes: 23 additions & 0 deletions clients/javascript/lib/gen_types/EventGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,34 @@ import type { ID } from './ID'
* Group of calendar events
*/
export type EventGroup = {
/**
* Unique ID
*/
id: ID
/**
* Calendar ID to which the group belongs
*/
calendarId: ID
/**
* User ID
*/
userId: ID
/**
* Account ID
*/
accountId: ID
/**
* Parent ID - this is an ID external to the system
* It allows to link groups of events together to an outside entity
*/
parentId: string | null
/**
* External ID - this is an ID external to the system
* It allows to link a group of events to an outside entity
*/
externalId: string | null
/**
* List of event IDs in the group
*/
eventIds: Array<ID>
}
28 changes: 28 additions & 0 deletions clients/javascript/lib/gen_types/EventGroupDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ID } from './ID'

/**
* Calendar event object
*/
export type EventGroupDTO = {
/**
* UUID of the event
*/
id: ID
/**
* Optional parent event ID
*/
parentId: string | null
/**
* Optional external ID
*/
externalId: string | null
/**
* UUID of the calendar
*/
calendarId: ID
/**
* UUID of the user
*/
userId: ID
}
12 changes: 12 additions & 0 deletions clients/javascript/lib/gen_types/EventGroupResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { EventGroupDTO } from './EventGroupDTO'

/**
* Calendar event response object
*/
export type EventGroupResponse = {
/**
* Calendar event retrieved
*/
eventGroup: EventGroupDTO
}
18 changes: 18 additions & 0 deletions clients/javascript/lib/gen_types/UpdateEventGroupRequestBody.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

/**
* Request body for updating an event
*/
export type UpdateEventGroupRequestBody = {
/**
* Optional parent event ID
* This is useful for external applications that need to link Nittei's events to a wider data model (e.g. a project, an order, etc.)
*/
parentId?: string
/**
* Optional external event ID
* This is useful for external applications that need to link Nittei's events to their own data models
* Default is None
*/
externalId?: string
}
4 changes: 4 additions & 0 deletions clients/javascript/lib/gen_types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@ export * from './CalendarSettingsDTO'
export * from './CreateAccountRequestBody'
export * from './CreateAccountResponseBody'
export * from './CreateCalendarRequestBody'
export * from './CreateEventGroupRequestBody'
export * from './CreateEventRequestBody'
export * from './CreateServiceEventIntendRequestBody'
export * from './CreateServiceRequestBody'
export * from './CreateUserRequestBody'
export * from './DateTimeQuery'
export * from './EventGroup'
export * from './EventGroupDTO'
export * from './EventGroupResponse'
export * from './EventInstance'
export * from './EventWithInstancesDTO'
export * from './GetCalendarEventsAPIResponse'
Expand Down Expand Up @@ -88,6 +91,7 @@ export * from './TimePlan'
export * from './TimeSpan'
export * from './UpdateCalendarRequestBody'
export * from './UpdateCalendarSettings'
export * from './UpdateEventGroupRequestBody'
export * from './UpdateEventRequestBody'
export * from './UpdateServiceRequestBody'
export * from './UpdateServiceUserRequestBody'
Expand Down
175 changes: 175 additions & 0 deletions crates/api/src/event_group/create_event_group.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
use actix_web::{web, HttpRequest, HttpResponse};
use nittei_api_structs::create_event_group::*;
use nittei_domain::{event_group::EventGroup, User, ID};
use nittei_infra::NitteiContext;

use crate::{
error::NitteiError,
shared::{
auth::{account_can_modify_user, protect_account_route},
usecase::{execute, UseCase},
},
};

pub async fn create_event_group_admin_controller(
http_req: HttpRequest,
path_params: web::Path<PathParams>,
body: actix_web_validator::Json<RequestBody>,
ctx: web::Data<NitteiContext>,
) -> Result<HttpResponse, NitteiError> {
let account = protect_account_route(&http_req, &ctx).await?;
let user = account_can_modify_user(&account, &path_params.user_id, &ctx).await?;

let body = body.0;
let usecase = CreateEventGroupUseCase {
parent_id: body.parent_id,
external_id: body.external_id,
user,
calendar_id: body.calendar_id,
};

execute(usecase, &ctx)
.await
.map(|group| HttpResponse::Created().json(APIResponse::new(group)))
.map_err(NitteiError::from)
}

#[derive(Debug, Default)]
pub struct CreateEventGroupUseCase {
pub calendar_id: ID,
pub user: User,
pub parent_id: Option<String>,
pub external_id: Option<String>,
}

#[derive(Debug, PartialEq)]
pub enum UseCaseError {
NotFound(ID),
StorageError,
}

impl From<UseCaseError> for NitteiError {
fn from(e: UseCaseError) -> Self {
match e {
UseCaseError::NotFound(calendar_id) => Self::NotFound(format!(
"The calendar with id: {}, was not found.",
calendar_id
)),
UseCaseError::StorageError => Self::InternalError,
}
}
}

impl From<anyhow::Error> for UseCaseError {
fn from(_: anyhow::Error) -> Self {
UseCaseError::StorageError
}
}

#[async_trait::async_trait(?Send)]
impl UseCase for CreateEventGroupUseCase {
type Response = EventGroup;

type Error = UseCaseError;

const NAME: &'static str = "CreateEvent";

async fn execute(&mut self, ctx: &NitteiContext) -> Result<Self::Response, Self::Error> {
let calendar = ctx
.repos
.calendars
.find(&self.calendar_id)
.await
.map_err(|_| UseCaseError::StorageError)?;
let calendar = match calendar {
Some(calendar) if calendar.user_id == self.user.id => calendar,
_ => return Err(UseCaseError::NotFound(self.calendar_id.clone())),
};

let g = EventGroup {
id: Default::default(),
parent_id: self.parent_id.clone(),
external_id: self.external_id.clone(),
calendar_id: calendar.id.clone(),
user_id: self.user.id.clone(),
account_id: self.user.account_id.clone(),
event_ids: vec![],
};

ctx.repos.event_groups.insert(&g).await?;

Ok(g)
}
}

#[cfg(test)]
mod test {
use nittei_domain::{Account, Calendar, User};
use nittei_infra::setup_context;

use super::*;

struct TestContext {
ctx: NitteiContext,
calendar: Calendar,
user: User,
}

async fn setup() -> TestContext {
let ctx = setup_context().await.unwrap();
let account = Account::default();
ctx.repos.accounts.insert(&account).await.unwrap();
let user = User::new(account.id.clone(), None);
ctx.repos.users.insert(&user).await.unwrap();
let calendar = Calendar::new(&user.id, &account.id, None, None);
ctx.repos.calendars.insert(&calendar).await.unwrap();

TestContext {
user,
calendar,
ctx,
}
}

#[actix_web::main]
#[test]
async fn creates_event_group() {
let TestContext {
ctx,
calendar,
user,
} = setup().await;

let mut usecase = CreateEventGroupUseCase {
calendar_id: calendar.id.clone(),
user,
..Default::default()
};

let res = usecase.execute(&ctx).await;

assert!(res.is_ok());
}

#[actix_web::main]
#[test]
async fn rejects_invalid_calendar_id() {
let TestContext {
ctx,
calendar: _,
user,
} = setup().await;

let mut usecase = CreateEventGroupUseCase {
user,
..Default::default()
};

let res = usecase.execute(&ctx).await;
assert!(res.is_err());
assert_eq!(
res.unwrap_err(),
UseCaseError::NotFound(usecase.calendar_id)
);
}
}
13 changes: 13 additions & 0 deletions crates/api/src/event_group/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
mod create_event_group;

use actix_web::web;
use create_event_group::create_event_group_admin_controller;

// Configure the routes for the event_group module
pub fn configure_routes(cfg: &mut web::ServiceConfig) {
// Create an event for a user (admin route)
cfg.route(
"/user/{user_id}/event_group",
web::post().to(create_event_group_admin_controller),
);
}
2 changes: 2 additions & 0 deletions crates/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod account;
mod calendar;
mod error;
mod event;
mod event_group;
mod http_logger;
mod job_schedulers;
mod schedule;
Expand Down Expand Up @@ -41,6 +42,7 @@ pub fn configure_server_api(cfg: &mut web::ServiceConfig) {
account::configure_routes(cfg);
calendar::configure_routes(cfg);
event::configure_routes(cfg);
event_group::configure_routes(cfg);
schedule::configure_routes(cfg);
service::configure_routes(cfg);
status::configure_routes(cfg);
Expand Down
Loading

0 comments on commit 3aab60e

Please sign in to comment.