Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
215 changes: 138 additions & 77 deletions packages/client/.aegir.js
Original file line number Diff line number Diff line change
@@ -1,96 +1,157 @@
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
})
echo.polka.post('/add-providers/:cid', (req, res) => {
callCount++
providers.set(req.params.cid, req.body)
res.end()
})
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)
})
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)
next();
lastCalledUrl = req.url;
});

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)
echo.polka.post("/add-providers/:cid", (req, res) => {
callCount++;
try {
console.log("Received POST request body:", req.body);
console.log("Content-Type:", req.headers["content-type"]);

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)
// Only process if content-type is application/json
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",
})
);
// Clear any existing providers for this CID
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);
}
});

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)
})
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);

await echo.start()
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(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.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();

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
26 changes: 18 additions & 8 deletions packages/client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,24 +122,35 @@ 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 Down Expand Up @@ -384,10 +395,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