Skip to content
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

refactor: replace got with ky #7479

Merged
merged 2 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
58 changes: 29 additions & 29 deletions packages/config/maintenance/importConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,56 +182,56 @@ function updateNumberOrDefault(

/** Retrieves the list of database IDs from the OpenSmartHouse DB */
async function fetchIDsOH(): Promise<number[]> {
const { got } = await import("got");
const data = (await got.get(ohUrlIDs).json()) as any;
const { default: ky } = await import("ky");
const data = (await ky.get(ohUrlIDs).json()) as any;
return data.devices.map((d: any) => d.id);
}

/** Retrieves the definition for a specific device from the OpenSmartHouse DB */
async function fetchDeviceOH(id: number): Promise<string> {
const { got } = await import("got");
const source = (await got.get(ohUrlDevice(id)).json()) as any;
const { default: ky } = await import("ky");
const source = (await ky.get(ohUrlDevice(id)).json()) as any;
return stringify(source, "\t");
}

/** Retrieves the definition for a specific device from the Z-Wave Alliance DB */
async function fetchDeviceZWA(id: number): Promise<string> {
const { got } = await import("got");
const source = (await got.get(zwaUrlDevice(id)).json()) as any;
const { default: ky } = await import("ky");
const source = (await ky.get(zwaUrlDevice(id)).json()) as any;
return stringify(source, "\t");
}

/** Downloads ozw master archive and store it on `tmpDir` */
async function downloadOZWConfig(): Promise<string> {
console.log("downloading ozw archive...");
const { got } = await import("got");
const { default: ky } = await import("ky");

// create tmp directory if missing
await fs.mkdir(ozwTempDir, { recursive: true });

// this will return a stream in `data` that we pipe into write stream
// to store the file in `tmpDir`
const data = got.stream.get(ozwTarUrl);

return new Promise(async (resolve, reject) => {
let fileHandle: fs.FileHandle | undefined;
try {
// Create a stream to write the file
const fileDest = path.join(ozwTempDir, ozwTarName);
const handle = await fs.open(fileDest, "w");
const stream = handle.createWriteStream();
data.pipe(stream);
let hasError = false;
stream.on("error", (err) => {
hasError = true;
stream.close();
reject(err);
fileHandle = await fs.open(fileDest, "w");
const writable = new WritableStream({
async write(chunk) {
await fileHandle!.write(chunk);
},
});

stream.on("close", () => {
if (!hasError) {
resolve(fileDest);
console.log("ozw archive stored in temporary directory");
}
});
});
// And pipe the response into the stream
const response = await ky.get(ozwTarUrl);
await response.body?.pipeTo(writable);

console.log("ozw archive stored in temporary directory");
return fileDest;
} finally {
await fileHandle?.close();
}
}

/** Extract `config` folder from ozw archive in `tmpDir` */
Expand Down Expand Up @@ -1701,15 +1701,15 @@ async function retrieveZWADeviceIds(
highestDeviceOnly: boolean = true,
manufacturer: number[] = [-1],
): Promise<number[]> {
const { got } = await import("got");
const { default: ky } = await import("ky");
const deviceIdsSet = new Set<number>();

for (const manu of manufacturer) {
let page = 1;
// Page 1
let currentUrl =
`https://products.z-wavealliance.org/search/DoAdvancedSearch?productName=&productIdentifier=&productDescription=&category=-1&brand=${manu}&regionId=-1&order=&page=${page}`;
const firstPage = await got.get(currentUrl).text();
const firstPage = await ky.get(currentUrl).text();
for (const i of firstPage.match(/(?<=productId=).*?(?=[\&\"])/g)!) {
deviceIdsSet.add(i);
}
Expand All @@ -1730,7 +1730,7 @@ async function retrieveZWADeviceIds(
);
currentUrl =
`https://products.z-wavealliance.org/search/DoAdvancedSearch?productName=&productIdentifier=&productDescription=&category=-1&brand=${manu}&regionId=-1&order=&page=${page}`;
const nextPage = await got.get(currentUrl).text();
const nextPage = await ky.get(currentUrl).text();
const nextPageIds = nextPage.match(
/(?<=productId=).*?(?=[\&\"])/g,
)!;
Expand Down Expand Up @@ -1814,8 +1814,8 @@ async function downloadDevicesOH(IDs?: number[]): Promise<void> {
async function downloadManufacturersOH(): Promise<void> {
process.stdout.write("Fetching manufacturers...");

const { got } = await import("got");
const data = await got.get(ohUrlManufacturers).json();
const { default: ky } = await import("ky");
const data = await ky.get(ohUrlManufacturers).json();

// Delete the last line
process.stdout.write("\r\x1b[K");
Expand Down
2 changes: 1 addition & 1 deletion packages/config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@
"comment-json": "^4.2.5",
"del-cli": "^6.0.0",
"es-main": "^1.3.0",
"got": "^13.0.0",
"js-levenshtein": "^1.1.6",
"ky": "^1.7.2",
"peggy": "^3.0.2",
"proxyquire": "^2.1.3",
"sinon": "^19.0.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/zwave-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
"alcalzone-shared": "^5.0.0",
"ansi-colors": "^4.1.3",
"execa": "^5.1.1",
"got": "^13.0.0",
"ky": "^1.7.2",
"mdns-server": "^1.0.11",
"p-queue": "^8.0.1",
"proper-lockfile": "^4.1.2",
Expand Down
57 changes: 29 additions & 28 deletions packages/zwave-js/src/lib/controller/FirmwareUpdateService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
guessFirmwareFileFormat,
} from "@zwave-js/core";
import { Bytes, formatId } from "@zwave-js/shared";
import type { Headers, OptionsOfTextResponseBody } from "got";
import type { Options as KyOptions } from "ky";
import type PQueue from "p-queue";
import type {
FirmwareUpdateDeviceID,
Expand Down Expand Up @@ -57,17 +57,14 @@ function cleanCache() {
}
}

async function cachedGot<T>(config: OptionsOfTextResponseBody): Promise<T> {
// Replaces got's built-in cache functionality because it uses Keyv internally
// which apparently has some issues: https://github.com/zwave-js/node-zwave-js/issues/5404

async function cachedRequest<T>(url: string, config: KyOptions): Promise<T> {
const hash = Bytes.view(
await digest(
"sha-256",
Bytes.from(JSON.stringify(config.json)),
),
).toString("hex");
const cacheKey = `${config.method}:${config.url!.toString()}:${hash}`;
const cacheKey = `${config.method}:${url}:${hash}`;

// Return cached requests if they are not stale yet
if (requestCache.has(cacheKey)) {
Expand All @@ -77,13 +74,15 @@ async function cachedGot<T>(config: OptionsOfTextResponseBody): Promise<T> {
}
}

const { got } = await import("got");
const response = await got(config);
const responseJson = JSON.parse(response.body) as T;
const { default: ky } = await import("ky");
const response = await ky(url, config);
const responseJson = await response.json<T>();

// Check if we can cache the response
if (response.statusCode === 200 && response.headers["cache-control"]) {
const cacheControl = response.headers["cache-control"];
if (response.status === 200 && response.headers.has("cache-control")) {
const cacheControl = response.headers.get("cache-control")!;
const age = response.headers.get("age");
const date = response.headers.get("date");

let maxAge: number | undefined;
const maxAgeMatch = cacheControl.match(/max-age=(\d+)/);
Expand All @@ -93,10 +92,10 @@ async function cachedGot<T>(config: OptionsOfTextResponseBody): Promise<T> {

if (maxAge) {
let currentAge: number;
if (response.headers.age) {
currentAge = parseInt(response.headers.age, 10);
} else if (response.headers.date) {
currentAge = (Date.now() - Date.parse(response.headers.date))
if (age) {
currentAge = parseInt(age, 10);
} else if (date) {
currentAge = (Date.now() - Date.parse(date))
/ 1000;
} else {
currentAge = 0;
Expand Down Expand Up @@ -173,12 +172,12 @@ export async function getAvailableFirmwareUpdates(
deviceId: FirmwareUpdateDeviceID,
options: GetAvailableFirmwareUpdateOptions,
): Promise<FirmwareUpdateInfo[]> {
const headers: Headers = {
const headers = new Headers({
"User-Agent": options.userAgent,
"Content-Type": "application/json",
};
});
if (options.apiKey) {
headers["X-API-Key"] = options.apiKey;
headers.set("X-API-Key", options.apiKey);
}

const body: Record<string, string> = {
Expand All @@ -195,9 +194,9 @@ export async function getAvailableFirmwareUpdates(
// Prereleases and/or RF region-specific updates are only available in v3
const apiVersion = options.includePrereleases || !!rfRegion ? "v3" : "v1";

const config: OptionsOfTextResponseBody = {
const url = `${serviceURL()}/api/${apiVersion}/updates`;
const config: KyOptions = {
method: "POST",
url: `${serviceURL()}/api/${apiVersion}/updates`,
json: body,
// Consider re-enabling this instead of using cachedGot()
// At the moment, the built-in caching has some issues though, so we stick
Expand All @@ -216,7 +215,7 @@ export async function getAvailableFirmwareUpdates(
}
// Weird types...
const result = (
await requestQueue.add(() => cachedGot(config))
await requestQueue.add(() => cachedRequest(url, config))
) as FirmwareUpdateServiceResponse[];

// Remember the device ID in the response, so we can use it later
Expand All @@ -242,14 +241,13 @@ export async function downloadFirmwareUpdate(
// TODO: Make request abort-able (requires AbortController, Node 14.17+ / Node 16)

// Download the firmware file
const { got } = await import("got");
const downloadResponse = await got.get(file.url, {
timeout: { request: DOWNLOAD_TIMEOUT },
responseType: "buffer",
const { default: ky } = await import("ky");
const downloadResponse = await ky.get(file.url, {
timeout: DOWNLOAD_TIMEOUT,
// TODO: figure out how to do maxContentLength: MAX_FIRMWARE_SIZE,
});

const rawData = downloadResponse.body;
const rawData = new Uint8Array(await downloadResponse.arrayBuffer());

const requestedPathname = new URL(file.url).pathname;
// The response may be redirected, so the filename information may be different
Expand All @@ -263,12 +261,15 @@ export async function downloadFirmwareUpdate(

// Infer the file type from the content-disposition header or the filename
let filename: string;
const contentDisposition = downloadResponse.headers.get(
"content-disposition",
);
if (
downloadResponse.headers["content-disposition"]?.startsWith(
contentDisposition?.startsWith(
"attachment; filename=",
)
) {
filename = downloadResponse.headers["content-disposition"]
filename = contentDisposition
.split("filename=")[1]
.replace(/^"/, "")
.replace(/[";]$/, "");
Expand Down
27 changes: 15 additions & 12 deletions packages/zwave-js/src/lib/driver/UpdateConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ import semverMaxSatisfying from "semver/ranges/max-satisfying.js";
export async function checkForConfigUpdates(
currentVersion: string,
): Promise<string | undefined> {
const { got } = await import("got");
const { default: ky } = await import("ky");
let registry: Record<string, unknown>;

try {
registry = await got
registry = await ky
.get("https://registry.npmjs.org/@zwave-js/config")
.json();
} catch {
Expand Down Expand Up @@ -66,11 +66,11 @@ export async function installConfigUpdate(
cacheDir: string;
},
): Promise<void> {
const { got } = await import("got");
const { default: ky } = await import("ky");

let registryInfo: any;
try {
registryInfo = await got
registryInfo = await ky
.get(`https://registry.npmjs.org/@zwave-js/config/${newVersion}`)
.json();
} catch {
Expand Down Expand Up @@ -137,16 +137,17 @@ export async function installConfigUpdate(

// Download the package tarball into the temporary directory
const tarFilename = path.join(tmpDir, "zjs-config-update.tgz");
let fileHandle: fs.FileHandle | undefined;
try {
const handle = await fs.open(tarFilename, "w");
const fstream = handle.createWriteStream({ autoClose: true });
const response = got.stream.get(url);
response.pipe(fstream);

await new Promise((resolve, reject) => {
response.on("error", reject);
response.on("end", resolve);
fileHandle = await fs.open(tarFilename, "w");
const writable = new WritableStream({
async write(chunk) {
await fileHandle!.write(chunk);
},
});

const response = await ky.get(url);
await response.body?.pipeTo(writable);
} catch (e) {
await freeLock();
throw new ZWaveError(
Expand All @@ -157,6 +158,8 @@ export async function installConfigUpdate(
}`,
ZWaveErrorCodes.Config_Update_InstallFailed,
);
} finally {
await fileHandle?.close();
}

// This should not be necessary in Docker. Leaving it here anyways in case
Expand Down
4 changes: 2 additions & 2 deletions packages/zwave-js/src/lib/telemetry/deviceConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ export async function reportMissingDeviceConfig(
// // If we tried to report this file earlier, we can skip the report
// if (missingDeviceConfigCache.has(configFingerprint)) return;
// // Otherwise ask our device DB if it exists
// const { got } = await import("got");
// const { default: ky } = await import("ky");
// try {
// const data = await got
// const data = await ky
// .get(
// `https://devices.zwave-js.io/public_api/getdeviceinfo/${configFingerprint.replace(
// /:/g,
Expand Down
4 changes: 2 additions & 2 deletions packages/zwave-js/src/lib/telemetry/statistics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ export async function compileStatistics(
export async function sendStatistics(
statistics: Record<string, any>,
): Promise<boolean | number> {
const { got } = await import("got");
const { default: ky } = await import("ky");

try {
const data = await got
const data = await ky
.post(statisticsUrl, {
json: { data: [statistics] },
headers: { "x-api-token": apiToken },
Expand Down
Loading
Loading