16811: Fix public favorites.
authorPeter Amstutz <peter.amstutz@curii.com>
Thu, 10 Sep 2020 21:28:08 +0000 (17:28 -0400)
committerPeter Amstutz <peter.amstutz@curii.com>
Thu, 10 Sep 2020 21:28:08 +0000 (17:28 -0400)
Previously, these were 'star' links owned by the all users group.

However, the all users group is now a 'role' and new rule (since
issue #16007) is that 'role' groups cannot directly own things.

So now public favorites are defined as a link owned by the system user
with tail_uuid as the all users group.

Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz@curii.com>

src/services/favorite-service/favorite-service.ts
src/store/favorite-panel/favorite-panel-middleware-service.ts
src/store/public-favorites-panel/public-favorites-middleware-service.ts
src/store/tree-picker/tree-picker-actions.ts

index fbb2a52f448920e8b34cccaac6219709193534c7..37c1380b6a6856eea91f43b17784e976601782d4 100644 (file)
@@ -23,20 +23,38 @@ export class FavoriteService {
     ) { }
 
     create(data: { userUuid: string; resource: { uuid: string; name: string } }) {
-        return this.linkService.create({
-            ownerUuid: data.userUuid,
+        const l = this.linkService.create({
+            // If this is for the all users group, it must be owned by
+            // the system user.
+            ownerUuid: (data.userUuid.substr(-22) === "-j7d0g-fffffffffffffff" ?
+                data.userUuid.substr(0, 5) + "-tpzed-000000000000000"
+                : data.userUuid),
             tailUuid: data.userUuid,
             headUuid: data.resource.uuid,
             linkClass: LinkClass.STAR,
             name: data.resource.name
         });
+
+        if (data.userUuid.substr(-22) === "-j7d0g-fffffffffffffff") {
+            // If this is for the all users group, we need to create a
+            // permission link as well.
+            l.then(result =>
+                this.linkService.create({
+                    tailUuid: data.userUuid,
+                    headUuid: result.uuid,
+                    linkClass: LinkClass.PERMISSION,
+                    name: "can_read"
+                }));
+        }
+
+        return l;
     }
 
     delete(data: { userUuid: string; resourceUuid: string; }) {
         return this.linkService
             .list({
                 filters: new FilterBuilder()
-                    .addEqual('owner_uuid', data.userUuid)
+                    .addEqual('tail_uuid', data.userUuid)
                     .addEqual('head_uuid', data.resourceUuid)
                     .addEqual('link_class', LinkClass.STAR)
                     .getFilters()
@@ -75,7 +93,7 @@ export class FavoriteService {
             .list({
                 filters: new FilterBuilder()
                     .addIn("head_uuid", resourceUuids)
-                    .addEqual("owner_uuid", userUuid)
+                    .addEqual("tail_uuid", userUuid)
                     .addEqual("link_class", LinkClass.STAR)
                     .getFilters()
             })
index f45a62f87c41e777471ab2aaf8adadac0de640bd..c9a06fe6fd4b3c90eb70258ced19247126610a38 100644 (file)
@@ -25,6 +25,7 @@ import { getSortColumn } from "~/store/data-explorer/data-explorer-reducer";
 import { getDataExplorerColumnFilters } from '~/store/data-explorer/data-explorer-middleware-service';
 import { serializeSimpleObjectTypeFilters } from '../resource-type-filters/resource-type-filters';
 import { ResourceKind } from "~/models/resource";
+import { LinkClass } from "~/models/link";
 
 export class FavoritePanelMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
@@ -59,7 +60,7 @@ export class FavoritePanelMiddlewareService extends DataExplorerMiddlewareServic
                 api.dispatch(progressIndicatorActions.START_WORKING(this.getId()));
                 const responseLinks = await this.services.linkService.list({
                     filters: new FilterBuilder()
-                        .addEqual("link_class", 'star')
+                        .addEqual("link_class", LinkClass.STAR)
                         .addEqual('tail_uuid', getUserUuid(api.getState()))
                         .addEqual('tail_kind', ResourceKind.USER)
                         .getFilters()
index 072cf4e430c49f8b47432321ce1cf36815707209..7dee483485411163c0b997c6cafc7c29559d2458 100644 (file)
@@ -54,14 +54,16 @@ export class PublicFavoritesMiddlewareService extends DataExplorerMiddlewareServ
             try {
                 api.dispatch(progressIndicatorActions.START_WORKING(this.getId()));
                 const uuidPrefix = api.getState().auth.config.uuidPrefix;
-                const uuid = `${uuidPrefix}-j7d0g-fffffffffffffff`;
+                const systemUuid = `${uuidPrefix}-tpzed-000000000000000`;
+                const allusersUuid = `${uuidPrefix}-j7d0g-fffffffffffffff`;
                 const responseLinks = await this.services.linkService.list({
                     limit: dataExplorer.rowsPerPage,
                     offset: dataExplorer.page * dataExplorer.rowsPerPage,
                     filters: new FilterBuilder()
                         .addEqual('link_class', LinkClass.STAR)
                         .addILike("name", dataExplorer.searchValue)
-                        .addEqual('owner_uuid', uuid)
+                        .addIn('owner_uuid', [allusersUuid, systemUuid])
+                        .addEqual('tail_uuid', allusersUuid)
                         .addIsA("head_uuid", typeFilters)
                         .getFilters()
                 });
index 5e880aad9587583ffaeebde865b675612b752785..49c1d60abbd95df0b0afd7dde3e0e227fac120fa 100644 (file)
@@ -271,36 +271,36 @@ export const loadPublicFavoritesProject = (params: LoadFavoritesProjectParams) =
     async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
         const { pickerId, includeCollections = false, includeFiles = false } = params;
         const uuidPrefix = getState().auth.config.uuidPrefix;
-        const uuid = `${uuidPrefix}-j7d0g-fffffffffffffff`;
-        if (uuid) {
+        const systemUuid = `${uuidPrefix}-tpzed-000000000000000`;
+        const allusersUuid = `${uuidPrefix}-j7d0g-fffffffffffffff`;
 
-            const filters = pipe(
-                (fb: FilterBuilder) => includeCollections
-                    ? fb.addIsA('head_uuid', [ResourceKind.PROJECT, ResourceKind.COLLECTION])
-                    : fb.addIsA('head_uuid', [ResourceKind.PROJECT]),
-                fb => fb
-                    .addEqual('link_class', LinkClass.STAR)
-                    .addEqual('owner_uuid', uuid)
-                    .getFilters(),
-            )(new FilterBuilder());
+        const filters = pipe(
+            (fb: FilterBuilder) => includeCollections
+                ? fb.addIsA('head_uuid', [ResourceKind.PROJECT, ResourceKind.COLLECTION])
+                : fb.addIsA('head_uuid', [ResourceKind.PROJECT]),
+            fb => fb
+                .addEqual('link_class', LinkClass.STAR)
+                .addIn('owner_uuid', [systemUuid, allusersUuid])
+                .addEqual('tail_uuid', allusersUuid)
+                .getFilters(),
+        )(new FilterBuilder());
 
-            const { items } = await services.linkService.list({ filters });
+        const { items } = await services.linkService.list({ filters });
 
-            dispatch<any>(receiveTreePickerData<LinkResource>({
-                id: 'Public Favorites',
-                pickerId,
-                data: items,
-                extractNodeData: item => ({
-                    id: item.headUuid,
-                    value: item,
-                    status: item.headKind === ResourceKind.PROJECT
+        dispatch<any>(receiveTreePickerData<LinkResource>({
+            id: 'Public Favorites',
+            pickerId,
+            data: items,
+            extractNodeData: item => ({
+                id: item.headUuid,
+                value: item,
+                status: item.headKind === ResourceKind.PROJECT
+                    ? TreeNodeStatus.INITIAL
+                    : includeFiles
                         ? TreeNodeStatus.INITIAL
-                        : includeFiles
-                            ? TreeNodeStatus.INITIAL
-                            : TreeNodeStatus.LOADED
-                }),
-            }));
-        }
+                        : TreeNodeStatus.LOADED
+            }),
+        }));
     };
 
 export const receiveTreePickerProjectsData = (id: string, projects: ProjectResource[], pickerId: string) =>