From fbf2436b43e527074143d83a0ffcf6fcd1fb29eb Mon Sep 17 00:00:00 2001 From: Alex Vernacchia Date: Mon, 16 Dec 2024 12:07:48 +0000 Subject: [PATCH 1/2] feat(api-v1.1): enable publish API v1.1 --- src/index.ts | 64 +++++++++++++++++----------------------------------- 1 file changed, 21 insertions(+), 43 deletions(-) diff --git a/src/index.ts b/src/index.ts index 66568ea5..daa8ee24 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,12 +3,8 @@ import got, { type OptionsOfTextResponseBody } from "got" export type Options = { productId: string - clientId: string - clientSecret: string - - accessTokenUrl: string - + apiKey: string; uploadOnly?: boolean } @@ -43,10 +39,8 @@ export const errorMap = { "Product ID is required. To get one, go to: https://partner.microsoft.com/en-us/dashboard/microsoftedge/{product-id}/package/dashboard", clientId: "Client ID is required. To get one: https://partner.microsoft.com/en-us/dashboard/microsoftedge/publishapi", - clientSecret: - "Client Secret is required. To get one: https://partner.microsoft.com/en-us/dashboard/microsoftedge/publishapi", - accessTokenUrl: - "Access token URL is required. To get one: https://partner.microsoft.com/en-us/dashboard/microsoftedge/publishapi" + apiKey: + "API Key is required. To get one: https://partner.microsoft.com/en-us/dashboard/microsoftedge/publishapi", } export const requiredFields = Object.keys(errorMap) as Array< @@ -84,28 +78,23 @@ export class EdgeAddonsAPI { * @returns the publish operation id */ async submit({ filePath = "", notes = "" }) { - const accessToken = await this.getAccessToken() - const uploadResp = await this.upload( - createReadStream(filePath), - accessToken + createReadStream(filePath) ) - await this.waitForUpload(uploadResp, accessToken) + await this.waitForUpload(uploadResp) if (this.options.uploadOnly) { return } - return this.publish(notes, accessToken) + return this.publish(notes) } - async publish(notes = "", _accessToken = null as string) { - const accessToken = _accessToken || (await this.getAccessToken()) - + async publish(notes = "") { const options = { headers: { - Authorization: `Bearer ${accessToken}`, + ...this.getPublishApiDefaultHeaders(), "Content-Type": "application/x-www-form-urlencoded" } } as OptionsOfTextResponseBody @@ -121,13 +110,11 @@ export class EdgeAddonsAPI { return publishResp.headers.location } - async upload(readStream = null as ReadStream, _accessToken = null as string) { - const accessToken = _accessToken || (await this.getAccessToken()) - + async upload(readStream = null as ReadStream) { const uploadResp = await got.post(this.uploadEndpoint, { body: readStream, headers: { - Authorization: `Bearer ${accessToken}`, + ...this.getPublishApiDefaultHeaders(), "Content-Type": "application/zip" } }) @@ -137,26 +124,21 @@ export class EdgeAddonsAPI { return uploadResp.headers.location } - async getPublishStatus(operationId: string, _accessToken = null as string) { - const accessToken = _accessToken || (await this.getAccessToken()) + async getPublishStatus(operationId: string) { const statusEndpoint = `${this.publishEndpoint}/operations/${operationId}` return got .get(statusEndpoint, { - headers: { - Authorization: `Bearer ${accessToken}` - } + headers: this.getPublishApiDefaultHeaders() }) .json() } async waitForUpload( operationId: string, - _accessToken = null as string, retryCount = 5, pollTime = 3000 ) { - const accessToken = _accessToken || (await this.getAccessToken()) const statusEndpoint = `${this.uploadEndpoint}/operations/${operationId}` let successMessage: string @@ -167,9 +149,7 @@ export class EdgeAddonsAPI { while (uploadStatus !== "Succeeded" && attempts < retryCount) { const statusResp = await got .get(statusEndpoint, { - headers: { - Authorization: `Bearer ${accessToken}` - } + headers: this.getPublishApiDefaultHeaders() }) .json() @@ -202,16 +182,14 @@ export class EdgeAddonsAPI { } } - getAccessToken = async () => { - const data = await got - .post(`${this.options.accessTokenUrl}`, { - body: `client_id=${this.options.clientId}&scope=${baseApiUrl}/.default&client_secret=${this.options.clientSecret}&grant_type=client_credentials`, - headers: { - "Content-Type": "application/x-www-form-urlencoded" - } - }) - .json() + private getPublishApiDefaultHeaders() { + // All requests need the following headers (https://learn.microsoft.com/en-us/microsoft-edge/extensions-chromium/publish/api/using-addons-api?tabs=v1-1): + // - Authorization: ApiKey $ApiKey; + // - X-ClientID: $ClientID; - return data.access_token + return { + Authorization: `ApiKey ${this.options.apiKey}`, + 'X-ClientID': this.options.clientId + } } } From a0b14cc9d0203c35f1f4d272fe1b218a401bae1f Mon Sep 17 00:00:00 2001 From: Alex Vernacchia Date: Tue, 17 Dec 2024 09:11:28 +0000 Subject: [PATCH 2/2] feat(api-v1.1): update readme --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fe15b209..0d5fc016 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ npm install --save-dev @plasmohq/edge-addons-api ### Authentication -You'll need to get a `productId`, `clientId`, `clientSecret`, and `accessTokenUrl` for your project. +You'll need to get a `productId`, `clientId`, and `apiKey` for your project. You can get these for your project by following the [Microsoft Edge Add-Ons API guide](https://docs.microsoft.com/en-us/microsoft-edge/extensions-chromium/publish/api/using-addons-api). @@ -59,8 +59,7 @@ import { EdgeAddonsAPI } from "@plasmohq/edge-addons-api" const client = new EdgeAddonsAPI({ productId, clientId, - clientSecret, - accessTokenUrl + apiKey }) await client.submit({