#Amplify 5 to 6 migration issues (how to configure custom prefix in Storage now)

16 messages · Page 1 of 1 (latest)

mellow sphinx
#

In aws-amplify 5 I used to configure the logic with Storage in a specific way with

export const configureS3forTenant = async (tenantId: string) => {
    Storage.configure({
        customPrefix: {
            public: `${tenantId}/`,
        },
    });
}

then I additionally map claims from the Idtoken to principal tags and then check that access in bucket policy to grant access to tenant specific folder to all users with that tenat id in custom attribute

I can't find a way to configure a custom prefix name for public in aws-amplify 6.

Are custom prefixes no longer supported in aws-amplify 6?

mellow sphinx
#

I think I found where the config option went for Amplify 6, it wasn't mentioned in migration guide, so I've got frustrated. but I dived into the Typescript definitions for Amplify.configure
and found that we still can configure the prefixes in Amplify 6 with prefixResolver

Amplify.configure(amplifyconfig, {
    Storage: {
        S3: {
            prefixResolver: prefixResolver
        }
    },
});
gritty mural
#

hey @mellow sphinx Did this work for you? i have been trying to make prefixResolver work to remove the 'public' prefix from the key. I have defined the key inside the S3 object along with bucket and region. But the 'public' key stil exists.

mellow sphinx
#

hi @gritty mural , yeah the prefixResolver works for me, here is my implementation

import {getCurrentUser} from "./user";
import {StorageAccessLevel} from "@aws-amplify/core";

export const prefixResolver = async (params: {
    accessLevel: StorageAccessLevel;
    targetIdentityId?: string;
}): Promise<string> =>  {
    if (params.accessLevel === 'private') {
        const tenantId = await getTenantId();
        return `${tenantId}/`;
    } else {
        return params.accessLevel
    }
}

Then when I upload file with

import {uploadData, TransferProgressEvent} from "@aws-amplify/storage";
const mimeType = type || file.type;
            const uploadTask = uploadData({
                key: s3FileName,
                data: file,
                options: {
                    accessLevel: 'private',
                    contentType: mimeType,
                }
            });
            uploadTask.result.then(() => {
                resolve({
                    s3FileName: s3FileName!,
                    mimeType: mimeType,
                });
            })

It's stored under the right prefix (folder)

#

In your case you want to skip the prefix for public folder then try to follow this example https://docs.amplify.aws/javascript/build-a-backend/storage/configure-access/#customize-object-key-path

Amplify.configure(amplifyconfig, {
  Storage: {
    S3: {
      prefixResolver: async ({ accessLevel, targetIdentityId }) => {
        if (accessLevel === 'guest') {
          return '';
        } else if (accessLevel === 'protected') {
          return `myProtectedPrefix/${targetIdentityId}/`;
        } else {
          return `myPrivatePrefix/${targetIdentityId}/`;
        }
      }
    }
  }
});

then if you use @aws-amplify/storage to upload and access quest level files they should be stored without public/ prefix

gritty mural
#

Thanks for your quick response @mellow sphinx I am only trying to generate image downloadable urls which i am planning to use in an <img> src.

i am configuring amplify the way you have done, to no avail. here's my config code:

Amplify.configure({
  Auth: {
    Cognito: {
      userPoolClientId: returnAmplifyConfig().userPoolClientId,
      userPoolId: returnAmplifyConfig().userPoolId,
      identityPoolId: returnAmplifyConfig().identityPoolId
    }
  },
  Storage: {
    S3: {
      bucket: 'XXX',
      region: 'XXX',
      prefixResolver: async ({ accessLevel, targetIdentityId }) => {
        console.log({ accessLevel, targetIdentityId }, 'index'); // there's no log on the console
        return ''; // this does absolutely nothing.
        // if (accessLevel === 'guest') {
        //   return '';
        // } else if (accessLevel === 'protected') {
        //   return ``;
        // } else {
        //   return '';
        // }
      }
    }
  }
});
mellow sphinx
# gritty mural Thanks for your quick response <@386476605280878595> I am only trying to genera...

to gain a url you will be able to use in img you need signedUrls

import {getUrl} from "@aws-amplify/storage";

export const PRESIGNED_URL_EXPIRATION_SECONDS = 900;
export const getPresignedUrl = async (filePath: string) => {
    const getUrlResult = await getUrl({
        key: filePath,
        options: {
            accessLevel: 'private',
            expiresIn: PRESIGNED_URL_EXPIRATION_SECONDS,
        },

    });
    return getUrlResult.url;
}

I guess you can try to pass accessLevel guest with your config it should build the right url. filePath is the key in your s3.
Although you will probably need appropriate access policy in the s3

#

what error do you see when you try to call getUrl? or were you trying to build the url directly to the file in s3?

gritty mural
#

so this is my getUrl implementation

const getUrlResult = await getUrl({
            key: `${folderName}/${fileName}`,
            options: {
              accessLevel: 'guest',
              // targetIdentityId: 'XXXX', // id of another user, if `accessLevel` is `guest` - what is targetIdentityId supposed to be? is this required?
              expiresIn: 3600 
            }
          });
#

i receive a link by doing getUrlResult.url.toString()....

this link opens up a xml file, content:

<Error>
<Code>NoSuchKey</Code>
<Message>The specified key does not exist.</Message>
<Key>public/job/300624/Irrigation Maintenance_300624_490ff7c3-a00b-409e-a34d-6f196dc4a943</Key>
<RequestId>12E9KJXFSH6TVBBA</RequestId>
<HostId>UpBj9YB/VjECi2P887InbsB38LzeUhBCFoWX6EYxmYt6yqbWcKuY98J5osZ2dwvwsngW2s1+zOM=</HostId>
</Error>
#

you see the key still has the public prefix

mellow sphinx
#

I see, your prefixResolver simply never called. It seems the porblem is with your Amplify.configure call
here is the right syntax

import amplifyconfig from './amplifyconfiguration.json';


/* @ts-ignore */
// window.LOG_LEVEL = 'DEBUG';
Amplify.configure(amplifyconfig, {
    API: {
        GraphQL: {
            headers: headerResolver,
            withCredentials: false
        }
    },
    Storage: {
        S3: {
            prefixResolver: prefixResolver
        }
    },
});

Amplify configure takes two arguments, and this config should go to the 2-end argument

configure(
        resourceConfig: ResourcesConfig | LegacyConfig,
        libraryOptions?: LibraryOptions
    ):
gritty mural
#

yes! you are right. prefixResolver goes into libraryOptions, the bucket and region go into amplify Config. am i an idiot or the docs don't make it clear? I found this explanation in their Migrate v5 to v6 page

mellow sphinx
#

Docs are beyond confusing on this matter, because there are examples were they call configure with one argument
Maybe it works if you already have configured it once I'm not sure

gritty mural
#

anyways thanks a lot @mellow sphinx ! You are a lifesaver