mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-21 16:08:35 +00:00
* feat: initial commit for collaboration feature * add dnd to comments * add positions endpoint * feat: encapsulate all http common logic in http-client * segregate sections and transfer responsibility of state * feat: use-spring to add fade effect ⚡ * fix: open in right * fix: left-right position css * add footer for message * integrate getcomment endpoint * use fromnow for date ago * add dnd * - Add data trasfer object for comment - Add class-validator package to check the response type from client - Add comment repository class for persistance layer - Add comment service with std. http methods - Update controller with all http methods - Update comment module - Fix http-client bug when error is thrown * fix http client bug when error is thrown * feat: add entity thread * feat: add migrations for thread and comment * update entitites * add tid to migration * filter comments by tid(thread_id) * fix: comment migration, add missing column comment * feat: integrate in ui * feat: split comments based on app_id * fix: dnd to correct position * package json engines * engines update * update npm * npm 6 to 7 * fix: add user initials to thread * fix: add firtname lastname to the comments * - Return user object when save thread called - Hide password field from user response - Fix created_at date typo - Instead of fetch all threads on new thread added, add the response to array of existing threads * feat: update ui components * change icon on comments view * ui fixes * fix: close icon close the popover * temp: comment select: false * use currentUser from localStorage * fix: on click outside if comment is open, dont hit addThread * fix: auth token issue in http-client * on drag hide the comment if open * add jwt auth * spec: add test for comment & thread * cleanup: remove console.log * feat: add comment actions * feat: add edit, delete, resolve options * feat: add mentions component * feat: add nestjs websockets * temp * websocket: establish client-server communication * ws: add message listner to comments module in ui * feat: add broadcast method to broadcast new events to all clients 💣 * ws: cleanup 🤙 * fix: remove max height from comment actions * feat: add user mentions, emoji support * fix: add static list of users - temp * update and delete iterations * - Rename comment, thread to comments, threads - Add conditional actions - Show edit, delete only if he is comment owner - Show resolve only if he is thread owner * reset engines * move svgr webpack to deps * fix: ui issues * remove log stmt * refactor: move resolved icon to comment-header * feat: allow comments to be added on top of widgets * feat: add keyboard shortcut * scroll to bottom on comment add * ui fixes * feat: add react toast for notification display * feat: add comment badge * fix: ws connection * fix: ws * remove rvrse * feat: add comment sidebar * feat: add comment right sidebar * fix: add missing foreign key elements * - upgrade typeorm to 0.2.38 - comment sidebar ui - added filter ui * feat: on click of right sidebar notificaiton open the comment box * reset engines * fix: add organization id to the comment and thread module * fix: add current version id * add currentversion id * disable comments if no id present * temp:checking for heroku deploy * fetch app on edit and deploy version * rename current_version_id to app_versions_id * ui fixes * show mentioned user in blue color * add ui changes * add authorization for create thread * change color to blue on click of comment, add auth for other endpoints of thread * update threads, notifications using socket * add auth for comments * remove events spec file * fix duplicate key error * fix notificaitons updation on edit, delete, resolve buttons clicked * update notifications for edit * feature toggle changes for frontend * add check for comments server * add emoji mart package for emoji * add reply count in comment sidebar * subtract 1 from count in comment sidebar * change empty text when no comments available
344 lines
11 KiB
TypeScript
344 lines
11 KiB
TypeScript
/* eslint-disable prefer-const */
|
|
import { JwtService } from '@nestjs/jwt';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { getConnection, Repository } from 'typeorm';
|
|
import { OrganizationUser } from 'src/entities/organization_user.entity';
|
|
import { Organization } from 'src/entities/organization.entity';
|
|
import { User } from 'src/entities/user.entity';
|
|
import { App } from 'src/entities/app.entity';
|
|
import { INestApplication } from '@nestjs/common';
|
|
import { Test } from '@nestjs/testing';
|
|
import { AppModule } from 'src/app.module';
|
|
import { AppVersion } from 'src/entities/app_version.entity';
|
|
import { DataQuery } from 'src/entities/data_query.entity';
|
|
import { DataSource } from 'src/entities/data_source.entity';
|
|
import { DataSourcesService } from 'src/services/data_sources.service';
|
|
import { DataSourcesModule } from 'src/modules/data_sources/data_sources.module';
|
|
import { ThreadRepository } from '@repositories/thread.repository';
|
|
import { GroupPermission } from 'src/entities/group_permission.entity';
|
|
import { UserGroupPermission } from 'src/entities/user_group_permission.entity';
|
|
import { AppGroupPermission } from 'src/entities/app_group_permission.entity';
|
|
|
|
export async function createNestAppInstance() {
|
|
let app: INestApplication;
|
|
|
|
const moduleRef = await Test.createTestingModule({
|
|
imports: [AppModule],
|
|
providers: [],
|
|
}).compile();
|
|
|
|
app = moduleRef.createNestApplication();
|
|
app.setGlobalPrefix('api');
|
|
await app.init();
|
|
|
|
return app;
|
|
}
|
|
|
|
export function authHeaderForUser(user: any): string {
|
|
const configService = new ConfigService();
|
|
const jwtService = new JwtService({
|
|
secret: configService.get<string>('SECRET_KEY_BASE'),
|
|
});
|
|
const authPayload = { username: user.id, sub: user.email };
|
|
const authToken = jwtService.sign(authPayload);
|
|
return `Bearer ${authToken}`;
|
|
}
|
|
|
|
export async function clearDB() {
|
|
const entities = getConnection().entityMetadatas;
|
|
for (const entity of entities) {
|
|
const repository = await getConnection().getRepository(entity.name);
|
|
await repository.query(`TRUNCATE ${entity.tableName} RESTART IDENTITY CASCADE;`);
|
|
}
|
|
}
|
|
|
|
export async function createApplication(nestApp, { name, user, isPublic, slug }: any) {
|
|
let appRepository: Repository<App>;
|
|
appRepository = nestApp.get('AppRepository');
|
|
|
|
user = user || (await (await createUser(nestApp, {})).user);
|
|
|
|
const newApp = await appRepository.save(
|
|
appRepository.create({
|
|
name,
|
|
user,
|
|
slug,
|
|
isPublic: isPublic || false,
|
|
organizationId: user.organization.id,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
})
|
|
);
|
|
|
|
await maybeCreateAdminAppGroupPermissions(nestApp, newApp);
|
|
await maybeCreateAllUsersAppGroupPermissions(nestApp, newApp);
|
|
|
|
return newApp;
|
|
}
|
|
|
|
export async function createApplicationVersion(nestApp, application) {
|
|
let appVersionsRepository: Repository<AppVersion>;
|
|
appVersionsRepository = nestApp.get('AppVersionRepository');
|
|
|
|
return await appVersionsRepository.save(
|
|
appVersionsRepository.create({
|
|
app: application,
|
|
name: 'v0',
|
|
})
|
|
);
|
|
}
|
|
|
|
export async function createUser(nestApp, { firstName, lastName, email, groups, organization, status }: any) {
|
|
let userRepository: Repository<User>;
|
|
let organizationRepository: Repository<Organization>;
|
|
let organizationUsersRepository: Repository<OrganizationUser>;
|
|
|
|
userRepository = nestApp.get('UserRepository');
|
|
organizationRepository = nestApp.get('OrganizationRepository');
|
|
organizationUsersRepository = nestApp.get('OrganizationUserRepository');
|
|
|
|
organization =
|
|
organization ||
|
|
(await organizationRepository.save(
|
|
organizationRepository.create({
|
|
name: 'test org',
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
})
|
|
));
|
|
|
|
const user = await userRepository.save(
|
|
userRepository.create({
|
|
firstName: firstName || 'test',
|
|
lastName: lastName || 'test',
|
|
email: email || 'dev@tooljet.io',
|
|
password: 'password',
|
|
organization,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
})
|
|
);
|
|
|
|
const orgUser = await organizationUsersRepository.save(
|
|
organizationUsersRepository.create({
|
|
user: user,
|
|
organization,
|
|
status: status || 'invited',
|
|
role: 'all_users',
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
})
|
|
);
|
|
|
|
await maybeCreateDefaultGroupPermissions(nestApp, user.organizationId);
|
|
await createUserGroupPermissions(
|
|
nestApp,
|
|
user,
|
|
groups || ['all_users', 'admin'] // default groups
|
|
);
|
|
|
|
return { organization, user, orgUser };
|
|
}
|
|
|
|
export async function createUserGroupPermissions(nestApp, user, groups) {
|
|
const groupPermissionRepository: Repository<GroupPermission> = nestApp.get('GroupPermissionRepository');
|
|
|
|
const userGroupPermissionRepository: Repository<UserGroupPermission> = nestApp.get('UserGroupPermissionRepository');
|
|
|
|
let userGroupPermissions = [];
|
|
|
|
for (const group of groups) {
|
|
let groupPermission: GroupPermission;
|
|
|
|
if (group == 'admin' || group == 'all_users') {
|
|
groupPermission = await groupPermissionRepository.findOneOrFail({
|
|
where: {
|
|
organizationId: user.organizationId,
|
|
group: group,
|
|
},
|
|
});
|
|
} else {
|
|
groupPermission = groupPermissionRepository.create({
|
|
organizationId: user.organizationId,
|
|
group: group,
|
|
});
|
|
await groupPermissionRepository.save(groupPermission);
|
|
}
|
|
|
|
const userGroupPermission = userGroupPermissionRepository.create({
|
|
groupPermissionId: groupPermission.id,
|
|
userId: user.id,
|
|
});
|
|
await userGroupPermissionRepository.save(userGroupPermission);
|
|
userGroupPermissions.push(userGroupPermission);
|
|
}
|
|
|
|
return userGroupPermissions;
|
|
}
|
|
|
|
export async function createAppGroupPermission(nestApp, app, groupId, permissions) {
|
|
const appGroupPermissionRepository: Repository<AppGroupPermission> = nestApp.get('AppGroupPermissionRepository');
|
|
|
|
const appGroupPermission = appGroupPermissionRepository.create({
|
|
groupPermissionId: groupId,
|
|
appId: app.id,
|
|
...permissions,
|
|
});
|
|
await appGroupPermissionRepository.save(appGroupPermission);
|
|
|
|
return appGroupPermission;
|
|
}
|
|
|
|
export async function createGroupPermission(nestApp, params) {
|
|
const groupPermissionRepository: Repository<GroupPermission> = nestApp.get('GroupPermissionRepository');
|
|
let groupPermission = groupPermissionRepository.create({
|
|
...params,
|
|
});
|
|
await groupPermissionRepository.save(groupPermission);
|
|
|
|
return groupPermission;
|
|
}
|
|
|
|
export async function maybeCreateDefaultGroupPermissions(nestApp, organizationId) {
|
|
const groupPermissionRepository: Repository<GroupPermission> = nestApp.get('GroupPermissionRepository');
|
|
|
|
const defaultGroups = ['all_users', 'admin'];
|
|
|
|
for (let group of defaultGroups) {
|
|
const orgDefaultGroupPermissions = await groupPermissionRepository.find({
|
|
where: {
|
|
organizationId: organizationId,
|
|
group: group,
|
|
},
|
|
});
|
|
|
|
if (orgDefaultGroupPermissions.length == 0) {
|
|
const groupPermission = groupPermissionRepository.create({
|
|
organizationId: organizationId,
|
|
group: group,
|
|
appCreate: group == 'admin',
|
|
appDelete: group == 'admin',
|
|
});
|
|
await groupPermissionRepository.save(groupPermission);
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function maybeCreateAdminAppGroupPermissions(nestApp, app) {
|
|
const groupPermissionRepository: Repository<GroupPermission> = nestApp.get('GroupPermissionRepository');
|
|
const appGroupPermissionRepository: Repository<AppGroupPermission> = nestApp.get('AppGroupPermissionRepository');
|
|
|
|
const orgAdminGroupPermissions = await groupPermissionRepository.findOne({
|
|
organizationId: app.organizationId,
|
|
group: 'admin',
|
|
});
|
|
|
|
if (orgAdminGroupPermissions) {
|
|
const adminGroupPermissions = {
|
|
read: true,
|
|
update: true,
|
|
delete: true,
|
|
};
|
|
|
|
const appGroupPermission = appGroupPermissionRepository.create({
|
|
groupPermissionId: orgAdminGroupPermissions.id,
|
|
appId: app.id,
|
|
...adminGroupPermissions,
|
|
});
|
|
await appGroupPermissionRepository.save(appGroupPermission);
|
|
}
|
|
}
|
|
|
|
export async function maybeCreateAllUsersAppGroupPermissions(nestApp, app) {
|
|
const groupPermissionRepository: Repository<GroupPermission> = nestApp.get('GroupPermissionRepository');
|
|
const appGroupPermissionRepository: Repository<AppGroupPermission> = nestApp.get('AppGroupPermissionRepository');
|
|
|
|
const orgGroupPermissions = await groupPermissionRepository.findOne({
|
|
organizationId: app.organizationId,
|
|
group: 'all_users',
|
|
});
|
|
|
|
if (orgGroupPermissions) {
|
|
const permissions = {
|
|
read: true,
|
|
update: false,
|
|
delete: false,
|
|
};
|
|
|
|
const appGroupPermission = appGroupPermissionRepository.create({
|
|
groupPermissionId: orgGroupPermissions.id,
|
|
appId: app.id,
|
|
...permissions,
|
|
});
|
|
await appGroupPermissionRepository.save(appGroupPermission);
|
|
}
|
|
}
|
|
|
|
export async function addAllUsersGroupToUser(nestApp, user) {
|
|
const groupPermissionRepository: Repository<GroupPermission> = nestApp.get('GroupPermissionRepository');
|
|
const userGroupPermissionRepository: Repository<UserGroupPermission> = nestApp.get('UserGroupPermissionRepository');
|
|
|
|
const orgDefaultGroupPermissions = await groupPermissionRepository.findOne({
|
|
where: {
|
|
organizationId: user.organizationId,
|
|
group: 'all_users',
|
|
},
|
|
});
|
|
|
|
const userGroupPermission = userGroupPermissionRepository.create({
|
|
groupPermissionId: orgDefaultGroupPermissions.id,
|
|
userId: user.id,
|
|
});
|
|
await userGroupPermissionRepository.save(userGroupPermission);
|
|
|
|
return user;
|
|
}
|
|
|
|
export async function createDataSource(nestApp, { name, application, kind, options }: any) {
|
|
let dataSourceRepository: Repository<DataSource>;
|
|
dataSourceRepository = nestApp.get('DataSourceRepository');
|
|
|
|
const dataSourcesService = nestApp.select(DataSourcesModule).get(DataSourcesService);
|
|
|
|
return await dataSourceRepository.save(
|
|
dataSourceRepository.create({
|
|
name,
|
|
options: await dataSourcesService.parseOptionsForCreate(options),
|
|
app: application,
|
|
kind,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
})
|
|
);
|
|
}
|
|
|
|
export async function createDataQuery(nestApp, { application, kind, dataSource, options }: any) {
|
|
let dataQueryRepository: Repository<DataQuery>;
|
|
dataQueryRepository = nestApp.get('DataQueryRepository');
|
|
|
|
return await dataQueryRepository.save(
|
|
dataQueryRepository.create({
|
|
options,
|
|
app: application,
|
|
kind,
|
|
dataSource,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
})
|
|
);
|
|
}
|
|
|
|
export async function createThread(nestInstance, { appId, x, y, user_id }: any) {
|
|
let threadRepository: ThreadRepository;
|
|
threadRepository = nestInstance.get('ThreadRepository');
|
|
|
|
return await threadRepository.createThread(
|
|
{
|
|
appId,
|
|
x,
|
|
y,
|
|
isResolved: false,
|
|
},
|
|
user_id
|
|
);
|
|
}
|