-
-
Save andrey-mikhailov/5448ccda70454d4f48a1a4cdea82c517 to your computer and use it in GitHub Desktop.
url_fixer.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
FROM apache/superset:1.2.0 | |
COPY ./valamis/ /app/valamis/ | |
USER root | |
# fix urls | |
RUN python /app/valamis/replace_url.py | tee /var/log/replace_url.log | |
USER superset |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from valamis.url_fixer import UrlFixer | |
prefix: str = "analytics" | |
# keyword - replacement - useQuotes | |
replacementsEverywhere: list = [ | |
("static", prefix), # js and css files | |
("api/v1", prefix), # superset APIs | |
("superset", prefix), # menu plus sign | |
("chart", prefix), # menu plus sign and menu Chart | |
("dashboard", prefix), # menu plus sign and menu Dashboard | |
("user", prefix), # menu Settings | |
("roles", prefix), # menu Settings | |
("logmodelview", prefix), # menu Settings | |
("annotationlayermodelview", prefix), # menu Settings | |
("csstemplatemodelview", prefix), # menu Settings | |
("dynamic-plugins", prefix), # menu Settings | |
("databaseview", prefix), # menu Data | |
("tablemodelview", prefix), # menu Data | |
("csvtodatabaseview", prefix), # menu Data | |
("exceltodatabaseview", prefix), # menu Data | |
("savedqueryview", prefix), # menu SQL Lab | |
("csstemplateasyncmodelview", prefix) # when creating a new dashboard | |
] | |
replacementsInPython: list = [ | |
('label=__("Databases"),', f'label=__("Databases"), href="/{prefix}/databaseview/list/",'), | |
('label=__("Annotation Layers"),', | |
f'label=__("Annotation Layers"), href="/{prefix}/annotationlayermodelview/list/",'), | |
('label=__("Charts"),', f'label=__("Charts"), href="/{prefix}/chart/list/",'), | |
('label=__("Dashboards"),', f'label=__("Dashboards"), href="/{prefix}/dashboard/list/",'), | |
('label=__("Plugins"),', f'label=__("Plugins"), href="/{prefix}/dynamic-plugins/list/",'), | |
('label=__("CSS Templates"),', f'label=__("CSS Templates"), href="/{prefix}/csstemplatemodelview/list/",'), | |
('label=__("Row level security"),', | |
f'label=__("Row level security"), href="/{prefix}/rowlevelsecurityfiltersmodelview/list/",'), | |
('label=__("Action Log"),', f'label=__("Action Log"), href="/{prefix}/logmodelview/list/",') | |
] | |
replacementsInJavascript: list = [ | |
("datasource", prefix), | |
("savedqueryviewapi", prefix), | |
("annotationmodelview", prefix), | |
("dashboardasync", prefix), | |
("sliceasync", prefix), | |
("tableschemaview", prefix) | |
] | |
simpleReplacementsInJavascript: list = [ | |
('window.location.pathname.split("/")[3]', 'window.location.pathname.split("/")[4]'), | |
('${window.location.origin}/superset/', '${window.location.origin}/' + prefix + '/superset/') | |
] | |
# TODO: override links for these elements as well. By default the features are disabled | |
# label = __("Dashboard Emails"), | |
# label = __("Chart Email Schedules"), | |
# label = __("Alerts"), | |
# label = __("Alerts & Reports"), | |
# label = __("Access requests"), | |
BASE_DIR = "/app/superset/" | |
ASSET_DIR = f"{BASE_DIR}static/assets/" | |
TEMPLATES_DIR = f"{BASE_DIR}templates/" | |
urlFixer: UrlFixer = UrlFixer() | |
urlFixer.smart_replace(replacementsEverywhere, ASSET_DIR, ".js") | |
urlFixer.smart_replace(replacementsInJavascript, ASSET_DIR, ".js") | |
urlFixer.simple_replace(simpleReplacementsInJavascript, ASSET_DIR, ".js") | |
urlFixer.smart_replace(replacementsEverywhere, ASSET_DIR, ".json") | |
urlFixer.smart_replace(replacementsEverywhere, TEMPLATES_DIR, ".html") | |
urlFixer.smart_replace(replacementsEverywhere, BASE_DIR, ".py") | |
urlFixer.simple_replace(replacementsInPython, BASE_DIR, ".py") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import filecmp | |
import os | |
import unittest | |
import pathlib | |
import shutil | |
from valamis.url_fixer import UrlFixer | |
class TestUrlFixer(unittest.TestCase): | |
urlFixer: UrlFixer = UrlFixer | |
prefix: str = "my-superset" | |
search_dir = f"{pathlib.Path(__file__).parent.absolute()}/examples/" | |
# replace keywords in a link | |
def test_replace_keywords(self): | |
replacements = [("static", self.prefix)] | |
# test double quotes | |
result = self.urlFixer.combine_with_prefix(replacements, '<a href="/static/logo"/>') | |
self.assertEqual(result, '<a href="/my-superset/static/logo"/>') | |
# test backtick quote | |
result = self.urlFixer.combine_with_prefix(replacements, '<a href=`/static/logo`/>') | |
self.assertEqual(result, '<a href=`/my-superset/static/logo`/>') | |
result = self.urlFixer.combine_with_prefix(replacements, "<a href='/static/logo'/>") | |
self.assertEqual(result, "<a href='/my-superset/static/logo'/>") | |
# ignore keywords if they are in the middle of string | |
def test_ignore_keywords(self): | |
replacements = [("static", self.prefix)] | |
orig = 'UPLOAD_FOLDER = BASE_DIR + "/app/static/uploads/"' | |
result = self.urlFixer.combine_with_prefix(replacements, orig) | |
self.assertEqual(result, orig) | |
orig = "UPLOAD_FOLDER = BASE_DIR + '/app/static/uploads/'" | |
result = self.urlFixer.combine_with_prefix(replacements, orig) | |
self.assertEqual(result, orig) | |
def test_ignore_lines_with_expose_decorator(self): | |
replacements = [("static", self.prefix)] | |
orig = '@expose("/static/")' | |
self.assertEqual( | |
self.urlFixer.combine_with_prefix(replacements, orig), | |
orig | |
) | |
def test_find_files(self): | |
self.assertListEqual( | |
sorted(self.urlFixer.find_files(self.search_dir, ".txt")), | |
[f"{self.search_dir}1.txt", f"{self.search_dir}2.txt"] | |
) | |
def test_replace_in_files(self): | |
replacements = [("static", self.prefix)] | |
temp_dir = "/tmp/superset-test/" | |
if not os.path.exists(temp_dir): | |
os.mkdir(temp_dir) | |
shutil.copyfile(f"{self.search_dir}1.txt", f"{temp_dir}1.txt") | |
#self.urlFixer.combine(replacements, temp_dir, ".txt") | |
self.urlFixer.smart_replace(replacements, temp_dir, ".txt") | |
self.assertTrue( | |
filecmp.cmp(f"{temp_dir}1.txt", f"{self.search_dir}2.txt") | |
) | |
def test_replace_appbuilder_names(self): | |
replacements = [("{{appbuilder.get_url_for_logout}}", "/my-superset/logout/")] | |
orig = '<li><a href="{{appbuilder.get_url_for_logout}}"><span class="fa fa-fw fa-sign-out"></span>{{_("Logout")}}</a></li>' | |
expected = '<li><a href="/my-superset/logout/"><span class="fa fa-fw fa-sign-out"></span>{{_("Logout")}}</a></li>' | |
self.assertEqual(self.urlFixer.replace_keyword(replacements, orig), expected) | |
def test_redirect_url(self): | |
replacements = [("appbuilder.get_url_for_login", '"/my-superset"' + " + appbuilder.get_url_for_login")] | |
orig = 'return redirect(appbuilder.get_url_for_login)' | |
expected = 'return redirect("/my-superset" + appbuilder.get_url_for_login)' | |
self.assertEqual(self.urlFixer.replace_keyword(replacements, orig), expected) | |
if __name__ == '__main__': | |
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import glob | |
class UrlFixer: | |
# replace all string starting with a single or double quote and keywords | |
@staticmethod | |
def combine_with_prefix(replacements: list, data: str) -> str: | |
for orig, prefix in replacements: | |
# skip lines with the word @expose | |
newdata = "" | |
for line in data.splitlines(True): | |
if "@expose" not in line: | |
# replace all occurrences of the required string but skip lines with @expose word | |
line = line.replace(f"'/{orig}/", f"'/{prefix}/{orig}/") | |
line = line.replace(f"`/{orig}/", f"`/{prefix}/{orig}/") | |
line = line.replace(f'"/{orig}/', f'"/{prefix}/{orig}/') | |
newdata += line | |
data = newdata | |
return data | |
@staticmethod | |
def replace_keyword(replacements: list, data: str) -> str: | |
for orig, dest in replacements: | |
data = data.replace(orig, dest) | |
return data | |
@staticmethod | |
def find_files(path: str, ext: str) -> list: | |
if not ext.startswith('.'): | |
raise Exception("extension must be start wit a dot") | |
if not path.endswith('/'): | |
raise Exception("path must be ended with a slash") | |
return glob.glob(f'{path}**/*{ext}', recursive=True) | |
@staticmethod | |
def simple_replace(replacements: list, path: str, ext: str) -> None: | |
UrlFixer._run(replacements, path, ext, UrlFixer.replace_keyword) | |
@staticmethod | |
# Wrap the string with slashes and add double, single quoutes or | |
# tick to the beginning of the string for searching | |
def smart_replace(replacements: list, path: str, ext: str) -> None: | |
UrlFixer._run(replacements, path, ext, UrlFixer.combine_with_prefix) | |
@staticmethod | |
def _run(replacements: list, path: str, ext: str, func) -> None: | |
for filename in UrlFixer.find_files(path, ext): | |
fin = open(filename, "rt") | |
content = fin.read() | |
new_content = func(replacements, content) | |
fin.close() | |
if content == new_content: | |
continue | |
print(f'Writing new content to the file: {filename}') | |
fin = open(filename, "wt") | |
fin.write(new_content) | |
fin.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I published these codes to github project https://github.com/andrey-mikhailov/superset-custom-path