Skip to content
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
12 changes: 11 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
{
"presets": [
[
"babel-preset-gatsby-package"
"babel-preset-gatsby-package",
{
"browser": true,
"targets": {
"node": true
}
}
],
[
"minify",
Expand All @@ -16,6 +22,10 @@
"plugins": [
"@babel/plugin-transform-runtime"
],
"only": [
"src/",
"tests/"
],
"ignore": [
"node_modules/"
]
Expand Down
12 changes: 2 additions & 10 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
{
"root": true,
"extends": [
"eslint:recommended",
"prettier",
"plugin:prettier/recommended"
],
"plugins": [
"react",
"babel",
"prettier"
],
"extends": ["eslint:recommended", "prettier", "plugin:prettier/recommended"],
"plugins": ["react", "babel", "prettier"],
"env": {
"node": true,
"es2021": true
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,13 @@ options: {

Set the log level for the Optimizely/Episerver API API requests. Supports `info`, `debug`, `warn`, `error`.

**Default:** `debug`.
**Default:** `info`.

```javascript
options: {
// ...

log_level: "debug";
log_level: "info";
}
```

Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,16 @@
"main": "index.js",
"dependencies": {
"axios": "^0.27.2",
"lodash": "^4.17.21",
"micro": "^9.3.4",
"qs": "^6.11.0",
"then-sleep": "^1.0.1",
"winston": "^3.8.1"
},
"peerDependencies": {
"gatsby": ">3.0.0"
},
"devDependencies": {
"@babel/cli": "^7.18.6",
"@babel/core": "^7.18.6",
"@babel/eslint-parser": "^7.16.5",
"@babel/plugin-transform-runtime": "^7.16.10",
"@babel/runtime": "^7.18.6",
"@commitlint/cli": "^17.0.3",
"@commitlint/config-conventional": "^17.0.3",
Expand All @@ -58,18 +55,21 @@
"commitizen": "^4.2.4",
"cross-env": "^7.0.3",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.19.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-babel": "^5.3.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.30.1",
"gatsby": "^4.18.2",
"eslint": "^8.19.0",
"gatsby-core-utils": "^3.18.1",
"gatsby": "^4.18.2",
"husky": "^8.0.1",
"lint-staged": "^13.0.3",
"prettier": "^2.7.1",
"react": "^18.2.0"
},
"peerDependencies": {
"gatsby": ">3.0.0"
},
"bugs": {
"url": "https://github.com/Epic-Design-Labs/gatsby-source-optimizely/issues"
},
Expand All @@ -78,7 +78,7 @@
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
"path": "./node_modules/cz-conventional-changelog"
}
},
"husky": {
Expand Down
5 changes: 2 additions & 3 deletions src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ export const REQUEST_TIMEOUT = 0;

// Auth
export const AUTH_REQUEST_CONTENT_TYPE_HEADER = "application/x-www-form-urlencoded";
export const AUTH_ENDPOINT = "/auth/token";

// Optimizely
export const OPTIMIZELY_AUTH_ENDPOINT = "/auth/token";

// Headers
export const ACCESS_CONTROL_ALLOW_HEADERS = "Content-Type,Accept";
export const ACCESS_CONTROL_ALLOW_CREDENTIALS = true;
export const CORS_ORIGIN = "*";
62 changes: 19 additions & 43 deletions src/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
"use strict";

import { ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, AUTH_REQUEST_CONTENT_TYPE_HEADER, CORS_ORIGIN, OPTIMIZELY_AUTH_ENDPOINT, REQUEST_ACCEPT_HEADER, REQUEST_TIMEOUT, REQUEST_URL_SLUG } from "./constants";
import { ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, CORS_ORIGIN, REQUEST_TIMEOUT, REQUEST_URL_SLUG } from "./constants";
import Auth from "./utils/auth";
import { convertObjectToString } from "./utils/convertValues";
import { logger } from "./utils/logger";
import Optimizely from "./utils/optimizely";
import qs from "qs";
import axios from "axios";

/**
* @description Create a node from the data
Expand All @@ -14,7 +13,7 @@ import axios from "axios";
* @param {Object} helpers
* @returns {Promise<void>} Node creation promise
*/
const handleCreateNodeFromData = (item, nodeType, helpers) => {
const handleCreateNodeFromData = (item, nodeType, helpers, endpoint, log) => {
const nodeMetadata = {
...item,
id: helpers.createNodeId(`${nodeType}-${item.id || item.name}`),
Expand All @@ -33,12 +32,12 @@ const handleCreateNodeFromData = (item, nodeType, helpers) => {
helpers
.createNode(node)
.then(() => {
// log.warn(`(OK) [CREATE NODE] ${endpoint} - ${helpers.createNodeId(`${nodeType}-${item.id || item.name}`)}`);
log.warn(`(OK) [CREATE NODE] ${endpoint} - ${helpers.createNodeId(`${nodeType}-${item.id || item.name}`)}`);

return node;
})
.catch((err) => {
// log.error(`(FAIL) [CREATE NODE] ${endpoint} - ${helpers.createNodeId(`${nodeType}-${item.id || item.name}`)}`, err.message);
log.error(`(FAIL) [CREATE NODE] ${endpoint} - ${helpers.createNodeId(`${nodeType}-${item.id || item.name}`)}`, err.message);

throw err;
});
Expand Down Expand Up @@ -104,9 +103,9 @@ exports.pluginOptionsSchema = ({ Joi }) =>
exports.sourceNodes = async ({ actions, createNodeId, createContentDigest }, pluginOptions) => {
// Prepare plugin options
const {
auth: { site_url = null, username = null, password = null, grant_type = "password", client_id = "Default" },
auth: { site_url = null, username = null, password = null, grant_type = "password", client_id = "Default", headers = {} },
endpoints = null,
log_level = "debug",
log_level = "info",
response_type = "json",
request_timeout = REQUEST_TIMEOUT
} = pluginOptions;
Expand All @@ -120,50 +119,27 @@ exports.sourceNodes = async ({ actions, createNodeId, createContentDigest }, plu
createNodeId
});

var config = {
method: "post",
url: site_url + REQUEST_URL_SLUG + OPTIMIZELY_AUTH_ENDPOINT,
headers: {
"Accept": REQUEST_ACCEPT_HEADER,
"Content-Type": AUTH_REQUEST_CONTENT_TYPE_HEADER
},
data: qs.stringify({
password: password,
username: username,
grant_type: grant_type,
client_id: client_id
})
};
// Authenticate with the Optimizely/Episerver
const auth = new Auth({ site_url, username, password, grant_type, client_id, log, response_type, request_timeout });

const { data } = await axios(config);
const authData = await auth.login();

const headers = {
"Authorization": `Bearer ${data.access_token}`,
"Access-Control-Allow-Headers": ACCESS_CONTROL_ALLOW_HEADERS,
"Access-Control-Allow-Credentials": ACCESS_CONTROL_ALLOW_CREDENTIALS,
"Access-Control-Allow-Origin": CORS_ORIGIN
};

console.log("------------");
console.log(`Bearer ${data.access_token}`);
console.log("------------");
console.log(authData);

// Create a new Optimizely instance
const optimizely = new Optimizely({
site_url,
username,
password,
grant_type,
client_id,
response_type,
headers,
headers: Object.assign(headers, {
"Authorization": `Bearer ${authData.access_token}`,
"Access-Control-Allow-Headers": ACCESS_CONTROL_ALLOW_HEADERS,
"Access-Control-Allow-Credentials": ACCESS_CONTROL_ALLOW_CREDENTIALS,
"Access-Control-Allow-Origin": CORS_ORIGIN
}),
log,
request_timeout
});

// Authenticate with Optimizely
// const authPromise = optimizely.checkAccessToken();

// Get the endpoints from the Optimizely/Episerver site and create nodes
await Promise.allSettled(
Object.entries(endpoints).map(
Expand All @@ -173,8 +149,8 @@ exports.sourceNodes = async ({ actions, createNodeId, createContentDigest }, plu
.then((res) => {
// Create node for each item in the response
return res && Array.isArray(res) && res.length > 0
? res.map((datum) => handleCreateNodeFromData(datum, nodeName, helpers))
: handleCreateNodeFromData(res, nodeName, helpers);
? res.map((datum) => handleCreateNodeFromData(datum, nodeName, helpers, site_url + REQUEST_URL_SLUG + endpoint, log))
: handleCreateNodeFromData(res, nodeName, helpers, site_url + REQUEST_URL_SLUG + endpoint, log);
})
.catch((err) => {
log.error(`An error occurred while fetching ${endpoint} endpoint data: ${err.message}`);
Expand Down
42 changes: 42 additions & 0 deletions src/utils/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import axios from "axios";
import qs from "qs";
import { AUTH_ENDPOINT, AUTH_REQUEST_CONTENT_TYPE_HEADER, REQUEST_ACCEPT_HEADER, REQUEST_URL_SLUG } from "../constants";

class Auth {
constructor(config) {
if (!config) {
throw new Error("Optimizely/Episerver API auth config required. It is required to make any call to the API");
}

this.site_url = config.site_url;
this.username = config.username;
this.password = config.password;
this.grant_type = config.grant_type;
this.client_id = config.client_id;
this.log = config.log;
this.response_type = config.response_type;
this.request_timeout = config.request_timeout;
}

async login() {
let config = {
method: "post",
url: this.site_url + REQUEST_URL_SLUG + AUTH_ENDPOINT,
headers: {
"Accept": REQUEST_ACCEPT_HEADER,
"Content-Type": AUTH_REQUEST_CONTENT_TYPE_HEADER
},
data: qs.stringify({
password: this.password,
username: this.username,
grant_type: this.grant_type,
client_id: this.client_id
})
};

const { data } = await axios(config);
return data;
}
}

export default Auth;
40 changes: 8 additions & 32 deletions src/utils/optimizely.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"use strict";

import qs from "qs";
import sleep from "then-sleep";
import { AUTH_REQUEST_CONTENT_TYPE_HEADER, OPTIMIZELY_AUTH_ENDPOINT } from "../constants";
import Request from "./request";

class Optimizely {
Expand All @@ -12,21 +10,19 @@ class Optimizely {
}

this.site_url = config.site_url;
this.username = config.username;
this.password = config.password;
this.grant_type = config.grant_type;
this.client_id = config.client_id;
this.response_type = config.response_type;
this.headers = config.headers;
this.log = config.log;
this.request_timeout = config.request_timeout;
}

// Handle API requests
async request(method, path, body = null) {
async request(method, path, body = null, headers = {}) {
await sleep(this.request_timeout);

// Prepare `path` for request execution
const request = new Request(this.site_url, {
headers: this.headers,
headers: Object.assign({}, this.headers, headers),
response_type: this.response_type,
log: this.log,
request_timeout: this.request_timeout
Expand All @@ -39,39 +35,19 @@ class Optimizely {
return data;
}

// Authorization token request
async checkAccessToken() {
this.log.warn("Authenticating with Optimizely... (this may take a few seconds)");

await sleep(this.request_timeout);

const body = qs.stringify({
username: this.username,
password: this.password,
grant_type: this.grant_type,
client_id: this.client_id
});

// Send authentication request
const response = await this.request("post", OPTIMIZELY_AUTH_ENDPOINT, body, {
"Content-Type": AUTH_REQUEST_CONTENT_TYPE_HEADER
});
return response;
}

// Handle `GET` request
async get(path) {
async get(path, headers = {}) {
await sleep(this.request_timeout);

const response = await this.request("get", path);
const response = await this.request("get", path, headers);
return response;
}

// Handle `POST` request
async post(path, body) {
async post(path, body, headers = {}) {
await sleep(this.request_timeout);

const response = await this.request("post", path, body);
const response = await this.request("post", path, body, headers);
return response;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/utils/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class Request {
},
(req) => Promise.resolve(req),
(err) => {
if (err.code === "ETIMEDOUT") {
if (err.code === "ETIMEDOUT" || err.code === "ECONNRESET") {
setTimeout(async () => {
// Send log message when restarting request
this.log.warn(`(${err.config.status + " " + err.config.statusText}) [${method.toUpperCase()}] ${this.hostname + path} request timed out. Restarting request...`);
Expand Down Expand Up @@ -67,7 +67,7 @@ class Request {
return Promise.resolve(res);
},
(err) => {
if (err.code === "ETIMEDOUT") {
if (err.code === "ETIMEDOUT" || err.code === "ECONNRESET") {
setTimeout(async () => {
// Send log message when restarting request
this.log.warn(`(${err.config.status + " " + err.config.statusText}) [${method.toUpperCase()}] ${this.hostname + path} request timed out. Restarting request...`);
Expand Down