Skip to content

Commit

Permalink
feat: backpack v2 endpoints for explorer (#141)
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrotambo authored May 5, 2023
1 parent a31e628 commit 545a650
Show file tree
Hide file tree
Showing 41 changed files with 3,360 additions and 835 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"singleQuote": true,
"trailingComma": "none",
"tabWidth": 2
}]
}],
"func-style": ["error", "declaration", { "allowArrowFunctions": false }]
}
}
25 changes: 25 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"files.exclude": {
"data*": true,
// "dist": true
},
"typescript.tsdk": "node_modules/typescript/lib",
"editor.formatOnSave": true,
"cSpell.words": [
"Ethereum"
],
"editor.rulers": [
120
],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.organizeImports": true
},
"eslint.validate": [
"typescript"
],
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"files.trimFinalNewlines": true,
"prettier.singleQuote": true
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "@dcl/lamb2",
"scripts": {
"build": "tsc -p tsconfig.json",
"build:watch": "tsc -p tsconfig.json --watch",
"lint:check": "eslint '**/*.{js,ts}'",
"lint:fix": "eslint '**/*.{js,ts}' --fix",
"start": "node --trace-warnings --abort-on-uncaught-exception --unhandled-rejections=strict dist/index.js",
Expand Down
2 changes: 1 addition & 1 deletion src/adapters/third-party-providers-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class ThirdPartyProviderFetcherError extends Error {
}
}

