Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 175 additions & 71 deletions packages/client/.aegir.js
Original file line number Diff line number Diff line change
@@ -1,96 +1,200 @@
import EchoServer from 'aegir/echo-server'
import body from 'body-parser'
import EchoServer from "aegir/echo-server";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to remove stylistic changes here and in the rest of the PR.

You might need to git checkout main -- <pathToFile> and then discard everything except the changes that are needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay! lemme try it out👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file still has a lot of single quotes changed to double quote, and semicolons added...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i cannot understand, despite disabling autosave, eslint, why extra spaces, semicolons and double quotes are added. can you please guide me how to configure my vs-setup--I have downloaded this vsc extension--https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint--and then ran npm run lint -- --fix, even did git checkout main --, and tried minimal code changes, yet

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so, from the root of the project, because this is a monorepo, you actually have to run npm run lint -- -- --fix

import body from "body-parser";

/** @type {import('aegir').PartialOptions} */
const options = {
test: {
before: async () => {
let callCount = 0
let lastCalledUrl = ''
const providers = new Map()
const peers = new Map()
const ipnsGet = new Map()
const ipnsPut = new Map()
const echo = new EchoServer()
echo.polka.use(body.raw({ type: 'application/vnd.ipfs.ipns-record'}))
echo.polka.use(body.text())
let callCount = 0;
let lastCalledUrl = "";
const providers = new Map();
const peers = new Map();
const ipnsGet = new Map();
const ipnsPut = new Map();
const echo = new EchoServer();
echo.polka.use(body.raw({ type: "application/vnd.ipfs.ipns-record" }));
echo.polka.use(body.text());
echo.polka.use(body.json());
echo.polka.use((req, res, next) => {
next()
lastCalledUrl = req.url
})
next();
lastCalledUrl = req.url;
});
// echo.polka.post("/add-providers/:cid", (req, res) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah!

// callCount++;
// try {
// console.log("Received POST request body:", req.body);
// console.log("Content-Type:", req.headers["content-type"]);

// if (req.headers["content-type"]?.includes("application/json")) {
// const data =
// typeof req.body === "string"
// ? {
// Providers: req.body
// .split("\n")
// .map((line) => JSON.parse(line)),
// }
// : req.body;
// providers.set(req.params.cid, data);
// res.end(JSON.stringify({ success: true }));
// } else {
// res.statusCode = 400;
// res.end(
// JSON.stringify({
// error: "Invalid content type. Expected application/json",
// code: "ERR_INVALID_INPUT",
// })
// );
// providers.delete(req.params.cid);
// }
// } catch (err) {
// console.error("Error in add-providers:", err);
// res.statusCode = 400;
// res.end(
// JSON.stringify({
// error: err.message,
// code: "ERR_INVALID_INPUT",
// })
// );
// providers.delete(req.params.cid);
// }
// });
echo.polka.post('/add-providers/:cid', (req, res) => {
callCount++
providers.set(req.params.cid, req.body)
res.end()
try {
if (!req.headers['content-type']?.includes('application/json')) {
res.statusCode = 400
res.end(JSON.stringify({
error: 'Invalid content type. Expected application/json',
code: 'ERR_INVALID_INPUT'
}))
providers.delete(req.params.cid)
return
}

const data = typeof req.body === 'string'
? { Providers: req.body.split('\n').map(line => JSON.parse(line)) }
: req.body

providers.set(req.params.cid, data)
res.end(JSON.stringify({ success: true }))
} catch (err) {
console.error('Error in add-providers:', err)
res.statusCode = 400
res.end(JSON.stringify({
error: err.message,
code: 'ERR_INVALID_INPUT'
}))
providers.delete(req.params.cid)
}
})
echo.polka.get('/routing/v1/providers/:cid', (req, res) => {
callCount++
const records = providers.get(req.params.cid) ?? '[]'
providers.delete(req.params.cid)
res.end(records)
try {
const providerData = providers.get(req.params.cid) || { Providers: [] }
const acceptHeader = req.headers.accept

if (acceptHeader?.includes('application/x-ndjson')) {
res.setHeader('Content-Type', 'application/x-ndjson')
const providers = Array.isArray(providerData.Providers) ? providerData.Providers : []
res.end(providers.map(p => JSON.stringify(p)).join('\n'))
} else {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify(providerData))
}
} catch (err) {
console.error('Error in get providers:', err)
res.statusCode = 500
res.end(JSON.stringify({ error: err.message }))
}
})
echo.polka.post('/add-peers/:peerId', (req, res) => {
callCount++
peers.set(req.params.peerId, req.body)
res.end()
})
echo.polka.get('/routing/v1/peers/:peerId', (req, res) => {
callCount++
const records = peers.get(req.params.peerId) ?? '[]'
peers.delete(req.params.peerId)
// echo.polka.get("/routing/v1/providers/:cid", (req, res) => {
// callCount++;
// try {
// console.log("GET request for CID:", req.params.cid);
// console.log("Accept header:", req.headers.accept);

res.end(records)
})
echo.polka.post('/add-ipns/:peerId', (req, res) => {
callCount++
ipnsGet.set(req.params.peerId, req.body)
res.end()
})
echo.polka.get('/routing/v1/ipns/:peerId', (req, res) => {
callCount++
const record = ipnsGet.get(req.params.peerId) ?? ''
ipnsGet.delete(req.params.peerId)
// const providerData = providers.get(req.params.cid) || {
// Providers: [],
// };
// const acceptHeader = req.headers.accept;
// if (acceptHeader?.includes("application/x-ndjson")) {
// res.setHeader("Content-Type", "application/x-ndjson");
// const providers = Array.isArray(providerData.Providers)
// ? providerData.Providers
// : [];
// res.end(providers.map((p) => JSON.stringify(p)).join("\n"));
// } else {
// res.setHeader("Content-Type", "application/json");
// res.end(JSON.stringify(providerData));
// }
// } catch (err) {
// console.error("Error in get providers:", err);
// res.statusCode = 500;
// res.end(JSON.stringify({ error: err.message }));
// }
// });
echo.polka.post("/add-peers/:peerId", (req, res) => {
callCount++;
peers.set(req.params.peerId, req.body);
res.end();
});
echo.polka.get("/routing/v1/peers/:peerId", (req, res) => {
callCount++;
const records = peers.get(req.params.peerId) ?? "[]";
peers.delete(req.params.peerId);

res.end(record)
})
echo.polka.put('/routing/v1/ipns/:peerId', (req, res) => {
callCount++
ipnsPut.set(req.params.peerId, req.body)
res.end()
})
echo.polka.get('/get-ipns/:peerId', (req, res) => {
callCount++
const record = ipnsPut.get(req.params.peerId) ?? ''
ipnsPut.delete(req.params.peerId)
res.end(records);
});
echo.polka.post("/add-ipns/:peerId", (req, res) => {
callCount++;
ipnsGet.set(req.params.peerId, req.body);
res.end();
});
echo.polka.get("/routing/v1/ipns/:peerId", (req, res) => {
callCount++;
const record = ipnsGet.get(req.params.peerId) ?? "";
ipnsGet.delete(req.params.peerId);

res.end(record)
})
echo.polka.get('/get-call-count', (req, res) => {
res.end(callCount.toString())
})
echo.polka.get('/reset-call-count', (req, res) => {
callCount = 0
res.end()
})
echo.polka.get('/last-called-url', (req, res) => {
res.end(lastCalledUrl)
})
res.end(record);
});
echo.polka.put("/routing/v1/ipns/:peerId", (req, res) => {
callCount++;
ipnsPut.set(req.params.peerId, req.body);
res.end();
});
echo.polka.get("/get-ipns/:peerId", (req, res) => {
callCount++;
const record = ipnsPut.get(req.params.peerId) ?? "";
ipnsPut.delete(req.params.peerId);

res.end(record);
});
echo.polka.get("/get-call-count", (req, res) => {
res.end(callCount.toString());
});
echo.polka.get("/reset-call-count", (req, res) => {
callCount = 0;
res.end();
});
echo.polka.get("/last-called-url", (req, res) => {
res.end(lastCalledUrl);
});

