Skip to content

Commit e6a1918

Browse files
Refactor uploads table (#400)
* wip Signed-off-by: Roy Scheeren <hi@royscheeren.com> * feat: update uploaded assets table Signed-off-by: Jeroen Branje <info@jeroenbranje.nl> * feat: add branch to workflow Signed-off-by: Jeroen Branje <info@jeroenbranje.nl> * feat: add logs to processHandler Signed-off-by: Jeroen Branje <info@jeroenbranje.nl> * feat: update processHandler test Signed-off-by: Jeroen Branje <info@jeroenbranje.nl> * feat: remove copy and delete file from processAssetUpload Signed-off-by: Jeroen Branje <info@jeroenbranje.nl> * feat: update processHandler test Signed-off-by: Jeroen Branje <info@jeroenbranje.nl> * feat: remove logs and DialogConfirm on delete Signed-off-by: Jeroen Branje <info@jeroenbranje.nl> * feat: remove branch and prettier Signed-off-by: Jeroen Branje <info@jeroenbranje.nl> * feat: rename locales Signed-off-by: Jeroen Branje <info@jeroenbranje.nl> * feat: add test for truncateCID Signed-off-by: Jeroen Branje <info@jeroenbranje.nl> --------- Signed-off-by: Roy Scheeren <hi@royscheeren.com> Signed-off-by: Jeroen Branje <info@jeroenbranje.nl> Co-authored-by: Jeroen Branje <info@jeroenbranje.nl>
1 parent 6b3eb5a commit e6a1918

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+3941
-220
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Meta, Story } from '@storybook/react'
2+
import React from 'react'
3+
4+
import IconButtonWithTooltip from './IconButtonWithTooltip'
5+
6+
export default {
7+
component: IconButtonWithTooltip,
8+
title: 'Components/IconButtonWithTooltip',
9+
} as Meta
10+
11+
const Template: Story = ({ icon, onClick, description }) => (
12+
<IconButtonWithTooltip icon={icon} onClick={() => onClick()}>
13+
{description}
14+
</IconButtonWithTooltip>
15+
)
16+
17+
export const IconButtonWithTooltipStory = Template.bind({})
18+
19+
IconButtonWithTooltipStory.args = {
20+
icon: <span>ICON</span>,
21+
onClick: () => console.log('action'),
22+
description: 'Tooltip content',
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import React, { FC, ReactNode, useState } from 'react'
2+
3+
import { TooltipType } from '../../../types'
4+
5+
interface Props {
6+
icon: JSX.Element
7+
type?: TooltipType
8+
children: ReactNode
9+
disabled?: boolean
10+
onClick: () => void
11+
}
12+
13+
const IconButtonWithTooltip: FC<Props> = ({ icon, children, disabled = false, onClick }) => {
14+
const [hover, setHover] = useState(false)
15+
16+
const handleMouseIn = () => !disabled && setHover(true)
17+
18+
const handleMouseOut = () => !disabled && setHover(false)
19+
20+
const styles = hover ? 'opacity-100 visible duration-100' : 'delay-300 opacity-0 invisible'
21+
22+
return (
23+
<div className="relative flex items-center justify-center">
24+
<button
25+
onMouseOver={handleMouseIn}
26+
onMouseOut={handleMouseOut}
27+
onFocus={handleMouseIn}
28+
onBlur={handleMouseOut}
29+
disabled={disabled}
30+
type="button"
31+
onClick={() => !disabled && onClick()}
32+
className={`inline-flex items-center text-gray-600 hover:text-gray-800 text-sm font-semibold disabled:cursor-not-allowed disabled:text-gray-400 disabled:hover:text-gray-400`}
33+
>
34+
{icon}
35+
</button>
36+
<div
37+
role="tooltip"
38+
className={`${styles} flex justify-center absolute z-30 bottom-full py-2 px-3 text-xs font-medium rounded-lg shadow-lg tracking-wide text-white bg-gray-700 transition mb-3 border border-gray-600`}
39+
>
40+
{children}
41+
<div className="block w-3 h-3 bg-gray-700 absolute -bottom-1.5 mx-auto transform rotate-45 z-0 border-r border-b border-gray-600" />
42+
</div>
43+
</div>
44+
)
45+
}
46+
47+
export default IconButtonWithTooltip
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { render, screen } from '@testing-library/react'
2+
import React from 'react'
3+
4+
import IconButtonWithTooltip from './IconButtonWithTooltip'
5+
6+
describe('atoms/IconButtonWithTooltip', () => {
7+
describe('render', () => {
8+
it('should render IconButtonWithTooltip with content', () => {
9+
const content = 'Tooltip content'
10+
// when ... rendering component
11+
render(
12+
<IconButtonWithTooltip icon={<span>Button</span>} onClick={() => {}}>
13+
{content}
14+
</IconButtonWithTooltip>,
15+
)
16+
const IconButtonWithTooltipElement = screen.getByText(content)
17+
18+
// then ... should render as expected
19+
expect(IconButtonWithTooltipElement).toBeInTheDocument()
20+
})
21+
})
22+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as IconButtonWithTooltip } from './IconButtonWithTooltip'

apps/design-system/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export {
1818
export { Grid, GridRow } from './components/Atoms/Grid'
1919
export { Heading } from './components/Atoms/Heading'
2020
export { HeadingWithTooltip } from './components/Atoms/HeadingWithTooltip'
21+
export { IconButtonWithTooltip } from './components/Atoms/IconButtonWithTooltip'
2122
export { LoadingIndicator } from './components/Atoms/LoadingIndicator'
2223
export { MemberProfileCard } from './components/Molecules/MemberProfileCard'
2324
export { Nav, NavItem } from './components/Atoms/Nav'

apps/envited.ascs.digital/common/asset/createModifiedManifest.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { equals, evolve, find, includes, map, pipe, propEq, propOr, tail } from
22

33
import { formatAssetUri, formatIpfsUri, formatMetadataUri } from './createTokenMetadata.utils'
44
import { AccessRole, ExtractedFileWithCID, ManifestLink } from './types'
5-
import { formatManifestLinkPath, isRemoteUrl } from './validateAndCreateMetadata.utils'
5+
import { formatManifestLinkPath, isRemoteUrl } from './utils'
66

77
export const createModifiedManifest = ({
88
assetCID,

apps/envited.ascs.digital/common/asset/createTokenMetadata.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { append, equals } from 'ramda'
33
import { TOKEN_TAGS } from '../constants/tokenTags'
44
import { extractFilenameFromPath, formatAssetUri, formatIpfsUri } from './createTokenMetadata.utils'
55
import { Manifest } from './types'
6-
import { formatManifestLinkPath, hasManifestThirdPartyLinks } from './validateAndCreateMetadata.utils'
6+
import { formatManifestLinkPath, hasManifestThirdPartyLinks } from './utils'
77

88
export const createTokenMetadata = ({
99
asset,

apps/envited.ascs.digital/common/asset/validateAndCreateMetadata.utils.integration.test.ts apps/envited.ascs.digital/common/asset/utils.integration.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { createFilename } from './validateAndCreateMetadata.utils'
1+
import { createFilename } from './utils'
22

3-
describe('common/asset/validateAndCreateMetadata.utils', () => {
3+
describe('common/asset/utils', () => {
44
describe('createFilename', () => {
55
it('should create a valid CID from a byte array', async () => {
66
// Create a sample byte array

apps/envited.ascs.digital/common/asset/validateAndCreateMetadata.utils.test.ts apps/envited.ascs.digital/common/asset/utils.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import manifest from '../fixtures/manifest.json'
22
import manifestRemoteAssetData from '../fixtures/manifestRemoteAssetData.json'
3-
import * as SUT from './validateAndCreateMetadata.utils'
3+
import * as SUT from './utils'
44

5-
describe('common/asset/validateAndCreateMetadata.utils', () => {
5+
describe('common/asset/utils', () => {
66
describe('createFilename', () => {
77
it('should create a filename', async () => {
88
const byteArray = 'BYTE_ARRAY'

apps/envited.ascs.digital/common/asset/validateAndCreateMetadata.utils.ts apps/envited.ascs.digital/common/asset/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { ExtractedFileWithCID, Manifest, ManifestLink } from './types'
2929

3030
export const _createFilename =
3131
({ raw, sha256, CID }: { raw: any; sha256: Hasher<'sha2-256', 18>; CID: any }) =>
32-
async (byteArray: any) => {
32+
async (byteArray: Uint8Array) => {
3333
try {
3434
const rawBytes = raw.encode(byteArray)
3535
const hash = await sha256.digest(rawBytes)

apps/envited.ascs.digital/common/asset/validateAndCreateMetadata.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
getDomainMetadataPath,
2121
getFileFromByteArray,
2222
getFilesAsPathAndByteArrayFromManifest,
23-
} from './validateAndCreateMetadata.utils'
23+
} from './utils'
2424

2525
export const _getShaclSchemaAndValidate =
2626
({

apps/envited.ascs.digital/common/aws/handlers/processAssetUpload/processAssetUpload.test.ts

+2-8
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ describe('common/aws/handlers/processAssetUpload', () => {
5959
const writeFileStub = jest.fn().mockReturnValue({
6060
done: uploadDoneStub,
6161
}) as any
62-
const copyFileStub = jest.fn().mockResolvedValue('COPIED') as any
6362
const deleteFileStub = jest.fn().mockReturnValue('SHACL_DATA') as any
6463
const getAssetStatusStub = jest.fn().mockReturnValue('ASSET_CID') as any
6564
const updateAssetStatusStub = jest.fn().mockReturnValue('UPDATED') as any
@@ -86,7 +85,6 @@ describe('common/aws/handlers/processAssetUpload', () => {
8685
const result = await SUT._main({
8786
readFile: readFileStub,
8887
writeFile: writeFileStub,
89-
copyFile: copyFileStub,
9088
deleteFile: deleteFileStub,
9189
validateAndCreateMetadata: validateAndCreateMetadataStub,
9290
getAsset: getAssetStatusStub,
@@ -99,8 +97,8 @@ describe('common/aws/handlers/processAssetUpload', () => {
9997
expect(readFileStub).toHaveBeenCalledWith({ Bucket: 'BUCKET_NAME', Key: 'OBJECT_KEY' })
10098
expect(validateAndCreateMetadataStub).toHaveBeenCalledWith('ASSET_BYTE_ARRAY', 'ASSET_CID')
10199
expect(validateAndCreateMetadataStub).toHaveBeenCalledTimes(1)
102-
expect(writeFileStub).toHaveBeenCalledTimes(5)
103-
expect(deleteFileStub).toHaveBeenCalledTimes(1)
100+
expect(writeFileStub).toHaveBeenCalledTimes(4)
101+
expect(deleteFileStub).toHaveBeenCalledTimes(0)
104102
expect(updateAssetStatusStub).toHaveBeenCalledWith(
105103
'ASSET_CID',
106104
'OBJECT_KEY',
@@ -110,7 +108,6 @@ describe('common/aws/handlers/processAssetUpload', () => {
110108
},
111109
'MODIFIED_MANIFEST',
112110
)
113-
expect(copyFileStub).toHaveBeenCalledTimes(1)
114111
expect(createGroupStub).toHaveBeenCalledWith('MINTER_ADDRESS')
115112
expect(uploadDoneStub).toHaveBeenCalledWith()
116113
expect(uploadFileStub).toHaveBeenCalledWith({ arrayBuffer: 'FILE_BUFFER', filename: 'PATH', group: 'GROUP_NAME' })
@@ -143,7 +140,6 @@ describe('common/aws/handlers/processAssetUpload', () => {
143140
const writeFileStub = jest.fn().mockReturnValue({
144141
done: uploadDoneStub,
145142
}) as any
146-
const copyFileStub = jest.fn().mockResolvedValue('COPIED') as any
147143
const deleteFileStub = jest.fn().mockReturnValue('SHACL_DATA') as any
148144
const getAssetStatusStub = jest.fn().mockReturnValue('ASSET_CID') as any
149145
const updateAssetStatusStub = jest.fn().mockReturnValue('UPDATED') as any
@@ -170,7 +166,6 @@ describe('common/aws/handlers/processAssetUpload', () => {
170166
const result = await SUT._main({
171167
readFile: readFileStub,
172168
writeFile: writeFileStub,
173-
copyFile: copyFileStub,
174169
deleteFile: deleteFileStub,
175170
validateAndCreateMetadata: validateShaclDataWithSchemaStub,
176171
getAsset: getAssetStatusStub,
@@ -187,7 +182,6 @@ describe('common/aws/handlers/processAssetUpload', () => {
187182
expect(writeFileStub).toHaveBeenCalledTimes(0)
188183
expect(deleteFileStub).toHaveBeenCalledWith({ Bucket: 'BUCKET_NAME', Key: 'OBJECT_KEY' })
189184
expect(uploadDoneStub).not.toHaveBeenCalledWith()
190-
expect(copyFileStub).toHaveBeenCalledTimes(0)
191185
expect(createGroupStub).toHaveBeenCalledTimes(0)
192186
expect(uploadFileStub).toHaveBeenCalledTimes(0)
193187
})

apps/envited.ascs.digital/common/aws/handlers/processAssetUpload/processAssetUpload.ts

+3-45
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
1-
import {
2-
CopyObjectCommandOutput,
3-
DeleteObjectCommandOutput,
4-
GetObjectCommandOutput,
5-
PutObjectCommandInput,
6-
} from '@aws-sdk/client-s3'
1+
import { DeleteObjectCommandOutput, GetObjectCommandOutput, PutObjectCommandInput } from '@aws-sdk/client-s3'
72
import { Upload } from '@aws-sdk/lib-storage'
83
import { S3Handler } from 'aws-lambda'
94
import { isNil, last, split } from 'ramda'
105
import ValidationReport from 'rdf-validate-shacl/src/validation-report'
116

127
import { getAsset, updateAsset, validateAndCreateMetadata } from '../../../asset'
138
import { ExtractedFileWithCID, ManifestExtractedFiles } from '../../../asset/types'
14-
import { copyFile, deleteFile, readFile, writeFile } from '../../../aws'
9+
import { deleteFile, readFile, writeFile } from '../../../aws'
1510
import { createGroup, uploadFile } from '../../../ipfs'
1611
import { log } from '../../../logger'
1712
import { Asset, AssetMetadata, AssetStatus } from '../../../types'
@@ -20,7 +15,6 @@ export const _main =
2015
({
2116
readFile,
2217
writeFile,
23-
copyFile,
2418
deleteFile,
2519
validateAndCreateMetadata,
2620
getAsset,
@@ -30,15 +24,6 @@ export const _main =
3024
}: {
3125
readFile: ({ Bucket, Key }: { Bucket: string; Key: string }) => Promise<GetObjectCommandOutput>
3226
writeFile: (params: PutObjectCommandInput) => Upload
33-
copyFile: ({
34-
Bucket,
35-
CopySource,
36-
Key,
37-
}: {
38-
Bucket: string
39-
CopySource: string
40-
Key: string
41-
}) => Promise<CopyObjectCommandOutput | undefined>
4227
deleteFile: ({ Bucket, Key }: { Bucket: string; Key: string }) => Promise<DeleteObjectCommandOutput | undefined>
4328
validateAndCreateMetadata: (
4429
byteArray: Uint8Array,
@@ -97,32 +82,9 @@ export const _main =
9782

9883
return
9984
}
100-
// Copy asset ZIP file to S3 with CID as name
101-
await copyFile({
102-
Bucket,
103-
CopySource: `${Bucket}/${Key}`,
104-
Key: assetCID,
105-
})
10685

10786
// Handle files for registered users
108-
const { owner, registeredUser } = files
109-
110-
if (owner) {
111-
const writeFilesToAssetPromises = owner.map(
112-
async ({ path, arrayBuffer }: { path: string; arrayBuffer: ArrayBuffer }) => {
113-
const writeToAsset = writeFile({
114-
Bucket,
115-
Key: `${assetCID}/${path}`,
116-
Body: Buffer.from(arrayBuffer),
117-
ContentEncoding: 'base64',
118-
})
119-
120-
return writeToAsset.done()
121-
},
122-
)
123-
124-
Promise.all(writeFilesToAssetPromises)
125-
}
87+
const { registeredUser } = files
12688

12789
if (visualizationFiles) {
12890
const writeFilesToIpfsPromises = visualizationFiles.map(
@@ -170,9 +132,6 @@ export const _main =
170132

171133
// Update stored asset in DB
172134
await updateAsset(assetCID, Key, AssetStatus.pending, metadata, modifiedManifest)
173-
174-
// Delete uploaded asset with the "old" name from S3
175-
await deleteFile({ Bucket, Key })
176135
} catch (err) {
177136
console.log(err)
178137
throw err
@@ -182,7 +141,6 @@ export const _main =
182141
export const main = _main({
183142
readFile,
184143
writeFile,
185-
copyFile,
186144
deleteFile,
187145
validateAndCreateMetadata,
188146
getAsset,

apps/envited.ascs.digital/common/database/queries/assets.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const deleteAsset = (db: DatabaseConnection) => async (id: string) =>
2121

2222
export const insertAsset =
2323
(db: DatabaseConnection) =>
24-
async ({ userId, cid, ownerId }: { userId: string; cid: string; ownerId: string }) =>
24+
async ({ userId, cid, ownerId, name }: { userId: string; cid: string; ownerId: string; name: string }) =>
2525
db
2626
.insert(asset)
2727
.values({
@@ -30,6 +30,9 @@ export const insertAsset =
3030
status: AssetStatus.processing,
3131
userId,
3232
owner: ownerId,
33+
name,
34+
createdAt: new Date(),
35+
updatedAt: new Date(),
3336
})
3437
.returning()
3538

apps/envited.ascs.digital/common/database/schema.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ export const profilesToBusinessCategoriesRelations = relations(profilesToBusines
179179
export const asset = pgTable('asset', {
180180
id: uuid('id').defaultRandom().primaryKey(),
181181
cid: text('cid'),
182+
name: text('name'),
182183
metadata: jsonb('metadata'),
183184
manifest: jsonb('manifest'),
184185
status: text('status', { enum: ['processing', 'rejected', 'pending', 'minted', 'completed'] }),
@@ -187,6 +188,8 @@ export const asset = pgTable('asset', {
187188
.references(() => user.id)
188189
.notNull(),
189190
hash: text('hash'),
191+
createdAt: timestamp('created_at'),
192+
updatedAt: timestamp('modified_at'),
190193
})
191194

192195
export const token = pgTable('token', {
@@ -210,7 +213,7 @@ export const token = pgTable('token', {
210213
displayUri: text('display_uri'),
211214
tokenMetadata: jsonb('token_metadata'),
212215
createdAt: timestamp('created_at'),
213-
modifiedAt: timestamp('modified_at'),
216+
updatedAt: timestamp('modified_at'),
214217
})
215218

216219
export const tokenTag = pgTable('tokenTag', {

apps/envited.ascs.digital/common/serverActions/assets/insert.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { forbiddenError, formatError, internalServerErrorError, unauthorizedErro
1111

1212
export const _insert =
1313
({ db, getServerSession, log }: { db: Database; getServerSession: () => Promise<Session | null>; log: Log }) =>
14-
async (cid: string) => {
14+
async ({ cid, name }: { cid: string; name: string }) => {
1515
try {
1616
const session = await getServerSession()
1717
if (isNil(session)) {
@@ -40,7 +40,7 @@ export const _insert =
4040
})
4141
}
4242

43-
const [result] = await connection.insertAsset({ userId, cid, ownerId: user.issuerId })
43+
const [result] = await connection.insertAsset({ userId, cid, name, ownerId: user.issuerId })
4444
return result
4545
} catch (error: unknown) {
4646
log.error(formatError(error))

apps/envited.ascs.digital/common/types/index.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
export { Language, AssetStatus, ButtonType, Columns, Size, ColorScheme, Role, CredentialType, FileType } from './types'
1+
export {
2+
Language,
3+
AssetAction,
4+
AssetStatus,
5+
ButtonType,
6+
Columns,
7+
Size,
8+
ColorScheme,
9+
Role,
10+
CredentialType,
11+
FileType,
12+
} from './types'
213
export type {
314
Action,
415
Asset,

0 commit comments

Comments
 (0)