type ThirdPartyResolversQueryResults = {
export type ThirdPartyResolversQueryResults = {
thirdParties: ThirdParty[]
}

Expand Down
28 changes: 18 additions & 10 deletions src/components.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import { createDotEnvConfigComponent } from '@well-known-components/env-config-provider'
import { createServerComponent, createStatusCheckComponent, IFetchComponent } from '@well-known-components/http-server'
import { createLogComponent } from '@well-known-components/logger'
import { createFetchComponent } from './ports/fetch'
import { createMetricsComponent, instrumentHttpServerWithMetrics } from '@well-known-components/metrics'
import { AppComponents, GlobalContext } from './types'
import { metricDeclarations } from './metrics'
import { createTheGraphComponent, TheGraphComponent } from './ports/the-graph'
import { createContentComponent } from './ports/content'
import { createOwnershipCachesComponent } from './ports/ownership-caches'
import {
createEmoteDefinitionsFetcherComponent,
createWearableDefinitionsFetcherComponent
} from './adapters/definitions-fetcher'
import { createWearablesCachesComponent } from './controllers/handlers/old-wearables-handler'
import { createElementsFetcherComponent } from './adapters/elements-fetcher'
import { createThirdPartyProvidersFetcherComponent } from './adapters/third-party-providers-fetcher'
import { fetchAllThirdPartyWearables } from './logic/fetch-elements/fetch-third-party-wearables'
import { createWearablesCachesComponent } from './controllers/handlers/old-wearables-handler'
import { fetchAllEmotes, fetchAllWearables } from './logic/fetch-elements/fetch-items'
import { fetchAllNames } from './logic/fetch-elements/fetch-names'
import { fetchAllLANDs } from './logic/fetch-elements/fetch-lands'
import { fetchAllNames } from './logic/fetch-elements/fetch-names'
import { fetchAllThirdPartyWearables } from './logic/fetch-elements/fetch-third-party-wearables'
import { metricDeclarations } from './metrics'
import { createContentComponent } from './ports/content'
import { createFetchComponent } from './ports/fetch'
import { createOwnershipCachesComponent } from './ports/ownership-caches'
import { createTheGraphComponent, TheGraphComponent } from './ports/the-graph'
import { AppComponents, BaseWearable, GlobalContext } from './types'
import { fetchAllBaseWearables } from './logic/fetch-elements/fetch-base-items'

// Initialize all the components of the app
export async function initComponents(
Expand Down Expand Up @@ -49,10 +50,16 @@ export async function initComponents(
const ownershipCaches = await createOwnershipCachesComponent({ config })
const thirdPartyProvidersFetcher = createThirdPartyProvidersFetcherComponent({ logs, theGraph })
const thirdPartyWearablesFetcher = createElementsFetcherComponent({ logs }, async (address) =>
fetchAllThirdPartyWearables({ theGraph, thirdPartyProvidersFetcher, fetch, logs }, address)
fetchAllThirdPartyWearables(
{ theGraph, thirdPartyProvidersFetcher, fetch, logs, wearableDefinitionsFetcher },
address
)
)
const wearableDefinitionsFetcher = await createWearableDefinitionsFetcherComponent({ config, logs, content })
const emoteDefinitionsFetcher = await createEmoteDefinitionsFetcherComponent({ config, logs, content })
const baseWearablesFetcher = createElementsFetcherComponent<BaseWearable>({ logs }, async (_address) =>
fetchAllBaseWearables({ wearableDefinitionsFetcher })
)
const wearablesFetcher = createElementsFetcherComponent({ logs }, async (address) =>
fetchAllWearables({ theGraph }, address)
)
Expand All @@ -75,6 +82,7 @@ export async function initComponents(
content,
theGraph,
ownershipCaches,
baseWearablesFetcher,
wearablesFetcher,
wearableDefinitionsFetcher,
emoteDefinitionsFetcher,
Expand Down
91 changes: 46 additions & 45 deletions src/controllers/handlers/emotes-handler.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,60 @@
import { EmoteDefinition } from '@dcl/schemas'
import { FetcherError } from '../../adapters/elements-fetcher'
import { fetchAndPaginate, paginationObject } from '../../logic/pagination'
import { ErrorResponse, HandlerContextWithPath, Item, PaginatedResponse } from '../../types'
import { createSorting } from '../../logic/sorting'
import {
ErrorResponse,
HandlerContextWithPath,
OnChainEmote,
OnChainEmoteResponse,
PaginatedResponse
} from '../../types'
import { createFilters } from './items-commons'

// TODO: change this name
type ItemResponse = Item & {
definition?: EmoteDefinition
function mapItemToItemResponse(item: OnChainEmote, definition: EmoteDefinition | undefined): OnChainEmoteResponse {
return {
urn: item.urn,
amount: item.individualData.length,
individualData: item.individualData,
name: item.name,
category: item.category,
rarity: item.rarity,
definition
}
}

export async function emotesHandler(
context: HandlerContextWithPath<'logs' | 'emotesFetcher' | 'emoteDefinitionsFetcher', '/users/:address/emotes'>
): Promise<PaginatedResponse<ItemResponse> | ErrorResponse> {
const { logs, emoteDefinitionsFetcher, emotesFetcher } = context.components
context: HandlerContextWithPath<'emotesFetcher' | 'emoteDefinitionsFetcher', '/users/:address/emotes'>
): Promise<PaginatedResponse<OnChainEmoteResponse> | ErrorResponse> {
const { emoteDefinitionsFetcher, emotesFetcher } = context.components
const { address } = context.params
const logger = logs.getLogger('emotes-handler')
const includeDefinitions = context.url.searchParams.has('includeDefinitions')
const pagination = paginationObject(context.url)
const pagination = paginationObject(context.url, Number.MAX_VALUE)
const filter = createFilters(context.url)
const sorting = createSorting(context.url)

try {
const page = await fetchAndPaginate<ItemResponse>(address, emotesFetcher.fetchOwnedElements, pagination)
const page = await fetchAndPaginate<OnChainEmote>(
address,
emotesFetcher.fetchOwnedElements,
pagination,
filter,
sorting
)

if (includeDefinitions) {
const emotes = page.elements
const definitions = await emoteDefinitionsFetcher.fetchItemsDefinitions(emotes.map((emote) => emote.urn))
const results: ItemResponse[] = []
for (let i = 0; i < emotes.length; ++i) {
results.push({
...emotes[i],
definition: includeDefinitions ? definitions[i] : undefined
})
}
page.elements = results
}
const results: OnChainEmoteResponse[] = []
const emotes = page.elements
const definitions = includeDefinitions
? await emoteDefinitionsFetcher.fetchItemsDefinitions(emotes.map((emote) => emote.urn))
: []

return {
status: 200,
body: {
...page
}
}
} catch (err: any) {
if (err instanceof FetcherError) {
return {
status: 502,
body: {
error: 'Cannot fetch emotes right now'
}
}
}
logger.error(err)
return {
status: 500,
body: {
error: 'Internal Server Error'
}
for (let i = 0; i < emotes.length; ++i) {
results.push(mapItemToItemResponse(emotes[i], includeDefinitions ? definitions[i] : undefined))
}

return {
status: 200,
body: {
...page,
elements: results
}
}
}
40 changes: 40 additions & 0 deletions src/controllers/handlers/errorHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { IHttpServerComponent } from '@well-known-components/interfaces'
import { InvalidRequestError } from '../../types'
import { FetcherError } from '../../adapters/elements-fetcher'

export async function errorHandler(
ctx: IHttpServerComponent.DefaultContext<object>,
next: () => Promise<IHttpServerComponent.IResponse>
): Promise<IHttpServerComponent.IResponse> {
try {
return await next()
} catch (error: any) {
if (error instanceof InvalidRequestError) {
return Promise.resolve({
status: 400,
body: {
error: 'Bad request',
message: error.message
}
})
}

if (error instanceof FetcherError) {
return {
status: 502,
body: {
error: 'Internal Server Error',
message: error.message
}
}
}

console.log(`Error handling ${ctx.url.toString()}: ${error.message}`)
return {
status: 500,
body: {
error: 'Internal Server Error'
}
}
}
}
Loading

0 comments on commit 545a650

Please sign in to comment.