diff --git a/client/app/pages/queries/VisualizationEmbed.jsx b/client/app/pages/queries/VisualizationEmbed.jsx
index c32951e172..961f5babf4 100644
--- a/client/app/pages/queries/VisualizationEmbed.jsx
+++ b/client/app/pages/queries/VisualizationEmbed.jsx
@@ -157,7 +157,7 @@ function VisualizationEmbed({ queryId, visualizationId, apiKey }) {
useEffect(() => {
let isCancelled = false;
- Query.get({ id: queryId }).$promise.then(result => {
+ Query.get({ id: queryId }).then(result => {
if (!isCancelled) {
setQuery(result);
}
diff --git a/client/app/pages/queries/hooks/useUnsavedChangesAlert.js b/client/app/pages/queries/hooks/useUnsavedChangesAlert.js
index 34c9b3afbe..04c1a6f4aa 100644
--- a/client/app/pages/queries/hooks/useUnsavedChangesAlert.js
+++ b/client/app/pages/queries/hooks/useUnsavedChangesAlert.js
@@ -1,5 +1,5 @@
import { useRef, useEffect } from "react";
-import { $rootScope } from "@/services/ng";
+import location from "@/services/location";
// TODO: This should be revisited and probably re-implemented when replacing Angular router with sth else
export default function useUnsavedChangesAlert(shouldShowAlert = false) {
@@ -16,14 +16,9 @@ export default function useUnsavedChangesAlert(shouldShowAlert = false) {
return shouldShowAlertRef.current ? unloadMessage : undefined;
};
- // ANGULAR_REMOVE_ME
- const unsubscribe = $rootScope.$on("$locationChangeStart", (event, next, current) => {
- if (next.split("?")[0] === current.split("?")[0] || next.split("#")[0] === current.split("#")[0]) {
- return;
- }
-
- if (shouldShowAlertRef.current && !window.confirm(confirmMessage)) {
- event.preventDefault();
+ const unsubscribe = location.confirmChange((nextLocation, currentLocation) => {
+ if (shouldShowAlertRef.current && nextLocation.path !== currentLocation.path) {
+ return confirmMessage;
}
});
diff --git a/client/app/services/auth.js b/client/app/services/auth.js
index afc7bffcc1..b7a2fe0b03 100644
--- a/client/app/services/auth.js
+++ b/client/app/services/auth.js
@@ -3,9 +3,6 @@ import { includes, extend } from "lodash";
import location from "@/services/location";
import { axios } from "@/services/axios";
-// eslint-disable-next-line import/no-mutable-exports
-export let Auth = null;
-
export const currentUser = {
canEdit(object) {
const userId = object.user_id || (object.user && object.user.id);
@@ -41,78 +38,63 @@ function updateSession(sessionData) {
extend(messages, session.messages);
}
-function AuthService($window, $q) {
- return {
- isAuthenticated() {
- return session.loaded && session.user.id;
- },
- login() {
- const next = encodeURI(location.url);
- logger("Calling login with next = %s", next);
- window.location.href = `login?next=${next}`;
- },
- logout() {
- logger("Logout.");
- $window.location.href = "logout";
- },
- loadSession() {
- logger("Loading session");
- if (session.loaded && session.user.id) {
- logger("Resolving with local value.");
- return $q.resolve(session);
- }
+export const Auth = {
+ isAuthenticated() {
+ return session.loaded && session.user.id;
+ },
+ login() {
+ const next = encodeURI(location.url);
+ logger("Calling login with next = %s", next);
+ window.location.href = `login?next=${next}`;
+ },
+ logout() {
+ logger("Logout.");
+ window.location.href = "logout";
+ },
+ loadSession() {
+ logger("Loading session");
+ if (session.loaded && session.user.id) {
+ logger("Resolving with local value.");
+ return Promise.resolve(session);
+ }
- this.setApiKey(null);
- return axios.get("api/session").then(data => {
- updateSession(data);
- return session;
- });
- },
- loadConfig() {
- logger("Loading config");
- return axios.get("/api/config").then(data => {
- updateSession({ client_config: data.client_config, user: { permissions: [] }, messages: [] });
- return data;
+ Auth.setApiKey(null);
+ return axios.get("api/session").then(data => {
+ updateSession(data);
+ return session;
+ });
+ },
+ loadConfig() {
+ logger("Loading config");
+ return axios.get("/api/config").then(data => {
+ updateSession({ client_config: data.client_config, user: { permissions: [] }, messages: [] });
+ return data;
+ });
+ },
+ setApiKey(apiKey) {
+ logger("Set API key to: %s", apiKey);
+ Auth.apiKey = apiKey;
+ },
+ getApiKey() {
+ return Auth.apiKey;
+ },
+ requireSession() {
+ logger("Requested authentication");
+ if (Auth.isAuthenticated()) {
+ return Promise.resolve(session);
+ }
+ return Auth.loadSession()
+ .then(() => {
+ if (Auth.isAuthenticated()) {
+ logger("Loaded session");
+ return session;
+ }
+ logger("Need to login, redirecting");
+ Auth.login();
+ })
+ .catch(() => {
+ logger("Need to login, redirecting");
+ Auth.login();
});
- },
- setApiKey(apiKey) {
- logger("Set API key to: %s", apiKey);
- this.apiKey = apiKey;
- },
- getApiKey() {
- return this.apiKey;
- },
- requireSession() {
- logger("Requested authentication");
- if (this.isAuthenticated()) {
- return $q.when(session);
- }
- return this.loadSession()
- .then(() => {
- if (this.isAuthenticated()) {
- logger("Loaded session");
- return session;
- }
- logger("Need to login, redirecting");
- this.login();
- })
- .catch(() => {
- logger("Need to login, redirecting");
- this.login();
- });
- },
- };
-}
-
-export default function init(ngModule) {
- ngModule.factory("Auth", AuthService);
- ngModule.value("currentUser", currentUser);
- ngModule.value("clientConfig", clientConfig);
- ngModule.value("messages", messages);
-
- ngModule.run($injector => {
- Auth = $injector.get("Auth");
- });
-}
-
-init.init = true;
+ },
+};
diff --git a/client/app/services/axios.js b/client/app/services/axios.js
index c102080746..ae08177be0 100644
--- a/client/app/services/axios.js
+++ b/client/app/services/axios.js
@@ -1,4 +1,5 @@
import axiosLib from "axios";
+import { Auth } from "@/services/auth";
export const axios = axiosLib.create();
@@ -7,18 +8,11 @@ const getResponse = ({ response }) => Promise.reject(response);
axios.interceptors.response.use(getData, getResponse);
-// TODO: revisit this definition when auth is updated
-export default function init(ngModule) {
- ngModule.run($injector => {
- axios.interceptors.request.use(config => {
- const apiKey = $injector.get("Auth").getApiKey();
- if (apiKey) {
- config.headers.Authorization = `Key ${apiKey}`;
- }
+axios.interceptors.request.use(config => {
+ const apiKey = Auth.getApiKey();
+ if (apiKey) {
+ config.headers.Authorization = `Key ${apiKey}`;
+ }
- return config;
- });
- });
-}
-
-init.init = true;
+ return config;
+});
diff --git a/client/app/services/location.js b/client/app/services/location.js
index d36f9accd6..f4a2ea000c 100644
--- a/client/app/services/location.js
+++ b/client/app/services/location.js
@@ -4,6 +4,18 @@ import { createBrowserHistory } from "history";
const history = createBrowserHistory();
+function normalizeLocation(rawLocation) {
+ const { pathname, search, hash } = rawLocation;
+ const result = {};
+
+ result.path = pathname;
+ result.search = mapValues(qs.parse(search), value => (isNil(value) ? true : value));
+ result.hash = trimStart(hash, "#");
+ result.url = `${pathname}${search}${hash}`;
+
+ return result;
+}
+
const location = {
listen(handler) {
if (isFunction(handler)) {
@@ -13,6 +25,16 @@ const location = {
}
},
+ confirmChange(handler) {
+ if (isFunction(handler)) {
+ return history.block(nextLocation => {
+ return handler(normalizeLocation(nextLocation), location);
+ });
+ } else {
+ return () => {};
+ }
+ },
+
update(newLocation, replace = false) {
if (replace) {
history.replace(newLocation);
@@ -45,13 +67,7 @@ const location = {
};
function locationChanged() {
- const { pathname, search, hash } = history.location;
-
- location.path = pathname;
- location.search = mapValues(qs.parse(search), value => isNil(value) ? true : value);
- location.hash = trimStart(hash, "#");
-
- return `${pathname}${search}${hash}`;
+ extend(location, normalizeLocation(history.location));
}
history.listen(locationChanged);
diff --git a/client/app/services/ng.js b/client/app/services/ng.js
deleted file mode 100644
index 29edf3980d..0000000000
--- a/client/app/services/ng.js
+++ /dev/null
@@ -1,13 +0,0 @@
-export let $http = null; // eslint-disable-line import/no-mutable-exports
-export let $q = null; // eslint-disable-line import/no-mutable-exports
-export let $rootScope = null; // eslint-disable-line import/no-mutable-exports
-
-export default function init(ngModule) {
- ngModule.run($injector => {
- $http = $injector.get("$http");
- $q = $injector.get("$q");
- $rootScope = $injector.get("$rootScope");
- });
-}
-
-init.init = true;
diff --git a/client/app/services/offline-listener.js b/client/app/services/offline-listener.js
index 8556097b67..e58a9cfa5f 100644
--- a/client/app/services/offline-listener.js
+++ b/client/app/services/offline-listener.js
@@ -8,8 +8,8 @@ function addOnlineListener(notificationKey) {
window.addEventListener("online", onlineStateHandler);
}
-export default function init(ngModule) {
- ngModule.run(() => {
+export default {
+ init() {
window.addEventListener("offline", () => {
notification.warning("Please check your Internet connection.", null, {
key: "connectionNotification",
@@ -17,7 +17,5 @@ export default function init(ngModule) {
});
addOnlineListener("connectionNotification");
});
- });
-}
-
-init.init = true;
+ },
+};
diff --git a/client/app/services/policy/DefaultPolicy.js b/client/app/services/policy/DefaultPolicy.js
index 4ff4ad4fb7..58a594013a 100644
--- a/client/app/services/policy/DefaultPolicy.js
+++ b/client/app/services/policy/DefaultPolicy.js
@@ -1,12 +1,11 @@
import { isArray } from "lodash";
-import { $q } from "@/services/ng";
import { currentUser, clientConfig } from "@/services/auth";
/* eslint-disable class-methods-use-this */
export default class DefaultPolicy {
refresh() {
- return $q.resolve(this);
+ return Promise.resolve(this);
}
canCreateDataSource() {
diff --git a/package-lock.json b/package-lock.json
index b9396fdb33..9377cae78b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1032,11 +1032,6 @@
"d3-interpolate": "1"
}
},
- "@types/angular": {
- "version": "1.6.54",
- "resolved": "https://registry.npmjs.org/@types/angular/-/angular-1.6.54.tgz",
- "integrity": "sha512-xA1FuozWXeRQ7FClUbvk8ePL+dydBeDoCWRPFTHU5+8uvVtIIfLGiHA8CMkwsbddFCYnTDVbLxG85a/HBx7LtA=="
- },
"@types/eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
@@ -1049,19 +1044,6 @@
"integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==",
"dev": true
},
- "@types/lodash": {
- "version": "4.14.121",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.121.tgz",
- "integrity": "sha512-ORj7IBWj13iYufXt/VXrCNMbUuCTJfhzme5kx9U/UtcIPdJYuvPDUAlHlbNhz/8lKCLy9XGIZnGrqXOtQbPGoQ=="
- },
- "@types/lodash.frompairs": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/@types/lodash.frompairs/-/lodash.frompairs-4.0.5.tgz",
- "integrity": "sha512-7ewlRQxR/HSyp/Ed3cKZN3WrKwiYOo5OVbdTm5N4iAuiFlavvwBaIt6V7Q/Ml/Ozqz2AtQj81RUyekzKn0oC+Q==",
- "requires": {
- "@types/lodash": "*"
- }
- },
"@types/node": {
"version": "11.9.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.5.tgz",
@@ -1547,16 +1529,6 @@
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
"optional": true
},
- "angular": {
- "version": "1.5.11",
- "resolved": "https://registry.npmjs.org/angular/-/angular-1.5.11.tgz",
- "integrity": "sha1-jFunOG8VllyazzQp9ogVU6raMNY="
- },
- "angular-route": {
- "version": "1.5.11",
- "resolved": "https://registry.npmjs.org/angular-route/-/angular-route-1.5.11.tgz",
- "integrity": "sha1-SWFPOhZ/VCkeRJ/ougXTnFiSS4M="
- },
"ansi-colors": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
@@ -2153,17 +2125,6 @@
"babel-runtime": "^6.22.0"
}
},
- "babel-plugin-angularjs-annotate": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/babel-plugin-angularjs-annotate/-/babel-plugin-angularjs-annotate-0.8.2.tgz",
- "integrity": "sha512-gkPuZr4aYxhJPjqrwYlnXQIzlRagp+vxJhqaoYc3w9gBZ2/H2fKIt7a3iXoC5JQGm7ee9Lj7CWPfnfcYX/34Iw==",
- "dev": true,
- "requires": {
- "babel-code-frame": "^6.26.0",
- "babel-types": "^6.26.0",
- "simple-is": "~0.2.0"
- }
- },
"babel-plugin-istanbul": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.1.tgz",
@@ -10800,11 +10761,6 @@
}
}
},
- "jquery": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz",
- "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg=="
- },
"js-base64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
@@ -11242,11 +11198,6 @@
"integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=",
"dev": true
},
- "lodash.frompairs": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/lodash.frompairs/-/lodash.frompairs-4.0.1.tgz",
- "integrity": "sha1-vE5SB/onV8E25XNhTpZkUGsrG9I="
- },
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
@@ -12235,17 +12186,6 @@
"double-bits": "^1.1.0"
}
},
- "ngcomponent": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ngcomponent/-/ngcomponent-4.1.0.tgz",
- "integrity": "sha512-cGL3iVoqMWTpCfaIwgRKhdaGqiy2Z+CCG0cVfjlBvdqE8saj8xap9B4OTf+qwObxLVZmDTJPDgx3bN6Q/lZ7BQ==",
- "requires": {
- "@types/angular": "^1.6.39",
- "@types/lodash": "^4.14.85",
- "angular": ">=1.5.0",
- "lodash": "^4.17.4"
- }
- },
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@@ -14838,18 +14778,6 @@
"react-lifecycles-compat": "^3.0.4"
}
},
- "react2angular": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/react2angular/-/react2angular-3.2.1.tgz",
- "integrity": "sha1-Q/MHfhvM3GeF81sBAt+mhF+uJms=",
- "requires": {
- "@types/angular": ">=1.5",
- "@types/lodash.frompairs": "^4.0.3",
- "angular": ">=1.5",
- "lodash.frompairs": "^4.0.1",
- "ngcomponent": "^4.1.0"
- }
- },
"read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
@@ -15984,12 +15912,6 @@
"resolved": "https://registry.npmjs.org/signum/-/signum-0.0.0.tgz",
"integrity": "sha1-q1UbEAM1EHCnBHg/GgnF52kfnPY="
},
- "simple-is": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz",
- "integrity": "sha1-Krt1qt453rXMgVzhDmGRFkhQuvA=",
- "dev": true
- },
"simplicial-complex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/simplicial-complex/-/simplicial-complex-1.0.0.tgz",
diff --git a/package.json b/package.json
index 8358d5735b..bc16571b6b 100644
--- a/package.json
+++ b/package.json
@@ -40,8 +40,6 @@
"@types/prop-types": "^15.5.2",
"@types/react": "^16.3.13",
"@types/react-dom": "^16.0.5",
- "angular": "~1.5.8",
- "angular-route": "~1.5.8",
"antd": "^3.19.7",
"axios": "^0.19.0",
"beautifymarker": "^1.0.7",
@@ -57,7 +55,6 @@
"font-awesome": "^4.7.0",
"history": "^4.10.1",
"hoist-non-react-statics": "^3.3.0",
- "jquery": "^3.2.1",
"leaflet": "^1.2.0",
"leaflet-fullscreen": "^1.0.2",
"leaflet.markercluster": "^1.1.0",
@@ -80,7 +77,6 @@
"react-resizable": "^1.8.0",
"react-sortable-hoc": "^1.10.1",
"react-virtualized": "^9.21.2",
- "react2angular": "^3.2.1",
"tinycolor2": "^1.4.1",
"universal-router": "^8.3.0",
"use-debounce": "^3.1.0",
@@ -97,7 +93,6 @@
"babel-eslint": "^10.0.3",
"babel-jest": "^24.1.0",
"babel-loader": "^8.0.5",
- "babel-plugin-angularjs-annotate": "^0.8.2",
"babel-plugin-transform-builtin-extend": "^1.1.2",
"copy-webpack-plugin": "^4.5.3",
"css-loader": "^0.28.7",
diff --git a/webpack.config.js b/webpack.config.js
index d99fbbaca2..38f03931b9 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,11 +1,10 @@
/* eslint-disable */
-const fs = require("fs");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const WebpackBuildNotifierPlugin = require("webpack-build-notifier");
const ManifestPlugin = require("webpack-manifest-plugin");
-const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const LessPluginAutoPrefix = require("less-plugin-autoprefix");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
@@ -20,8 +19,8 @@ const redashBackend = process.env.REDASH_BACKEND || "http://localhost:5000";
const basePath = path.join(__dirname, "client");
const appPath = path.join(__dirname, "client", "app");
-const extensionsRelativePath = process.env.EXTENSIONS_DIRECTORY ||
- path.join("client", "app", "extensions");
+const extensionsRelativePath =
+ process.env.EXTENSIONS_DIRECTORY || path.join("client", "app", "extensions");
const extensionPath = path.join(__dirname, extensionsRelativePath);
const config = {
@@ -41,16 +40,14 @@ const config = {
},
resolve: {
symlinks: false,
- extensions: ['.js', '.jsx'],
+ extensions: [".js", ".jsx"],
alias: {
"@": appPath,
- "extensions": extensionPath
- },
+ extensions: extensionPath
+ }
},
plugins: [
new WebpackBuildNotifierPlugin({ title: "Redash" }),
- // Enforce angular to use jQuery instead of jqLite
- new webpack.ProvidePlugin({ "window.jQuery": "jquery" }),
// bundle only default `moment` locale (`en`)
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),
new HtmlWebpackPlugin({
@@ -68,20 +65,19 @@ const config = {
}),
new ManifestPlugin({
fileName: "asset-manifest.json",
- publicPath: "",
+ publicPath: ""
}),
new CopyWebpackPlugin([
{ from: "client/app/assets/robots.txt" },
{ from: "client/app/unsupported.html" },
{ from: "client/app/unsupportedRedirect.js" },
{ from: "client/app/assets/css/*.css", to: "styles/", flatten: true },
- { from: "node_modules/jquery/dist/jquery.min.js", to: "js/jquery.min.js" },
- { from: "client/app/assets/fonts", to: "fonts/" },
+ { from: "client/app/assets/fonts", to: "fonts/" }
])
],
optimization: {
splitChunks: {
- chunks: (chunk) => {
+ chunks: chunk => {
return chunk.name != "server";
}
}
@@ -154,7 +150,7 @@ const config = {
},
{
test: /\.geo\.json$/,
- type: 'javascript/auto',
+ type: "javascript/auto",
use: [
{
loader: "file-loader",