feat: User operations on join org
This commit is contained in:
389
CODE_REVIEW.md
Normal file
389
CODE_REVIEW.md
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
# Code Review — Kaa Khane (Multi-Tenant SaaS Backend)
|
||||||
|
|
||||||
|
## What This Is
|
||||||
|
|
||||||
|
A NestJS backend for a multi-tenant SaaS platform. It models the core of any team-based product: users belong to organizations, organizations have roles (`owner`, `admin`, `member`), and membership is managed through a request/invitation flow. Stack: NestJS + Prisma + PostgreSQL + Redis (via Keyv). This is a learning project but the ambition is real — you've touched AsyncLocalStorage, operation-based authorization, cache resilience, and transactional writes. That's not beginner stuff.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What You're Doing Well
|
||||||
|
|
||||||
|
**1. AsyncLocalStorage for request context**
|
||||||
|
This is a legitimately good pattern. Instead of threading `req.user` through every function signature, you store it in ALS and services pull it from context. This is how production NestJS apps at scale handle per-request state. Most people learning NestJS never get here.
|
||||||
|
|
||||||
|
**2. Cache-aside with graceful degradation**
|
||||||
|
`CacheService.getOrSet()` is well thought out. It tracks `redisAvailable`, silently falls back to DB, and doesn't let a Redis outage crash the app. The pattern is correct. The placement of `console.warn` in a service is slightly off (more on that below) but the intent is solid.
|
||||||
|
|
||||||
|
**3. Parallel DB checks with `Promise.all`**
|
||||||
|
You're doing this in multiple places — running independent queries concurrently instead of awaiting them in sequence. This is the right instinct and most junior devs miss it.
|
||||||
|
|
||||||
|
**4. Operation-based authorization**
|
||||||
|
`AuthorizationService.canPerformOperation()` with a switch statement and named operations (`INVITE_USERS`, `DELETE_ORGANIZATION`) is cleaner than scattering raw role checks everywhere. It's a solid foundation.
|
||||||
|
|
||||||
|
**5. Transactions where they matter**
|
||||||
|
`createNewOrganization` and `userOrganiaztionRequestAction` use `$transaction` to ensure atomicity. You understand when to use them.
|
||||||
|
|
||||||
|
**6. DTO validation layer**
|
||||||
|
Using `class-validator` + `@nestjs/swagger` together is standard and correct. `@AtLeastOneField()` is a good custom validator — you're not reaching for a library when a simple decorator does the job.
|
||||||
|
|
||||||
|
**7. Global `AuthGuard` with `@Public()` opt-out**
|
||||||
|
Secure by default. Making all routes protected and requiring explicit `@Public()` to open them is the right model. A lot of apps do it backwards.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Issues and What to Improve
|
||||||
|
|
||||||
|
Organized by severity: `[critical]`, `[high]`, `[medium]`, `[low]`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1. Hardcoded JWT secret `[critical]`
|
||||||
|
|
||||||
|
**File:** `src/auth/auth.service.ts:60`, `src/auth/guards/auth.guard.ts:38`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
secret: 'demo';
|
||||||
|
```
|
||||||
|
|
||||||
|
You know this is wrong (you left a TODO). But let me explain _why_ it's critical: if this ever reaches a staging environment and someone rotates the secret, every user is logged out. Worse, the secret is in version control. Fix this before you add anything else.
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// in JwtModule registration (auth.module.ts)
|
||||||
|
JwtModule.registerAsync({
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: (config: ConfigService) => ({
|
||||||
|
secret: config.getOrThrow<string>('JWT_SECRET'),
|
||||||
|
signOptions: { expiresIn: '15m' },
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
```
|
||||||
|
|
||||||
|
Then remove the `secret` override from every `signAsync` / `verifyAsync` call. The module-level secret is used automatically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. `userOrganiaztionRequestAction` uses `this.prisma` inside a `$transaction` `[critical]`
|
||||||
|
|
||||||
|
**File:** `src/organization-membership/organization-membership.service.ts:226`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
return await this.prisma.$transaction(async (tx) => {
|
||||||
|
await tx.organizationJoinRequest.update(...) // ✅ uses tx
|
||||||
|
|
||||||
|
if (userAction === ORGANIZATION_JOIN_REQUEST.ACCEPTED)
|
||||||
|
await this.prisma.organizationUserJoinTable.create(...) // ❌ uses this.prisma — NOT the tx
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
The `create` inside the transaction is using `this.prisma` (the global client) instead of `tx` (the transaction client). If the second write fails, the first write is NOT rolled back. The whole point of wrapping in `$transaction` is broken here.
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await tx.organizationUserJoinTable.create({ ... })
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Two flows conflated into one method — and the semantics are wrong `[high]`
|
||||||
|
|
||||||
|
**File:** `src/organization-membership/organization-membership.service.ts:180`
|
||||||
|
|
||||||
|
`userOrganiaztionRequestAction` is currently called by the user (the invitee) to accept or reject an invitation. But based on your plan, you also want an org admin to accept/reject a join request. These are fundamentally different actors:
|
||||||
|
|
||||||
|
| Flow | Actor | What they act on |
|
||||||
|
| -------------------------- | ---------------------- | ---------------------------------- |
|
||||||
|
| Accept/reject invitation | The invited **user** | Their own pending `INVITED` record |
|
||||||
|
| Accept/reject join request | An **org admin/owner** | Someone else's `REQUESTED` record |
|
||||||
|
|
||||||
|
Right now the method doesn't distinguish by `requestType`. An org admin could use it to act on _their own_ data when they should be acting on _another user's_ data. They need to be two methods:
|
||||||
|
|
||||||
|
- `userRespondToInvitation(userId, dto)` — user accepts/rejects where `requestType = INVITED`
|
||||||
|
- `adminRespondToJoinRequest(adminUserId, targetUserId, orgId, action)` — admin acts on `requestType = REQUESTED`, with a `canPerformOperation` check first
|
||||||
|
|
||||||
|
This is not a minor naming issue. It's a modeling issue. The current code would let a user "accept" their own join request.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. `organizationExists` vs `findById` — two methods doing nearly the same thing `[high]`
|
||||||
|
|
||||||
|
**File:** `src/organization/organization.service.ts:121`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async organizationExists(orgId: string) {
|
||||||
|
return await this.prisma.organization.findUnique({
|
||||||
|
where: { id: orgId },
|
||||||
|
select: { id: true }, // minimal select — just checking existence
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findById(orgId: string) {
|
||||||
|
return await this.cacheService.getOrSet(orgId, async () =>
|
||||||
|
await this.prisma.organization.findUnique({ where: { id: orgId } }),
|
||||||
|
);
|
||||||
|
// returns full org object, goes through cache
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
These have different purposes (one is a lightweight existence check, one returns full data with caching) but the distinction is opaque from the call site. `OrganizationMembershipService` calls both, inconsistently.
|
||||||
|
|
||||||
|
More importantly: `organizationExists` bypasses the cache entirely. If you call `findById` first (which caches), then call `organizationExists`, you've now made an extra DB round-trip for no reason.
|
||||||
|
|
||||||
|
**What to do:**
|
||||||
|
|
||||||
|
- Consolidate. Use `findById` (cached) everywhere.
|
||||||
|
- If you need a boolean check, add a thin wrapper: `async assertOrganizationExists(orgId)` that calls `findById` and throws `NotFoundException` if null. Then services call that one line instead of the two-line check-then-throw pattern you repeat everywhere.
|
||||||
|
- Delete `organizationExists` or make it call `findById` internally.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Repeated guard pattern — check existence, throw — everywhere `[high]`
|
||||||
|
|
||||||
|
You do this in almost every method:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const orgExists = await this.orgService.findById(dto.orgId);
|
||||||
|
if (!orgExists) throw new NotFoundException('Organization');
|
||||||
|
```
|
||||||
|
|
||||||
|
And separately:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const canUserDeleteOrganization = await this.authorization.canPerformOperation(...);
|
||||||
|
if (!canUserDeleteOrganization) throw new ForbiddenException('Not enough permission');
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the "guard clause" pattern and the repetition itself isn't wrong — guard clauses are good. What's wrong is that the _authorization check_ is manually wired in every service method instead of being declarative. This will become painful as you add more operations.
|
||||||
|
|
||||||
|
**Better pattern — make `canPerformOperation` throw directly:**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// authorization.service.ts
|
||||||
|
async assertCanPerformOperation(
|
||||||
|
userId: string,
|
||||||
|
orgId: string,
|
||||||
|
operation: USER_ORGANIZATION_OPERATIONS,
|
||||||
|
): Promise<void> {
|
||||||
|
const allowed = await this.canPerformOperation(userId, orgId, operation);
|
||||||
|
if (!allowed) throw new ForbiddenException('Insufficient permissions');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the call site is one line with no `if`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await this.authorization.assertCanPerformOperation(userId, orgId, INVITE_USERS);
|
||||||
|
```
|
||||||
|
|
||||||
|
Clean. Unambiguous. And you can add logging, metrics, or audit trail in one place.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. `isAdmin()` is dead code `[medium]`
|
||||||
|
|
||||||
|
**File:** `src/authorization/authorization.service.ts:49`
|
||||||
|
|
||||||
|
`isAdmin()` is defined but never called. Either use it in `canInvite` (which currently re-implements the same logic inline) or delete it.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// current canInvite re-implements admin check inline:
|
||||||
|
return (
|
||||||
|
isUserPartOfOrganization.role === ORG_ROLE.admin ||
|
||||||
|
isUserPartOfOrganization.role === ORG_ROLE.owner
|
||||||
|
);
|
||||||
|
|
||||||
|
// isAdmin() does the same thing but only for admin role.
|
||||||
|
// Clean it up or wire it in.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. `AuthorizationService` hits the DB every time — no caching `[medium]`
|
||||||
|
|
||||||
|
**File:** `src/authorization/authorization.service.ts:61`
|
||||||
|
|
||||||
|
Every call to `canPerformOperation` does a `findUnique` on `OrganizationUserJoinTable`. In `inviteUserToOrg` alone, you've already called `orgService.findById` (which hits cache), but then `canPerformOperation` hits DB cold. Membership data is relatively stable — it changes when someone joins, leaves, or is promoted.
|
||||||
|
|
||||||
|
**Fix:** Cache the membership record in `CacheService` using a compound key like `membership:{userId}:{orgId}`. Invalidate it when a member joins, leaves, or their role changes.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const key = `membership:${userId}:${orgId}`;
|
||||||
|
return this.cacheService.getOrSet(key, () =>
|
||||||
|
this.prisma.organizationUserJoinTable.findUnique(...)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8. `deleteAnOrganization` has broken cascade logic `[medium]`
|
||||||
|
|
||||||
|
**File:** `src/organization/organization.service.ts:99`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await tx.organization.delete({ where: { id: orgId } });
|
||||||
|
|
||||||
|
await tx.organizationUserJoinTable.delete({
|
||||||
|
where: { userId_orgId: { userId, orgId } }, // only deletes the OWNER's row
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This only deletes the owner's row from the join table. All other members' rows are left orphaned (or fail with a FK constraint — depending on your Prisma schema cascade setting). Also, `OrganizationJoinRequest` records for this org are not cleaned up.
|
||||||
|
|
||||||
|
You should either:
|
||||||
|
|
||||||
|
- Set `onDelete: Cascade` in the Prisma schema on those relations (so deleting the org auto-cleans everything), or
|
||||||
|
- Manually `deleteMany` all join rows and requests in the transaction before deleting the org
|
||||||
|
|
||||||
|
Option 1 (schema cascade) is cleaner for this case.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9. `console.log` / `console.warn` in production code `[medium]`
|
||||||
|
|
||||||
|
**Files:** `src/cache/cache.service.ts:13,18`, `src/cache/cache.service.ts:62`, `src/prisma/prisma.service.ts:40`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
console.log(a); // in deleteKey — what is `a`?
|
||||||
|
console.warn('Redis disconnected');
|
||||||
|
```
|
||||||
|
|
||||||
|
Use NestJS's built-in `Logger`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { Logger } from '@nestjs/common';
|
||||||
|
|
||||||
|
private readonly logger = new Logger(CacheService.name);
|
||||||
|
|
||||||
|
this.logger.warn('Redis disconnected');
|
||||||
|
this.logger.error('Prisma connection failed', err.stack);
|
||||||
|
```
|
||||||
|
|
||||||
|
The NestJS logger respects log levels, outputs structured logs, and can be replaced with a production logger (Pino, Winston) without touching service code.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 10. Controller stubs are wired wrong — copy-paste bug `[medium]`
|
||||||
|
|
||||||
|
**File:** `src/organization-membership/organization-membership.controller.ts:54`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
@Get('organization/:id/invitations')
|
||||||
|
async getOrganizationInvitations(@Param('id') orgId: string) {
|
||||||
|
return await this.orgMemService.getMemebersOfOrganization(orgId); // ❌ wrong method
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`getOrganizationInvitations` is calling `getMemebersOfOrganization`. Both endpoints return the same data. This is a copy-paste leftover.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 11. `userOrganizationJoinRequestList` does manual enum validation `[low]`
|
||||||
|
|
||||||
|
**File:** `src/organization-membership/organization-membership.service.ts:135`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async userOrganizationJoinRequestList(userId: string, requestType: string) {
|
||||||
|
const joinReqType: ORGANIZATION_JOIN_REQUEST_TYPE | undefined =
|
||||||
|
ORGANIZATION_JOIN_REQUEST_TYPE[requestType];
|
||||||
|
if (!joinReqType) throw new BadRequestException('Invalid request type');
|
||||||
|
```
|
||||||
|
|
||||||
|
This manual enum validation in the service layer exists because `requestType` comes in as a raw `string`. The fix is to validate it in the DTO/query params using `@IsEnum(ORGANIZATION_JOIN_REQUEST_TYPE)`. Then the service never receives an invalid value — NestJS's validation pipe rejects it before it gets there. The service layer should not be doing input validation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 12. `BaseException` exists but is never used `[low]`
|
||||||
|
|
||||||
|
**File:** `common/exceptions/custom-exceptions.ts`
|
||||||
|
|
||||||
|
You built a `BaseException` class with `code`, `message`, and `HttpStatus`. Nothing uses it. Either start using it for domain-specific errors (e.g., `OrganizationNotFoundError extends BaseException`) or delete it. Dead abstractions add noise.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 13. Typos in method and variable names `[low]`
|
||||||
|
|
||||||
|
- `userOrganiaztionRequestAction` → `userOrganizationRequestAction`
|
||||||
|
- `getMemebersOfOrganization` → `getMembersOfOrganization`
|
||||||
|
|
||||||
|
These propagate to controller method names, controller routes, and service interface. Fix them now while the surface area is small.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## General Guidelines to Internalize
|
||||||
|
|
||||||
|
### On DRY vs. "right abstraction"
|
||||||
|
|
||||||
|
DRY (Don't Repeat Yourself) is often misapplied. The goal isn't zero duplication — it's avoiding duplication of _knowledge_ (the same decision made in two places). Two methods that both check `if (!org) throw NotFoundException` are DRY violations only if they're checking the same _thing_. The right fix is `assertOrganizationExists()` — a single function that owns the "org must exist" rule. The callers delegate to it.
|
||||||
|
|
||||||
|
### On service layer responsibilities
|
||||||
|
|
||||||
|
A service method should do one thing: execute a business operation. Input validation belongs in DTOs + pipes. Authorization belongs in guards or a dedicated authorization service (you're halfway there). Existence checks belong in helper methods on the service. What remains in the method body should read like a business narrative: "check user is allowed → do the thing → return result."
|
||||||
|
|
||||||
|
### On the transaction client (`this.prisma.client`)
|
||||||
|
|
||||||
|
You built the ALS-based `prisma.client` getter for shared transactions across services. It's a good idea — but it only works if services call `this.prisma.client.xxx` instead of `this.prisma.xxx`. Right now no service uses it. Either commit to the pattern (use `client` everywhere) or remove the getter to avoid confusion. Half-implemented patterns are worse than no pattern — they create false confidence.
|
||||||
|
|
||||||
|
### On authorization: where you are vs. where to go
|
||||||
|
|
||||||
|
**Where you are:** operation-based (`canPerformOperation` switch). This is fine for 3-5 operations. As the app grows, the switch becomes unmaintainable and you end up with logic spread across the switch, `isOwner`, `canInvite`, etc.
|
||||||
|
|
||||||
|
**Where to go next:** a role-permission matrix.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const PERMISSIONS: Record<ORG_ROLE, USER_ORGANIZATION_OPERATIONS[]> = {
|
||||||
|
[ORG_ROLE.owner]: [
|
||||||
|
UPDATE_ORGANIZATION,
|
||||||
|
DELETE_ORGANIZATION,
|
||||||
|
INVITE_USERS,
|
||||||
|
REMOVE_MEMBERS,
|
||||||
|
],
|
||||||
|
[ORG_ROLE.admin]: [INVITE_USERS, REMOVE_MEMBERS],
|
||||||
|
[ORG_ROLE.member]: [],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Then `canPerformOperation` becomes:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async canPerformOperation(userId, orgId, operation) {
|
||||||
|
const membership = await this.getMembership(userId, orgId);
|
||||||
|
if (!membership) return false;
|
||||||
|
return PERMISSIONS[membership.role].includes(operation);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Adding a new operation is one line in the matrix. No new DB queries, no new switch case.
|
||||||
|
|
||||||
|
**Beyond that:** [CASL](https://casl.js.org/) is the standard Node.js authorization library for more complex scenarios (attribute-level permissions, "can user edit _this specific_ resource"). You don't need it now but it's worth knowing it exists.
|
||||||
|
|
||||||
|
### On caching: what to cache, what to invalidate
|
||||||
|
|
||||||
|
Cache data that is: (a) read frequently, (b) expensive to recompute, (c) stable. Organization data and membership data fit all three. The rule is simple: **every write must invalidate or update the relevant cache key**. Right now you invalidate on org delete but not on org update. When you cache membership, you must invalidate on role change, member removal, and member addition.
|
||||||
|
|
||||||
|
Use structured cache keys: `org:{orgId}`, `membership:{userId}:{orgId}`. Bare IDs as keys (like you're doing now with just `orgId`) work but collide if you ever cache different types under the same store.
|
||||||
|
|
||||||
|
### On the two-flow membership design (your plan)
|
||||||
|
|
||||||
|
Your instinct to split into two separate flows is correct. Model it explicitly:
|
||||||
|
|
||||||
|
- `POST /organization-membership/invite` — org admin invites a user (creates `INVITED` record)
|
||||||
|
- `POST /organization-membership/request` — user requests to join (creates `REQUESTED` record)
|
||||||
|
- `PATCH /organization-membership/user/respond` — **user** accepts/rejects their own `INVITED` record
|
||||||
|
- `PATCH /organization-membership/organization/:id/respond` — **admin** accepts/rejects a `REQUESTED` record
|
||||||
|
|
||||||
|
The actor and the target record type are different in each case. Wire your service methods to match this — each method should assert `requestType` before acting.
|
||||||
|
|
||||||
|
### On what to build next (priority order)
|
||||||
|
|
||||||
|
1. Fix the `$transaction` bug (item 2 above) — data integrity issue
|
||||||
|
2. Split the two membership flows into two methods
|
||||||
|
3. Move JWT secret to `ConfigService`
|
||||||
|
4. Add `assertOrganizationExists()` / `assertCanPerformOperation()` helpers — this cleans up every service method
|
||||||
|
5. Fix the cascade on org delete
|
||||||
|
6. Wire up the remaining controller stubs
|
||||||
|
7. Add pagination to `getOrganizations()`
|
||||||
|
8. Replace `console.log` with `Logger`
|
||||||
|
9. Cache membership in `AuthorizationService`
|
||||||
|
10. Delete dead code (`isAdmin`, `BaseException`, commented-out `acceptInvitation`)
|
||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { DataResponse, MessageResponse } from 'common/http';
|
import { DataResponse, MessageResponse } from 'common/http';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ResponseInterceptor<T> implements NestInterceptor<T, any> {
|
export class ResponseInterceptor<T> implements NestInterceptor<T, any> {
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
export const ORGANIZATION_JOIN_REQUEST = {
|
export const ORGANIZATION_JOIN_REQUEST = {
|
||||||
PENDING: 'PENDING',
|
PENDING: 'PENDING',
|
||||||
ACCEPTED: 'ACCEPTED',
|
ACCEPTED: 'ACCEPTED',
|
||||||
REJECTED: 'REJECTED'
|
REJECTED: 'REJECTED',
|
||||||
|
CANCELLED: 'CANCELLED'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ORGANIZATION_JOIN_REQUEST = (typeof ORGANIZATION_JOIN_REQUEST)[keyof typeof ORGANIZATION_JOIN_REQUEST]
|
export type ORGANIZATION_JOIN_REQUEST = (typeof ORGANIZATION_JOIN_REQUEST)[keyof typeof ORGANIZATION_JOIN_REQUEST]
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const config: runtime.GetPrismaClientConfig = {
|
|||||||
"clientVersion": "7.3.0",
|
"clientVersion": "7.3.0",
|
||||||
"engineVersion": "9d6ad21cbbceab97458517b147a6a09ff43aa735",
|
"engineVersion": "9d6ad21cbbceab97458517b147a6a09ff43aa735",
|
||||||
"activeProvider": "postgresql",
|
"activeProvider": "postgresql",
|
||||||
"inlineSchema": "model OrganizationJoinRequest {\n userId String\n orgId String\n status ORGANIZATION_JOIN_REQUEST @default(PENDING)\n requestType ORGANIZATION_JOIN_REQUEST_TYPE\n requestedOn DateTime @default(now())\n role ORG_ROLE @default(member)\n updatedAt DateTime @updatedAt\n rejectReason String?\n\n organization Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@unique([userId, orgId])\n @@map(\"organization_join_request\")\n}\n\nenum ORGANIZATION_JOIN_REQUEST {\n PENDING\n ACCEPTED\n REJECTED\n}\n\nenum ORGANIZATION_JOIN_REQUEST_TYPE {\n INVITED\n REQUESTED\n}\n\nmodel OrganizationUserJoinTable {\n userId String\n orgId String\n role ORG_ROLE @default(member)\n joinedDate DateTime @default(now())\n\n organization Organization @relation(fields: [orgId], references: [id], onDelete: Restrict)\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@unique([userId, orgId])\n @@map(\"organization_user_join\")\n}\n\nenum ORG_ROLE {\n owner\n admin\n member\n}\n\nmodel Organization {\n id String @id @default(uuid())\n name String\n description String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n members OrganizationUserJoinTable[]\n requestingMembers OrganizationJoinRequest[]\n\n @@map(\"organization\")\n}\n\n// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel User {\n id String @id @default(uuid())\n firstName String\n middleName String?\n lastName String\n email String @unique\n password String\n role USER_ROLE @default(user)\n isVerified Boolean? @default(false) // TODO: Email using queue\n refreshToken String?\n profilePicture String?\n isDeleted Boolean? @default(false)\n deletedAt DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n organizations OrganizationUserJoinTable[]\n organizationsRequested OrganizationJoinRequest[]\n\n @@map(\"user\")\n}\n\nenum USER_ROLE {\n superadmin\n user\n}\n",
|
"inlineSchema": "model OrganizationJoinRequest {\n userId String\n orgId String\n status ORGANIZATION_JOIN_REQUEST @default(PENDING)\n requestType ORGANIZATION_JOIN_REQUEST_TYPE\n requestedOn DateTime @default(now())\n role ORG_ROLE @default(member)\n updatedAt DateTime @updatedAt\n rejectReason String?\n requestMessage String?\n\n organization Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@unique([userId, orgId])\n @@map(\"organization_join_request\")\n}\n\nenum ORGANIZATION_JOIN_REQUEST {\n PENDING\n ACCEPTED\n REJECTED\n CANCELLED\n}\n\nenum ORGANIZATION_JOIN_REQUEST_TYPE {\n INVITED\n REQUESTED\n}\n\nmodel OrganizationUserJoinTable {\n userId String\n orgId String\n role ORG_ROLE @default(member)\n joinedDate DateTime @default(now())\n\n organization Organization @relation(fields: [orgId], references: [id], onDelete: Restrict)\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@unique([userId, orgId])\n @@map(\"organization_user_join\")\n}\n\nenum ORG_ROLE {\n owner\n admin\n member\n}\n\nmodel Organization {\n id String @id @default(uuid())\n name String\n description String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n members OrganizationUserJoinTable[]\n requestingMembers OrganizationJoinRequest[]\n\n @@map(\"organization\")\n}\n\n// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel User {\n id String @id @default(uuid())\n firstName String\n middleName String?\n lastName String\n email String @unique\n password String\n role USER_ROLE @default(user)\n isVerified Boolean? @default(false) // TODO: Email using queue\n refreshToken String?\n profilePicture String?\n isDeleted Boolean? @default(false)\n deletedAt DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n organizations OrganizationUserJoinTable[]\n organizationsRequested OrganizationJoinRequest[]\n\n @@map(\"user\")\n}\n\nenum USER_ROLE {\n superadmin\n user\n}\n",
|
||||||
"runtimeDataModel": {
|
"runtimeDataModel": {
|
||||||
"models": {},
|
"models": {},
|
||||||
"enums": {},
|
"enums": {},
|
||||||
@@ -28,7 +28,7 @@ const config: runtime.GetPrismaClientConfig = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config.runtimeDataModel = JSON.parse("{\"models\":{\"OrganizationJoinRequest\":{\"fields\":[{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"orgId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"status\",\"kind\":\"enum\",\"type\":\"ORGANIZATION_JOIN_REQUEST\"},{\"name\":\"requestType\",\"kind\":\"enum\",\"type\":\"ORGANIZATION_JOIN_REQUEST_TYPE\"},{\"name\":\"requestedOn\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"role\",\"kind\":\"enum\",\"type\":\"ORG_ROLE\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"rejectReason\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organization\",\"kind\":\"object\",\"type\":\"Organization\",\"relationName\":\"OrganizationToOrganizationJoinRequest\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"OrganizationJoinRequestToUser\"}],\"dbName\":\"organization_join_request\"},\"OrganizationUserJoinTable\":{\"fields\":[{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"orgId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"role\",\"kind\":\"enum\",\"type\":\"ORG_ROLE\"},{\"name\":\"joinedDate\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"organization\",\"kind\":\"object\",\"type\":\"Organization\",\"relationName\":\"OrganizationToOrganizationUserJoinTable\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"OrganizationUserJoinTableToUser\"}],\"dbName\":\"organization_user_join\"},\"Organization\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"members\",\"kind\":\"object\",\"type\":\"OrganizationUserJoinTable\",\"relationName\":\"OrganizationToOrganizationUserJoinTable\"},{\"name\":\"requestingMembers\",\"kind\":\"object\",\"type\":\"OrganizationJoinRequest\",\"relationName\":\"OrganizationToOrganizationJoinRequest\"}],\"dbName\":\"organization\"},\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"middleName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"role\",\"kind\":\"enum\",\"type\":\"USER_ROLE\"},{\"name\":\"isVerified\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"refreshToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"profilePicture\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"isDeleted\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"deletedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"organizations\",\"kind\":\"object\",\"type\":\"OrganizationUserJoinTable\",\"relationName\":\"OrganizationUserJoinTableToUser\"},{\"name\":\"organizationsRequested\",\"kind\":\"object\",\"type\":\"OrganizationJoinRequest\",\"relationName\":\"OrganizationJoinRequestToUser\"}],\"dbName\":\"user\"}},\"enums\":{},\"types\":{}}")
|
config.runtimeDataModel = JSON.parse("{\"models\":{\"OrganizationJoinRequest\":{\"fields\":[{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"orgId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"status\",\"kind\":\"enum\",\"type\":\"ORGANIZATION_JOIN_REQUEST\"},{\"name\":\"requestType\",\"kind\":\"enum\",\"type\":\"ORGANIZATION_JOIN_REQUEST_TYPE\"},{\"name\":\"requestedOn\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"role\",\"kind\":\"enum\",\"type\":\"ORG_ROLE\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"rejectReason\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"requestMessage\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organization\",\"kind\":\"object\",\"type\":\"Organization\",\"relationName\":\"OrganizationToOrganizationJoinRequest\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"OrganizationJoinRequestToUser\"}],\"dbName\":\"organization_join_request\"},\"OrganizationUserJoinTable\":{\"fields\":[{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"orgId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"role\",\"kind\":\"enum\",\"type\":\"ORG_ROLE\"},{\"name\":\"joinedDate\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"organization\",\"kind\":\"object\",\"type\":\"Organization\",\"relationName\":\"OrganizationToOrganizationUserJoinTable\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"OrganizationUserJoinTableToUser\"}],\"dbName\":\"organization_user_join\"},\"Organization\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"members\",\"kind\":\"object\",\"type\":\"OrganizationUserJoinTable\",\"relationName\":\"OrganizationToOrganizationUserJoinTable\"},{\"name\":\"requestingMembers\",\"kind\":\"object\",\"type\":\"OrganizationJoinRequest\",\"relationName\":\"OrganizationToOrganizationJoinRequest\"}],\"dbName\":\"organization\"},\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"middleName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"role\",\"kind\":\"enum\",\"type\":\"USER_ROLE\"},{\"name\":\"isVerified\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"refreshToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"profilePicture\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"isDeleted\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"deletedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"organizations\",\"kind\":\"object\",\"type\":\"OrganizationUserJoinTable\",\"relationName\":\"OrganizationUserJoinTableToUser\"},{\"name\":\"organizationsRequested\",\"kind\":\"object\",\"type\":\"OrganizationJoinRequest\",\"relationName\":\"OrganizationJoinRequestToUser\"}],\"dbName\":\"user\"}},\"enums\":{},\"types\":{}}")
|
||||||
|
|
||||||
async function decodeBase64AsWasm(wasmBase64: string): Promise<WebAssembly.Module> {
|
async function decodeBase64AsWasm(wasmBase64: string): Promise<WebAssembly.Module> {
|
||||||
const { Buffer } = await import('node:buffer')
|
const { Buffer } = await import('node:buffer')
|
||||||
|
|||||||
@@ -750,7 +750,8 @@ export const OrganizationJoinRequestScalarFieldEnum = {
|
|||||||
requestedOn: 'requestedOn',
|
requestedOn: 'requestedOn',
|
||||||
role: 'role',
|
role: 'role',
|
||||||
updatedAt: 'updatedAt',
|
updatedAt: 'updatedAt',
|
||||||
rejectReason: 'rejectReason'
|
rejectReason: 'rejectReason',
|
||||||
|
requestMessage: 'requestMessage'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type OrganizationJoinRequestScalarFieldEnum = (typeof OrganizationJoinRequestScalarFieldEnum)[keyof typeof OrganizationJoinRequestScalarFieldEnum]
|
export type OrganizationJoinRequestScalarFieldEnum = (typeof OrganizationJoinRequestScalarFieldEnum)[keyof typeof OrganizationJoinRequestScalarFieldEnum]
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ export const OrganizationJoinRequestScalarFieldEnum = {
|
|||||||
requestedOn: 'requestedOn',
|
requestedOn: 'requestedOn',
|
||||||
role: 'role',
|
role: 'role',
|
||||||
updatedAt: 'updatedAt',
|
updatedAt: 'updatedAt',
|
||||||
rejectReason: 'rejectReason'
|
rejectReason: 'rejectReason',
|
||||||
|
requestMessage: 'requestMessage'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type OrganizationJoinRequestScalarFieldEnum = (typeof OrganizationJoinRequestScalarFieldEnum)[keyof typeof OrganizationJoinRequestScalarFieldEnum]
|
export type OrganizationJoinRequestScalarFieldEnum = (typeof OrganizationJoinRequestScalarFieldEnum)[keyof typeof OrganizationJoinRequestScalarFieldEnum]
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export type OrganizationJoinRequestMinAggregateOutputType = {
|
|||||||
role: $Enums.ORG_ROLE | null
|
role: $Enums.ORG_ROLE | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
rejectReason: string | null
|
rejectReason: string | null
|
||||||
|
requestMessage: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestMaxAggregateOutputType = {
|
export type OrganizationJoinRequestMaxAggregateOutputType = {
|
||||||
@@ -44,6 +45,7 @@ export type OrganizationJoinRequestMaxAggregateOutputType = {
|
|||||||
role: $Enums.ORG_ROLE | null
|
role: $Enums.ORG_ROLE | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
rejectReason: string | null
|
rejectReason: string | null
|
||||||
|
requestMessage: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestCountAggregateOutputType = {
|
export type OrganizationJoinRequestCountAggregateOutputType = {
|
||||||
@@ -55,6 +57,7 @@ export type OrganizationJoinRequestCountAggregateOutputType = {
|
|||||||
role: number
|
role: number
|
||||||
updatedAt: number
|
updatedAt: number
|
||||||
rejectReason: number
|
rejectReason: number
|
||||||
|
requestMessage: number
|
||||||
_all: number
|
_all: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +71,7 @@ export type OrganizationJoinRequestMinAggregateInputType = {
|
|||||||
role?: true
|
role?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
rejectReason?: true
|
rejectReason?: true
|
||||||
|
requestMessage?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestMaxAggregateInputType = {
|
export type OrganizationJoinRequestMaxAggregateInputType = {
|
||||||
@@ -79,6 +83,7 @@ export type OrganizationJoinRequestMaxAggregateInputType = {
|
|||||||
role?: true
|
role?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
rejectReason?: true
|
rejectReason?: true
|
||||||
|
requestMessage?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestCountAggregateInputType = {
|
export type OrganizationJoinRequestCountAggregateInputType = {
|
||||||
@@ -90,6 +95,7 @@ export type OrganizationJoinRequestCountAggregateInputType = {
|
|||||||
role?: true
|
role?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
rejectReason?: true
|
rejectReason?: true
|
||||||
|
requestMessage?: true
|
||||||
_all?: true
|
_all?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +180,7 @@ export type OrganizationJoinRequestGroupByOutputType = {
|
|||||||
role: $Enums.ORG_ROLE
|
role: $Enums.ORG_ROLE
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
rejectReason: string | null
|
rejectReason: string | null
|
||||||
|
requestMessage: string | null
|
||||||
_count: OrganizationJoinRequestCountAggregateOutputType | null
|
_count: OrganizationJoinRequestCountAggregateOutputType | null
|
||||||
_min: OrganizationJoinRequestMinAggregateOutputType | null
|
_min: OrganizationJoinRequestMinAggregateOutputType | null
|
||||||
_max: OrganizationJoinRequestMaxAggregateOutputType | null
|
_max: OrganizationJoinRequestMaxAggregateOutputType | null
|
||||||
@@ -206,6 +213,7 @@ export type OrganizationJoinRequestWhereInput = {
|
|||||||
role?: Prisma.EnumORG_ROLEFilter<"OrganizationJoinRequest"> | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFilter<"OrganizationJoinRequest"> | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFilter<"OrganizationJoinRequest"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"OrganizationJoinRequest"> | Date | string
|
||||||
rejectReason?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null
|
rejectReason?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null
|
||||||
|
requestMessage?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null
|
||||||
organization?: Prisma.XOR<Prisma.OrganizationScalarRelationFilter, Prisma.OrganizationWhereInput>
|
organization?: Prisma.XOR<Prisma.OrganizationScalarRelationFilter, Prisma.OrganizationWhereInput>
|
||||||
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
||||||
}
|
}
|
||||||
@@ -219,6 +227,7 @@ export type OrganizationJoinRequestOrderByWithRelationInput = {
|
|||||||
role?: Prisma.SortOrder
|
role?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
rejectReason?: Prisma.SortOrderInput | Prisma.SortOrder
|
rejectReason?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
requestMessage?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
organization?: Prisma.OrganizationOrderByWithRelationInput
|
organization?: Prisma.OrganizationOrderByWithRelationInput
|
||||||
user?: Prisma.UserOrderByWithRelationInput
|
user?: Prisma.UserOrderByWithRelationInput
|
||||||
}
|
}
|
||||||
@@ -236,6 +245,7 @@ export type OrganizationJoinRequestWhereUniqueInput = Prisma.AtLeast<{
|
|||||||
role?: Prisma.EnumORG_ROLEFilter<"OrganizationJoinRequest"> | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFilter<"OrganizationJoinRequest"> | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFilter<"OrganizationJoinRequest"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"OrganizationJoinRequest"> | Date | string
|
||||||
rejectReason?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null
|
rejectReason?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null
|
||||||
|
requestMessage?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null
|
||||||
organization?: Prisma.XOR<Prisma.OrganizationScalarRelationFilter, Prisma.OrganizationWhereInput>
|
organization?: Prisma.XOR<Prisma.OrganizationScalarRelationFilter, Prisma.OrganizationWhereInput>
|
||||||
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
user?: Prisma.XOR<Prisma.UserScalarRelationFilter, Prisma.UserWhereInput>
|
||||||
}, "userId_orgId">
|
}, "userId_orgId">
|
||||||
@@ -249,6 +259,7 @@ export type OrganizationJoinRequestOrderByWithAggregationInput = {
|
|||||||
role?: Prisma.SortOrder
|
role?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
rejectReason?: Prisma.SortOrderInput | Prisma.SortOrder
|
rejectReason?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
requestMessage?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
_count?: Prisma.OrganizationJoinRequestCountOrderByAggregateInput
|
_count?: Prisma.OrganizationJoinRequestCountOrderByAggregateInput
|
||||||
_max?: Prisma.OrganizationJoinRequestMaxOrderByAggregateInput
|
_max?: Prisma.OrganizationJoinRequestMaxOrderByAggregateInput
|
||||||
_min?: Prisma.OrganizationJoinRequestMinOrderByAggregateInput
|
_min?: Prisma.OrganizationJoinRequestMinOrderByAggregateInput
|
||||||
@@ -266,6 +277,7 @@ export type OrganizationJoinRequestScalarWhereWithAggregatesInput = {
|
|||||||
role?: Prisma.EnumORG_ROLEWithAggregatesFilter<"OrganizationJoinRequest"> | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEWithAggregatesFilter<"OrganizationJoinRequest"> | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"OrganizationJoinRequest"> | Date | string
|
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"OrganizationJoinRequest"> | Date | string
|
||||||
rejectReason?: Prisma.StringNullableWithAggregatesFilter<"OrganizationJoinRequest"> | string | null
|
rejectReason?: Prisma.StringNullableWithAggregatesFilter<"OrganizationJoinRequest"> | string | null
|
||||||
|
requestMessage?: Prisma.StringNullableWithAggregatesFilter<"OrganizationJoinRequest"> | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestCreateInput = {
|
export type OrganizationJoinRequestCreateInput = {
|
||||||
@@ -275,6 +287,7 @@ export type OrganizationJoinRequestCreateInput = {
|
|||||||
role?: $Enums.ORG_ROLE
|
role?: $Enums.ORG_ROLE
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
rejectReason?: string | null
|
rejectReason?: string | null
|
||||||
|
requestMessage?: string | null
|
||||||
organization: Prisma.OrganizationCreateNestedOneWithoutRequestingMembersInput
|
organization: Prisma.OrganizationCreateNestedOneWithoutRequestingMembersInput
|
||||||
user: Prisma.UserCreateNestedOneWithoutOrganizationsRequestedInput
|
user: Prisma.UserCreateNestedOneWithoutOrganizationsRequestedInput
|
||||||
}
|
}
|
||||||
@@ -288,6 +301,7 @@ export type OrganizationJoinRequestUncheckedCreateInput = {
|
|||||||
role?: $Enums.ORG_ROLE
|
role?: $Enums.ORG_ROLE
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
rejectReason?: string | null
|
rejectReason?: string | null
|
||||||
|
requestMessage?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestUpdateInput = {
|
export type OrganizationJoinRequestUpdateInput = {
|
||||||
@@ -297,6 +311,7 @@ export type OrganizationJoinRequestUpdateInput = {
|
|||||||
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
requestMessage?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
organization?: Prisma.OrganizationUpdateOneRequiredWithoutRequestingMembersNestedInput
|
organization?: Prisma.OrganizationUpdateOneRequiredWithoutRequestingMembersNestedInput
|
||||||
user?: Prisma.UserUpdateOneRequiredWithoutOrganizationsRequestedNestedInput
|
user?: Prisma.UserUpdateOneRequiredWithoutOrganizationsRequestedNestedInput
|
||||||
}
|
}
|
||||||
@@ -310,6 +325,7 @@ export type OrganizationJoinRequestUncheckedUpdateInput = {
|
|||||||
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
requestMessage?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestCreateManyInput = {
|
export type OrganizationJoinRequestCreateManyInput = {
|
||||||
@@ -321,6 +337,7 @@ export type OrganizationJoinRequestCreateManyInput = {
|
|||||||
role?: $Enums.ORG_ROLE
|
role?: $Enums.ORG_ROLE
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
rejectReason?: string | null
|
rejectReason?: string | null
|
||||||
|
requestMessage?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestUpdateManyMutationInput = {
|
export type OrganizationJoinRequestUpdateManyMutationInput = {
|
||||||
@@ -330,6 +347,7 @@ export type OrganizationJoinRequestUpdateManyMutationInput = {
|
|||||||
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
requestMessage?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestUncheckedUpdateManyInput = {
|
export type OrganizationJoinRequestUncheckedUpdateManyInput = {
|
||||||
@@ -341,6 +359,7 @@ export type OrganizationJoinRequestUncheckedUpdateManyInput = {
|
|||||||
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
requestMessage?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestUserIdOrgIdCompoundUniqueInput = {
|
export type OrganizationJoinRequestUserIdOrgIdCompoundUniqueInput = {
|
||||||
@@ -357,6 +376,7 @@ export type OrganizationJoinRequestCountOrderByAggregateInput = {
|
|||||||
role?: Prisma.SortOrder
|
role?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
rejectReason?: Prisma.SortOrder
|
rejectReason?: Prisma.SortOrder
|
||||||
|
requestMessage?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestMaxOrderByAggregateInput = {
|
export type OrganizationJoinRequestMaxOrderByAggregateInput = {
|
||||||
@@ -368,6 +388,7 @@ export type OrganizationJoinRequestMaxOrderByAggregateInput = {
|
|||||||
role?: Prisma.SortOrder
|
role?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
rejectReason?: Prisma.SortOrder
|
rejectReason?: Prisma.SortOrder
|
||||||
|
requestMessage?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestMinOrderByAggregateInput = {
|
export type OrganizationJoinRequestMinOrderByAggregateInput = {
|
||||||
@@ -379,6 +400,7 @@ export type OrganizationJoinRequestMinOrderByAggregateInput = {
|
|||||||
role?: Prisma.SortOrder
|
role?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
rejectReason?: Prisma.SortOrder
|
rejectReason?: Prisma.SortOrder
|
||||||
|
requestMessage?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestListRelationFilter = {
|
export type OrganizationJoinRequestListRelationFilter = {
|
||||||
@@ -506,6 +528,7 @@ export type OrganizationJoinRequestCreateWithoutOrganizationInput = {
|
|||||||
role?: $Enums.ORG_ROLE
|
role?: $Enums.ORG_ROLE
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
rejectReason?: string | null
|
rejectReason?: string | null
|
||||||
|
requestMessage?: string | null
|
||||||
user: Prisma.UserCreateNestedOneWithoutOrganizationsRequestedInput
|
user: Prisma.UserCreateNestedOneWithoutOrganizationsRequestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,6 +540,7 @@ export type OrganizationJoinRequestUncheckedCreateWithoutOrganizationInput = {
|
|||||||
role?: $Enums.ORG_ROLE
|
role?: $Enums.ORG_ROLE
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
rejectReason?: string | null
|
rejectReason?: string | null
|
||||||
|
requestMessage?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestCreateOrConnectWithoutOrganizationInput = {
|
export type OrganizationJoinRequestCreateOrConnectWithoutOrganizationInput = {
|
||||||
@@ -557,6 +581,7 @@ export type OrganizationJoinRequestScalarWhereInput = {
|
|||||||
role?: Prisma.EnumORG_ROLEFilter<"OrganizationJoinRequest"> | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFilter<"OrganizationJoinRequest"> | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFilter<"OrganizationJoinRequest"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"OrganizationJoinRequest"> | Date | string
|
||||||
rejectReason?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null
|
rejectReason?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null
|
||||||
|
requestMessage?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestCreateWithoutUserInput = {
|
export type OrganizationJoinRequestCreateWithoutUserInput = {
|
||||||
@@ -566,6 +591,7 @@ export type OrganizationJoinRequestCreateWithoutUserInput = {
|
|||||||
role?: $Enums.ORG_ROLE
|
role?: $Enums.ORG_ROLE
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
rejectReason?: string | null
|
rejectReason?: string | null
|
||||||
|
requestMessage?: string | null
|
||||||
organization: Prisma.OrganizationCreateNestedOneWithoutRequestingMembersInput
|
organization: Prisma.OrganizationCreateNestedOneWithoutRequestingMembersInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,6 +603,7 @@ export type OrganizationJoinRequestUncheckedCreateWithoutUserInput = {
|
|||||||
role?: $Enums.ORG_ROLE
|
role?: $Enums.ORG_ROLE
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
rejectReason?: string | null
|
rejectReason?: string | null
|
||||||
|
requestMessage?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestCreateOrConnectWithoutUserInput = {
|
export type OrganizationJoinRequestCreateOrConnectWithoutUserInput = {
|
||||||
@@ -613,6 +640,7 @@ export type OrganizationJoinRequestCreateManyOrganizationInput = {
|
|||||||
role?: $Enums.ORG_ROLE
|
role?: $Enums.ORG_ROLE
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
rejectReason?: string | null
|
rejectReason?: string | null
|
||||||
|
requestMessage?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestUpdateWithoutOrganizationInput = {
|
export type OrganizationJoinRequestUpdateWithoutOrganizationInput = {
|
||||||
@@ -622,6 +650,7 @@ export type OrganizationJoinRequestUpdateWithoutOrganizationInput = {
|
|||||||
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
requestMessage?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
user?: Prisma.UserUpdateOneRequiredWithoutOrganizationsRequestedNestedInput
|
user?: Prisma.UserUpdateOneRequiredWithoutOrganizationsRequestedNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,6 +662,7 @@ export type OrganizationJoinRequestUncheckedUpdateWithoutOrganizationInput = {
|
|||||||
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
requestMessage?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestUncheckedUpdateManyWithoutOrganizationInput = {
|
export type OrganizationJoinRequestUncheckedUpdateManyWithoutOrganizationInput = {
|
||||||
@@ -643,6 +673,7 @@ export type OrganizationJoinRequestUncheckedUpdateManyWithoutOrganizationInput =
|
|||||||
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
requestMessage?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestCreateManyUserInput = {
|
export type OrganizationJoinRequestCreateManyUserInput = {
|
||||||
@@ -653,6 +684,7 @@ export type OrganizationJoinRequestCreateManyUserInput = {
|
|||||||
role?: $Enums.ORG_ROLE
|
role?: $Enums.ORG_ROLE
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
rejectReason?: string | null
|
rejectReason?: string | null
|
||||||
|
requestMessage?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestUpdateWithoutUserInput = {
|
export type OrganizationJoinRequestUpdateWithoutUserInput = {
|
||||||
@@ -662,6 +694,7 @@ export type OrganizationJoinRequestUpdateWithoutUserInput = {
|
|||||||
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
requestMessage?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
organization?: Prisma.OrganizationUpdateOneRequiredWithoutRequestingMembersNestedInput
|
organization?: Prisma.OrganizationUpdateOneRequiredWithoutRequestingMembersNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,6 +706,7 @@ export type OrganizationJoinRequestUncheckedUpdateWithoutUserInput = {
|
|||||||
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
requestMessage?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestUncheckedUpdateManyWithoutUserInput = {
|
export type OrganizationJoinRequestUncheckedUpdateManyWithoutUserInput = {
|
||||||
@@ -683,6 +717,7 @@ export type OrganizationJoinRequestUncheckedUpdateManyWithoutUserInput = {
|
|||||||
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
role?: Prisma.EnumORG_ROLEFieldUpdateOperationsInput | $Enums.ORG_ROLE
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
requestMessage?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -696,6 +731,7 @@ export type OrganizationJoinRequestSelect<ExtArgs extends runtime.Types.Extensio
|
|||||||
role?: boolean
|
role?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
rejectReason?: boolean
|
rejectReason?: boolean
|
||||||
|
requestMessage?: boolean
|
||||||
organization?: boolean | Prisma.OrganizationDefaultArgs<ExtArgs>
|
organization?: boolean | Prisma.OrganizationDefaultArgs<ExtArgs>
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["organizationJoinRequest"]>
|
}, ExtArgs["result"]["organizationJoinRequest"]>
|
||||||
@@ -709,6 +745,7 @@ export type OrganizationJoinRequestSelectCreateManyAndReturn<ExtArgs extends run
|
|||||||
role?: boolean
|
role?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
rejectReason?: boolean
|
rejectReason?: boolean
|
||||||
|
requestMessage?: boolean
|
||||||
organization?: boolean | Prisma.OrganizationDefaultArgs<ExtArgs>
|
organization?: boolean | Prisma.OrganizationDefaultArgs<ExtArgs>
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["organizationJoinRequest"]>
|
}, ExtArgs["result"]["organizationJoinRequest"]>
|
||||||
@@ -722,6 +759,7 @@ export type OrganizationJoinRequestSelectUpdateManyAndReturn<ExtArgs extends run
|
|||||||
role?: boolean
|
role?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
rejectReason?: boolean
|
rejectReason?: boolean
|
||||||
|
requestMessage?: boolean
|
||||||
organization?: boolean | Prisma.OrganizationDefaultArgs<ExtArgs>
|
organization?: boolean | Prisma.OrganizationDefaultArgs<ExtArgs>
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["organizationJoinRequest"]>
|
}, ExtArgs["result"]["organizationJoinRequest"]>
|
||||||
@@ -735,9 +773,10 @@ export type OrganizationJoinRequestSelectScalar = {
|
|||||||
role?: boolean
|
role?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
rejectReason?: boolean
|
rejectReason?: boolean
|
||||||
|
requestMessage?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrganizationJoinRequestOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"userId" | "orgId" | "status" | "requestType" | "requestedOn" | "role" | "updatedAt" | "rejectReason", ExtArgs["result"]["organizationJoinRequest"]>
|
export type OrganizationJoinRequestOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"userId" | "orgId" | "status" | "requestType" | "requestedOn" | "role" | "updatedAt" | "rejectReason" | "requestMessage", ExtArgs["result"]["organizationJoinRequest"]>
|
||||||
export type OrganizationJoinRequestInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type OrganizationJoinRequestInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
organization?: boolean | Prisma.OrganizationDefaultArgs<ExtArgs>
|
organization?: boolean | Prisma.OrganizationDefaultArgs<ExtArgs>
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||||
@@ -766,6 +805,7 @@ export type $OrganizationJoinRequestPayload<ExtArgs extends runtime.Types.Extens
|
|||||||
role: $Enums.ORG_ROLE
|
role: $Enums.ORG_ROLE
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
rejectReason: string | null
|
rejectReason: string | null
|
||||||
|
requestMessage: string | null
|
||||||
}, ExtArgs["result"]["organizationJoinRequest"]>
|
}, ExtArgs["result"]["organizationJoinRequest"]>
|
||||||
composites: {}
|
composites: {}
|
||||||
}
|
}
|
||||||
@@ -1199,6 +1239,7 @@ export interface OrganizationJoinRequestFieldRefs {
|
|||||||
readonly role: Prisma.FieldRef<"OrganizationJoinRequest", 'ORG_ROLE'>
|
readonly role: Prisma.FieldRef<"OrganizationJoinRequest", 'ORG_ROLE'>
|
||||||
readonly updatedAt: Prisma.FieldRef<"OrganizationJoinRequest", 'DateTime'>
|
readonly updatedAt: Prisma.FieldRef<"OrganizationJoinRequest", 'DateTime'>
|
||||||
readonly rejectReason: Prisma.FieldRef<"OrganizationJoinRequest", 'String'>
|
readonly rejectReason: Prisma.FieldRef<"OrganizationJoinRequest", 'String'>
|
||||||
|
readonly requestMessage: Prisma.FieldRef<"OrganizationJoinRequest", 'String'>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "organization_join_request" ADD COLUMN "requestMessage" TEXT;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
-- This is an empty migration.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterEnum
|
||||||
|
ALTER TYPE "ORGANIZATION_JOIN_REQUEST" ADD VALUE 'CANCELLED';
|
||||||
@@ -7,6 +7,7 @@ model OrganizationJoinRequest {
|
|||||||
role ORG_ROLE @default(member)
|
role ORG_ROLE @default(member)
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
rejectReason String?
|
rejectReason String?
|
||||||
|
requestMessage String?
|
||||||
|
|
||||||
organization Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
organization Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
@@ -19,6 +20,7 @@ enum ORGANIZATION_JOIN_REQUEST {
|
|||||||
PENDING
|
PENDING
|
||||||
ACCEPTED
|
ACCEPTED
|
||||||
REJECTED
|
REJECTED
|
||||||
|
CANCELLED
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ORGANIZATION_JOIN_REQUEST_TYPE {
|
enum ORGANIZATION_JOIN_REQUEST_TYPE {
|
||||||
|
|||||||
2
src/cache/cache.service.ts
vendored
2
src/cache/cache.service.ts
vendored
@@ -44,6 +44,7 @@ export class CacheService {
|
|||||||
// Fallback to DB
|
// Fallback to DB
|
||||||
const fresh = await factory();
|
const fresh = await factory();
|
||||||
|
|
||||||
|
if (fresh) {
|
||||||
// Try setting cache only if Redis available
|
// Try setting cache only if Redis available
|
||||||
if (this.redisAvailable) {
|
if (this.redisAvailable) {
|
||||||
try {
|
try {
|
||||||
@@ -52,6 +53,7 @@ export class CacheService {
|
|||||||
this.redisAvailable = false;
|
this.redisAvailable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return fresh;
|
return fresh;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ import { NestFactory } from '@nestjs/core';
|
|||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||||
|
import { Logger } from '@nestjs/common';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
|
|
||||||
const swaggerConfig = new DocumentBuilder()
|
const swaggerConfig = new DocumentBuilder()
|
||||||
.setTitle('Kaa Khane')
|
.setTitle('MultiTenant Saas')
|
||||||
.setDescription(`API Documentation for Kaa Khane`)
|
.setDescription(`API Documentation for a simple MultiTenant Saas Application`)
|
||||||
.setVersion('0.0.1')
|
.setVersion('0.0.1')
|
||||||
.addGlobalResponse(
|
.addGlobalResponse(
|
||||||
{
|
{
|
||||||
@@ -46,5 +47,7 @@ async function bootstrap() {
|
|||||||
const port = config.get<number>('PORT') ?? 3000;
|
const port = config.get<number>('PORT') ?? 3000;
|
||||||
|
|
||||||
await app.listen(port);
|
await app.listen(port);
|
||||||
|
|
||||||
|
Logger.log(`Listning on port ${port}`)
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|||||||
1
src/organization-membership/constants/index.ts
Normal file
1
src/organization-membership/constants/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from "./type"
|
||||||
4
src/organization-membership/constants/type.ts
Normal file
4
src/organization-membership/constants/type.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export enum USER_ORG_ACCEPT_REJECT_ACTION {
|
||||||
|
ACCEPT = 'ACCEPT',
|
||||||
|
REJECT = 'REJECT'
|
||||||
|
}
|
||||||
@@ -1 +1,3 @@
|
|||||||
export * from './invite-to-org.dto';
|
export * from './invite-to-org.dto';
|
||||||
|
export * from './join-request.dto'
|
||||||
|
export * from "./user-invitation-action.dto"
|
||||||
|
|||||||
@@ -12,15 +12,6 @@ export class InviteUserToOrganizationRequestDTO {
|
|||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
invitedUserEmail: string;
|
invitedUserEmail: string;
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
description: 'Organization id',
|
|
||||||
example: 'eeec2c79-766a-4174-8004-2e57642095fe',
|
|
||||||
type: 'string',
|
|
||||||
})
|
|
||||||
@IsUUID()
|
|
||||||
@IsNotEmpty()
|
|
||||||
orgId: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Role to assign',
|
description: 'Role to assign',
|
||||||
example: ORG_ROLE.member,
|
example: ORG_ROLE.member,
|
||||||
|
|||||||
13
src/organization-membership/dto/join-request.dto.ts
Normal file
13
src/organization-membership/dto/join-request.dto.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
import { IsNotEmpty, IsOptional, IsString, IsUUID } from "class-validator";
|
||||||
|
|
||||||
|
export class JoinRequestToOrganizationRequestDTO {
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Message along with the request',
|
||||||
|
example: 'I would like to join',
|
||||||
|
type: 'string',
|
||||||
|
})
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
requestMessage?: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
import { IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID } from "class-validator";
|
||||||
|
import { USER_ORG_ACCEPT_REJECT_ACTION } from "../constants";
|
||||||
|
|
||||||
|
|
||||||
|
export class UserOrganizationInvitationActionRequestDTO {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Action',
|
||||||
|
example: USER_ORG_ACCEPT_REJECT_ACTION.ACCEPT,
|
||||||
|
type: 'string',
|
||||||
|
})
|
||||||
|
@IsEnum(USER_ORG_ACCEPT_REJECT_ACTION)
|
||||||
|
@IsNotEmpty()
|
||||||
|
action: string
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Message(reject reason)',
|
||||||
|
example: 'Bad sry or smth',
|
||||||
|
type: 'string',
|
||||||
|
})
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
@@ -1,62 +1,145 @@
|
|||||||
import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param, ParseEnumPipe, ParseUUIDPipe, Patch, Post, Query } from '@nestjs/common';
|
||||||
import { OrganizationMembershipService } from './organization-membership.service';
|
import { OrganizationMembershipService } from './organization-membership.service';
|
||||||
import { RequestContextService } from 'core/als/request-context.service';
|
import { RequestContextService } from 'core/als/request-context.service';
|
||||||
import { ApiBearerAuth } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiParam, ApiQuery } from '@nestjs/swagger';
|
||||||
import { InviteUserToOrganizationRequestDTO } from './dto';
|
import { InviteUserToOrganizationRequestDTO, JoinRequestToOrganizationRequestDTO, UserOrganizationInvitationActionRequestDTO } from './dto';
|
||||||
|
import { ORGANIZATION_JOIN_REQUEST_TYPE } from 'prisma/generated/prisma/enums';
|
||||||
|
|
||||||
@Controller('organization-membership')
|
/* NOTE: Regarding endpoint path naming
|
||||||
|
* - Since we follow REST style, endpoint are resource based.
|
||||||
|
* - So insted of /organization/:orgId/invitation-action, we user ..../invitation/:invitationId
|
||||||
|
* (invitationid points to a resource)
|
||||||
|
* */
|
||||||
|
|
||||||
|
@Controller('membership')
|
||||||
@ApiBearerAuth('access-token')
|
@ApiBearerAuth('access-token')
|
||||||
export class OrganizationMembershipController {
|
export class OrganizationMembershipController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly orgMemService: OrganizationMembershipService,
|
private readonly orgMemService: OrganizationMembershipService,
|
||||||
private readonly requestContext: RequestContextService,
|
private readonly requestContext: RequestContextService,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
@Post('/invite')
|
|
||||||
async inviteUserToOrg(@Body() body: InviteUserToOrganizationRequestDTO) {
|
|
||||||
const user = this.requestContext.user;
|
|
||||||
return await this.orgMemService.inviteUserToOrg(user.userId, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Post('/request')
|
|
||||||
async requestToJoinOrg() {}
|
|
||||||
|
|
||||||
// TODO: Move invite to org. section and join to user. Also option to cancel invitation and join req.
|
|
||||||
|
|
||||||
/* *
|
/* *
|
||||||
* USER OPERATIONS
|
* USER OPERATIONS
|
||||||
* */
|
* */
|
||||||
@Patch('user/accept-invitation')
|
@ApiOperation({ summary: 'Send request to join an organization' })
|
||||||
acceptInvitation() {}
|
@ApiParam({
|
||||||
|
name: 'orgId',
|
||||||
@Patch('user/reject-invitation')
|
type: String,
|
||||||
rejectInvitation() {}
|
})
|
||||||
|
@Post('organization/:orgId/join-request')
|
||||||
@Get('user/invitations')
|
async requestToJoinOrg(
|
||||||
async getUserInvitations() {
|
@Param('orgId', new ParseUUIDPipe()) orgId: string,
|
||||||
|
@Body() body: JoinRequestToOrganizationRequestDTO
|
||||||
|
) {
|
||||||
const user = this.requestContext.user;
|
const user = this.requestContext.user;
|
||||||
return await this.orgMemService.getUserInvitations(user.userId);
|
return await this.orgMemService.usersRequestToJoin(user.userId, orgId, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ summary: 'Cancel a sent invitation to join an organization' })
|
||||||
|
@ApiParam({
|
||||||
|
name: 'orgId',
|
||||||
|
type: String,
|
||||||
|
})
|
||||||
|
@Delete('organization/:orgId/join-request')
|
||||||
|
async cancelRequestToJoinOrg(@Param('orgId', new ParseUUIDPipe()) orgId: string) {
|
||||||
|
const user = this.requestContext.user;
|
||||||
|
return await this.orgMemService.userCancelOrgJoinRequest(user.userId, orgId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ summary: 'Accept or reject an invitation from an organization' })
|
||||||
|
@ApiParam({
|
||||||
|
name: 'orgId',
|
||||||
|
type: String,
|
||||||
|
})
|
||||||
|
@Patch('organization/:orgId/invitation')
|
||||||
|
async acceptOrRejectInvitation(
|
||||||
|
@Param('orgId', new ParseUUIDPipe()) orgId: string,
|
||||||
|
@Body() body: UserOrganizationInvitationActionRequestDTO
|
||||||
|
) {
|
||||||
|
const user = this.requestContext.user;
|
||||||
|
return await this.orgMemService.userOrganiaztionRequestAction(
|
||||||
|
user.userId,
|
||||||
|
orgId,
|
||||||
|
body
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ summary: 'List invitations recieved or join requests sent' })
|
||||||
|
@ApiQuery({
|
||||||
|
name: 'requestType',
|
||||||
|
enum: ORGANIZATION_JOIN_REQUEST_TYPE,
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@Get('me/invitations')
|
||||||
|
async getUserInvitations(
|
||||||
|
@Query(
|
||||||
|
'requestType',
|
||||||
|
new ParseEnumPipe(
|
||||||
|
ORGANIZATION_JOIN_REQUEST_TYPE, { optional: true }
|
||||||
|
)
|
||||||
|
) requestType?: ORGANIZATION_JOIN_REQUEST_TYPE
|
||||||
|
) {
|
||||||
|
const user = this.requestContext.user;
|
||||||
|
return await this.orgMemService.getUserInvitations(user.userId, requestType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ summary: 'List organizations user is part of' })
|
||||||
|
@Get('me/organizations')
|
||||||
|
async getUserOrganizations() {
|
||||||
|
const user = this.requestContext.user;
|
||||||
|
return await this.orgMemService.getOrganizationsOfUser(user.userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ summary: 'Leave an organization' })
|
||||||
|
@ApiParam({
|
||||||
|
name: 'orgId',
|
||||||
|
type: String,
|
||||||
|
})
|
||||||
|
@Delete('organization/:orgId/member/me')
|
||||||
|
async leaveOrganization(
|
||||||
|
@Param('orgId', new ParseUUIDPipe()) orgId: string,
|
||||||
|
) {
|
||||||
|
const user = this.requestContext.user;
|
||||||
|
return await this.orgMemService.userLeaveAnOrganization(user.userId, orgId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('user/organizations')
|
|
||||||
async getUserOrganizations() {}
|
|
||||||
|
|
||||||
/* *
|
/* *
|
||||||
* ORGANIZATION OPERATIONS
|
* ORGANIZATION OPERATIONS
|
||||||
* */
|
* */
|
||||||
@Get('organization/:id/members')
|
|
||||||
async getOrganizationMemebers(@Param('id') orgId: string) {
|
|
||||||
return await this.orgMemService.getMemebersOfOrganization(orgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get('organization/:id/invitations')
|
// @ApiOperation({ summary: 'Invite user to organization' })
|
||||||
async getOrganizationInvitations(@Param('id') orgId: string) {
|
// @ApiParam({
|
||||||
return await this.orgMemService.getMemebersOfOrganization(orgId);
|
// name: 'orgId',
|
||||||
}
|
// type: String,
|
||||||
|
// })
|
||||||
@Patch('organization/:id/accept-request')
|
// @Post('organization/:orgId/invitation')
|
||||||
acceptJoinRequest() {}
|
// async inviteUserToOrg(
|
||||||
|
// @Param('orgId', new ParseUUIDPipe()) orgId: string,
|
||||||
@Patch('organization/:id/reject-request')
|
// @Body() body: InviteUserToOrganizationRequestDTO
|
||||||
rejectJoinRequest() {}
|
// ) {
|
||||||
|
// const user = this.requestContext.user;
|
||||||
|
// return await this.orgMemService.inviteUserToOrg(
|
||||||
|
// user.userId,
|
||||||
|
// orgId,
|
||||||
|
// body
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// @Get('organization/:id/members')
|
||||||
|
// async getOrganizationMemebers(@Param('id') orgId: string) {
|
||||||
|
// return await this.orgMemService.getMemebersOfOrganization(orgId);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Get('organization/:id/invitations')
|
||||||
|
// async getOrganizationInvitations(@Param('id') orgId: string) {
|
||||||
|
// return await this.orgMemService.getOrganizationRequestList(orgId);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Patch('organization/:id/accept-request')
|
||||||
|
// acceptJoinRequest() { }
|
||||||
|
//
|
||||||
|
// @Patch('organization/:id/reject-request')
|
||||||
|
// rejectJoinRequest() { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { OrganizationService } from 'src/organization/organization.service';
|
import { OrganizationService } from 'src/organization/organization.service';
|
||||||
import { UserService } from 'src/user/user.service';
|
import { UserService } from 'src/user/user.service';
|
||||||
import { InviteUserToOrganizationRequestDTO } from './dto';
|
import { InviteUserToOrganizationRequestDTO, UserOrganizationInvitationActionRequestDTO } from './dto';
|
||||||
import { PrismaService } from 'src/prisma/prisma.service';
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
import {
|
import {
|
||||||
ORGANIZATION_JOIN_REQUEST,
|
ORGANIZATION_JOIN_REQUEST,
|
||||||
@@ -15,6 +15,8 @@ import {
|
|||||||
import { AuthorizationService } from 'src/authorization/authorization.service';
|
import { AuthorizationService } from 'src/authorization/authorization.service';
|
||||||
import { USER_ORGANIZATION_OPERATIONS } from 'src/authorization/operations';
|
import { USER_ORGANIZATION_OPERATIONS } from 'src/authorization/operations';
|
||||||
import { Prisma } from 'prisma/generated/prisma/client';
|
import { Prisma } from 'prisma/generated/prisma/client';
|
||||||
|
import { JoinRequestToOrganizationRequestDTO } from './dto/join-request.dto';
|
||||||
|
import { USER_ORG_ACCEPT_REJECT_ACTION } from './constants';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrganizationMembershipService {
|
export class OrganizationMembershipService {
|
||||||
@@ -23,14 +25,15 @@ export class OrganizationMembershipService {
|
|||||||
private readonly orgService: OrganizationService,
|
private readonly orgService: OrganizationService,
|
||||||
private readonly prisma: PrismaService,
|
private readonly prisma: PrismaService,
|
||||||
private readonly authorization: AuthorizationService,
|
private readonly authorization: AuthorizationService,
|
||||||
) {}
|
) { }
|
||||||
async inviteUserToOrg(
|
async inviteUserToOrg(
|
||||||
userId: string,
|
userId: string,
|
||||||
|
orgId: string,
|
||||||
dto: InviteUserToOrganizationRequestDTO,
|
dto: InviteUserToOrganizationRequestDTO,
|
||||||
) {
|
) {
|
||||||
const { invitedUserEmail, ...rest } = dto;
|
const { invitedUserEmail, ...rest } = dto;
|
||||||
const [orgExists, invitedUser] = await Promise.all([
|
const [orgExists, invitedUser] = await Promise.all([
|
||||||
this.orgService.organizationExists(dto.orgId),
|
this.orgService.findById(orgId),
|
||||||
this.userService.findByEmail(invitedUserEmail),
|
this.userService.findByEmail(invitedUserEmail),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -41,7 +44,7 @@ export class OrganizationMembershipService {
|
|||||||
await this.prisma.organizationUserJoinTable.findUnique({
|
await this.prisma.organizationUserJoinTable.findUnique({
|
||||||
where: {
|
where: {
|
||||||
userId_orgId: {
|
userId_orgId: {
|
||||||
orgId: dto.orgId,
|
orgId: orgId,
|
||||||
userId: invitedUser.id,
|
userId: invitedUser.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -52,7 +55,7 @@ export class OrganizationMembershipService {
|
|||||||
|
|
||||||
const canInviteUser = await this.authorization.canPerformOperation(
|
const canInviteUser = await this.authorization.canPerformOperation(
|
||||||
userId,
|
userId,
|
||||||
dto.orgId,
|
orgId,
|
||||||
USER_ORGANIZATION_OPERATIONS.INVITE_USERS,
|
USER_ORGANIZATION_OPERATIONS.INVITE_USERS,
|
||||||
);
|
);
|
||||||
if (!canInviteUser) throw new ForbiddenException('Insufficient Permission');
|
if (!canInviteUser) throw new ForbiddenException('Insufficient Permission');
|
||||||
@@ -62,6 +65,7 @@ export class OrganizationMembershipService {
|
|||||||
data: {
|
data: {
|
||||||
...rest,
|
...rest,
|
||||||
userId: invitedUser.id,
|
userId: invitedUser.id,
|
||||||
|
orgId,
|
||||||
requestType: ORGANIZATION_JOIN_REQUEST_TYPE.INVITED,
|
requestType: ORGANIZATION_JOIN_REQUEST_TYPE.INVITED,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -77,62 +81,226 @@ export class OrganizationMembershipService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestToJoin() {}
|
/* *
|
||||||
|
* USER OPERATIONS
|
||||||
// TODO: reject, rejectReason
|
* */
|
||||||
async acceptInvitation(userId: string, orgId: string) {
|
async usersRequestToJoin(
|
||||||
const orgExists = await this.orgService.organizationExists(orgId);
|
userId: string,
|
||||||
if (!orgExists) throw new NotFoundException('Organization');
|
orgId: string,
|
||||||
|
dto: JoinRequestToOrganizationRequestDTO
|
||||||
const [userAlreadyPart, isUserInvited] = await Promise.all([
|
) {
|
||||||
this.prisma.organizationUserJoinTable.findUnique({
|
const [
|
||||||
where: {
|
orgExists,
|
||||||
userId_orgId: {
|
invitationAlreadySent,
|
||||||
orgId,
|
userAlreadyPartOf
|
||||||
userId,
|
] = await Promise.all([
|
||||||
},
|
this.orgService.findById(orgId),
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
this.prisma.organizationJoinRequest.findUnique({
|
this.prisma.organizationJoinRequest.findUnique({
|
||||||
where: {
|
where: {
|
||||||
userId_orgId: {
|
userId_orgId: {
|
||||||
orgId,
|
|
||||||
userId,
|
userId,
|
||||||
|
orgId: orgId
|
||||||
|
},
|
||||||
|
status: ORGANIZATION_JOIN_REQUEST.PENDING
|
||||||
|
},
|
||||||
|
select: { orgId: true }
|
||||||
|
}),
|
||||||
|
this.prisma.organizationUserJoinTable.findUnique({
|
||||||
|
where: {
|
||||||
|
userId_orgId: {
|
||||||
|
orgId: orgId,
|
||||||
|
userId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select: { userId: true }
|
||||||
|
})
|
||||||
|
])
|
||||||
|
|
||||||
|
if (!orgExists)
|
||||||
|
throw new NotFoundException("Organization")
|
||||||
|
if (invitationAlreadySent)
|
||||||
|
throw new BadRequestException("Invitation to join this organization already sent")
|
||||||
|
if (userAlreadyPartOf)
|
||||||
|
throw new BadRequestException("User already part of the organization")
|
||||||
|
|
||||||
|
return await this.prisma.organizationJoinRequest.create({
|
||||||
|
data: {
|
||||||
|
orgId: orgId,
|
||||||
|
userId,
|
||||||
|
requestType: ORGANIZATION_JOIN_REQUEST_TYPE.REQUESTED,
|
||||||
|
requestMessage: dto.requestMessage,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async userCancelOrgJoinRequest(
|
||||||
|
userId: string,
|
||||||
|
orgId: string
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
await this.prisma.organizationJoinRequest.update({
|
||||||
|
where: {
|
||||||
|
userId_orgId: {
|
||||||
|
userId,
|
||||||
|
orgId
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: ORGANIZATION_JOIN_REQUEST.CANCELLED,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
throw new NotFoundException("Join request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List of organizations that:
|
||||||
|
* - user have requested to join
|
||||||
|
* - have send invitations to user
|
||||||
|
*
|
||||||
|
* filtered by requestType
|
||||||
|
* */
|
||||||
|
async userOrganizationJoinRequestList(
|
||||||
|
userId: string,
|
||||||
|
requestType: string
|
||||||
|
) {
|
||||||
|
const joinReqType: ORGANIZATION_JOIN_REQUEST_TYPE | undefined = ORGANIZATION_JOIN_REQUEST_TYPE[requestType];
|
||||||
|
if (!joinReqType)
|
||||||
|
throw new BadRequestException("Invalid request type")
|
||||||
|
|
||||||
|
return await this.prisma.organizationJoinRequest.findMany({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
requestType: joinReqType
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async userOrganiaztionRequestAction(
|
||||||
|
userId: string,
|
||||||
|
orgId: string,
|
||||||
|
dto: UserOrganizationInvitationActionRequestDTO
|
||||||
|
) {
|
||||||
|
const [orgExists, hasUserSendRequest] = await Promise.all([
|
||||||
|
this.orgService.findById(orgId),
|
||||||
|
this.prisma.organizationJoinRequest.findUnique({
|
||||||
|
where: {
|
||||||
|
userId_orgId: {
|
||||||
|
userId,
|
||||||
|
orgId: orgId
|
||||||
},
|
},
|
||||||
status: ORGANIZATION_JOIN_REQUEST.PENDING,
|
status: ORGANIZATION_JOIN_REQUEST.PENDING,
|
||||||
},
|
requestType: ORGANIZATION_JOIN_REQUEST_TYPE.REQUESTED
|
||||||
}),
|
}
|
||||||
]);
|
})
|
||||||
|
])
|
||||||
|
|
||||||
if (userAlreadyPart)
|
if (!orgExists)
|
||||||
throw new BadRequestException('User already part of this organization');
|
throw new NotFoundException("Organization")
|
||||||
if (!isUserInvited)
|
if (!hasUserSendRequest)
|
||||||
throw new BadRequestException(
|
throw new BadRequestException("No pending join request")
|
||||||
'User has no invitations from this organization',
|
|
||||||
);
|
|
||||||
|
|
||||||
|
try {
|
||||||
return await this.prisma.$transaction(async (tx) => {
|
return await this.prisma.$transaction(async (tx) => {
|
||||||
|
const userAction = dto.action === USER_ORG_ACCEPT_REJECT_ACTION.ACCEPT
|
||||||
|
? ORGANIZATION_JOIN_REQUEST.ACCEPTED
|
||||||
|
: ORGANIZATION_JOIN_REQUEST.REJECTED;
|
||||||
|
|
||||||
await tx.organizationJoinRequest.update({
|
await tx.organizationJoinRequest.update({
|
||||||
where: {
|
where: {
|
||||||
userId_orgId: {
|
userId_orgId: {
|
||||||
userId,
|
userId,
|
||||||
orgId,
|
orgId: orgId,
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
status: ORGANIZATION_JOIN_REQUEST.ACCEPTED,
|
status: userAction,
|
||||||
},
|
...(
|
||||||
|
userAction === ORGANIZATION_JOIN_REQUEST.REJECTED && dto.message
|
||||||
|
? { rejectReason: dto.message }
|
||||||
|
: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return await tx.organizationUserJoinTable.create({
|
if (userAction === ORGANIZATION_JOIN_REQUEST.ACCEPTED)
|
||||||
|
await tx.organizationUserJoinTable.create({
|
||||||
data: {
|
data: {
|
||||||
orgId,
|
userId: userId,
|
||||||
userId,
|
orgId: orgId,
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (err instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
|
if (err.code === 'P2002')
|
||||||
|
throw new BadRequestException('User already part of this organization.');
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: reject, rejectReason
|
||||||
|
//
|
||||||
|
// async acceptInvitation(userId: string, orgId: string) {
|
||||||
|
// const orgExists = await this.orgService.organizationExists(orgId);
|
||||||
|
// if (!orgExists) throw new NotFoundException('Organization');
|
||||||
|
//
|
||||||
|
// const [userAlreadyPart, isUserInvited] = await Promise.all([
|
||||||
|
// this.prisma.organizationUserJoinTable.findUnique({
|
||||||
|
// where: {
|
||||||
|
// userId_orgId: {
|
||||||
|
// orgId,
|
||||||
|
// userId,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// }),
|
||||||
|
//
|
||||||
|
// this.prisma.organizationJoinRequest.findUnique({
|
||||||
|
// where: {
|
||||||
|
// userId_orgId: {
|
||||||
|
// orgId,
|
||||||
|
// userId,
|
||||||
|
// },
|
||||||
|
// status: ORGANIZATION_JOIN_REQUEST.PENDING,
|
||||||
|
// },
|
||||||
|
// }),
|
||||||
|
// ]);
|
||||||
|
//
|
||||||
|
// if (userAlreadyPart)
|
||||||
|
// throw new BadRequestException('User already part of this organization');
|
||||||
|
// if (!isUserInvited)
|
||||||
|
// throw new BadRequestException(
|
||||||
|
// 'User has no invitations from this organization',
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// return await this.prisma.$transaction(async (tx) => {
|
||||||
|
// await tx.organizationJoinRequest.update({
|
||||||
|
// where: {
|
||||||
|
// userId_orgId: {
|
||||||
|
// userId,
|
||||||
|
// orgId,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// data: {
|
||||||
|
// status: ORGANIZATION_JOIN_REQUEST.ACCEPTED,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// return await tx.organizationUserJoinTable.create({
|
||||||
|
// data: {
|
||||||
|
// orgId,
|
||||||
|
// userId,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
async getUserInvitations(
|
async getUserInvitations(
|
||||||
userId: string,
|
userId: string,
|
||||||
@@ -153,8 +321,58 @@ export class OrganizationMembershipService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getOrganizationsOfUser(userId: string) {
|
||||||
|
return await this.prisma.organizationJoinRequest.findMany({
|
||||||
|
where: {
|
||||||
|
userId
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
organization: {
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
description: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async userLeaveAnOrganization(userId: string, orgId: string) {
|
||||||
|
try {
|
||||||
|
await this.prisma.organizationUserJoinTable.delete({
|
||||||
|
where: {
|
||||||
|
userId_orgId: { userId, orgId }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
throw new NotFoundException("Membership not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOrganizationRequestList(
|
||||||
|
orgId: string,
|
||||||
|
requestType: ORGANIZATION_JOIN_REQUEST_TYPE = ORGANIZATION_JOIN_REQUEST_TYPE.REQUESTED,
|
||||||
|
status: ORGANIZATION_JOIN_REQUEST = ORGANIZATION_JOIN_REQUEST.PENDING,
|
||||||
|
) {
|
||||||
|
// TODO: Check can perform
|
||||||
|
return await this.prisma.organizationJoinRequest.findMany({
|
||||||
|
where: {
|
||||||
|
orgId: orgId,
|
||||||
|
status: status,
|
||||||
|
requestType: requestType,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
user: {
|
||||||
|
select: { email: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async getMemebersOfOrganization(orgId: string) {
|
async getMemebersOfOrganization(orgId: string) {
|
||||||
const orgExists = await this.orgService.organizationExists(orgId);
|
const orgExists = await this.orgService.findById(orgId);
|
||||||
if (!orgExists) throw new NotFoundException('Organization');
|
if (!orgExists) throw new NotFoundException('Organization');
|
||||||
|
|
||||||
return await this.prisma.organizationUserJoinTable.findMany({
|
return await this.prisma.organizationUserJoinTable.findMany({
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class OrganizationService {
|
|||||||
// private readonly reqContext: RequestContextService,
|
// private readonly reqContext: RequestContextService,
|
||||||
private readonly authorization: AuthorizationService,
|
private readonly authorization: AuthorizationService,
|
||||||
private readonly cacheService: CacheService,
|
private readonly cacheService: CacheService,
|
||||||
) {}
|
) { }
|
||||||
async createNewOrganization(
|
async createNewOrganization(
|
||||||
userId: string,
|
userId: string,
|
||||||
dto: CreateNewOrganizationRequestDTO,
|
dto: CreateNewOrganizationRequestDTO,
|
||||||
@@ -60,7 +60,7 @@ export class OrganizationService {
|
|||||||
orgId: string,
|
orgId: string,
|
||||||
dto: UpdateOrganizationRequestDTO,
|
dto: UpdateOrganizationRequestDTO,
|
||||||
) {
|
) {
|
||||||
const orgExists = await this.organizationExists(orgId);
|
const orgExists = await this.findById(orgId);
|
||||||
if (!orgExists) throw new NotFoundException('Organization');
|
if (!orgExists) throw new NotFoundException('Organization');
|
||||||
|
|
||||||
const canUserUpdateOrganization =
|
const canUserUpdateOrganization =
|
||||||
@@ -83,7 +83,7 @@ export class OrganizationService {
|
|||||||
|
|
||||||
// TODO: Either empty or choose someone to be owner
|
// TODO: Either empty or choose someone to be owner
|
||||||
async deleteAnOrganization(userId: string, orgId: string) {
|
async deleteAnOrganization(userId: string, orgId: string) {
|
||||||
const orgExists = await this.organizationExists(orgId);
|
const orgExists = await this.findById(orgId);
|
||||||
if (!orgExists) throw new NotFoundException('Organization');
|
if (!orgExists) throw new NotFoundException('Organization');
|
||||||
|
|
||||||
const canUserDeleteOrganization =
|
const canUserDeleteOrganization =
|
||||||
@@ -118,13 +118,16 @@ export class OrganizationService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async organizationExists(orgId: string) {
|
// async organizationExists(orgId: string) {
|
||||||
return await this.prisma.organization.findUnique({
|
// return await this.prisma.organization.findUnique({
|
||||||
where: { id: orgId },
|
// where: { id: orgId },
|
||||||
select: { id: true },
|
// select: { id: true },
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Its neat, it caches info plus only selects id so faster DB search as well
|
||||||
|
* */
|
||||||
async findById(orgId: string) {
|
async findById(orgId: string) {
|
||||||
const organization = await this.cacheService.getOrSet(
|
const organization = await this.cacheService.getOrSet(
|
||||||
orgId,
|
orgId,
|
||||||
|
|||||||
Reference in New Issue
Block a user