Skip to content

Commit

Permalink
Merge pull request #50 from decentraland/feat/add-land-router-client-…
Browse files Browse the repository at this point in the history
…functions

feat: Add lands router endpoint functions
  • Loading branch information
LautaroPetaccio committed Aug 27, 2022
2 parents 4777171 + 9acf70b commit 9b39d0c
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 0 deletions.
190 changes: 190 additions & 0 deletions src/client/BuilderClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
import {
GetNFTParams,
GetNFTsResponse,
LandHashes,
LandCoords,
NFT,
ServerResponse,
ThirdParty
Expand Down Expand Up @@ -928,3 +930,191 @@ describe('when getting a third party', () => {
})
})
})

describe('when creating the LAND redirection file', () => {
let url: string
let coords: { x: number; y: number }

beforeEach(() => {
coords = { x: 100, y: 400 }
url = `/v1/lands/${coords.x},${coords.y}/redirection`
})

describe('and the endpoint responds with non 200 status code', () => {
let response: ServerResponse<unknown>

beforeEach(() => {
response = {
ok: false,
data: {},
error: 'Some error'
}
nock(testUrl).post(url).reply(500, response)
})

it("should throw an error with the server's error message", () => {
return expect(
client.createLandRedirectionFile(coords, 'en-us')
).rejects.toEqual(new ClientError('Some error', 500, {}))
})
})

describe('and the data returned from the endpoint has ok as false', () => {
let response: ServerResponse<unknown>

beforeEach(() => {
response = {
ok: false,
data: {},
error: 'Some error'
}
nock(testUrl).post(url).reply(200, response)
})

it("should throw an error with the server's error message", () => {
return expect(
client.createLandRedirectionFile(coords, 'en-us')
).rejects.toEqual(new ClientError('Some error', 200, {}))
})
})

describe('and the endpoint responds with a 200 status code and the hashed data', () => {
let response: ServerResponse<LandHashes>

beforeEach(() => {
response = {
ok: true,
data: {
contentHash:
'e301017012205453e784584c205c23a771c67af071129721f0e21b0472e3061361005393a908',
ipfsHash: 'QmU1qAKrZKEUinZ7j7gbPcJ7dKSkJ6diHLk9YrB4PbLr7q'
}
}
nock(testUrl).post(url).reply(200, response)
})

it('should return the LAND hashes of the redirection file of the sent coordinates', () => {
return expect(
client.createLandRedirectionFile(coords, 'en-us')
).resolves.toEqual(response.data)
})
})
})

describe('when getting LAND redirection hashes', () => {
let url: string
let coords: LandCoords[]

describe("and the amount of cords don't overpass the URL length limit", () => {
let response: ServerResponse<(LandCoords & LandHashes)[]>

beforeEach(() => {
coords = [
{ x: 100, y: 400 },
{ x: 150, y: 450 }
]
url = `/v1/lands/redirectionHashes?${coords
.map((coord) => `coords=${coord.x},${coord.y}`)
.join('&')}`
response = {
ok: true,
data: coords.map((coord) => ({
x: coord.x,
y: coord.y,
contentHash: `${coord.x},${coord.y}-content-hash`,
ipfsHash: `${coord.x},${coord.y}-ipfs-hash`
}))
}
nock(testUrl).get(url).reply(200, response)
})

it('should respond with the hashes of each coord', () => {
return expect(
client.getLandRedirectionHashes(coords, 'en-us')
).resolves.toEqual(response.data)
})
})

describe('and the amount of cords overpass the URL length limit', () => {
let responses: ServerResponse<(LandCoords & LandHashes)[]>[]

beforeEach(() => {
coords = Array.from({ length: 200 }, (_, i) => ({ x: i, y: i }))
responses = [
{
ok: true,
data: coords.slice(0, 148).map((coord) => ({
x: coord.x,
y: coord.y,
contentHash: `${coord.x},${coord.y}-content-hash`,
ipfsHash: `${coord.x},${coord.y}-ipfs-hash`
}))
},
{
ok: true,
data: coords.slice(148).map((coord) => ({
x: coord.x,
y: coord.y,
contentHash: `${coord.x},${coord.y}-content-hash`,
ipfsHash: `${coord.x},${coord.y}-ipfs-hash`
}))
}
]

responses.forEach((response) => {
nock(testUrl)
.get(
`/v1/lands/redirectionHashes?${response.data
.map((coord) => `coords=${coord.x},${coord.y}`)
.join('&')}`
)
.reply(200, response)
})
})

it('should respond with the hashes of each coord by concatenating the response of multiple requests', () => {
return expect(
client.getLandRedirectionHashes(coords, 'en-us')
).resolves.toEqual(responses.flatMap((response) => response.data))
})
})

describe('and the endpoint responds with a non 200 status code', () => {
beforeEach(() => {
coords = Array.from({ length: 10 }, (_, i) => ({ x: i, y: i }))
url = `/v1/lands/redirectionHashes?${coords
.map((coord) => `coords=${coord.x},${coord.y}`)
.join('&')}`
nock(testUrl).get(url).reply(500, {})
})

it('should throw an error with the message of the failed request', () => {
return expect(
client.getLandRedirectionHashes(coords, 'en-us')
).rejects.toEqual(new ClientError('Unknown error', 500, null))
})
})

describe('and the endpoint responds with 200 status code but with ok as false', () => {
let response: ServerResponse<unknown>

beforeEach(() => {
response = {
ok: false,
data: {},
error: 'Some error'
}
coords = Array.from({ length: 10 }, (_, i) => ({ x: i, y: i }))
url = `/v1/lands/redirectionHashes?${coords
.map((coord) => `coords=${coord.x},${coord.y}`)
.join('&')}`
nock(testUrl).get(url).reply(200, response)
})

it('should throw an error with the message of the failed request', () => {
return expect(
client.getLandRedirectionHashes(coords, 'en-us')
).rejects.toEqual(new ClientError('Some error', 200, null))
})
})
})
81 changes: 81 additions & 0 deletions src/client/BuilderClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@ import {
GetNFTParams,
GetNFTsParams,
GetNFTsResponse,
LandCoords,
LandHashes,
NFT,
ServerResponse,
ThirdParty
} from './types'
import { URL_MAX_LENGTH } from './constants'

