-
Notifications
You must be signed in to change notification settings - Fork 17
fix: AssetPacks router #798
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
5593b41
2226ead
3326d7e
532ba8a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| import { ExpressApp } from '../common/ExpressApp' | ||
| import { AssetRouter } from './Asset.router' | ||
| import { Asset } from './Asset.model' | ||
| import { getDefaultEthAddress } from '../AssetPack/utils' | ||
|
|
||
| jest.mock('./Asset.model') | ||
| jest.mock('../AssetPack/utils') | ||
|
|
||
| const anOwnerAddress = 'anOwnerAddress' | ||
|
|
||
| const anAsset = { | ||
| id: 'anAssetId', | ||
| asset_pack_id: 'anAssetPackId', | ||
| name: 'anAsset', | ||
| model: 'model.glb', | ||
| category: 'decorations', | ||
| contents: {}, | ||
| tags: [], | ||
| metrics: { | ||
| triangles: 0, | ||
| materials: 0, | ||
| textures: 0, | ||
| meshes: 0, | ||
| bodies: 0, | ||
| entities: 0, | ||
| }, | ||
| } | ||
|
|
||
| const anotherAsset = { | ||
| id: 'anotherAssetId', | ||
| asset_pack_id: 'anotherAssetPackId', | ||
| name: 'anotherAsset', | ||
| model: 'another.glb', | ||
| category: 'decorations', | ||
| contents: {}, | ||
| tags: [], | ||
| metrics: { | ||
| triangles: 0, | ||
| materials: 0, | ||
| textures: 0, | ||
| meshes: 0, | ||
| bodies: 0, | ||
| entities: 0, | ||
| }, | ||
| } | ||
|
|
||
| describe('Asset router', () => { | ||
| let router: AssetRouter | ||
| let req: { | ||
| params: Record<string, string> | ||
| query: Record<string, unknown> | ||
| auth: { ethAddress: string } | ||
| } | ||
|
|
||
| beforeEach(() => { | ||
| router = new AssetRouter(new ExpressApp()) | ||
| req = { params: {}, query: {}, auth: { ethAddress: anOwnerAddress } } | ||
| }) | ||
|
|
||
| afterEach(() => { | ||
| jest.resetAllMocks() | ||
| }) | ||
|
|
||
| describe('when getting a single asset', () => { | ||
| beforeEach(() => { | ||
| req.params = { id: anAsset.id } | ||
| }) | ||
|
|
||
| describe('when the asset belongs to the caller', () => { | ||
| beforeEach(() => { | ||
| ;(Asset.findByIds as jest.Mock).mockResolvedValueOnce([anAsset]) | ||
| }) | ||
|
|
||
| it('should call findByIds with the caller and default addresses', async () => { | ||
| await (router as any).getAsset(req) | ||
| expect(Asset.findByIds).toHaveBeenCalledWith( | ||
| [anAsset.id], | ||
| [anOwnerAddress, getDefaultEthAddress()] | ||
| ) | ||
| }) | ||
|
|
||
| it('should return the asset', async () => { | ||
| const result = await (router as any).getAsset(req) | ||
| expect(result).toEqual(anAsset) | ||
| }) | ||
| }) | ||
|
|
||
| describe('when the asset does not belong to the caller or the default address', () => { | ||
| beforeEach(() => { | ||
| ;(Asset.findByIds as jest.Mock).mockResolvedValueOnce([]) | ||
| }) | ||
|
|
||
| it('should return null', async () => { | ||
| const result = await (router as any).getAsset(req) | ||
| expect(result).toBeNull() | ||
| }) | ||
| }) | ||
| }) | ||
|
|
||
| describe('when getting multiple assets', () => { | ||
| beforeEach(() => { | ||
| req.query = { id: [anAsset.id, anotherAsset.id] } | ||
| }) | ||
|
|
||
| describe('when all assets belong to the caller or the default address', () => { | ||
| beforeEach(() => { | ||
| ;(Asset.findByIds as jest.Mock).mockResolvedValueOnce([ | ||
| anAsset, | ||
| anotherAsset, | ||
| ]) | ||
| }) | ||
|
|
||
| it('should call findByIds with the caller and default addresses', async () => { | ||
| await (router as any).getAssets(req) | ||
| expect(Asset.findByIds).toHaveBeenCalledWith( | ||
| [anAsset.id, anotherAsset.id], | ||
| [anOwnerAddress, getDefaultEthAddress()] | ||
| ) | ||
| }) | ||
|
|
||
| it('should return all visible assets', async () => { | ||
| const result = await (router as any).getAssets(req) | ||
| expect(result).toEqual([anAsset, anotherAsset]) | ||
| }) | ||
| }) | ||
|
|
||
| describe('when some assets belong to another user', () => { | ||
| beforeEach(() => { | ||
| ;(Asset.findByIds as jest.Mock).mockResolvedValueOnce([anAsset]) | ||
| }) | ||
|
|
||
| it('should return only the visible assets', async () => { | ||
| const result = await (router as any).getAssets(req) | ||
| expect(result).toEqual([anAsset]) | ||
| }) | ||
| }) | ||
|
|
||
| describe('when all assets belong to another user', () => { | ||
| beforeEach(() => { | ||
| ;(Asset.findByIds as jest.Mock).mockResolvedValueOnce([]) | ||
| }) | ||
|
|
||
| it('should return an empty array', async () => { | ||
| const result = await (router as any).getAssets(req) | ||
| expect(result).toEqual([]) | ||
| }) | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,8 +10,11 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from '../middleware' | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getUploader, S3Content } from '../S3' | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { AssetPack } from '../AssetPack' | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getDefaultEthAddress } from '../AssetPack/utils' | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Asset } from './Asset.model' | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { withAuthentication } from '../middleware/authentication' | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { withAuthentication, AuthRequest } from '../middleware/authentication' | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const DEFAULT_ETH_ADDRESS = getDefaultEthAddress() | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export class AssetRouter extends Router { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mount() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -54,14 +57,20 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.router.get( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| '/assets/:id', | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| withCors, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| withAuthentication, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failureCode scanning / CodeQL Missing rate limiting High
This route handler performs
authorization Error loading related location Loading
Copilot AutofixAI 4 days ago In general, the fix is to introduce a rate-limiting middleware (for example, using In this file, the best approach without changing existing functionality is:
Concretely, within
Suggested changeset
2
src/Asset/Asset.router.ts
package.json
Outside changed files
This fix introduces these dependencies
Copilot is powered by AI and may make mistakes. Always verify output.
Refresh and try again.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| withAssetExists, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| server.handleRequest(this.getAsset) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Get a multiple assets | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.router.get('/assets', withCors, server.handleRequest(this.getAssets)) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.router.get( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| '/assets', | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| withCors, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| withAuthentication, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check failureCode scanning / CodeQL Missing rate limiting High
This route handler performs
authorization Error loading related location Loading
Copilot AutofixAI 4 days ago In general, to fix missing rate limiting on an Express route, you add a rate-limiting middleware (for example using For this specific code, the minimal, non-breaking fix is to (1) import
Suggested changeset
2
src/Asset/Asset.router.ts
package.json
Outside changed files
This fix introduces these dependencies
Copilot is powered by AI and may make mistakes. Always verify output.
Refresh and try again.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| server.handleRequest(this.getAssets) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async assetBelongsToPackMiddleware(req: Request) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -80,14 +89,20 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This handler is only here so `server.handleRequest` has a valid callback and it can return the appropiate formated response | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private getAsset(req: Request) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private async getAsset(req: AuthRequest) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const id = server.extractFromReq(req, 'id') | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Asset.findOne(id) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const eth_address = req.auth.ethAddress | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [asset] = await Asset.findByIds( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [id], | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [eth_address, DEFAULT_ETH_ADDRESS] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return asset ?? null | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private getAssets(req: Request) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private async getAssets(req: AuthRequest) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const reqIds = server.extractFromReq<string | string[]>(req, 'id') | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ids: string[] = Array.isArray(reqIds) ? reqIds : [reqIds] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Asset.findByIds(ids) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const eth_address = req.auth.ethAddress | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Asset.findByIds(ids, [eth_address, DEFAULT_ETH_ADDRESS]) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need the is_deleted condition here and not in the original query?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch, will add it to the original query, as we don't want to show deleted assets even using the id.