Skip to content

Fix fetch issues #197

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

Merged
merged 4 commits into from
Sep 18, 2023
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
22 changes: 3 additions & 19 deletions .github/workflows/lint.and.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ name: 🔎 Lint
on:
workflow_dispatch:
push:
branches-ignore: [gh-pages]
branches-ignore: [ gh-pages ]
pull_request:
branches-ignore: [gh-pages]
types: [opened, synchronize]
branches-ignore: [ gh-pages ]
types: [ opened, synchronize ]

jobs:
build:
Expand Down Expand Up @@ -38,19 +38,3 @@ jobs:

- name: 🔎 Lint
run: yarn lint


test:
name: ⚠️ Test
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: 16

- name: ⚡ Install dependencies
run: yarn install --frozen-lockfile

- name: ⚠️ Test
run: yarn test
186 changes: 0 additions & 186 deletions jest.config.ts

This file was deleted.

11 changes: 3 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,23 @@
"url": "https://github.com/netresearch/node-magento-eqp.git"
},
"types": "dist/index.d.ts",
"author": "TheDevMinerTV <tobigames200@gmail.com>",
"author": "DevMiner <devminer@devminer.xyz>",
"license": "MIT",
"scripts": {
"build": "yarn build:lib && yarn build:docs",
"build:lib": "tsc",
"build:lib:dev": "tsc -w",
"build:docs": "typedoc --out docs --entryPoints src/index.ts",
"lint": "eslint src tests --ext .ts",
"prepack": "yarn build:lib",
"test": "jest"
"lint": "eslint src --ext .ts",
"prepack": "yarn build:lib"
},
"devDependencies": {
"@types/jest": "^29.5.3",
"@types/node": "^8.10.66",
"@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0",
"axios-mock-adapter": "^1.21.5",
"eslint": "^8.45.0",
"eslint-config-prettier": "^9.0.0",
"jest": "^29.6.1",
"prettier": "^3.0.0",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typedoc": "^0.25.0",
"typescript": "^5.1.6"
Expand Down
57 changes: 18 additions & 39 deletions src/AuthenticatedAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,61 @@
import { AxiosRequestConfig } from 'axios';
import { Adapter } from './types/adapters';

export class AuthenticatedAdapter {
protected authenticated = false;
mageId?: string;
const replaceMageIdInURL = (url: string, mageId: string) => url.replace(/\|MAGE_ID\|/, mageId);
const addHeaders = (config: AxiosRequestConfig | undefined, headers: Record<string, string>) => ({ ...config, headers: { ...config?.headers, ...headers } });

export class AuthenticatedAdapter {
constructor(
protected readonly baseAdapter: Adapter,
protected readonly credentials: {
appSecret: string;
appId: string;
autoRefresh?: boolean;
tokenTTL?: number;
}
) {}

protected replaceMageIdInURL(url: string): string {
return url.replace(/\|MAGE_ID\|/, this.mageId as string);
}
) { }

async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
await this.authenticate();
const [mageId, headers] = await this.authenticate();

return this.baseAdapter.get(this.replaceMageIdInURL(url), config);
return this.baseAdapter.get(replaceMageIdInURL(url, mageId), addHeaders(config, headers));
}

async post<T>(url: string, body: unknown, config?: AxiosRequestConfig): Promise<T> {
await this.authenticate();
const [mageId, headers] = await this.authenticate();

return this.baseAdapter.post(this.replaceMageIdInURL(url), body, config);
return this.baseAdapter.post(replaceMageIdInURL(url, mageId), body, addHeaders(config, headers));
}

async put<T>(url: string, body: unknown, config?: AxiosRequestConfig): Promise<T> {
await this.authenticate();
const [mageId, headers] = await this.authenticate();

return this.baseAdapter.put(this.replaceMageIdInURL(url), body, config);
return this.baseAdapter.put(replaceMageIdInURL(url, mageId), body, addHeaders(config, headers));
}

async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
await this.authenticate();
const [mageId, headers] = await this.authenticate();

return this.baseAdapter.delete(this.replaceMageIdInURL(url), config);
return this.baseAdapter.delete(replaceMageIdInURL(url, mageId), addHeaders(config, headers));
}

async getMageId(): Promise<string> {
await this.authenticate();
const [mageId] = await this.authenticate();

return this.mageId as string;
return mageId;
}

/**
* Authenticate with the API. You can find the App ID and secret with the next link.
* @see https://developer.magento.com/account/apikeys
* @see https://devdocs.magento.com/marketplace/eqp/v1/auth.html#session-token
*/
protected async authenticate(): Promise<void> {
this.mageId = undefined;

const { expires_in, mage_id, ust } = await this.baseAdapter.post<{
protected async authenticate() {
const { mage_id, ust } = await this.baseAdapter.post<{
mage_id: string;
ust: string;
expires_in: number;
}>(
'/app/session/token',
{ grant_type: 'session', expires_in: this.credentials.tokenTTL ?? 7200 },
{ grant_type: 'session', expires_in: 360 },
{
auth: {
username: this.credentials.appId,
Expand All @@ -72,19 +64,6 @@ export class AuthenticatedAdapter {
}
);

this.mageId = mage_id;

this.baseAdapter.setHeader('Authorization', `Bearer ${ust}`);

if (this.credentials.autoRefresh) {
// Re-run this function 5 seconds before the token expires
setTimeout(async () => {
this.authenticated = false;

await this.authenticate();
}, expires_in * 1000 - 5);
}

this.authenticated = true;
return [mage_id, { authorization: `Bearer ${ust}` }] as const;
}
}
18 changes: 10 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,22 @@ export class EQP {
readonly reportService: ReportService;
readonly packageService: PackageService;

/** The authenticated user's Magento ID */
get mageId(): string | undefined {
return this.adapter.mageId;
/**
* Fetch the user's Mage ID
* @see https://developer.adobe.com/commerce/marketplace/guides/eqp/v1/auth/#authentication-and-authorization-flow
*/
getMageId() {
return this.adapter.getMageId();
}

constructor(options: EQPOptions) {
options.environment ??= 'production';

this.adapter = new AuthenticatedAdapter(
options.adapter ??
new AxiosAdapter(`https://developer${(options.environment ?? 'production') === 'staging' ? '-stg' : ''}-api.magento.com/rest/v1`),
options.adapter ?? new AxiosAdapter(`https://commercedeveloper${options.environment === 'sandbox' ? '-sandbox' : ''}-api.adobe.com/rest/v1`),
{
appId: options.appId,
appSecret: options.appSecret,
autoRefresh: options.autoRefresh ?? false,
tokenTTL: options.expiresIn
appSecret: options.appSecret
}
);

Expand Down
2 changes: 1 addition & 1 deletion src/types/options.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Adapter } from './adapters';

export type Environment = 'staging' | 'production';
export type Environment = 'sandbox' | 'production';

export interface EQPOptions {
environment?: Environment;
Expand Down
Loading