await echo.start()
await echo.start();

return {
env: {
ECHO_SERVER: `http://${echo.host}:${echo.port}`
ECHO_SERVER: `http://${echo.host}:${echo.port}`,
},
echo
}
echo,
};
},
after: async (_, beforeResult) => {
if (beforeResult.echo != null) {
await beforeResult.echo.stop()
await beforeResult.echo.stop();
}
}
}
}
},
},
};

export default options
export default options;
1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
"@libp2p/logger": "^5.0.1",
"@libp2p/peer-id": "^5.0.1",
"@multiformats/multiaddr": "^12.3.1",
"@playwright/test": "^1.50.1",
"any-signal": "^4.1.1",
"browser-readablestream-to-it": "^2.0.7",
"ipns": "^10.0.0",
Expand Down
30 changes: 21 additions & 9 deletions packages/client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,24 +122,34 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
const getOptions = { headers: { Accept: 'application/x-ndjson' }, signal }
const res = await this.#makeRequest(url.toString(), getOptions)

if (res.status === 404) {
if (res == null) {
throw new BadResponseError('No response received')
}
if (!res.ok) {
if (res.status === 404) {
// https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes
// 404 (Not Found): must be returned if no matching records are found
throw new NotFoundError('No matching records found')
}
throw new NotFoundError('No matching records found')
}

if (res.status === 422) {
if (res.status === 422) {
// https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes
// 422 (Unprocessable Entity): request does not conform to schema or semantic constraints
throw new InvalidRequestError('Request does not conform to schema or semantic constraints')
throw new InvalidRequestError('Request does not conform to schema or semantic constraints')
}
throw new BadResponseError(`Unexpected status code: ${res.status}`)
}

if (res.body == null) {
throw new BadResponseError('Routing response had no body')
}

const contentType = res.headers.get('Content-Type')
if (contentType === 'application/json') {
if (contentType == null) {
throw new BadResponseError('No Content-Type header received')
}

if (contentType?.startsWith('application/json')) {
const body = await res.json()

for (const provider of body.Providers) {
Expand All @@ -148,16 +158,19 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
yield record
}
}
} else {
} else if (contentType.includes('application/x-ndjson')) {
for await (const provider of ndjson(toIt(res.body))) {
const record = this.#conformToPeerSchema(provider)
if (record != null) {
yield record
}
}
} else {
throw new BadResponseError(`Unsupported Content-Type: ${contentType}`)
}
} catch (err) {
log.error('getProviders errored:', err)
throw err
} finally {
signal.clear()
onFinish.resolve()
Expand Down Expand Up @@ -384,10 +397,9 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
const requestMethod = options.method ?? 'GET'
const key = `${requestMethod}-${url}`

// Only try to use cache for GET requests
if (requestMethod === 'GET') {
const cachedResponse = await this.cache?.match(url)
if (cachedResponse != null) {
if (cachedResponse?.ok === true) {
// Check if the cached response has expired
const expires = parseInt(cachedResponse.headers.get('x-cache-expires') ?? '0', 10)
if (expires > Date.now()) {
Expand Down
Loading
Loading