diff --git a/api-collection/Workspace Membership Controller/Remove users from workspace.bru b/api-collection/Workspace Membership Controller/Remove users from workspace.bru index b4272c56..5d7a7e9b 100644 --- a/api-collection/Workspace Membership Controller/Remove users from workspace.bru +++ b/api-collection/Workspace Membership Controller/Remove users from workspace.bru @@ -10,6 +10,20 @@ delete { auth: none } +params:query { + userEmails: user1@gmail.com,user2@gmail.com +} + params:path { workspace_slug: workspace-1-cahli } + +docs { + ## Description + + Remove one or more users from the workspace. + + ### Request Query Params + + The endpoint accepts a string of comma separated emails +} diff --git a/apps/api/src/workspace-membership/controller/workspace-membership.controller.ts b/apps/api/src/workspace-membership/controller/workspace-membership.controller.ts index 2e561ebb..32c827f9 100644 --- a/apps/api/src/workspace-membership/controller/workspace-membership.controller.ts +++ b/apps/api/src/workspace-membership/controller/workspace-membership.controller.ts @@ -53,12 +53,12 @@ export class WorkspaceMembershipController { async removeUsers( @CurrentUser() user: User, @Param('workspaceSlug') workspaceSlug: Workspace['slug'], - @Body() userEmails: User['email'][] + @Query('userEmails') userEmails: string = '' ) { return this.workspaceMembershipService.removeUsersFromWorkspace( user, workspaceSlug, - userEmails + userEmails.split(/\s*,\s*/) ) } diff --git a/apps/api/src/workspace-membership/workspace-membership.e2e.spec.ts b/apps/api/src/workspace-membership/workspace-membership.e2e.spec.ts index 01954cff..a606a17d 100644 --- a/apps/api/src/workspace-membership/workspace-membership.e2e.spec.ts +++ b/apps/api/src/workspace-membership/workspace-membership.e2e.spec.ts @@ -524,7 +524,9 @@ describe('Workspace Membership Controller Tests', () => { 'x-e2e-user-email': user1.email }, url: `/workspace-membership/${workspace1.slug}/remove-users`, - payload: [user2.id] + query: { + userEmails: user2.email + } }) expect(response.statusCode).toBe(200) @@ -548,7 +550,9 @@ describe('Workspace Membership Controller Tests', () => { 'x-e2e-user-email': user1.email }, url: `/workspace-membership/${workspace1.slug}/remove-users`, - payload: [user1.email] + query: { + userEmails: user1.email + } }) expect(response.statusCode).toBe(400) diff --git a/packages/api-client/src/controllers/workspace-membership.ts b/packages/api-client/src/controllers/workspace-membership.ts index d49f8475..5f6f750a 100644 --- a/packages/api-client/src/controllers/workspace-membership.ts +++ b/packages/api-client/src/controllers/workspace-membership.ts @@ -22,7 +22,7 @@ import { IsMemberResponse, GetMembersRequest, GetMembersResponse -} from '@api-client/types/workspace-membership.types' +} from '@keyshade/schema' import { ClientResponse } from '@keyshade/schema' export default class WorkspaceMembershipController { @@ -61,7 +61,7 @@ export default class WorkspaceMembershipController { headers?: Record ): Promise> { const response = await this.apiClient.delete( - `/api/workspace-membership/${request.workspaceSlug}/remove-users`, + `/api/workspace-membership/${request.workspaceSlug}/remove-users?userEmails=${request.userEmails}`, headers ) return await parseResponse(response) diff --git a/packages/api-client/src/core/client.ts b/packages/api-client/src/core/client.ts index 3ff85d6c..67df7df3 100644 --- a/packages/api-client/src/core/client.ts +++ b/packages/api-client/src/core/client.ts @@ -71,9 +71,14 @@ export class APIClient { * Sends a DELETE request to the specified URL and returns a Promise that resolves to the response data. * * @param url - The URL to send the DELETE request to. + * @param headers - Optional headers to include in the request. * @returns A Promise that resolves to the response data. */ - delete(url: string, headers?: Record): Promise { + delete( + url: string, + headers?: Record, + data?: any + ): Promise { return this.request(url, { method: 'DELETE', headers: { diff --git a/packages/api-client/src/types/workspace-membership.types.d.ts b/packages/api-client/src/types/workspace-membership.types.d.ts deleted file mode 100644 index df22dff6..00000000 --- a/packages/api-client/src/types/workspace-membership.types.d.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { Page } from '../../../../apps/cli/src/types/index.types' - -enum Authority { - CREATE_PROJECT, - READ_USERS, - ADD_USER, - REMOVE_USER, - UPDATE_USER_ROLE, - READ_WORKSPACE, - UPDATE_WORKSPACE, - DELETE_WORKSPACE, - CREATE_WORKSPACE_ROLE, - READ_WORKSPACE_ROLE, - UPDATE_WORKSPACE_ROLE, - DELETE_WORKSPACE_ROLE, - WORKSPACE_ADMIN, - READ_PROJECT, - UPDATE_PROJECT, - DELETE_PROJECT, - CREATE_SECRET, - READ_SECRET, - UPDATE_SECRET, - DELETE_SECRET, - CREATE_ENVIRONMENT, - READ_ENVIRONMENT, - UPDATE_ENVIRONMENT, - DELETE_ENVIRONMENT, - CREATE_VARIABLE, - READ_VARIABLE, - UPDATE_VARIABLE, - DELETE_VARIABLE, - CREATE_INTEGRATION, - READ_INTEGRATION, - UPDATE_INTEGRATION, - DELETE_INTEGRATION, - CREATE_WORKSPACE, - CREATE_API_KEY, - READ_API_KEY, - UPDATE_API_KEY, - DELETE_API_KEY, - UPDATE_PROFILE, - READ_SELF, - UPDATE_SELF, - READ_EVENT -} - -export interface CreateWorkspaceMember { - email: string - roleSlugs: string[] -} - -export interface TransferOwnershipRequest { - workspaceSlug: string - userEmail: string -} - -export interface TransferOwnershipResponse {} - -export interface InviteUsersRequest { - emails: string[] - workspaceSlug: string - members: CreateWorkspaceMember[] -} - -export interface InviteUsersResponse {} - -export interface RemoveUsersRequest { - workspaceSlug: string - userEmails: string[] -} - -export interface RemoveUsersResponse {} - -export interface UpdateMemberRoleRequest { - workspaceSlug: string - userEmail: string - roleSlugs: string[] -} - -export interface UpdateMemberRoleResponse {} - -export interface AcceptInvitationRequest { - workspaceSlug: string -} - -export interface AcceptInvitationResponse {} - -export interface DeclineInvitationRequest { - workspaceSlug: string -} - -export interface DeclineInvitationResponse {} - -export interface CancelInvitationRequest { - workspaceSlug: string - userEmail: string -} - -export interface CancelInvitationResponse {} - -export interface LeaveWorkspaceRequest { - workspaceSlug: string -} - -export interface LeaveWorkspaceResponse {} - -export interface IsMemberRequest { - workspaceSlug: string - userEmail: string -} - -export type IsMemberResponse = boolean - -export interface GetMembersRequest extends Page { - workspaceSlug: string - page?: number - limit?: number - sort?: string - order?: string - search?: string -} - -export interface GetMembersResponse - extends Page<{ - metadata: Record - id: string - user: { - id: string - email: string - name: string | null - } - roles: { - id: string - role: { - id: string - name: string - description: string | null - colorCode: string | null - authorities: Authority[] - projects: { - id: string - }[] - } - }[] - }> {} diff --git a/packages/api-client/tests/config/setup.ts b/packages/api-client/tests/config/setup.ts index 1a4e0f20..6d281d36 100644 --- a/packages/api-client/tests/config/setup.ts +++ b/packages/api-client/tests/config/setup.ts @@ -31,7 +31,7 @@ function executeCommand( function startAPI(): Promise { return new Promise((resolve) => { - const apiProcess = exec('cd ../../ && pnpm run start:api', { + const apiProcess = exec('pnpm run --filter=api start', { env: { PATH: process.env.PATH, DATABASE_URL: 'postgresql://prisma:prisma@localhost:5432/tests', diff --git a/packages/api-client/tests/workspace-membership.spec.ts b/packages/api-client/tests/workspace-membership.spec.ts index 412ad4b3..ff067b97 100644 --- a/packages/api-client/tests/workspace-membership.spec.ts +++ b/packages/api-client/tests/workspace-membership.spec.ts @@ -58,7 +58,7 @@ describe('Workspace Membership Controller Tests', () => { it('should remove users', async () => { const request = { workspaceSlug: workspaceSlug!, - userEmails: ['invitee@example.com'] + userEmails: 'invitee@example.com' } const response = await workspaceMembershipController.removeUsers(request, { 'x-e2e-user-email': userEmail diff --git a/packages/eslint-config-custom/package.json b/packages/eslint-config-custom/package.json index ed0f9c8c..1f2cbcae 100644 --- a/packages/eslint-config-custom/package.json +++ b/packages/eslint-config-custom/package.json @@ -1,11 +1,11 @@ { - "name": "eslint-config-custom", - "license": "MIT", - "version": "0.0.0", - "private": true, - "devDependencies": { - "@vercel/style-guide": "^5.0.0", - "eslint-config-turbo": "^2.3.1", - "typescript": "^4.5.3" - } + "name": "eslint-config-custom", + "license": "MIT", + "version": "0.0.0", + "private": true, + "devDependencies": { + "@vercel/style-guide": "^5.0.0", + "eslint-config-turbo": "^2.3.1", + "typescript": "^4.5.3" + } } diff --git a/packages/schema/src/index.ts b/packages/schema/src/index.ts index 3b09fba5..484794c8 100644 --- a/packages/schema/src/index.ts +++ b/packages/schema/src/index.ts @@ -14,3 +14,4 @@ export * from './workspace' export * from './workspace-role' export * from './event' export * from './api-key' +export * from './workspace-membership' diff --git a/packages/schema/src/index.types.ts b/packages/schema/src/index.types.ts index 86a1ea0a..144c5892 100644 --- a/packages/schema/src/index.types.ts +++ b/packages/schema/src/index.types.ts @@ -10,3 +10,4 @@ export * from './event/index.types' export * from './integration/index.types' export * from './api-key/index.types' export * from './workspace-role/index.types' +export * from './workspace-membership/index.types' diff --git a/packages/schema/src/user/index.ts b/packages/schema/src/user/index.ts index 1f6f116c..144a3097 100644 --- a/packages/schema/src/user/index.ts +++ b/packages/schema/src/user/index.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import { WorkspaceSchema } from '@/workspace' -export const GetSelfResponseSchema = z.object({ +export const UserSchema = z.object({ id: z.string(), email: z.string().email(), name: z.string(), @@ -9,7 +9,10 @@ export const GetSelfResponseSchema = z.object({ isActive: z.boolean(), isOnboardingFinished: z.boolean(), isAdmin: z.boolean(), - authProvider: z.string(), + authProvider: z.string() +}) + +export const GetSelfResponseSchema = UserSchema.extend({ defaultWorkspace: WorkspaceSchema }) @@ -20,9 +23,7 @@ export const UpdateSelfRequestSchema = z.object({ email: z.string().email().optional() }) -export const UpdateSelfResponseSchema = GetSelfResponseSchema.partial().omit({ - defaultWorkspace: true -}) +export const UpdateSelfResponseSchema = UserSchema export const DeleteSelfRequestSchema = z.void() @@ -32,10 +33,7 @@ export const ValidateEmailChangeOTPRequestSchema = z.object({ otp: z.string().min(6).max(6) }) -export const ValidateEmailChangeOTPResponseSchema = - GetSelfResponseSchema.partial().omit({ - defaultWorkspace: true - }) +export const ValidateEmailChangeOTPResponseSchema = UserSchema export const ResendEmailChangeOTPRequestSchema = z.void() diff --git a/packages/schema/src/workspace-membership/index.ts b/packages/schema/src/workspace-membership/index.ts new file mode 100644 index 00000000..3c873f20 --- /dev/null +++ b/packages/schema/src/workspace-membership/index.ts @@ -0,0 +1,99 @@ +import { z } from 'zod' +import { authorityEnum } from '@/enums' +import { PageRequestSchema, PageResponseSchema } from '@/pagination' +import { WorkspaceSchema } from '@/workspace' +import { UserSchema } from '@/user' + +export const CreateWorkspaceMemberSchema = z.object({ + email: z.string().email(), + roleSlugs: z.array(z.string()) +}) + +export const TransferOwnershipRequestSchema = z.object({ + workspaceSlug: WorkspaceSchema.shape.slug, + userEmail: UserSchema.shape.email +}) + +export const TransferOwnershipResponseSchema = z.void() + +export const InviteUsersRequestSchema = z.object({ + workspaceSlug: WorkspaceSchema.shape.slug, + members: z.array(CreateWorkspaceMemberSchema) +}) + +export const InviteUsersResponseSchema = z.void() + +export const RemoveUsersRequestSchema = z.object({ + workspaceSlug: WorkspaceSchema.shape.slug, + userEmails: z.string() // comma separated emails +}) + +export const RemoveUsersResponseSchema = z.void() + +export const UpdateMemberRoleRequestSchema = z.object({ + workspaceSlug: WorkspaceSchema.shape.slug, + userEmail: UserSchema.shape.email, + roleSlugs: z.array(z.string()) +}) + +export const UpdateMemberRoleResponseSchema = z.void() + +export const AcceptInvitationRequestSchema = z.object({ + workspaceSlug: WorkspaceSchema.shape.slug +}) + +export const AcceptInvitationResponseSchema = z.void() + +export const DeclineInvitationRequestSchema = z.object({ + workspaceSlug: WorkspaceSchema.shape.slug +}) + +export const DeclineInvitationResponseSchema = z.void() + +export const CancelInvitationRequestSchema = z.object({ + workspaceSlug: WorkspaceSchema.shape.slug, + userEmail: UserSchema.shape.email +}) + +export const CancelInvitationResponseSchema = z.void() + +export const LeaveWorkspaceRequestSchema = z.object({ + workspaceSlug: WorkspaceSchema.shape.slug +}) + +export const LeaveWorkspaceResponseSchema = z.void() + +export const IsMemberRequestSchema = z.object({ + workspaceSlug: WorkspaceSchema.shape.slug, + userEmail: UserSchema.shape.email +}) + +export const IsMemberResponseSchema = z.boolean() + +export const GetMembersRequestSchema = PageRequestSchema.extend({ + workspaceSlug: WorkspaceSchema.shape.slug +}) + +export const GetMembersResponseSchema = PageResponseSchema( + z.object({ + id: z.string(), + user: UserSchema, + roles: z.array( + z.object({ + id: z.string(), + role: z.object({ + id: z.string(), + name: z.string(), + description: z.string().nullable(), + colorCode: z.string().nullable(), + authorities: z.array(authorityEnum), + projects: z.array( + z.object({ + id: z.string() + }) + ) + }) + }) + ) + }) +) diff --git a/packages/schema/src/workspace-membership/index.types.ts b/packages/schema/src/workspace-membership/index.types.ts new file mode 100644 index 00000000..f1349be1 --- /dev/null +++ b/packages/schema/src/workspace-membership/index.types.ts @@ -0,0 +1,89 @@ +import { z } from 'zod' + +import { + CreateWorkspaceMemberSchema, + TransferOwnershipRequestSchema, + TransferOwnershipResponseSchema, + InviteUsersRequestSchema, + InviteUsersResponseSchema, + RemoveUsersRequestSchema, + RemoveUsersResponseSchema, + UpdateMemberRoleRequestSchema, + UpdateMemberRoleResponseSchema, + AcceptInvitationRequestSchema, + AcceptInvitationResponseSchema, + DeclineInvitationRequestSchema, + DeclineInvitationResponseSchema, + CancelInvitationRequestSchema, + CancelInvitationResponseSchema, + LeaveWorkspaceRequestSchema, + LeaveWorkspaceResponseSchema, + IsMemberRequestSchema, + IsMemberResponseSchema, + GetMembersRequestSchema, + GetMembersResponseSchema +} from './' + +export type CreateWorkspaceMember = z.infer + +export type TransferOwnershipRequest = z.infer< + typeof TransferOwnershipRequestSchema +> + +export type TransferOwnershipResponse = z.infer< + typeof TransferOwnershipResponseSchema +> + +export type InviteUsersRequest = z.infer + +export type InviteUsersResponse = z.infer + +export type RemoveUsersRequest = z.infer + +export type RemoveUsersResponse = z.infer + +export type UpdateMemberRoleRequest = z.infer< + typeof UpdateMemberRoleRequestSchema +> + +export type UpdateMemberRoleResponse = z.infer< + typeof UpdateMemberRoleResponseSchema +> + +export type AcceptInvitationRequest = z.infer< + typeof AcceptInvitationRequestSchema +> + +export type AcceptInvitationResponse = z.infer< + typeof AcceptInvitationResponseSchema +> + +export type DeclineInvitationRequest = z.infer< + typeof DeclineInvitationRequestSchema +> + +export type DeclineInvitationResponse = z.infer< + typeof DeclineInvitationResponseSchema +> + +export type CancelInvitationRequest = z.infer< + typeof CancelInvitationRequestSchema +> + +export type CancelInvitationResponse = z.infer< + typeof CancelInvitationResponseSchema +> + +export type LeaveWorkspaceRequest = z.infer + +export type LeaveWorkspaceResponse = z.infer< + typeof LeaveWorkspaceResponseSchema +> + +export type IsMemberRequest = z.infer + +export type IsMemberResponse = z.infer + +export type GetMembersRequest = z.infer + +export type GetMembersResponse = z.infer diff --git a/packages/schema/tests/api-key.spec.ts b/packages/schema/tests/api-key.spec.ts index 111f8d58..c0cb9ba9 100644 --- a/packages/schema/tests/api-key.spec.ts +++ b/packages/schema/tests/api-key.spec.ts @@ -244,7 +244,6 @@ describe('API Key Schema Tests', () => { } } }) - console.log(result.error?.issues) expect(result.success).toBe(true) }) diff --git a/packages/schema/tests/user.spec.ts b/packages/schema/tests/user.spec.ts index c1eacbf2..a13b1f30 100644 --- a/packages/schema/tests/user.spec.ts +++ b/packages/schema/tests/user.spec.ts @@ -1,4 +1,5 @@ import { + UserSchema, GetSelfResponseSchema, UpdateSelfRequestSchema, UpdateSelfResponseSchema, @@ -11,6 +12,66 @@ import { } from '@/user' describe('User Schema Tests', () => { + // Tests for UserSchema + it('should validate a valid UserSchema', () => { + const result = UserSchema.safeParse({ + id: 'user123', + email: 'user@example.com', + name: 'User Name', + profilePictureUrl: 'http://example.com/profile.jpg', + isActive: true, + isOnboardingFinished: true, + isAdmin: false, + authProvider: 'google' + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid UserSchema', () => { + const result = UserSchema.safeParse({ + id: 'user123', + email: 'user@example', // Invalid email + name: 'User Name', + profilePictureUrl: 'http://example.com/profile.jpg', + isActive: true, + isOnboardingFinished: true, + isAdmin: false, + authProvider: 'google' + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(1) + }) + + it('should not validate an invalid UserSchema with missing fields', () => { + const result = UserSchema.safeParse({ + id: 'user123', + email: 'user@example.com', + name: 'User Name', + isActive: true, + isOnboardingFinished: true, + isAdmin: false, + authProvider: 'google' + // Missing profilePictureUrl + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(1) + }) + + it('should not validate an invalid UserSchema with incorrect types', () => { + const result = UserSchema.safeParse({ + id: 'user123', + email: 'user@example.com', + name: 'User Name', + profilePictureUrl: 'http://example.com/profile.jpg', + isActive: 'true', // Should be a boolean + isOnboardingFinished: true, + isAdmin: false, + authProvider: 'google' + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(1) // Adjust the number based on incorrect types + }) + // Tests for GetSelfResponseSchema it('should validate a valid GetSelfResponseSchema', () => { const result = GetSelfResponseSchema.safeParse({ @@ -72,7 +133,11 @@ describe('User Schema Tests', () => { id: 'user123', email: 'jane@example.com', name: 'Jane Doe', - isActive: true + isActive: true, + profilePictureUrl: null, + isOnboardingFinished: false, + isAdmin: false, + authProvider: 'email' }) expect(result.success).toBe(true) }) @@ -81,9 +146,10 @@ describe('User Schema Tests', () => { const result = UpdateSelfResponseSchema.safeParse({ id: 123, // Should be a string email: 'jane@example.com' + // Missing required fields }) expect(result.success).toBe(false) - expect(result.error?.issues.length).toBe(1) + expect(result.error?.issues.length).toBe(7) }) // Tests for DeleteSelfRequestSchema diff --git a/packages/schema/tests/workspace-membership.spec.ts b/packages/schema/tests/workspace-membership.spec.ts new file mode 100644 index 00000000..121360b3 --- /dev/null +++ b/packages/schema/tests/workspace-membership.spec.ts @@ -0,0 +1,431 @@ +// workspace-membership.spec.ts +import { + CreateWorkspaceMemberSchema, + TransferOwnershipRequestSchema, + TransferOwnershipResponseSchema, + InviteUsersRequestSchema, + InviteUsersResponseSchema, + RemoveUsersRequestSchema, + RemoveUsersResponseSchema, + UpdateMemberRoleRequestSchema, + UpdateMemberRoleResponseSchema, + AcceptInvitationRequestSchema, + AcceptInvitationResponseSchema, + DeclineInvitationRequestSchema, + DeclineInvitationResponseSchema, + CancelInvitationRequestSchema, + CancelInvitationResponseSchema, + LeaveWorkspaceRequestSchema, + LeaveWorkspaceResponseSchema, + IsMemberRequestSchema, + IsMemberResponseSchema, + GetMembersRequestSchema, + GetMembersResponseSchema +} from '@/workspace-membership' + +describe('Workspace Membership Schema Tests', () => { + // Tests for CreateWorkspaceMemberSchema + it('should validate a valid CreateWorkspaceMemberSchema', () => { + const result = CreateWorkspaceMemberSchema.safeParse({ + email: 'user@example.com', + roleSlugs: ['admin', 'editor'] + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid CreateWorkspaceMemberSchema', () => { + const result = CreateWorkspaceMemberSchema.safeParse({ + email: 'user@example', // Invalid email + roleSlugs: 'admin' // Should be an array + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(2) + }) + + // Tests for TransferOwnershipRequestSchema + it('should validate a valid TransferOwnershipRequestSchema', () => { + const result = TransferOwnershipRequestSchema.safeParse({ + workspaceSlug: 'workspace-slug', + userEmail: 'user@example.com' + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid TransferOwnershipRequestSchema', () => { + const result = TransferOwnershipRequestSchema.safeParse({ + workspaceSlug: 123, // Should be a string + userEmail: 'user@example' // Invalid email + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(2) + }) + + // Tests for TransferOwnershipResponseSchema + it('should validate a valid TransferOwnershipResponseSchema', () => { + const result = TransferOwnershipResponseSchema.safeParse(undefined) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid TransferOwnershipResponseSchema', () => { + const result = TransferOwnershipResponseSchema.safeParse({ + unexpectedField: 'value' + }) + expect(result.success).toBe(false) + }) + + // Tests for InviteUsersRequestSchema + it('should validate a valid InviteUsersRequestSchema', () => { + const result = InviteUsersRequestSchema.safeParse({ + workspaceSlug: 'workspace-slug', + members: [ + { + email: 'user1@example.com', + roleSlugs: ['admin', 'editor'] + }, + { + email: 'user2@example.com', + roleSlugs: ['viewer'] + } + ] + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid InviteUsersRequestSchema', () => { + const result = InviteUsersRequestSchema.safeParse({ + workspaceSlug: 123, // Should be a string + members: [ + { + email: 'user1@example', // Invalid email + roleSlugs: 'admin' // Should be an array + } + ] + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(3) + }) + + // Tests for InviteUsersResponseSchema + it('should validate a valid InviteUsersResponseSchema', () => { + const result = InviteUsersResponseSchema.safeParse(undefined) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid InviteUsersResponseSchema', () => { + const result = InviteUsersResponseSchema.safeParse({ + unexpectedField: 'value' + }) + expect(result.success).toBe(false) + }) + + // Tests for RemoveUsersRequestSchema + it('should validate a valid RemoveUsersRequestSchema', () => { + const result = RemoveUsersRequestSchema.safeParse({ + workspaceSlug: 'workspace-slug', + userEmails: 'user1@example.com,user2@example.com' + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid RemoveUsersRequestSchema', () => { + const result = RemoveUsersRequestSchema.safeParse({ + workspaceSlug: 123, // Should be a string + userEmails: ['user1@example.com', 'user2@example.com'] // Should be a string of comma-separated emails + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(2) + }) + + // Tests for RemoveUsersResponseSchema + it('should validate a valid RemoveUsersResponseSchema', () => { + const result = RemoveUsersResponseSchema.safeParse(undefined) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid RemoveUsersResponseSchema', () => { + const result = RemoveUsersResponseSchema.safeParse({ + unexpectedField: 'value' + }) + expect(result.success).toBe(false) + }) + + // Tests for UpdateMemberRoleRequestSchema + it('should validate a valid UpdateMemberRoleRequestSchema', () => { + const result = UpdateMemberRoleRequestSchema.safeParse({ + workspaceSlug: 'workspace-slug', + userEmail: 'user@example.com', + roleSlugs: ['admin', 'editor'] + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid UpdateMemberRoleRequestSchema', () => { + const result = UpdateMemberRoleRequestSchema.safeParse({ + workspaceSlug: 123, // Should be a string + userEmail: 'user@example', // Invalid email + roleSlugs: 'admin' // Should be an array + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(3) + }) + + // Tests for UpdateMemberRoleResponseSchema + it('should validate a valid UpdateMemberRoleResponseSchema', () => { + const result = UpdateMemberRoleResponseSchema.safeParse(undefined) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid UpdateMemberRoleResponseSchema', () => { + const result = UpdateMemberRoleResponseSchema.safeParse({ + unexpectedField: 'value' + }) + expect(result.success).toBe(false) + }) + + // Tests for AcceptInvitationRequestSchema + it('should validate a valid AcceptInvitationRequestSchema', () => { + const result = AcceptInvitationRequestSchema.safeParse({ + workspaceSlug: 'workspace-slug' + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid AcceptInvitationRequestSchema', () => { + const result = AcceptInvitationRequestSchema.safeParse({ + workspaceSlug: 123 // Should be a string + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(1) + }) + + // Tests for AcceptInvitationResponseSchema + it('should validate a valid AcceptInvitationResponseSchema', () => { + const result = AcceptInvitationResponseSchema.safeParse(undefined) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid AcceptInvitationResponseSchema', () => { + const result = AcceptInvitationResponseSchema.safeParse({ + unexpectedField: 'value' + }) + expect(result.success).toBe(false) + }) + + // Tests for DeclineInvitationRequestSchema + it('should validate a valid DeclineInvitationRequestSchema', () => { + const result = DeclineInvitationRequestSchema.safeParse({ + workspaceSlug: 'workspace-slug' + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid DeclineInvitationRequestSchema', () => { + const result = DeclineInvitationRequestSchema.safeParse({ + workspaceSlug: 123 // Should be a string + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(1) + }) + + // Tests for DeclineInvitationResponseSchema + it('should validate a valid DeclineInvitationResponseSchema', () => { + const result = DeclineInvitationResponseSchema.safeParse(undefined) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid DeclineInvitationResponseSchema', () => { + const result = DeclineInvitationResponseSchema.safeParse({ + unexpectedField: 'value' + }) + expect(result.success).toBe(false) + }) + + // Tests for CancelInvitationRequestSchema + it('should validate a valid CancelInvitationRequestSchema', () => { + const result = CancelInvitationRequestSchema.safeParse({ + workspaceSlug: 'workspace-slug', + userEmail: 'user@example.com' + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid CancelInvitationRequestSchema', () => { + const result = CancelInvitationRequestSchema.safeParse({ + workspaceSlug: 123, // Should be a string + userEmail: 'user@example' // Invalid email + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(2) + }) + + // Tests for CancelInvitationResponseSchema + it('should validate a valid CancelInvitationResponseSchema', () => { + const result = CancelInvitationResponseSchema.safeParse(undefined) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid CancelInvitationResponseSchema', () => { + const result = CancelInvitationResponseSchema.safeParse({ + unexpectedField: 'value' + }) + expect(result.success).toBe(false) + }) + + // Tests for LeaveWorkspaceRequestSchema + it('should validate a valid LeaveWorkspaceRequestSchema', () => { + const result = LeaveWorkspaceRequestSchema.safeParse({ + workspaceSlug: 'workspace-slug' + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid LeaveWorkspaceRequestSchema', () => { + const result = LeaveWorkspaceRequestSchema.safeParse({ + workspaceSlug: 123 // Should be a string + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(1) + }) + + // Tests for LeaveWorkspaceResponseSchema + it('should validate a valid LeaveWorkspaceResponseSchema', () => { + const result = LeaveWorkspaceResponseSchema.safeParse(undefined) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid LeaveWorkspaceResponseSchema', () => { + const result = LeaveWorkspaceResponseSchema.safeParse({ + unexpectedField: 'value' + }) + expect(result.success).toBe(false) + }) + + // Tests for IsMemberRequestSchema + it('should validate a valid IsMemberRequestSchema', () => { + const result = IsMemberRequestSchema.safeParse({ + workspaceSlug: 'workspace-slug', + userEmail: 'user@example.com' + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid IsMemberRequestSchema', () => { + const result = IsMemberRequestSchema.safeParse({ + workspaceSlug: 123, // Should be a string + userEmail: 'user@example' // Invalid email + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(2) + }) + + // Tests for IsMemberResponseSchema + it('should validate a valid IsMemberResponseSchema', () => { + const result = IsMemberResponseSchema.safeParse(true) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid IsMemberResponseSchema', () => { + const result = IsMemberResponseSchema.safeParse('true') // Should be a boolean + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(1) + }) + + // Tests for GetMembersRequestSchema + it('should validate a valid GetMembersRequestSchema', () => { + const result = GetMembersRequestSchema.safeParse({ + page: 1, + limit: 10, + sort: 'name', + order: 'asc', + search: 'admin', + workspaceSlug: 'workspace-slug' + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid GetMembersRequestSchema', () => { + const result = GetMembersRequestSchema.safeParse({ + page: 'one', // Should be a number + limit: 10, + sort: 'name', + order: 'asc', + search: 'admin', + workspaceSlug: 123 // Should be a string + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(2) + }) + + // Tests for GetMembersResponseSchema + it('should validate a valid GetMembersResponseSchema', () => { + const result = GetMembersResponseSchema.safeParse({ + items: [ + { + id: 'member123', + user: { + id: 'user123', + email: 'user@example.com', + name: 'User Name', + profilePictureUrl: 'http://example.com/profile.jpg', + isActive: true, + isOnboardingFinished: true, + isAdmin: false, + authProvider: 'google' + }, + roles: [ + { + id: 'role123', + role: { + id: 'role123', + name: 'Admin Role', + description: 'Role with admin privileges', + colorCode: '#FF5733', + authorities: ['CREATE_PROJECT'], + projects: [ + { + id: 'project123' + } + ] + } + } + ] + } + ], + metadata: { + page: 1, + perPage: 10, + pageCount: 1, + totalCount: 1, + links: { + self: 'http://example.com/page/1', + first: 'http://example.com/page/1', + previous: null, + next: null, + last: 'http://example.com/page/1' + } + } + }) + expect(result.success).toBe(true) + }) + + it('should not validate an invalid GetMembersResponseSchema', () => { + const result = GetMembersResponseSchema.safeParse({ + items: 'not-an-array', // Should be an array + metadata: { + page: 1, + perPage: 10, + pageCount: 1, + totalCount: 1, + links: { + self: 'http://example.com/page/1', + first: 'http://example.com/page/1', + previous: null, + next: null, + last: 'http://example.com/page/1' + } + } + }) + expect(result.success).toBe(false) + expect(result.error?.issues).toHaveLength(1) + }) +}) diff --git a/turbo.json b/turbo.json index 56b6c631..ad639003 100644 --- a/turbo.json +++ b/turbo.json @@ -4,7 +4,7 @@ "build": { "dependsOn": ["^build"], "outputs": ["dist/**"], - "inputs": ["NEXT_PUBLIC_BACKEND_URL"] + "env": ["NEXT_PUBLIC_BACKEND_URL"] }, "build#cli": { "dependsOn": ["build#api-client"] @@ -33,4 +33,4 @@ "inputs": ["dist/**"] } } -} \ No newline at end of file +}