Browse Source

Merge branch 'develop' into feat/pnpm

pull/5903/head
Wing-Kam Wong 1 year ago
parent
commit
c205833a59
  1. 13
      README.md
  2. 4
      packages/nc-gui/components/project/InviteProjectCollabSection.vue
  3. 2
      packages/nc-lib-gui/package.json
  4. 2
      packages/nocodb-sdk/package.json
  5. 4
      packages/nocodb/package.json
  6. 2
      packages/nocodb/src/run/testDocker.ts
  7. 4
      packages/nocodb/src/services/api-docs/swagger/templates/params.ts
  8. 21
      packages/nocodb/src/services/project-users/project-users.service.ts
  9. 2
      packages/nocodb/src/version-upgrader/NcUpgrader.ts
  10. 23
      tests/playwright/pages/Account/Users.ts
  11. 72
      tests/playwright/tests/db/usersAccounts/accountUserManagement.spec.ts
  12. 12
      tests/playwright/tests/db/usersAccounts/rolesCreate.spec.ts

13
README.md

@ -192,14 +192,15 @@ npm start
Access Dashboard using: [http://localhost:8080/dashboard](http://localhost:8080/dashboard) Access Dashboard using: [http://localhost:8080/dashboard](http://localhost:8080/dashboard)
# Screenshots # Screenshots
![2](https://github.com/nocodb/nocodb/assets/86527202/a127c05e-2121-4af2-a342-128e0e2d0291)
![3](https://github.com/nocodb/nocodb/assets/86527202/674da952-8a06-4848-a0e8-a7b02d5f5c88)
![4](https://github.com/nocodb/nocodb/assets/86527202/cbc5152a-9caf-4f77-a8f7-92a9d06d025b)
![5](https://github.com/nocodb/nocodb/assets/86527202/dc75dfdc-c486-4f5a-a853-2a8f9e6b569a)
![1](https://user-images.githubusercontent.com/35857179/194844858-d353bd15-1edf-406c-889b-ba60f76831f4.png)
![2](https://user-images.githubusercontent.com/35857179/194844872-1a1094b9-761b-4ab6-a0ab-8e11dcae6571.png)
![3](https://user-images.githubusercontent.com/35857179/194844881-23f12c4c-7a5f-403e-928c-ef4c53b2665d.png)
![4](https://user-images.githubusercontent.com/35857179/194844885-faaf042f-bad2-4924-84f0-2c08813271d8.png)
![5](https://user-images.githubusercontent.com/35857179/194844886-a17006e0-979d-493f-83c4-0e72f5a9b716.png) ![5](https://user-images.githubusercontent.com/35857179/194844886-a17006e0-979d-493f-83c4-0e72f5a9b716.png)
![6](https://user-images.githubusercontent.com/35857179/194844890-b9f265ae-6e40-4fa5-9267-d1367c27c8e6.png) ![7](https://github.com/nocodb/nocodb/assets/86527202/be64e619-7295-43e2-aa95-cace4462b17f)
![7](https://user-images.githubusercontent.com/35857179/194844891-bee9aea3-aff3-4247-a918-b2f3fbbc672e.png) ![8](https://github.com/nocodb/nocodb/assets/86527202/4538bf5a-371f-4ec1-a867-8197e5824286)
![8](https://user-images.githubusercontent.com/35857179/194844893-82d5e21b-ae61-41bd-9990-31ad659bf490.png) ![8](https://user-images.githubusercontent.com/35857179/194844893-82d5e21b-ae61-41bd-9990-31ad659bf490.png)
![9](https://user-images.githubusercontent.com/35857179/194844897-cfd79946-e413-4c97-b16d-eb4d7678bb79.png) ![9](https://user-images.githubusercontent.com/35857179/194844897-cfd79946-e413-4c97-b16d-eb4d7678bb79.png)
![10](https://user-images.githubusercontent.com/35857179/194844902-c0122570-0dd5-41cf-a26f-6f8d71fefc99.png) ![10](https://user-images.githubusercontent.com/35857179/194844902-c0122570-0dd5-41cf-a26f-6f8d71fefc99.png)

4
packages/nc-gui/components/project/InviteProjectCollabSection.vue

@ -26,10 +26,10 @@ const usersData = ref<{
const isInvitingCollaborators = ref(false) const isInvitingCollaborators = ref(false)
const inviteCollaborator = async () => { const inviteCollaborator = async () => {
isInvitingCollaborators.value = true
if (isInvitingCollaborators.value) return if (isInvitingCollaborators.value) return
isInvitingCollaborators.value = true
try { try {
usersData.value = await inviteUser(inviteData) usersData.value = await inviteUser(inviteData)
usersData.roles = inviteData.roles usersData.roles = inviteData.roles

2
packages/nc-lib-gui/package.json

@ -1,6 +1,6 @@
{ {
"name": "nc-lib-gui", "name": "nc-lib-gui",
"version": "0.111.3", "version": "0.111.4",
"description": "NocoDB GUI", "description": "NocoDB GUI",
"author": { "author": {
"name": "NocoDB", "name": "NocoDB",

2
packages/nocodb-sdk/package.json

@ -1,6 +1,6 @@
{ {
"name": "nocodb-sdk", "name": "nocodb-sdk",
"version": "0.111.3", "version": "0.111.4",
"description": "NocoDB SDK", "description": "NocoDB SDK",
"main": "build/main/index.js", "main": "build/main/index.js",
"typings": "build/main/index.d.ts", "typings": "build/main/index.d.ts",

4
packages/nocodb/package.json

@ -1,6 +1,6 @@
{ {
"name": "nocodb", "name": "nocodb",
"version": "0.111.3", "version": "0.111.4",
"description": "NocoDB Backend", "description": "NocoDB Backend",
"main": "dist/bundle.js", "main": "dist/bundle.js",
"author": { "author": {
@ -129,7 +129,7 @@
"mysql2": "^3.2.0", "mysql2": "^3.2.0",
"nanoid": "^3.1.20", "nanoid": "^3.1.20",
"nc-help": "0.2.88", "nc-help": "0.2.88",
"nc-lib-gui": "0.111.3", "nc-lib-gui": "0.111.4",
"nc-plugin": "^0.1.3", "nc-plugin": "^0.1.3",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"nestjs-kafka": "^1.0.6", "nestjs-kafka": "^1.0.6",

2
packages/nocodb/src/run/testDocker.ts

@ -40,7 +40,7 @@ process.env[`DEBUG`] = 'xc*';
console.log(admin_response.data); console.log(admin_response.data);
} }
for (let i = 0; i < 8; i++) { for (let i = 0; i < 4; i++) {
if (!(await User.getByEmail(`user-${i}@nocodb.com`))) { if (!(await User.getByEmail(`user-${i}@nocodb.com`))) {
const response = await axios.post( const response = await axios.post(
`http://localhost:${ `http://localhost:${

4
packages/nocodb/src/services/api-docs/swagger/templates/params.ts

@ -1,4 +1,4 @@
import { RelationTypes, UITypes } from 'nocodb-sdk'; import { isLinksOrLTAR, RelationTypes, UITypes } from 'nocodb-sdk'
import type { LinkToAnotherRecordColumn } from '~/models'; import type { LinkToAnotherRecordColumn } from '~/models';
import type { SwaggerColumn } from '../getSwaggerColumnMetas'; import type { SwaggerColumn } from '../getSwaggerColumnMetas';
@ -100,7 +100,7 @@ export const columnNameQueryParam = {
export const columnNameParam = (columns: SwaggerColumn[]) => { export const columnNameParam = (columns: SwaggerColumn[]) => {
const columnNames = []; const columnNames = [];
for (const { column } of columns) { for (const { column } of columns) {
if (column.uidt !== UITypes.LinkToAnotherRecord || column.system) continue; if (!isLinksOrLTAR(column) || column.system) continue;
columnNames.push(column.title); columnNames.push(column.title);
} }

21
packages/nocodb/src/services/project-users/project-users.service.ts

@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { import {
AppEvents, AppEvents,
extractRolesObj,
OrgUserRoles, OrgUserRoles,
PluginCategory, PluginCategory,
ProjectRoles, ProjectRoles,
@ -53,6 +54,26 @@ export class ProjectUsersService {
param.projectUser, param.projectUser,
); );
if (
getProjectRolePower({
project_roles: extractRolesObj(param.projectUser.roles),
}) > getProjectRolePower(param.req.user)
) {
NcError.badRequest(`Insufficient privilege to invite with this role`);
}
if (
![
ProjectRoles.CREATOR,
ProjectRoles.EDITOR,
ProjectRoles.COMMENTER,
ProjectRoles.VIEWER,
ProjectRoles.NO_ACCESS,
].includes(param.projectUser.roles as ProjectRoles)
) {
NcError.badRequest('Invalid role');
}
const emails = (param.projectUser.email || '') const emails = (param.projectUser.email || '')
.toLowerCase() .toLowerCase()
.split(/\s*,\s*/) .split(/\s*,\s*/)

2
packages/nocodb/src/version-upgrader/NcUpgrader.ts

@ -68,7 +68,7 @@ export default class NcUpgrader {
'', '',
'nc_store', 'nc_store',
{ {
value: JSON.stringify(config), value: JSON.stringify({ version: config.version }),
}, },
{ {
key: NcUpgrader.STORE_KEY, key: NcUpgrader.STORE_KEY,

23
tests/playwright/pages/Account/Users.ts

@ -33,9 +33,7 @@ export class AccountUsersPage extends BasePage {
return this.accountPage.get().locator(`[data-testid="nc-super-user-list"]`); return this.accountPage.get().locator(`[data-testid="nc-super-user-list"]`);
} }
async invite({ email: _email, role }: { email: string; role: string }) { async invite({ email, role }: { email: string; role: string }) {
const email = this.prefixEmail(_email);
await this.inviteUserBtn.click(); await this.inviteUserBtn.click();
await this.inviteUserModal.locator(`input[placeholder="E-mail"]`).fill(email); await this.inviteUserModal.locator(`input[placeholder="E-mail"]`).fill(email);
await this.inviteUserModal.locator(`.nc-user-roles`).click(); await this.inviteUserModal.locator(`.nc-user-roles`).click();
@ -58,21 +56,18 @@ export class AccountUsersPage extends BasePage {
await this.inviteUserModal.locator(`button.ant-btn-icon-only:visible`).first().click(); await this.inviteUserModal.locator(`button.ant-btn-icon-only:visible`).first().click();
} }
getUserRow({ email: _email }: { email: string }) { async getUserRow({ email }: { email: string }) {
const email = this.prefixEmail(_email); // ensure page is loaded
await this.get().waitFor();
return this.get().locator(`tr:has-text("${email}")`); return this.get().locator(`tr:has-text("${email}")`);
} }
async updateRole({ email, role }: { email: string; role: string }) { async updateRole({ email, role }: { email: string; role: string }) {
const userRow = this.getUserRow({ email }); const userRow = await this.getUserRow({ email });
await userRow.locator(`.nc-user-roles`).click(); await userRow.locator(`.nc-user-roles`).click();
await this.rootPage.locator(`.nc-users-list-role-option:visible:has-text("${role}")`).waitFor();
// todo: replace delay with waitForSelector await this.rootPage.locator(`.nc-users-list-role-option:visible:has-text("${role}")`).last().click();
await new Promise(resolve => setTimeout(resolve, 400)); await this.rootPage.locator(`.nc-users-list-role-option`).last().waitFor({ state: 'hidden' });
await this.rootPage.locator(`.nc-users-list-role-option:visible:has-text("${role}")`).click();
await this.verifyToast({ message: 'Successfully updated the user details' });
} }
async inviteMore() { async inviteMore() {
@ -80,7 +75,7 @@ export class AccountUsersPage extends BasePage {
} }
async openRowActionMenu({ email }: { email: string }) { async openRowActionMenu({ email }: { email: string }) {
const userRow = this.getUserRow({ email }); const userRow = await this.getUserRow({ email });
return userRow.locator(`.nc-user-row-action`).click(); return userRow.locator(`.nc-user-row-action`).click();
} }

72
tests/playwright/tests/db/usersAccounts/accountUserManagement.spec.ts

@ -1,32 +1,55 @@
import { test } from '@playwright/test'; import { expect, test } from '@playwright/test';
import { AccountPage } from '../../../pages/Account'; import { AccountPage } from '../../../pages/Account';
import { AccountUsersPage } from '../../../pages/Account/Users'; import { AccountUsersPage } from '../../../pages/Account/Users';
import { SignupPage } from '../../../pages/SignupPage'; import { SignupPage } from '../../../pages/SignupPage';
import setup, { unsetup } from '../../../setup'; import setup, { unsetup } from '../../../setup';
import { WorkspacePage } from '../../../pages/WorkspacePage';
import { getDefaultPwd } from '../../../tests/utils/general'; import { getDefaultPwd } from '../../../tests/utils/general';
import { Api } from 'nocodb-sdk';
import { DashboardPage } from '../../../pages/Dashboard';
import { LoginPage } from '../../../pages/LoginPage'; import { LoginPage } from '../../../pages/LoginPage';
let api: Api<any>;
const roleDb = [ const roleDb = [
{ email: 'creator@nocodb.com', role: 'Organization Level Creator', url: '' }, { email: 'org_creator@nocodb.com', role: 'Organization Level Creator', url: '' },
{ email: 'viewer@nocodb.com', role: 'Organization Level Viewer', url: '' }, { email: 'org_viewer@nocodb.com', role: 'Organization Level Viewer', url: '' },
]; ];
test.describe.skip('User roles', () => { test.describe('User roles', () => {
let accountUsersPage: AccountUsersPage; let accountUsersPage: AccountUsersPage;
let accountPage: AccountPage; let accountPage: AccountPage;
let signupPage: SignupPage; let signupPage: SignupPage;
let workspacePage: WorkspacePage; let loginPage: LoginPage;
let dashboard: DashboardPage;
// @ts-ignore // @ts-ignore
let context: any; let context: any;
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
context = await setup({ page, isEmptyProject: true }); context = await setup({ page, isEmptyProject: true, isSuperUser: true });
dashboard = new DashboardPage(page, context.project);
accountPage = new AccountPage(page); accountPage = new AccountPage(page);
accountUsersPage = new AccountUsersPage(accountPage); accountUsersPage = new AccountUsersPage(accountPage);
signupPage = new SignupPage(accountPage.rootPage); signupPage = new SignupPage(accountPage.rootPage);
workspacePage = new WorkspacePage(accountPage.rootPage); loginPage = new LoginPage(accountPage.rootPage);
try {
api = new Api({
baseURL: `http://localhost:8080/`,
headers: {
'xc-auth': context.token,
},
});
} catch (e) {
console.log(e);
}
// check if user already exists; if so- remove them
for (let i = 0; i < roleDb.length; i++) {
const user = await api.orgUsers.list();
if (user.list.length > 0) {
const u = user.list.find((u: any) => u.email === roleDb[i].email);
if (u) await api.orgUsers.delete(u.id);
}
}
}); });
test.afterEach(async () => { test.afterEach(async () => {
@ -45,18 +68,21 @@ test.describe.skip('User roles', () => {
role: roleDb[i].role, role: roleDb[i].role,
}); });
await accountUsersPage.closeInvite(); await accountUsersPage.closeInvite();
await signupAndVerify(i);
await accountPage.signOut();
const loginPage = new LoginPage(accountPage.rootPage);
await loginPage.fillEmail({ email: 'user@nocodb.com', withoutPrefix: true });
await loginPage.fillPassword(getDefaultPwd());
await loginPage.submit();
} }
// update role await signupAndVerify(0);
await accountUsersPage.goto(); await accountUsersPage.goto();
await signupAndVerify(1);
await dashboard.signOut();
await loginPage.signIn({
email: 'user@nocodb.com',
password: getDefaultPwd(),
withoutPrefix: true,
});
await accountUsersPage.goto();
// change role
for (let i = 0; i < roleDb.length; i++) { for (let i = 0; i < roleDb.length; i++) {
await accountUsersPage.updateRole({ await accountUsersPage.updateRole({
email: roleDb[i].email, email: roleDb[i].email,
@ -81,10 +107,16 @@ test.describe.skip('User roles', () => {
await signupPage.signUp({ await signupPage.signUp({
email: roleDb[roleIdx].email, email: roleDb[roleIdx].email,
password: getDefaultPwd(), password: getDefaultPwd(),
withoutPrefix: true,
}); });
await workspacePage.checkWorkspaceCreateButton({ // wait for page rendering to complete after sign up
exists: roleDb[roleIdx].role === 'Organization Level Creator', await dashboard.rootPage.waitForTimeout(1000);
});
if (roleDb[roleIdx].role === 'Organization Level Creator') {
expect(await dashboard.leftSidebar.btn_newProject.isVisible()).toBeTruthy();
} else {
expect(await dashboard.leftSidebar.btn_newProject.isVisible()).toBeFalsy();
}
} }
}); });

12
tests/playwright/tests/db/usersAccounts/rolesCreate.spec.ts

@ -1,11 +1,10 @@
import { test } from '@playwright/test'; import { test } from '@playwright/test';
import { DashboardPage } from '../../../pages/Dashboard'; import { DashboardPage } from '../../../pages/Dashboard';
import setup, { unsetup } from '../../../setup'; import setup, { unsetup } from '../../../setup';
import { SettingsPage } from '../../../pages/Dashboard/Settings';
import { SignupPage } from '../../../pages/SignupPage'; import { SignupPage } from '../../../pages/SignupPage';
import { ProjectsPage } from '../../../pages/ProjectsPage'; import { ProjectsPage } from '../../../pages/ProjectsPage';
import { getDefaultPwd } from '../../../tests/utils/general'; import { getDefaultPwd } from '../../../tests/utils/general';
import { WorkspacePage } from '../../../pages/WorkspacePage'; import { isEE } from '../../../setup/db';
const roleDb = [ const roleDb = [
{ email: 'creator@nocodb.com', role: 'creator', url: '' }, { email: 'creator@nocodb.com', role: 'creator', url: '' },
@ -15,27 +14,26 @@ const roleDb = [
]; ];
test.describe.skip('User roles', () => { test.describe.skip('User roles', () => {
if (isEE()) {
test.skip();
}
let dashboard: DashboardPage; let dashboard: DashboardPage;
let settings: SettingsPage;
let signupPage: SignupPage; let signupPage: SignupPage;
let projectsPage: ProjectsPage; let projectsPage: ProjectsPage;
let workspacePage: WorkspacePage;
let context: any; let context: any;
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
context = await setup({ page, isEmptyProject: false }); context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project); dashboard = new DashboardPage(page, context.project);
settings = dashboard.settings;
signupPage = new SignupPage(page); signupPage = new SignupPage(page);
projectsPage = new ProjectsPage(page); projectsPage = new ProjectsPage(page);
workspacePage = new WorkspacePage(page);
}); });
test.afterEach(async () => { test.afterEach(async () => {
await unsetup(context); await unsetup(context);
}); });
test.skip('Create role', async () => { test('Create role', async () => {
test.slow(); test.slow();
for (let i = 0; i < roleDb.length; i++) { for (let i = 0; i < roleDb.length; i++) {

Loading…
Cancel
Save