feat: Allow viewers to be upgraded to editors on individual collections (#4023)

* Improve types

* More types, fix default permission for viewers added to collection

* fix change of default role for CollectionGroup

* Restore policy

* test

* tests
This commit is contained in:
Tom Moor
2022-08-31 08:12:27 +02:00
committed by GitHub
parent b8115ae3ce
commit 212985e18f
42 changed files with 537 additions and 435 deletions

View File

@@ -21,11 +21,12 @@ import {
Length as SimpleLength,
} from "sequelize-typescript";
import isUUID from "validator/lib/isUUID";
import { CollectionPermission } from "@shared/types";
import { sortNavigationNodes } from "@shared/utils/collections";
import { SLUG_URL_REGEX } from "@shared/utils/urlHelpers";
import { CollectionValidation } from "@shared/validations";
import slugify from "@server/utils/slugify";
import { NavigationNode, CollectionSort } from "~/types";
import type { NavigationNode, CollectionSort } from "~/types";
import CollectionGroup from "./CollectionGroup";
import CollectionUser from "./CollectionUser";
import Document from "./Document";
@@ -39,9 +40,6 @@ import IsHexColor from "./validators/IsHexColor";
import Length from "./validators/Length";
import NotContainsUrl from "./validators/NotContainsUrl";
// without this indirection, the app crashes on starup
type Sort = CollectionSort;
@Scopes(() => ({
withAllMemberships: {
include: [
@@ -174,9 +172,9 @@ class Collection extends ParanoidModel {
@Column
index: string | null;
@IsIn([["read", "read_write"]])
@Column
permission: "read" | "read_write" | null;
@IsIn([Object.values(CollectionPermission)])
@Column(DataType.STRING)
permission: CollectionPermission | null;
@Default(false)
@Column
@@ -193,7 +191,7 @@ class Collection extends ParanoidModel {
@Column({
type: DataType.JSONB,
validate: {
isSort(value: Sort) {
isSort(value: CollectionSort) {
if (
typeof value !== "object" ||
!value.direction ||
@@ -213,7 +211,7 @@ class Collection extends ParanoidModel {
},
},
})
sort: Sort;
sort: CollectionSort;
// getters
@@ -255,14 +253,14 @@ class Collection extends ParanoidModel {
model: Collection,
options: { transaction: Transaction }
) {
if (model.permission !== "read_write") {
if (model.permission !== CollectionPermission.ReadWrite) {
return CollectionUser.findOrCreate({
where: {
collectionId: model.id,
userId: model.createdById,
},
defaults: {
permission: "read_write",
permission: CollectionPermission.ReadWrite,
createdById: model.createdById,
},
transaction: options.transaction,

View File

@@ -8,6 +8,7 @@ import {
DataType,
Scopes,
} from "sequelize-typescript";
import { CollectionPermission } from "@shared/types";
import Collection from "./Collection";
import Group from "./Group";
import User from "./User";
@@ -33,10 +34,10 @@ import Fix from "./decorators/Fix";
@Table({ tableName: "collection_groups", modelName: "collection_group" })
@Fix
class CollectionGroup extends BaseModel {
@Default("read_write")
@IsIn([["read", "read_write", "maintainer"]])
@Column
permission: string;
@Default(CollectionPermission.ReadWrite)
@IsIn([Object.values(CollectionPermission)])
@Column(DataType.STRING)
permission: CollectionPermission;
// associations

View File

@@ -8,6 +8,7 @@ import {
DataType,
Scopes,
} from "sequelize-typescript";
import { CollectionPermission } from "@shared/types";
import Collection from "./Collection";
import User from "./User";
import BaseModel from "./base/BaseModel";
@@ -32,10 +33,10 @@ import Fix from "./decorators/Fix";
@Table({ tableName: "collection_users", modelName: "collection_user" })
@Fix
class CollectionUser extends BaseModel {
@Default("read_write")
@IsIn([["read", "read_write", "maintainer"]])
@Column
permission: string;
@Default(CollectionPermission.ReadWrite)
@IsIn([Object.values(CollectionPermission)])
@Column(DataType.STRING)
permission: CollectionPermission;
// associations

View File

@@ -19,6 +19,7 @@ import {
IsUrl,
AllowNull,
} from "sequelize-typescript";
import { CollectionPermission } from "@shared/types";
import { getBaseDomain, RESERVED_SUBDOMAINS } from "@shared/utils/domains";
import env from "@server/env";
import { generateAvatarUrl } from "@server/utils/avatars";
@@ -172,7 +173,7 @@ class Team extends ParanoidModel {
teamId: this.id,
createdById: userId,
sort: Collection.DEFAULT_SORT,
permission: "read_write",
permission: CollectionPermission.ReadWrite,
},
{
transaction,

View File

@@ -1,3 +1,4 @@
import { CollectionPermission } from "@shared/types";
import { buildUser, buildTeam, buildCollection } from "@server/test/factories";
import { getTestDatabase } from "@server/test/support";
import CollectionUser from "./CollectionUser";
@@ -39,7 +40,7 @@ describe("user model", () => {
});
const collection = await buildCollection({
teamId: team.id,
permission: "read_write",
permission: CollectionPermission.ReadWrite,
});
const response = await user.collectionIds();
expect(response.length).toEqual(1);
@@ -52,7 +53,7 @@ describe("user model", () => {
});
const collection = await buildCollection({
teamId: team.id,
permission: "read",
permission: CollectionPermission.Read,
});
const response = await user.collectionIds();
expect(response.length).toEqual(1);
@@ -83,7 +84,7 @@ describe("user model", () => {
createdById: user.id,
collectionId: collection.id,
userId: user.id,
permission: "read",
permission: CollectionPermission.Read,
});
const response = await user.collectionIds();
expect(response.length).toEqual(1);

View File

@@ -22,6 +22,7 @@ import {
AllowNull,
} from "sequelize-typescript";
import { languages } from "@shared/i18n";
import { CollectionPermission } from "@shared/types";
import { stringToColor } from "@shared/utils/color";
import env from "@server/env";
import { ValidationError } from "../errors";
@@ -219,6 +220,12 @@ class User extends ParanoidModel {
return stringToColor(this.id);
}
get defaultCollectionPermission(): CollectionPermission {
return this.isViewer
? CollectionPermission.Read
: CollectionPermission.ReadWrite;
}
/**
* Returns a code that can be used to delete this user account. The code will
* be rotated when the user signs out.
@@ -298,8 +305,8 @@ class User extends ParanoidModel {
return collectionStubs
.filter(
(c) =>
c.permission === "read" ||
c.permission === "read_write" ||
c.permission === CollectionPermission.Read ||
c.permission === CollectionPermission.ReadWrite ||
c.memberships.length > 0 ||
c.collectionGroupMemberships.length > 0
)