refine icon indicating copy to clipboard operation
refine copied to clipboard

[BUG] Appwrite `dataProvider` meta.permissions (`@refinedev/appwrite`)

Open abdelrahman-essawy opened this issue 11 months ago • 7 comments

Describe the bug

Hello, how to override these default permissions in appwrite dataProvider? I don't want public permissions, only the permissions i set in meta at creation/edition.

  const { saveButtonProps, formProps, form } = useForm<HttpError>({
    meta: {
      readPermissions: [Permission.read(Role.user(identity.$id))],
      writePermissions: [Permission.write(Role.user(identity.$id))],
    },
  });

I was able to walk-around it by making custom dataProviderWrapper that overrides the original create method

  return {
  ...originalDataProvider,
  create: async ({ resource, variables, meta }) => {
    const permissions = [
      // Permission.read(Role.any()),
      // Permission.write(Role.any()),
      ...(meta?.readPermissions ?? [Permission.read(Role.any())]),
      ...(meta?.writePermissions ?? [Permission.write(Role.any())]),
    ];

Steps To Reproduce

  1. create new app
npm create refine-app@latest -- --example data-provider-appwrite
  1. use the following snippet with any roles.
  const { saveButtonProps, formProps, form } = useForm<HttpError>({
    meta: {
      readPermissions: [Permission.read(Role.user(identity.$id))],
      writePermissions: [Permission.read(Role.user(identity.$id))],
    },
  });

Expected behavior

expected behavior is to be able to complete override the meta params.

Packages

    "@refinedev/antd": "^5.37.5",
    "@refinedev/appwrite": "^6.4.7",
    "@refinedev/core": "^4.48.0",
    "@refinedev/devtools": "^1.1.35",
    "@refinedev/kbar": "^1.3.7",
    "@refinedev/nextjs-router": "^6.0.1",
    "@refinedev/react-hook-form": "^4.8.15",

Additional Context

sample after override

export const customDataProvider = (
  appwriteClient: Appwrite,
  options: { databaseId: string } = { databaseId: 'default' }
): Required<DataProvider> => {
  const { databaseId } = options;
  const database = new Databases(appwriteClient);
  const originalDataProvider: Required<DataProvider> = dataProvider(
    appwriteClient,
    {
      databaseId,
    }
  );

  return {
    ...originalDataProvider,
    create: async ({ resource, variables, meta }) => {
      const permissions = [
        // Permission.read(Role.any()),
        // Permission.write(Role.any()),
        ...(meta?.readPermissions ?? [Permission.read(Role.any())]), // new permisions
        ...(meta?.writePermissions ?? [Permission.write(Role.any())]), // new permisions
      ];

      const { $id, ...restData } = await database.createDocument(
        databaseId,
        resource,
        meta?.documentId ?? ID.unique(),
        variables as unknown as object,
        permissions
      );

      return {
        data: {
          id: $id,
          ...restData,
        },
      } as any;
    },
}

abdelrahman-essawy avatar Mar 18 '24 21:03 abdelrahman-essawy

Hey @abdelrahman-essawy, it seems appwrite data provider appends permissions, on top of the default ones. It could be updated to have a logic, if user provides permissions, it only uses them.

As a workaround, you can swizzle the data provider and use it. See the documentation here.

BatuhanW avatar Mar 19 '24 09:03 BatuhanW

@BatuhanW Great!, I will be more than happy to open PR for that, if that's okay.

abdelrahman-essawy avatar Mar 19 '24 10:03 abdelrahman-essawy

@abdelrahman-essawy sure thing. Only concern is, this could be a breaking change for existing users (Not sure if it's a valid case tho).

Perhaps, we could add defaultPermissions to the 2nd argument (options), with default value having Permission.read(Role.any()). This way, existing users wouldn't be affected, and also if someone is using same permissions everywhere, they wouldn't need to pass it to every hook.

Assigning issue to you, looking forward to your PR!

BatuhanW avatar Mar 19 '24 10:03 BatuhanW

Hey @abdelrahman-essawy discussed with the team, and decided to go with this implementation.

  • Let's remove all hard-coded inline Permission.read(Role.any()), Permission.write(Role.any()) from all methods.
  • Let's have options.defaultReadPermissions, options.defaultWritePermissions argument as arrays, but default values should be empty arrays. (Unline what I said in the previous comment.)
  • If user passes these permissions from meta, let's use them, ignoring options.defaultReadPermissions and/or options.defaultReadPermissions completely.

BatuhanW avatar Mar 19 '24 11:03 BatuhanW

but default values should be empty arrays. (Unline what I said in the previous comment.)

isn't that a breaking change? users who didn't pass anything before, expects to have Permission.read(Role.any()), Permission.write(Role.any()) passed by default.

notice that if you passed an empty array of permissions

image

appwrite will treat it with no permissions at all.

image

abdelrahman-essawy avatar Mar 19 '24 11:03 abdelrahman-essawy

but default values should be empty arrays. (Unline what I said in the previous comment.)

isn't that a breaking change? users who didn't pass anything before, expects to have Permission.read(Role.any()), Permission.write(Role.any()) passed by default.

notice that if you passed an empty array of permissions

image

appwrite will treat it with no permissions at all.

image

@BatuhanW hello, what do you think of this ?

abdelrahman-essawy avatar Mar 20 '24 13:03 abdelrahman-essawy

Hey @abdelrahman-essawy, there is no different with passing an empty array and Permission.any from AppWrite's side.

BatuhanW avatar Mar 22 '24 08:03 BatuhanW