export class BuilderClient {
private fetch: (url: string, init?: RequestInit) => Promise<Response>
private readonly AUTH_CHAIN_HEADER_PREFIX = 'x-identity-auth-chain-'
private readonly getIdentity: () => AuthIdentity
private readonly getAddress: () => string
private readonly baseUrl: string

constructor(
url: string,
Expand All @@ -30,6 +34,8 @@ export class BuilderClient {
identity instanceof Function ? identity() : identity
this.getAddress = () => (address instanceof Function ? address() : address)

this.baseUrl = url

this.fetch = (path: string, init?: RequestInit) => {
const method: string = init?.method ?? path ?? 'get'
const fullUrl = url + path
Expand Down Expand Up @@ -318,4 +324,79 @@ export class BuilderClient {

return body.data
}

public async createLandRedirectionFile(
{ x, y }: LandCoords,
locale: string
): Promise<LandHashes> {
let res: Response

try {
res = await this.fetch(`/v1/lands/${x},${y}/redirection`, {
method: 'post',
headers: {
'accept-language': locale
}
})
} catch (e) {
throw new ClientError(e.message, undefined, null)
}

const body: ServerResponse<LandHashes> = await res.json()

if (!res.ok || !body.ok) {
throw new ClientError(body.error || 'Unknown error', res.status, null)
}

return body.data
}

public async getLandRedirectionHashes(
coordsList: LandCoords[],
locale: string
): Promise<(LandCoords & LandHashes)[]> {
const basePath = '/v1/lands/redirectionHashes'
const paths: string[] = []
let path = basePath

for (const [index, coord] of coordsList.entries()) {
const newPath = `${path}${index === 0 ? '?' : '&'}coords=${coord.x},${
coord.y
}`
if (newPath.length + this.baseUrl.length > URL_MAX_LENGTH) {
paths.push(path)
path = `${basePath}?coords=${coord.x},${coord.y}`
} else {
path = newPath
}
}
paths.push(path)

let output: (LandCoords & LandHashes)[] = []

for (const path of paths) {
let res: Response
console.log('Requesting a path', path)

try {
res = await this.fetch(path, {
headers: {
'accept-language': locale
}
})
} catch (e) {
throw new ClientError(e.message, undefined, null)
}

const body: ServerResponse<(LandCoords & LandHashes)[]> = await res.json()

if (!res.ok || !body.ok) {
throw new ClientError(body.error || 'Unknown error', res.status, null)
}

output = output.concat(body.data)
}

return output
}
}
1 change: 1 addition & 0 deletions src/client/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const URL_MAX_LENGTH = 2048
14 changes: 14 additions & 0 deletions src/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,17 @@ export type GetNFTParams = {
}

// END - Builder Server NFT

// START - Builder Server LAND

export type LandCoords = {
x: number
y: number
}

export type LandHashes = {
ipfsHash: string
contentHash: string
}

// END - Builder Server LAND

0 comments on commit 9b39d0c

Please sign in to comment.