Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions __test__/assetSidebarWidget.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe("AssetSidebarWidget", () => {
user: {} as any,
currentBranch: "mock_branch",
region: "region",
endpoints: { CMA: "", APP: "",DEVELOPER_HUB:"" },
};

let connection: { sendToParent: (...props: any[]) => any };
Expand Down
1 change: 1 addition & 0 deletions __test__/fieldModifierLocation/entry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ describe("FieldModifierLocationEntry", () => {
extension_uid: "extension_uid",
installation_uid: "installation_uid",
region: "NA",
endpoints: { CMA: "", APP: "",DEVELOPER_HUB:"" },
stack: {
api_key: "api_key",
created_at: "created_at",
Expand Down
1 change: 1 addition & 0 deletions __test__/organizationFullPage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const mockData: IOrgFullPageLocationInitData = {
installation_uid: "installation_uid",
extension_uid: "extension_uid",
region: "NA",
endpoints:{CMA:"",APP:"",DEVELOPER_HUB:""},
stack: {} as any,
user: {} as any,
currentBranch: "currentBranch",
Expand Down
1 change: 1 addition & 0 deletions __test__/uiLocation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const initData: IAppConfigInitData = {
installation_uid: "installation_uid",
extension_uid: "extension_uid",
region: "NA",
endpoints: { CMA: "https://api.contentstack.io", APP: "https://app.contentstack.app",DEVELOPER_HUB:"" },
stack: mockStackData,
user: {} as any,
currentBranch: "currentBranch",
Expand Down
2 changes: 1 addition & 1 deletion __test__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ describe("formatAppRegion", () => {
});

it("should return unknown for any invalid region", () => {
expect(formatAppRegion("invalid")).toBe(Region.UNKNOWN);
expect(formatAppRegion("invalid")).toBe("invalid");
});
});
335 changes: 82 additions & 253 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { GenericObjectType } from "./types/common.types";
import { Entry } from "./types/entry.types";
import { Asset, ContentType, Schema, StackDetail } from "./types/stack.types";
import { OrganizationDetails } from "./types/organization.types";
import { ContentstackEndpoints } from './types/api.type';
import { User } from "./types/user.types";
import Window from "./window";

Expand Down Expand Up @@ -102,6 +103,7 @@ declare interface ICommonInitData {
type: LocationType;
user: User;
manifest?: Manifest;
endpoints: ContentstackEndpoints;
}

export declare interface IOrgFullPageLocationInitData extends ICommonInitData {
Expand Down Expand Up @@ -254,4 +256,7 @@ export enum Region {
AZURE_NA = "AZURE_NA",
AZURE_EU = "AZURE_EU",
GCP_NA = "GCP_NA",
GCP_EU = "GCP_EU",
}

export type RegionType = "UNKNOWN" | "NA" | "EU" | "AZURE_NA" | "AZURE_EU" | "GCP_NA" | string;
13 changes: 10 additions & 3 deletions src/types/api.type.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import { AxiosRequestConfig, AxiosResponse, } from 'axios'
export type RequestConfig = AxiosRequestConfig
export type ProxyResponse = AxiosResponse
import { AxiosRequestConfig, AxiosResponse } from "axios";
export type RequestConfig = AxiosRequestConfig;
export type ProxyResponse = AxiosResponse;

export type ContentstackEndpoints = {
APP: string;
CMA: string;
DEVELOPER_HUB: string;
[key:string]:string;
};
36 changes: 24 additions & 12 deletions src/uiLocation.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AxiosRequestConfig, AxiosResponse } from "axios";
import postRobot from "post-robot";
import EventEmitter from "wolfy87-eventemitter";

Expand Down Expand Up @@ -26,15 +27,15 @@ import {
InitializationData,
LocationType,
Manifest,
Region,
IOrgFullPageLocation,
RegionType,
} from "./types";
import { GenericObjectType } from "./types/common.types";
import { User } from "./types/user.types";
import { formatAppRegion, onData, onError } from "./utils/utils";
import Window from "./window";
import { dispatchApiRequest, dispatchAdapter } from './utils/adapter';
import { AxiosRequestConfig, AxiosResponse } from 'axios';
import { dispatchApiRequest, dispatchAdapter } from "./utils/adapter";
import { ContentstackEndpoints } from "./types/api.type";

const emitter = new EventEmitter();

Expand Down Expand Up @@ -69,6 +70,8 @@ class UiLocation {
*/
private config: GenericObjectType;

readonly endpoints: ContentstackEndpoints;

/**
* This holds the instance of Cross-domain communication library for posting messages between windows.
*/
Expand Down Expand Up @@ -103,7 +106,7 @@ class UiLocation {
/**
* The Contentstack Region on which the app is running.
*/
readonly region: Region;
readonly region: RegionType;
version: number | null;

ids: {
Expand Down Expand Up @@ -177,14 +180,15 @@ class UiLocation {
FullPage: null,
FieldModifierLocation: null,
ContentTypeSidebarWidget: null,
OrganizationFullPage: null
OrganizationFullPage: null,
};

window["postRobot"] = postRobot;

this.modal = new Modal();

this.region = formatAppRegion(initializationData.region);
this.endpoints = initializationData.endpoints;

const stack = new Stack(initializationData.stack, postRobot, {
currentBranch: initializationData.currentBranch,
Expand Down Expand Up @@ -287,10 +291,10 @@ class UiLocation {

case LocationType.ORGANIZATION_FULL_PAGE: {
this.location.OrganizationFullPage = {
currentOrganization: initializationData.organization,
currentOrganization: initializationData.organization,
};
break;
}
}

case LocationType.CONTENT_TYPE_SIDEBAR_WIDGET: {
this.location.ContentTypeSidebarWidget =
Expand Down Expand Up @@ -468,24 +472,32 @@ class UiLocation {
/**
* Method used to get the Contentstack Region on which the app is running.
*/
getCurrentRegion = (): Region => {
getCurrentRegion = (): RegionType => {
return this.region;
};

getEndpoints = (): ContentstackEndpoints => {
return this.endpoints;
};
/**
* Method used to make an API request to the Contentstack's CMA APIs.
*/

api = (url: string, option?: RequestInit): Promise<Response> => dispatchApiRequest(url, option) as Promise<Response>;
api = (url: string, option?: RequestInit): Promise<Response> =>
dispatchApiRequest(url, option) as Promise<Response>;

/**
* Method used to create an adapter for management sdk.
*/
createAdapter = (): (config: AxiosRequestConfig) => Promise<AxiosResponse> => {
createAdapter = (): ((
config: AxiosRequestConfig
) => Promise<AxiosResponse>) => {
return (config: AxiosRequestConfig): Promise<AxiosResponse> => {
return dispatchAdapter(postRobot)(config) as Promise<AxiosResponse>;
return dispatchAdapter(postRobot)(config) as Promise<
AxiosResponse<any, any>
>;
};
};
};

/**
* Method used to initialize the App SDK.
Expand Down
112 changes: 80 additions & 32 deletions src/utils/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,95 @@
import PostRobot from 'post-robot';
import { AxiosRequestConfig, AxiosResponse } from 'axios';
import { onError, fetchToAxiosConfig } from './utils';
import PostRobot from "post-robot";
import { Response } from "node-fetch";
import {
AxiosError,
AxiosHeaders,
AxiosRequestConfig,
AxiosResponse,
} from "axios";

import { fetchToAxiosConfig } from "./utils";

/**
* Dispatches a request using PostRobot.
* @param postRobot - The PostRobot instance.
* @returns A function that takes AxiosRequestConfig and returns a promise.
*/
export const dispatchAdapter = (postRobot: typeof PostRobot) => (config: AxiosRequestConfig)=> {
return postRobot
.sendToParent("apiAdapter", config )
.then(({ data }) => ({ ...data, config }))
.catch(onError);
};
export const dispatchAdapter =
(postRobot: typeof PostRobot) => (config: AxiosRequestConfig) => {
return new Promise((resolve, reject) => {
postRobot
.sendToParent("apiAdapter", config)
.then((event: unknown) => {
const { data: response } = event as { data: AxiosResponse };

if (response.status >= 400) {
return reject({ ...response, config });
}
resolve({
data: response.data,
status: response.status,
statusText: response.statusText,
headers: response.headers,
config: config,
});
})
.catch(() => {
return reject(
new AxiosError(
"Something went wrong with the request",
"ERR_INTERNAL_SERVER",
{
...config,
headers: config.headers as AxiosHeaders,
},
null,
undefined
)
);
});
});
};
/**
GitHub Copilot
To handle errors generically and robustly, we can refactor the code to ensure that all errors are properly processed and converted into a standardized format. Here's how you can fix and improve the error handling in the provided code:

Refactored Code

* Dispatches an API request using axios and PostRobot.
* @param url - The URL of the API endpoint.
* @param options - Optional request options.
* @returns A promise that resolves to a partial Response object.
*/
Comment thread
Amitkanswal marked this conversation as resolved.
export const dispatchApiRequest = async (url: string, options?: RequestInit): Promise<Response> => {
try {
const config = fetchToAxiosConfig(url, options);
const responseData = await dispatchAdapter(PostRobot)(config) as AxiosResponse;
return new Response(responseData.data,{
status: responseData.status,
statusText: responseData.statusText,
headers: new Headers(responseData.config.headers || {}),
});
export const dispatchApiRequest = async (
url: string,
options?: RequestInit
): Promise<Response> => {
try {
const config = fetchToAxiosConfig(url, options);
const response = (await dispatchAdapter(PostRobot)(
config
)) as AxiosResponse;

} catch (error: any) {
if (error.response) {
const fetchResponse = new Response(error.response.data, {
status: error.response.status,
statusText: error.response.statusText,
headers: new Headers(error.response.headers)
});
return Promise.reject(fetchResponse);
} else if (error.request) {
return Promise.reject(new Response(null, { status: 0, statusText: 'Network Error' }));
} else {
return Promise.reject(new Response(null, { status: 0, statusText: error.message }));
return new Response(response?.data, {
status: response.status,
statusText: response.statusText,
url: response.config.url,
headers: new Headers(Object.entries(response.headers ?? {})),
});
} catch (err: any) {
if (err.response) {
return new Response(err.response?.data, {
status: err.status,
statusText: err.statusText,
headers: new Headers(
Object.entries(err.response.headers ?? {})
),
});
}
return new Response(err.stack, {
status: err.status || 500,
statusText: err.message || "Internal Server Error",
Comment thread
Amitkanswal marked this conversation as resolved.
headers: new Headers(Object.entries(err.headers ?? {})),
});
}
}
};
};
Loading
Loading