Skip to main content

alibi_api/plugins/organization/
native.rs

1//! Typed logical organization operations, independent of HTTP request construction.
2use super::{OrganizationPlugin, handlers, types};
3use crate::plugins::endpoint::{definition, error_response, validation};
4use alibi_core::endpoint::{
5    EndpointCall, EndpointDefinition, EndpointInput, EndpointResponse, ServerEndpoint,
6};
7use alibi_core::entity::{AuthOrganization, AuthUser};
8use alibi_core::session::SessionRequest;
9use alibi_core::types::{Team, TeamMember};
10use alibi_core::utils::json::JsValue;
11use alibi_core::wire::InvitationView;
12use alibi_core::{AuthContext, AuthError, AuthResponse, AuthResult, AuthSchema, HttpMethod};
13
14pub(super) fn definitions() -> Vec<EndpointDefinition> {
15    vec![
16        definition(
17            "listOrganizations",
18            "listOrganizations",
19            Some("/organization/list"),
20            HttpMethod::Get,
21        ),
22        definition(
23            "updateOrganization",
24            "updateOrganization",
25            Some("/organization/update"),
26            HttpMethod::Post,
27        ),
28        definition(
29            "getOrganization",
30            "getOrganization",
31            Some("/organization/get-organization"),
32            HttpMethod::Get,
33        ),
34        definition(
35            "getFullOrganization",
36            "getFullOrganization",
37            Some("/organization/get-full-organization"),
38            HttpMethod::Get,
39        ),
40        definition(
41            "checkOrganizationSlug",
42            "checkOrganizationSlug",
43            Some("/organization/check-slug"),
44            HttpMethod::Post,
45        ),
46        definition(
47            "setActiveOrganization",
48            "setActiveOrganization",
49            Some("/organization/set-active"),
50            HttpMethod::Post,
51        ),
52        definition(
53            "leaveOrganization",
54            "leaveOrganization",
55            Some("/organization/leave"),
56            HttpMethod::Post,
57        ),
58        definition(
59            "inviteMember",
60            "inviteMember",
61            Some("/organization/invite-member"),
62            HttpMethod::Post,
63        ),
64        definition(
65            "getInvitation",
66            "getInvitation",
67            Some("/organization/get-invitation"),
68            HttpMethod::Get,
69        ),
70        definition(
71            "listInvitations",
72            "listInvitations",
73            Some("/organization/list-invitations"),
74            HttpMethod::Get,
75        ),
76        definition(
77            "listUserInvitations",
78            "listUserInvitations",
79            Some("/organization/list-user-invitations"),
80            HttpMethod::Get,
81        ),
82        definition(
83            "acceptInvitation",
84            "acceptInvitation",
85            Some("/organization/accept-invitation"),
86            HttpMethod::Post,
87        ),
88        definition(
89            "rejectInvitation",
90            "rejectInvitation",
91            Some("/organization/reject-invitation"),
92            HttpMethod::Post,
93        ),
94        definition(
95            "cancelInvitation",
96            "cancelInvitation",
97            Some("/organization/cancel-invitation"),
98            HttpMethod::Post,
99        ),
100        definition(
101            "getActiveMember",
102            "getActiveMember",
103            Some("/organization/get-active-member"),
104            HttpMethod::Get,
105        ),
106        definition(
107            "getActiveMemberRole",
108            "getActiveMemberRole",
109            Some("/organization/get-active-member-role"),
110            HttpMethod::Get,
111        ),
112        definition(
113            "listMembers",
114            "listMembers",
115            Some("/organization/list-members"),
116            HttpMethod::Get,
117        ),
118        definition(
119            "updateMemberRole",
120            "updateMemberRole",
121            Some("/organization/update-member-role"),
122            HttpMethod::Post,
123        ),
124        definition(
125            "hasPermission",
126            "hasPermission",
127            Some("/organization/has-permission"),
128            HttpMethod::Post,
129        ),
130        definition(
131            "createTeam",
132            "createTeam",
133            Some("/organization/create-team"),
134            HttpMethod::Post,
135        ),
136        definition(
137            "updateTeam",
138            "updateTeam",
139            Some("/organization/update-team"),
140            HttpMethod::Post,
141        ),
142        definition(
143            "removeTeam",
144            "removeTeam",
145            Some("/organization/remove-team"),
146            HttpMethod::Post,
147        ),
148        definition(
149            "setActiveTeam",
150            "setActiveTeam",
151            Some("/organization/set-active-team"),
152            HttpMethod::Post,
153        ),
154        definition(
155            "addTeamMember",
156            "addTeamMember",
157            Some("/organization/add-team-member"),
158            HttpMethod::Post,
159        ),
160        definition(
161            "removeTeamMember",
162            "removeTeamMember",
163            Some("/organization/remove-team-member"),
164            HttpMethod::Post,
165        ),
166        definition(
167            "listTeams",
168            "listTeams",
169            Some("/organization/list-teams"),
170            HttpMethod::Get,
171        ),
172        definition(
173            "listUserTeams",
174            "listUserTeams",
175            Some("/organization/list-user-teams"),
176            HttpMethod::Get,
177        ),
178        definition(
179            "listTeamMembers",
180            "listTeamMembers",
181            Some("/organization/list-team-members"),
182            HttpMethod::Get,
183        ),
184        definition(
185            "createOrganizationRole",
186            "createOrganizationRole",
187            Some("/organization/create-role"),
188            HttpMethod::Post,
189        ),
190        definition(
191            "updateOrganizationRole",
192            "updateOrganizationRole",
193            Some("/organization/update-role"),
194            HttpMethod::Post,
195        ),
196        definition(
197            "deleteOrganizationRole",
198            "deleteOrganizationRole",
199            Some("/organization/delete-role"),
200            HttpMethod::Post,
201        ),
202        definition(
203            "getOrganizationRole",
204            "getOrganizationRole",
205            Some("/organization/get-role"),
206            HttpMethod::Get,
207        ),
208        definition(
209            "listOrganizationRoles",
210            "listOrganizationRoles",
211            Some("/organization/list-roles"),
212            HttpMethod::Get,
213        ),
214    ]
215}
216
217pub(super) fn validate(call: &EndpointCall) -> AuthResult<EndpointInput> {
218    // Native calls validate logical values after before hooks, without media-type or URL decoding.
219    match call.operation_id() {
220        "updateOrganization" => {
221            drop(handlers::org_input::update_value(call.body().cloned()).map_err(error_response)?);
222        }
223        "getOrganization" => {
224            let _: types::GetOrganizationQuery = call
225                .query_as()
226                .map_err(|error| validation(error.to_string()))?;
227        }
228        "getFullOrganization" => {
229            let _: types::GetFullOrganizationQuery = call
230                .query_as()
231                .map_err(|error| validation(error.to_string()))?;
232        }
233        "checkOrganizationSlug" => {
234            let _: types::CheckSlugRequest = call
235                .body_as()
236                .map_err(|error| validation(error.to_string()))?;
237        }
238        "setActiveOrganization" => {
239            drop(
240                handlers::org_input::set_active_value(call.body().cloned())
241                    .map_err(error_response)?,
242            );
243        }
244        "leaveOrganization" => {
245            let _: types::LeaveOrganizationRequest = call
246                .body_as()
247                .map_err(|error| validation(error.to_string()))?;
248        }
249        "inviteMember" => {
250            drop(
251                handlers::org_input::invitation_create_value(call.body().cloned())
252                    .map_err(error_response)?,
253            );
254        }
255        "getInvitation" => {
256            let _: types::GetInvitationQuery = call
257                .query_as()
258                .map_err(|error| validation(error.to_string()))?;
259        }
260        "listInvitations" => {
261            let _: types::ListInvitationsQuery = call
262                .query_as()
263                .map_err(|error| validation(error.to_string()))?;
264        }
265        "listUserInvitations" => {
266            let _: types::ListUserInvitationsQuery = call
267                .query_as()
268                .map_err(|error| validation(error.to_string()))?;
269        }
270        "acceptInvitation" => {
271            let _: types::AcceptInvitationRequest = call
272                .body_as()
273                .map_err(|error| validation(error.to_string()))?;
274        }
275        "rejectInvitation" => {
276            let _: types::RejectInvitationRequest = call
277                .body_as()
278                .map_err(|error| validation(error.to_string()))?;
279        }
280        "cancelInvitation" => {
281            let _: types::CancelInvitationRequest = call
282                .body_as()
283                .map_err(|error| validation(error.to_string()))?;
284        }
285        "getActiveMemberRole" => {
286            let _: types::GetActiveMemberRoleQuery = call
287                .query_as()
288                .map_err(|error| validation(error.to_string()))?;
289        }
290        "listMembers" => {
291            let _: types::ListMembersQuery = call
292                .query_as()
293                .map_err(|error| validation(error.to_string()))?;
294        }
295        "updateMemberRole" => {
296            drop(
297                handlers::org_input::member_role_update_value(call.body().cloned())
298                    .map_err(error_response)?,
299            );
300        }
301        "hasPermission" => {
302            let _: types::HasPermissionRequest = call
303                .body_as()
304                .map_err(|error| validation(error.to_string()))?;
305        }
306        "createTeam" => {
307            let _: types::CreateTeamRequest = call
308                .body_as()
309                .map_err(|error| validation(error.to_string()))?;
310        }
311        "updateTeam" => {
312            let _: types::UpdateTeamRequest = call
313                .body_as()
314                .map_err(|error| validation(error.to_string()))?;
315        }
316        "removeTeam" => {
317            let _: types::RemoveTeamRequest = call
318                .body_as()
319                .map_err(|error| validation(error.to_string()))?;
320        }
321        "setActiveTeam" => {
322            let _: types::SetActiveTeamRequest = call
323                .body_as()
324                .map_err(|error| validation(error.to_string()))?;
325        }
326        "addTeamMember" => {
327            let _: types::TeamMemberRequest = call
328                .body_as()
329                .map_err(|error| validation(error.to_string()))?;
330        }
331        "removeTeamMember" => {
332            let _: types::TeamMemberRequest = call
333                .body_as()
334                .map_err(|error| validation(error.to_string()))?;
335        }
336        "listTeams" => {
337            let _: types::TeamQuery = call
338                .query_as()
339                .map_err(|error| validation(error.to_string()))?;
340        }
341        "listUserTeams" => {
342            let _: types::TeamQuery = call
343                .query_as()
344                .map_err(|error| validation(error.to_string()))?;
345        }
346        "listTeamMembers" => {
347            let _: types::TeamQuery = call
348                .query_as()
349                .map_err(|error| validation(error.to_string()))?;
350        }
351        "createOrganizationRole" => {
352            let _: types::CreateRoleRequest = call
353                .body_as()
354                .map_err(|error| validation(error.to_string()))?;
355        }
356        "updateOrganizationRole" => {
357            let _: types::UpdateRoleRequest = call
358                .body_as()
359                .map_err(|error| validation(error.to_string()))?;
360        }
361        "deleteOrganizationRole" => {
362            let _: types::RoleQuery = call
363                .body_as()
364                .map_err(|error| validation(error.to_string()))?;
365        }
366        "getOrganizationRole" => {
367            let _: types::RoleQuery = call
368                .query_as()
369                .map_err(|error| validation(error.to_string()))?;
370        }
371        "listOrganizationRoles" => {
372            let _: types::RoleQuery = call
373                .query_as()
374                .map_err(|error| validation(error.to_string()))?;
375        }
376        _ => {}
377    }
378    Ok(EndpointInput {
379        body: call.body().cloned(),
380        query: call.query().cloned(),
381    })
382}
383
384impl OrganizationPlugin {
385    /// Construct a typed native call through installed dispatch hooks.
386    /// # Errors
387    /// Returns a serialization error for input that cannot be represented.
388    #[must_use]
389    pub fn list_organizations_endpoint() -> ServerEndpoint<Vec<types::OrganizationResponse>> {
390        ServerEndpoint::new("organization", "listOrganizations")
391    }
392    /// Construct a typed native call through installed dispatch hooks.
393    /// # Errors
394    /// Returns a serialization error for input that cannot be represented.
395    pub fn update_endpoint(
396        input: &types::UpdateOrganizationRequest,
397    ) -> AuthResult<ServerEndpoint<Option<types::CreatedOrganizationResponse>>> {
398        ServerEndpoint::new("organization", "updateOrganization").with_body(input)
399    }
400    /// Construct a typed native call through installed dispatch hooks.
401    /// # Errors
402    /// Returns a serialization error for input that cannot be represented.
403    pub fn get_organization_endpoint(
404        input: &types::GetOrganizationQuery,
405    ) -> AuthResult<ServerEndpoint<Option<types::OrganizationResponse>>> {
406        ServerEndpoint::new("organization", "getOrganization").with_query(input)
407    }
408    /// Construct a typed native call through installed dispatch hooks.
409    /// # Errors
410    /// Returns a serialization error for input that cannot be represented.
411    pub fn get_full_organization_endpoint(
412        input: &types::GetFullOrganizationQuery,
413    ) -> AuthResult<
414        ServerEndpoint<
415            Option<types::FullOrganizationResponse<types::OrganizationResponse, InvitationView>>,
416        >,
417    > {
418        ServerEndpoint::new("organization", "getFullOrganization").with_query(input)
419    }
420    /// Construct a typed native call through installed dispatch hooks.
421    /// # Errors
422    /// Returns a serialization error for input that cannot be represented.
423    pub fn check_slug_endpoint(
424        input: &types::CheckSlugRequest,
425    ) -> AuthResult<ServerEndpoint<types::CheckSlugResponse>> {
426        ServerEndpoint::new("organization", "checkOrganizationSlug").with_body(input)
427    }
428    /// Construct a typed native call through installed dispatch hooks.
429    /// # Errors
430    /// Returns a serialization error for input that cannot be represented.
431    pub fn set_active_endpoint(
432        input: &types::SetActiveOrganizationRequest,
433    ) -> AuthResult<ServerEndpoint<Option<types::OrganizationResponse>>> {
434        ServerEndpoint::new("organization", "setActiveOrganization").with_body(input)
435    }
436    /// Construct a typed native call through installed dispatch hooks.
437    /// # Errors
438    /// Returns a serialization error for input that cannot be represented.
439    pub fn leave_endpoint(
440        input: &types::LeaveOrganizationRequest,
441    ) -> AuthResult<ServerEndpoint<types::SuccessResponse>> {
442        ServerEndpoint::new("organization", "leaveOrganization").with_body(input)
443    }
444    /// Construct a typed native call through installed dispatch hooks.
445    /// # Errors
446    /// Returns a serialization error for input that cannot be represented.
447    pub fn invite_member_endpoint(
448        input: &types::InviteMemberRequest,
449    ) -> AuthResult<ServerEndpoint<InvitationView>> {
450        ServerEndpoint::new("organization", "inviteMember").with_body(input)
451    }
452    /// Construct a typed native call through installed dispatch hooks.
453    /// # Errors
454    /// Returns a serialization error for input that cannot be represented.
455    pub fn get_invitation_endpoint(
456        input: &types::GetInvitationQuery,
457    ) -> AuthResult<ServerEndpoint<types::GetInvitationResponse<InvitationView>>> {
458        ServerEndpoint::new("organization", "getInvitation").with_query(input)
459    }
460    /// Construct a typed native call through installed dispatch hooks.
461    /// # Errors
462    /// Returns a serialization error for input that cannot be represented.
463    pub fn list_invitations_endpoint(
464        input: &types::ListInvitationsQuery,
465    ) -> AuthResult<ServerEndpoint<Vec<InvitationView>>> {
466        ServerEndpoint::new("organization", "listInvitations").with_query(input)
467    }
468    /// Construct a typed native call through installed dispatch hooks.
469    /// # Errors
470    /// Returns a serialization error for input that cannot be represented.
471    pub fn list_user_invitations_endpoint(
472        input: &types::ListUserInvitationsQuery,
473    ) -> AuthResult<ServerEndpoint<Vec<types::UserInvitationResponse<InvitationView>>>> {
474        ServerEndpoint::new("organization", "listUserInvitations").with_query(input)
475    }
476    /// Construct a typed native call through installed dispatch hooks.
477    /// # Errors
478    /// Returns a serialization error for input that cannot be represented.
479    pub fn accept_invitation_endpoint(
480        input: &types::AcceptInvitationRequest,
481    ) -> AuthResult<
482        ServerEndpoint<types::AcceptInvitationResponse<InvitationView, types::BasicMemberResponse>>,
483    > {
484        ServerEndpoint::new("organization", "acceptInvitation").with_body(input)
485    }
486    /// Construct a typed native call through installed dispatch hooks.
487    /// # Errors
488    /// Returns a serialization error for input that cannot be represented.
489    pub fn reject_invitation_endpoint(
490        input: &types::RejectInvitationRequest,
491    ) -> AuthResult<
492        ServerEndpoint<
493            types::AcceptInvitationResponse<InvitationView, Option<types::BasicMemberResponse>>,
494        >,
495    > {
496        ServerEndpoint::new("organization", "rejectInvitation").with_body(input)
497    }
498    /// Construct a typed native call through installed dispatch hooks.
499    /// # Errors
500    /// Returns a serialization error for input that cannot be represented.
501    pub fn cancel_invitation_endpoint(
502        input: &types::CancelInvitationRequest,
503    ) -> AuthResult<ServerEndpoint<InvitationView>> {
504        ServerEndpoint::new("organization", "cancelInvitation").with_body(input)
505    }
506    /// Construct a typed native call through installed dispatch hooks.
507    /// # Errors
508    /// Returns a serialization error for input that cannot be represented.
509    #[must_use]
510    pub fn get_active_member_endpoint() -> ServerEndpoint<types::MemberResponse> {
511        ServerEndpoint::new("organization", "getActiveMember")
512    }
513    /// Construct a typed native call through installed dispatch hooks.
514    /// # Errors
515    /// Returns a serialization error for input that cannot be represented.
516    pub fn get_active_member_role_endpoint(
517        input: &types::GetActiveMemberRoleQuery,
518    ) -> AuthResult<ServerEndpoint<types::GetActiveMemberRoleResponse>> {
519        ServerEndpoint::new("organization", "getActiveMemberRole").with_query(input)
520    }
521    /// Construct a typed native call through installed dispatch hooks.
522    /// # Errors
523    /// Returns a serialization error for input that cannot be represented.
524    pub fn list_members_endpoint(
525        input: &types::ListMembersQuery,
526    ) -> AuthResult<ServerEndpoint<types::ListMembersResponse>> {
527        ServerEndpoint::new("organization", "listMembers").with_query(input)
528    }
529    /// Construct a typed native call through installed dispatch hooks.
530    /// # Errors
531    /// Returns a serialization error for input that cannot be represented.
532    pub fn update_member_role_endpoint(
533        input: &types::UpdateMemberRoleRequest,
534    ) -> AuthResult<ServerEndpoint<types::BasicMemberResponse>> {
535        ServerEndpoint::new("organization", "updateMemberRole").with_body(input)
536    }
537    /// Construct a typed native call through installed dispatch hooks.
538    /// # Errors
539    /// Returns a serialization error for input that cannot be represented.
540    pub fn has_permission_endpoint(
541        input: &types::HasPermissionRequest,
542    ) -> AuthResult<ServerEndpoint<types::HasPermissionResponse>> {
543        ServerEndpoint::new("organization", "hasPermission").with_body(input)
544    }
545    /// Construct a typed native call through installed dispatch hooks.
546    /// # Errors
547    /// Returns a serialization error for input that cannot be represented.
548    pub fn create_team_endpoint(
549        input: &types::CreateTeamRequest,
550    ) -> AuthResult<ServerEndpoint<Team>> {
551        ServerEndpoint::new("organization", "createTeam").with_body(input)
552    }
553    /// Construct a typed native call through installed dispatch hooks.
554    /// # Errors
555    /// Returns a serialization error for input that cannot be represented.
556    pub fn update_team_endpoint(
557        input: &types::UpdateTeamRequest,
558    ) -> AuthResult<ServerEndpoint<Team>> {
559        ServerEndpoint::new("organization", "updateTeam").with_body(input)
560    }
561    /// Construct a typed native call through installed dispatch hooks.
562    /// # Errors
563    /// Returns a serialization error for input that cannot be represented.
564    pub fn remove_team_endpoint(
565        input: &types::RemoveTeamRequest,
566    ) -> AuthResult<ServerEndpoint<types::MessageResponse>> {
567        ServerEndpoint::new("organization", "removeTeam").with_body(input)
568    }
569    /// Construct a typed native call through installed dispatch hooks.
570    /// # Errors
571    /// Returns a serialization error for input that cannot be represented.
572    pub fn set_active_team_endpoint(
573        input: &types::SetActiveTeamRequest,
574    ) -> AuthResult<ServerEndpoint<Option<Team>>> {
575        ServerEndpoint::new("organization", "setActiveTeam").with_body(input)
576    }
577    /// Construct a typed native call through installed dispatch hooks.
578    /// # Errors
579    /// Returns a serialization error for input that cannot be represented.
580    pub fn add_team_member_endpoint(
581        input: &types::TeamMemberRequest,
582    ) -> AuthResult<ServerEndpoint<TeamMember>> {
583        ServerEndpoint::new("organization", "addTeamMember").with_body(input)
584    }
585    /// Construct a typed native call through installed dispatch hooks.
586    /// # Errors
587    /// Returns a serialization error for input that cannot be represented.
588    pub fn remove_team_member_endpoint(
589        input: &types::TeamMemberRequest,
590    ) -> AuthResult<ServerEndpoint<types::MessageResponse>> {
591        ServerEndpoint::new("organization", "removeTeamMember").with_body(input)
592    }
593    /// Construct a typed native call through installed dispatch hooks.
594    /// # Errors
595    /// Returns a serialization error for input that cannot be represented.
596    pub fn list_teams_endpoint(input: &types::TeamQuery) -> AuthResult<ServerEndpoint<Vec<Team>>> {
597        ServerEndpoint::new("organization", "listTeams").with_query(input)
598    }
599    /// Construct a typed native call through installed dispatch hooks.
600    /// # Errors
601    /// Returns a serialization error for input that cannot be represented.
602    pub fn list_user_teams_endpoint(
603        input: &types::TeamQuery,
604    ) -> AuthResult<ServerEndpoint<Vec<Team>>> {
605        ServerEndpoint::new("organization", "listUserTeams").with_query(input)
606    }
607    /// Construct a typed native call through installed dispatch hooks.
608    /// # Errors
609    /// Returns a serialization error for input that cannot be represented.
610    pub fn list_team_members_endpoint(
611        input: &types::TeamQuery,
612    ) -> AuthResult<ServerEndpoint<Vec<TeamMember>>> {
613        ServerEndpoint::new("organization", "listTeamMembers").with_query(input)
614    }
615    /// Construct a typed native call through installed dispatch hooks.
616    /// # Errors
617    /// Returns a serialization error for input that cannot be represented.
618    pub fn create_role_endpoint(
619        input: &types::CreateRoleRequest,
620    ) -> AuthResult<ServerEndpoint<types::CreateRoleResponse>> {
621        ServerEndpoint::new("organization", "createOrganizationRole").with_body(input)
622    }
623    /// Construct a typed native call through installed dispatch hooks.
624    /// # Errors
625    /// Returns a serialization error for input that cannot be represented.
626    pub fn update_role_endpoint(
627        input: &types::UpdateRoleRequest,
628    ) -> AuthResult<ServerEndpoint<types::UpdateRoleResponse>> {
629        ServerEndpoint::new("organization", "updateOrganizationRole").with_body(input)
630    }
631    /// Construct a typed native call through installed dispatch hooks.
632    /// # Errors
633    /// Returns a serialization error for input that cannot be represented.
634    pub fn delete_role_endpoint(
635        input: &types::RoleQuery,
636    ) -> AuthResult<ServerEndpoint<types::SuccessResponse>> {
637        ServerEndpoint::new("organization", "deleteOrganizationRole").with_body(input)
638    }
639    /// Construct a typed native call through installed dispatch hooks.
640    /// # Errors
641    /// Returns a serialization error for input that cannot be represented.
642    pub fn get_role_endpoint(
643        input: &types::RoleQuery,
644    ) -> AuthResult<ServerEndpoint<types::RoleResponse>> {
645        ServerEndpoint::new("organization", "getOrganizationRole").with_query(input)
646    }
647    /// Construct a typed native call through installed dispatch hooks.
648    /// # Errors
649    /// Returns a serialization error for input that cannot be represented.
650    pub fn list_roles_endpoint(
651        input: &types::RoleQuery,
652    ) -> AuthResult<ServerEndpoint<Vec<types::RoleResponse>>> {
653        ServerEndpoint::new("organization", "listOrganizationRoles").with_query(input)
654    }
655}
656
657fn response(output: AuthResponse) -> AuthResult<EndpointResponse> {
658    let value = if output.body.is_empty() {
659        JsValue::Null
660    } else {
661        alibi_core::utils::json::from_slice::<JsValue>(&output.body)?
662    };
663    let mut response = if output.status >= 400 {
664        EndpointResponse::error(error_response(output.clone())).with_error_body(value)
665    } else {
666        EndpointResponse::value(value)
667    };
668    response.set_status(Some(output.status));
669    response.merge_headers(output.headers);
670    Ok(response)
671}
672
673fn query_pairs(call: &EndpointCall) -> AuthResult<std::collections::HashMap<String, String>> {
674    call.query()
675        .and_then(JsValue::as_object)
676        .map(|fields| {
677            fields
678                .iter()
679                .map(|(key, value)| {
680                    value
681                        .coerce_string()
682                        .map(|value| (key.clone(), value))
683                        .map_err(validation)
684                })
685                .collect()
686        })
687        .unwrap_or_else(|| Ok(Default::default()))
688}
689
690pub(super) async fn execute<S: AuthSchema>(
691    plugin: &OrganizationPlugin,
692    call: &EndpointCall,
693    ctx: &AuthContext<S>,
694) -> AuthResult<EndpointResponse> {
695    let config = &plugin.config;
696    if call.operation_id() == "listUserInvitations" {
697        let query: types::ListUserInvitationsQuery = call.query_as()?;
698        if let Some(email) = query.email.as_deref().filter(|email| !email.is_empty()) {
699            if call.request().is_some() {
700                return Err(AuthError::bad_request(
701                    "User email cannot be passed for client side API calls.",
702                ));
703            }
704            return EndpointResponse::json(
705                &handlers::invitation::list_user_invitations_for_email_core(email, ctx).await?,
706            );
707        }
708    }
709    let (user, session) = ctx.require_cached_session(call).await.map_err(|error| {
710        if matches!(
711            error,
712            AuthError::Unauthenticated | AuthError::SessionNotFound
713        ) {
714            handlers::extension_common::org_error(401, "UNAUTHORIZED")
715        } else {
716            error
717        }
718    })?;
719    match call.operation_id() {
720        "listOrganizations" => {
721            EndpointResponse::json(&handlers::org::list_organizations_core(&user, ctx).await?)
722        }
723        "updateOrganization" => {
724            let (body, metadata) =
725                handlers::org_input::update_value(call.body().cloned()).map_err(error_response)?;
726            EndpointResponse::json(
727                &handlers::org::update_organization_core(
728                    &body, metadata, &user, &session, config, ctx,
729                )
730                .await?,
731            )
732        }
733        "getOrganization" => {
734            let query: types::GetOrganizationQuery = call.query_as()?;
735            let organization =
736                if let Some(slug) = query.organization_slug.as_deref().filter(|s| !s.is_empty()) {
737                    ctx.database.get_organization_by_slug(slug).await?
738                } else if let Some(id) = query
739                    .organization_id
740                    .as_deref()
741                    .filter(|s| !s.is_empty())
742                    .or(session.active_organization_id.as_deref())
743                {
744                    ctx.database.get_organization_by_id(id).await?
745                } else {
746                    return EndpointResponse::json(&Option::<types::OrganizationResponse>::None);
747                }
748                .ok_or_else(|| AuthError::bad_request("Organization not found"))?;
749            if ctx
750                .database
751                .get_member(&organization.id(), &user.id())
752                .await?
753                .is_none()
754            {
755                drop(
756                    ctx.database
757                        .update_session_active_organization_record(&session.token, None)
758                        .await?,
759                );
760                return Err(AuthError::forbidden(
761                    "User is not a member of the organization",
762                ));
763            }
764            EndpointResponse::json(&types::OrganizationResponse::from_stored_organization(
765                &organization,
766            )?)
767        }
768        "getFullOrganization" => EndpointResponse::json(
769            &handlers::org::get_full_organization_core(
770                &call.query_as()?,
771                &user,
772                &session,
773                config,
774                ctx,
775            )
776            .await
777            .map_err(AuthError::from)?,
778        ),
779        "checkOrganizationSlug" => {
780            EndpointResponse::json(&handlers::org::check_slug_core(&call.body_as()?, ctx).await?)
781        }
782        "setActiveOrganization" => {
783            let body = handlers::org_input::set_active_value(call.body().cloned())
784                .map_err(error_response)?;
785            let organization =
786                handlers::org::set_active_organization_core(&body, &user, &session, ctx).await?;
787            if organization.is_some()
788                || session
789                    .active_organization_id
790                    .as_deref()
791                    .is_some_and(|id| !id.is_empty())
792            {
793                use alibi_core::utils::cookie_utils::{
794                    create_session_cookie_with_max_age, create_session_like_cookie,
795                    related_cookie_name, sign_cookie_value, verify_cookie_value,
796                };
797                let preference = related_cookie_name(&ctx.config, "dont_remember");
798                let dont_remember = call.session_headers().get("cookie").is_some_and(|header| {
799                    cookie::Cookie::split_parse(header)
800                        .flatten()
801                        .find(|cookie| cookie.name() == preference)
802                        .and_then(|cookie| {
803                            verify_cookie_value(cookie.value(), ctx.config.current_secret())
804                        })
805                        .is_some_and(|value| !value.is_empty())
806                });
807                call.queue_response_header(
808                    "set-cookie",
809                    create_session_cookie_with_max_age(
810                        Some(&session.token),
811                        (!dont_remember).then(|| ctx.config.session.expires_in.num_seconds()),
812                        &ctx.config,
813                    )?,
814                );
815                if dont_remember {
816                    call.queue_response_header(
817                        "set-cookie",
818                        create_session_like_cookie(
819                            &preference,
820                            &sign_cookie_value("true", ctx.config.current_secret()),
821                            None,
822                            &ctx.config,
823                        )?,
824                    );
825                }
826            }
827            EndpointResponse::json(&organization)
828        }
829        "leaveOrganization" => EndpointResponse::json(
830            &handlers::org::leave_organization_core(&call.body_as()?, &user, &session, config, ctx)
831                .await?,
832        ),
833        "inviteMember" => {
834            let body = handlers::org_input::invitation_create_value(call.body().cloned())
835                .map_err(error_response)?;
836            EndpointResponse::json(
837                &handlers::invitation::invite_member_core(&body, &user, &session, config, ctx)
838                    .await?,
839            )
840        }
841        "getInvitation" => EndpointResponse::json(
842            &handlers::invitation::get_invitation_core(&call.query_as()?, &user, config, ctx)
843                .await?,
844        ),
845        "listInvitations" => EndpointResponse::json(
846            &handlers::invitation::list_invitations_core(&call.query_as()?, &user, &session, ctx)
847                .await?,
848        ),
849        "listUserInvitations" => EndpointResponse::json(
850            &handlers::invitation::list_user_invitations_core(&user, ctx).await?,
851        ),
852        "acceptInvitation" => {
853            let transport = handlers::invitation_acceptance::AcceptanceTransport::native(call);
854            let result = handlers::invitation_acceptance::accept(
855                &call.body_as()?,
856                &user,
857                &session,
858                config,
859                ctx,
860                &transport,
861            )
862            .await;
863            if matches!(result, Err(AuthError::Internal(_) | AuthError::Database(_))) {
864                transport.discard_cookies();
865            }
866            EndpointResponse::json(&result?)
867        }
868        "rejectInvitation" => EndpointResponse::json(
869            &handlers::invitation::reject_invitation_core(&call.body_as()?, &user, config, ctx)
870                .await?,
871        ),
872        "cancelInvitation" => EndpointResponse::json(
873            &handlers::invitation::cancel_invitation_core(&call.body_as()?, &user, config, ctx)
874                .await?,
875        ),
876        "getActiveMember" => EndpointResponse::json(
877            &handlers::member::get_active_member_core(&user, &session, ctx).await?,
878        ),
879        "getActiveMemberRole" => EndpointResponse::json(
880            &handlers::member::get_active_member_role_core(&call.query_as()?, &user, &session, ctx)
881                .await?,
882        ),
883        "listMembers" => EndpointResponse::json(
884            &handlers::member::list_members_core(&call.query_as()?, &user, &session, config, ctx)
885                .await
886                .map_err(AuthError::from)?,
887        ),
888        "updateMemberRole" => {
889            let body = handlers::org_input::member_role_update_value(call.body().cloned())
890                .map_err(error_response)?;
891            response(
892                handlers::member::update_member_role_response(&body, &user, &session, config, ctx)
893                    .await?,
894            )
895        }
896        "hasPermission" => EndpointResponse::json(
897            &handlers::has_permission_core(&call.body_as()?, &user, &session, config, ctx).await?,
898        ),
899        "createTeam" | "updateTeam" | "removeTeam" | "setActiveTeam" | "addTeamMember"
900        | "removeTeamMember" | "listTeams" | "listUserTeams" | "listTeamMembers" => {
901            let path = match call.operation_id() {
902                "createTeam" => "/organization/create-team",
903                "updateTeam" => "/organization/update-team",
904                "removeTeam" => "/organization/remove-team",
905                "setActiveTeam" => "/organization/set-active-team",
906                "addTeamMember" => "/organization/add-team-member",
907                "removeTeamMember" => "/organization/remove-team-member",
908                "listTeams" => "/organization/list-teams",
909                "listUserTeams" => "/organization/list-user-teams",
910                "listTeamMembers" => "/organization/list-team-members",
911                _ => return Err(AuthError::not_found("Unknown operation")),
912            };
913            let method = call.default_method();
914            let query = query_pairs(call)?;
915            let body = call
916                .body()
917                .map(JsValue::to_json_value)
918                .transpose()
919                .map_err(|error| validation(error.to_string()))?;
920            let result = handlers::team::team_core(
921                method,
922                path,
923                body,
924                &query,
925                user,
926                session,
927                call.request(),
928                false,
929                ctx,
930                config,
931            )
932            .await?;
933            response(result.ok_or_else(|| AuthError::not_found("Unknown operation"))?)
934        }
935        "createOrganizationRole"
936        | "updateOrganizationRole"
937        | "deleteOrganizationRole"
938        | "getOrganizationRole"
939        | "listOrganizationRoles" => {
940            let path = match call.operation_id() {
941                "createOrganizationRole" => "/organization/create-role",
942                "updateOrganizationRole" => "/organization/update-role",
943                "deleteOrganizationRole" => "/organization/delete-role",
944                "getOrganizationRole" => "/organization/get-role",
945                "listOrganizationRoles" => "/organization/list-roles",
946                _ => return Err(AuthError::not_found("Unknown operation")),
947            };
948            let method = call.default_method();
949            let query = query_pairs(call)?;
950            let body = call
951                .body()
952                .map(JsValue::to_json_value)
953                .transpose()
954                .map_err(|error| validation(error.to_string()))?
955                .and_then(|value| value.as_object().cloned())
956                .unwrap_or_default();
957            let result =
958                handlers::role::role_core(method, path, body, &query, call, ctx, config).await?;
959            response(result.ok_or_else(|| AuthError::not_found("Unknown operation"))?)
960        }
961        _ => Err(AuthError::not_found("Unregistered organization operation")),
962    }
963}