diff --git a/.circleci/config.yml b/.circleci/config.yml index ae06455..b82b5ff 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,17 +1,19 @@ version: 2 defaults: &defaults docker: - - image: circleci/python:2.7-stretch-browsers + - image: cimg/python:3.11.0-browsers install_dependency: &install_dependency name: Installation of build and deployment dependencies. command: | + sudo apt update sudo apt install jq - sudo pip install awscli --upgrade - sudo pip install docker-compose + sudo apt install python3-pip + sudo pip3 install awscli --upgrade + sudo pip3 install docker-compose install_deploysuite: &install_deploysuite name: Installation of install_deploysuite. command: | - git clone --branch v1.4.2 https://github.com/topcoder-platform/tc-deploy-scripts ../buildscript + git clone --branch v1.4.15 https://github.com/topcoder-platform/tc-deploy-scripts ../buildscript cp ./../buildscript/master_deploy.sh . cp ./../buildscript/buildenv.sh . cp ./../buildscript/awsconfiguration.sh . @@ -56,7 +58,15 @@ jobs: environment: DEPLOY_ENV: "DEV" LOGICAL_ENV: "dev" - APPNAME: "micro-frontends-taas-admin-app" + APPNAME: "micro-frontends-forums-app" + steps: *builddeploy_steps + + "build-qa": + <<: *defaults + environment: + DEPLOY_ENV: "QA" + LOGICAL_ENV: "qa" + APPNAME: "micro-frontends-forums-app" steps: *builddeploy_steps "build-prod": @@ -64,7 +74,7 @@ jobs: environment: DEPLOY_ENV: "PROD" LOGICAL_ENV: "prod" - APPNAME: "micro-frontends-taas-admin-app" + APPNAME: "micro-frontends-forums-app" steps: *builddeploy_steps workflows: @@ -77,8 +87,16 @@ workflows: filters: branches: only: - - dev + - develop + # Development builds are executed on "develop" branch only. + - "build-qa": + context: org-global + filters: + branches: + only: + - qa + # Production builds are exectuted only on tagged commits to the # master branch. - "build-prod": diff --git a/README.md b/README.md index 294170a..7db5a7c 100644 --- a/README.md +++ b/README.md @@ -103,21 +103,21 @@ Some config files are using domain `local.topcoder-dev.com`. You can change it t 2. Run **Navbar** micro-app: ```sh - git clone https://github.com/topcoder-platform/micro-frontends-navbar-app.git - cd micro-frontends-navbar-app + git clone https://github.com/topcoder-platform/mfe-header.git + cd mfe-header ``` - Update in file `micro-frontends-navbar-app/config/dev.js` values for `ACCOUNTS_APP_CONNECTOR` and `AUTH` to `http://localhost:5000` so Navbar app which handles authentication uses our local Authentication service. + Update in file `mfe-header/config/dev.js` values for `ACCOUNTS_APP_CONNECTOR` and `AUTH` to `http://localhost:5000` so Navbar app which handles authentication uses our local Authentication service. ```sh - # inside folder "micro-frontends-navbar-app" run: + # inside folder "mfe-header" run: nvm use # or make sure to use Node 10 npm i # to install dependencies npm run dev - # this host navbar app as http://localhost:3001/navbar/topcoder-micro-frontends-navbar-app.js + # this host navbar app as http://localhost:3001/navbar/topcoder-mfe-header.js ``` 3. Run **Forums** micro-app: @@ -147,4 +147,4 @@ Some config files are using domain `local.topcoder-dev.com`. You can change it t - If you are not logged-in yet, you should be redirected to the login page. - If you cannot see the application and redirect doesn't happen, make sure that file "http://local.topcoder-dev.com:8602/forums-app/topcoder-micro-frontends--app.js" is loaded successfully in the Network tab. -Congratulations, you successfully run the project. If you had some issue, please, try to go through README of https://github.com/topcoder-platform/micro-frontends-frame and https://github.com/topcoder-platform/micro-frontends-navbar-app. +Congratulations, you successfully run the project. If you had some issue, please, try to go through README of https://github.com/topcoder-platform/micro-frontends-frame and https://github.com/topcoder-platform/mfe-header. diff --git a/config/index.js b/config/index.js index ba0ff0d..167f29b 100644 --- a/config/index.js +++ b/config/index.js @@ -1,6 +1,14 @@ /* global process */ module.exports = (() => { - const appEnv = process.env.APPENV === "prod" ? "prod" : "dev"; - return require(`./${appEnv}`); -})(); + const env = process.env.APPENV || "dev"; + + console.log(`APPENV: "${env}"`); + + // for security reason don't let to require any arbitrary file defined in process.env + if (["prod", "dev", "qa"].indexOf(env) < 0) { + return require("./dev"); + } + + return require("./" + env); +})(); \ No newline at end of file diff --git a/config/qa.js b/config/qa.js new file mode 100644 index 0000000..b992998 --- /dev/null +++ b/config/qa.js @@ -0,0 +1,7 @@ +module.exports = { + API: { + V3: "https://api.topcoder-qa.com/v3", + V5: "https://api.topcoder-qa.com/v5", + }, + VANILLA_EMBED_JS: "https://vanilla.topcoder-qa.com/js/embed.js", +}; diff --git a/package.json b/package.json index ef4a7df..9143e16 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "start": "node server.js", "dev": "cross-env APPMODE=development webpack-dev-server --port 8602 --host 0.0.0.0", "dev-https": "cross-env APPMODE=development webpack-dev-server --https --port 8602 --host 0.0.0.0", + "qa": "cross-env APPMODE=development webpack-dev-server --port 8602 --host 0.0.0.0", "build": "webpack --mode=${APPMODE:-production} --env.config=${APPENV:-prod}", "analyze": "webpack --mode=production --env.analyze=true", "lint": "eslint ./src --ext .js,.jsx", diff --git a/src/embed-test.js b/src/embed-test.js new file mode 100644 index 0000000..061e847 --- /dev/null +++ b/src/embed-test.js @@ -0,0 +1,397 @@ +/* eslint-disable */ +export default () => { + if (window.vanilla == undefined) window.vanilla = {}; + window.vanilla_lazy_load = undefined + + if (window.vanilla.embeds == undefined) window.vanilla.embeds = {}; + + window.vanilla.embed = function (host) { + var scripts = document.getElementsByTagName("script"), + id = Math.floor(Math.random() * 100000).toString(), + embedUrl = window.location.href.split("#")[0], + jsPath = "/js/embed.js", + currentPath = window.location.hash.substr(1), + disablePath = window != top; + + var optStr = function (name, defaultValue, definedValue) { + if (window["vanilla_" + name]) { + if (definedValue == undefined) return window["vanilla_" + name]; + else return definedValue.replace("%s", window["vanilla_" + name]); + } + return defaultValue; + }; + + if (!currentPath || disablePath) currentPath = "/"; + + if (currentPath.substr(0, 1) != "/") currentPath = "/" + currentPath; + + if (window.gadgets) embedUrl = ""; + + if (typeof host == "undefined") { + host = ""; + window.host_base_url = ""; + for (var i = 0; i < scripts.length; i++) { + if (scripts[i].src.indexOf(jsPath) > 0) { + host = scripts[i].src; + host = host.replace("http://", "").replace("https://", ""); + host = host.substr(0, host.indexOf(jsPath)); + + host_base_url = scripts[i].src; + host_base_url = host_base_url.substr( + 0, + host_base_url.indexOf(jsPath) + ); + if (host_base_url.substring(host_base_url.length - 1) != "/") + host_base_url += "/"; + } + } + } + + window.vanilla.embeds[id] = this; + if (window.postMessage) { + var onMessage = function (e) { + console.log("---- message ----", e); + + // Check that we're getting a vanilla message + if (typeof e.data === "string") { + var message = e.data.split(":"); + var frame = document.getElementById("vanilla" + id); + + // Unload event's source is undefined + var isUnload = message[0] == "unload"; + if (!frame || (!isUnload && frame.contentWindow != e.source)) { + return; + } + processMessage(message); + } + }; + if (window.addEventListener) { + window.addEventListener("message", onMessage, false); + } else { + window.attachEvent("onmessage", onMessage); + } + } else { + var messageId = null; + setInterval(function () { + try { + var vid = "vanilla" + id; + var hash = window.frames[vid].frames["messageFrame"].location.hash; + hash = hash.substr(6); + } catch (e) { + return; + } + + var message = hash.split(":"); + var newMessageId = message[0]; + if (newMessageId == messageId) { + return; + } + + messageId = newMessageId; + message.splice(0, 1); + processMessage(message); + }, 200); + } + + const checkHash = function () { + debugger; + var path = window.location.hash.substr(1) || "/"; + if (path != currentPath) { + currentPath = path; + window.frames["vanilla" + id].location.replace(vanillaUrl(path)); + } + }; + + if (!window.gadgets) { + if (!disablePath) { + if ("onhashchange" in window) { + if (window.addEventListener) + window.addEventListener("hashchange", checkHash, false); + else window.attachEvent("onhashchange", checkHash); + } else { + setInterval(checkHash, 300); + } + } + } + + // Strip param out of str if it exists + const stripParam = function (str, param) { + var pIndex = str.indexOf(param); + if (pIndex > -1) { + var pStr = str.substr(pIndex); + var tIndex = pStr.indexOf("&"); + var trail = tIndex > -1 ? pStr.substr(tIndex + 1) : ""; + var pre = currentPath.substr(pIndex - 1, 1); + if (pre == "&" || pre == "?") pIndex--; + + return str.substr(0, pIndex) + (trail.length > 0 ? pre : "") + trail; + } + return str; + }; + + const processMessage = function (message) { + var iframe = document.getElementById("vanilla" + id); + + if (message[0] == "height") { + setHeight(message[1]); + + if (message[1] > 0) { + iframe.style.visibility = "visible"; + } + } else if (message[0] == "location") { + if (message[1] != "") { + iframe.style.visibility = "visible"; + } + + if (disablePath) { + //currentPath = cmd[1]; + } else { + currentPath = window.location.hash.substr(1); + if (currentPath != message[1]) { + currentPath = message[1]; + // Strip off the values that this script added + currentPath = stripParam(currentPath, "remote="); // 1 + currentPath = stripParam(currentPath, "locale="); // 2 + //window.location.hash = currentPath; + } + } + } else if (message[0] == "unload") { + // Scroll to the top of the IFRAME if the top position is not in the view. + var currentScrollAmount = + window.pageYOffset || document.documentElement.scrollTop; + var offsetTop = offsetFromTop("vanilla" + id); + if (offsetTop - currentScrollAmount < 0) { + window.scrollTo(0, offsetTop); + } + + iframe.style.visibility = "hidden"; + } else if (message[0] == "scrolltop") { + window.scrollTo(0, document.getElementById("vanilla" + id).offsetTop); + } else if (message[0] == "scrollto") { + window.scrollTo( + 0, + document.getElementById("vanilla" + id).offsetTop - + 40 + + message[1] * 1 + ); + } else if (message[0] == "unembed") { + document.location = "http://" + host + window.location.hash.substr(1); + } + }; + + const offsetFromTop = function (id) { + var node = document.getElementById(id), + top = 0, + topScroll = 0; + if (node.offsetParent) { + do { + top += node.offsetTop; + topScroll += node.offsetParent ? node.offsetParent.scrollTop : 0; + } while ((node = node.offsetParent)); + return top - topScroll; + } + return -1; + }; + + const setHeight = function (height) { + if (optStr("height")) return; + // if (document.getElementById("vanilla" + id).style["height"] === ( height - 21 ) + 'px') { + // return + // } + + // document.getElementById("vanilla" + id).style["height"] = height + "px"; + document.getElementById("vanilla" + id).style["height"] = 'calc(100vh - 76px)' + if (window.gadgets && gadgets.window && gadgets.window.adjustHeight) { + try { + gadgets.window.adjustHeight(); + } catch (ex) { + // Do nothing... + } + } + }; + + const vanillaUrl = function (path) { + // What type of embed are we performing? + var embed_type = + typeof vanilla_embed_type == "undefined" + ? "standard" + : vanilla_embed_type; + // Are we loading a particular discussion based on discussion_id? + var discussion_id = + typeof vanilla_discussion_id == "undefined" ? 0 : vanilla_discussion_id; + // Are we loading a particular discussion based on foreign_id? + var foreign_id = + typeof vanilla_identifier == "undefined" ? "" : vanilla_identifier; + // Is there a foreign type defined? Possibly used to render the discussion + // body a certain way in the forum? Also used to filter down to foreign + // types so that matching foreign_id's across type don't clash. + var foreign_type = + typeof vanilla_type == "undefined" ? "page" : vanilla_type; + // If embedding comments, should the newly created discussion be placed in a specific category? + var category_id = + typeof vanilla_category_id == "undefined" ? "" : vanilla_category_id; + // If embedding comments, this value will be used to reference the foreign content. Defaults to the url of the page this file is included in. + var foreign_url = + typeof vanilla_url == "undefined" + ? document.URL.split("#")[0] + : vanilla_url; + // Are we forcing a locale via Multilingual plugin? + var embed_locale = + typeof vanilla_embed_locale == "undefined" ? "" : vanilla_embed_locale; + if (typeof vanilla_lazy_load == "undefined") vanilla_lazy_load = true; + + // If path was defined, and we're sitting at app root, use the defined path instead. + if (typeof vanilla_path != "undefined" && path == "/") + path = vanilla_path; + + // Force type based on incoming variables + if (discussion_id != "" || foreign_id != "") embed_type = "comments"; + + var result = ""; + + if (embed_type == "comments") { + result = + "//" + + host + + "/discussion/embed/" + + // Break the cache. /embed/ gets cached, looks like you are not logged in. + "&c=" + + new Date().getTime() + + "&vanilla_identifier=" + + encodeURIComponent(foreign_id) + + "&vanilla_url=" + + encodeURIComponent(foreign_url); + + if (typeof vanilla_type != "undefined") + result += "&vanilla_type=" + encodeURIComponent(vanilla_type); + + if (typeof vanilla_discussion_id != "undefined") + result += + "&vanilla_discussion_id=" + + encodeURIComponent(vanilla_discussion_id); + + if (typeof vanilla_category_id != "undefined") + result += + "&vanilla_category_id=" + encodeURIComponent(vanilla_category_id); + + if (typeof vanilla_title != "undefined") + result += "&title=" + encodeURIComponent(vanilla_title); + } else { + result = + "//" + + host + + path + + "&remote=" + + encodeURIComponent(embedUrl) + + "&locale=" + + encodeURIComponent(embed_locale); + } + + if (window.vanilla_sso) { + result += "&sso=" + encodeURIComponent(vanilla_sso); + } + + return result.replace(/\?/g, "&").replace("&", "?"); // Replace the first occurrence of amp with question. + }; + var vanillaIframe = document.createElement("iframe"); + vanillaIframe.id = "vanilla" + id; + vanillaIframe.name = "vanilla" + id; + vanillaUrl(currentPath); + vanillaIframe.src = + "https://vanilla.topcoder-dev.com/?remote=https://local.topcoder-dev.com/forums&locale="; + vanillaIframe.scrolling = "yes"; + vanillaIframe.frameBorder = "0"; + vanillaIframe.allowTransparency = true; + vanillaIframe.border = "0"; + vanillaIframe.width = "100%"; + vanillaIframe.style.width = "100%"; + vanillaIframe.style.border = "0"; + vanillaIframe.style.display = "block"; // must be block + + if (window.postMessage) { + vanillaIframe.height = "0"; + vanillaIframe.style.height = "0"; + } else { + vanillaIframe.height = "300"; + vanillaIframe.style.height = "300px"; + } + + var img = document.createElement("div"); + img.className = "vn-loading"; + img.style.textAlign = "center"; + img.innerHTML = window.vanilla_loadinghtml + ? vanilla_loadinghtml + : ''; + + var container = document.getElementById("vanilla-comments"); + // Couldn't find the container, so dump it out and try again. + if (!container) document.write('
'); + container = document.getElementById("vanilla-comments"); + + if (container) { + var loaded = function () { + if (img) { + container.removeChild(img); + img = null; + } + vanillaIframe.style.visibility = "visible"; + }; + + if (vanillaIframe.addEventListener) { + vanillaIframe.addEventListener("load", loaded, true); + } else if (vanillaIframe.attachEvent) { + vanillaIframe.attachEvent("onload", loaded); + } else setTimeout(2000, loaded); + + container.appendChild(img); + + // If jQuery is present in the page, include our defer-until-visible script + if (vanilla_lazy_load && typeof jQuery != "undefined") { + jQuery.ajax({ + url: host_base_url + "js/library/jquery.appear.js", + dataType: "script", + cache: true, + success: function () { + // setTimeout(function() { + + if (jQuery.fn.appear) + jQuery("#vanilla-comments").appear(function () { + container.appendChild(vanillaIframe); + }); + else container.appendChild(vanillaIframe); // fallback + // }, 10000); + }, + }); + } else { + container.appendChild(vanillaIframe); // fallback: just load it + } + } + + // Include our embed css into the page + var vanilla_embed_css = document.createElement("link"); + vanilla_embed_css.rel = "stylesheet"; + vanilla_embed_css.type = "text/css"; + vanilla_embed_css.href = + host_base_url + + (host_base_url.substring(host_base_url.length - 1) == "/" ? "" : "/") + + "applications/dashboard/design/embed.css"; + ( + document.getElementsByTagName("head")[0] || + document.getElementsByTagName("body")[0] + ).appendChild(vanilla_embed_css); + + return this; + }; + try { + if (window.location.hash.substr(0, 6) != "#poll:") window.vanilla.embed(); + } catch (e) { + var error = document.createElement("div"); + error.style.padding = "10px"; + error.style.fontSize = "12px"; + error.style.fontFamily = "lucida grande"; + error.style.background = "#ffffff"; + error.style.color = "#000000"; + error.appendChild(document.createTextNode("Failed to embed Vanilla: " + e)); + document.getElementById("vanilla-comments").appendChild(error); + } +}; diff --git a/src/root.component.jsx b/src/root.component.jsx index 2296576..8f9462b 100644 --- a/src/root.component.jsx +++ b/src/root.component.jsx @@ -1,30 +1,34 @@ -import React, { useLayoutEffect } from "react"; -import { - disableSidebarForRoute, - setNotificationPlatform, - PLATFORM, -} from "@topcoder/micro-frontends-navbar-app"; +import React, { useEffect } from "react"; import { VANILLA_EMBED_JS } from "./constants"; +import { disableSidebarForRoute } from "@topcoder/mfe-header"; import "styles/global.scss"; +// import ebmedJs from "./embed-test"; export default function Root() { - useLayoutEffect(() => { - // disableSidebarForRoute(`${APP_BASE_PATH}/*`); - // setNotificationPlatform(PLATFORM.TAAS); + useEffect(() => { + // ebmedJs(); + disableSidebarForRoute("/forums/*"); + const script = document.createElement("script"); + script.src = VANILLA_EMBED_JS; + script.async = true; + document.body.appendChild(script); + return () => { + document.body.removeChild(script); + }; }, []); return ( <> - +
-
- - Discussions by +
+ + Discussions by Vanilla