From ffb974c1019af5ce4bc7c3c49a16a13cbe03e9e5 Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Wed, 7 Oct 2020 13:38:57 +0200 Subject: [PATCH 001/230] push up ABC class --- posthog.json | 0 posthog/plugins/__init__.py | 81 +++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 posthog.json create mode 100644 posthog/plugins/__init__.py diff --git a/posthog.json b/posthog.json new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py new file mode 100644 index 0000000000000..0cc1f208aed38 --- /dev/null +++ b/posthog/plugins/__init__.py @@ -0,0 +1,81 @@ +import importlib +import os +import tempfile +import zipfile +from abc import ABC, abstractmethod + +import pip +import requests + +PLUGIN_PATH = os.path.join("posthog", "plugins") +URL_TEMPLATE = "{repo}/archive/{branch}.zip" +DEFAULT_BRANCHES = ["main", "master"] +PATH = os.path.abspath(os.getcwd()) +ABS_PLUGIN_PATH = os.path.join(PATH, PLUGIN_PATH) + +modules = [] + + +def install(reqs): + if hasattr(pip, "main"): + pip.main(["install", "-r", reqs]) + else: + pip._internal.main(["install", "-r", reqs]) + + +def get_installed_plugins(): + plugins = [ + x for x in os.listdir(ABS_PLUGIN_PATH) if os.path.isdir(os.path.join(ABS_PLUGIN_PATH, x)) and x != "__pycache__" + ] + return plugins + + +def import_plugin(plugin): + plugin_root = ".".join(os.path.split(PLUGIN_PATH)) + plugin_module_name = plugin_root + "." + plugin + try: + importlib.import_module(plugin_module_name) + except Exception as e: + return + return plugin_module_name + + +def load_plugins(plugins): + for repo in plugins: + download_plugin(repo) + + plugins = get_installed_plugins() + for plugin in plugins: + req_file = os.path.join(ABS_PLUGIN_PATH, plugin, "requirements.txt") + install(req_file) + module = import_plugin(plugin) + modules.append(module) + + +def download_plugin(repo): + for branch in DEFAULT_BRANCHES: + try: + url = URL_TEMPLATE.format(repo=repo, branch=branch) + r = requests.get(url) + break + except requests.RequestException: + continue + with tempfile.TemporaryFile() as f: + f.write(r.content) + with zipfile.ZipFile(f, "r") as zip_ref: + plugin_path = os.path.join(PATH, PLUGIN_PATH) + zip_ref.extractall(plugin_path) + + +class PluginBaseClass(ABC): + @abstractmethod + def process_event(self, event): + pass + + @abstractmethod + def process_person(self, event): + pass + + @abstractmethod + def process_identify(self, event): + pass From 9cd8318a4803cf5b44ea32277c44fe574728fbde Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Wed, 7 Oct 2020 14:33:04 +0200 Subject: [PATCH 002/230] plugin progress --- posthog/plugins/__init__.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 0cc1f208aed38..8c41c52ecb9e2 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -1,8 +1,11 @@ +import datetime import importlib import os import tempfile import zipfile from abc import ABC, abstractmethod +from dataclasses import dataclass +from typing import Any, Dict import pip import requests @@ -67,6 +70,28 @@ def download_plugin(repo): zip_ref.extractall(plugin_path) +def get_plugin_modules(): + return PluginBaseClass.__subclasses__() + + +def exec_event_plugins(event): + mods = get_plugin_modules() + for p in mods: + f = getattr(p, "process_event") + event = f(event) + return event + + +@dataclass +class PosthogEvent: + ip: str + site_url: str + event: str + distinct_id: str + properties: Dict[Any, Any] + timestamp: datetime.datetime + + class PluginBaseClass(ABC): @abstractmethod def process_event(self, event): From 41076d79737c65b329ebcc991558cb214d5cedbd Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Wed, 7 Oct 2020 14:33:40 +0200 Subject: [PATCH 003/230] blah --- posthog/apps.py | 9 +++++++++ posthog/tasks/process_event.py | 18 ++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/posthog/apps.py b/posthog/apps.py index cb945bc7b5af6..c946a15db8dbe 100644 --- a/posthog/apps.py +++ b/posthog/apps.py @@ -1,9 +1,11 @@ +import json import os import posthoganalytics from django.apps import AppConfig from django.conf import settings +from posthog.plugins import load_plugins from posthog.utils import get_git_branch, get_git_commit, get_machine_id from posthog.version import VERSION @@ -15,6 +17,13 @@ class PostHogConfig(AppConfig): def ready(self): posthoganalytics.api_key = "sTMFPsFhdP1Ssg" posthoganalytics.personal_api_key = os.environ.get("POSTHOG_PERSONAL_API_KEY") + + # Load plugins + with open("posthog.json", "r") as f: + conf = json.loads(f.read()) + plugins = conf["plugins"] + load_plugins(plugins) + if settings.DEBUG: # log development server launch to posthog if os.getenv("RUN_MAIN") == "true": diff --git a/posthog/tasks/process_event.py b/posthog/tasks/process_event.py index c7f78d25dea03..33a3882dbe82a 100644 --- a/posthog/tasks/process_event.py +++ b/posthog/tasks/process_event.py @@ -10,6 +10,7 @@ from sentry_sdk import capture_exception from posthog.models import Element, Event, Person, Team, User +from posthog.plugins import PosthogEvent, exec_event_plugins def _alias(previous_distinct_id: str, distinct_id: str, team_id: int, retry_if_failed: bool = True,) -> None: @@ -220,13 +221,26 @@ def process_event( _set_is_identified(team_id=team_id, distinct_id=distinct_id) properties = data.get("properties", data.get("$set", {})) + event = data["event"] - _capture( + plugin_event = PosthogEvent( ip=ip, site_url=site_url, team_id=team_id, - event=data["event"], + event=event, distinct_id=distinct_id, properties=properties, timestamp=handle_timestamp(data, now, sent_at), ) + + e = exec_event_plugins(plugin_event) + + _capture( + ip=e.ip, + site_url=e.site_url, + team_id=e.team_id, + event=e.event, + distinct_id=e.distinct_id, + properties=e.properties, + timestamp=handle_timestamp(data, now, sent_at), + ) From 66fed09e1b16caff8f23edf91cfc577e9ce79145 Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Wed, 7 Oct 2020 16:34:14 +0200 Subject: [PATCH 004/230] add posthog config for plugins --- posthog.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/posthog.json b/posthog.json index e69de29bb2d1d..5d3d918a7026b 100644 --- a/posthog.json +++ b/posthog.json @@ -0,0 +1,3 @@ +{ + "plugins": ["https://github.com/fuziontech/helloworldplugin"] +} From 862a7d82abcbb904eeea1b0fa13949848d1355d1 Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Wed, 7 Oct 2020 16:36:30 +0200 Subject: [PATCH 005/230] test gitignore --- .gitignore | 1 + package.json | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 2b4466b007e4c..4f79b75f1458b 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ yarn-error.log yalc.lock cypress/screenshots/* docker-compose.prod.yml +posthog/plugins/*/ \ No newline at end of file diff --git a/package.json b/package.json index d43da41a33409..2564a9e4101ac 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "d3": "^5.15.0", "d3-sankey": "^0.12.3", "editor": "^1.0.0", - "eslint-plugin-cypress": "^2.11.1", "funnel-graph-js": "^1.4.1", "kea": "^2.2.0", "kea-loaders": "^0.3.0", @@ -83,8 +82,6 @@ "concurrently": "^5.3.0", "css-loader": "^3.4.2", "cssnano": "^4.1.10", - "cypress": "^4.11.0", - "cypress-terminal-report": "^1.4.1", "eslint": "^7.8.0", "eslint-config-prettier": "^6.11.0", "eslint-plugin-prettier": "^3.1.4", From bbfc09162ba9e66e43b27b70ca52515b0011a133 Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Wed, 7 Oct 2020 17:29:54 +0200 Subject: [PATCH 006/230] new functionality for plugins --- .dockerignore | 1 + dev.Dockerfile | 1 + posthog/plugins/__init__.py | 35 ++++++++++++++++++++++++++-------- posthog/tasks/process_event.py | 4 ++-- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/.dockerignore b/.dockerignore index 5ca4b6168b2db..f5e7562caf969 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,6 +7,7 @@ !manage.py !gunicorn.config.py !babel.config.js +!posthog.json !package.json !yarn.lock !webpack.config.js diff --git a/dev.Dockerfile b/dev.Dockerfile index a67b5ab0e6df5..cf1c8fdf9c4af 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -21,6 +21,7 @@ RUN mkdir /code/requirements/ COPY requirements/dev.txt /code/requirements/ RUN pip install -r requirements/dev.txt --compile +COPY posthog.json /code/ COPY package.json /code/ COPY yarn.lock /code/ COPY webpack.config.js /code/ diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 8c41c52ecb9e2..57be5f3c0f7b9 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -5,7 +5,7 @@ import zipfile from abc import ABC, abstractmethod from dataclasses import dataclass -from typing import Any, Dict +from typing import Any, Callable, Dict import pip import requests @@ -74,14 +74,29 @@ def get_plugin_modules(): return PluginBaseClass.__subclasses__() -def exec_event_plugins(event): +def exec_plugins(event): mods = get_plugin_modules() - for p in mods: - f = getattr(p, "process_event") - event = f(event) + for mod in mods: + event = exec_plugin(mod, event, "process_event") + if event.event == "$identify": + event = exec_plugin(mod, event, "process_identify") + if event.event == "$create_alias": + event = exec_plugin(mod, event, "process_alias") + + +def exec_plugin(module, event, method="process_event"): + f = getattr(module, method) + event = f(event) return event +def schedule_tasks(): + mods = get_plugin_modules() + for mod in mods: + f = getattr(mod, "schedule_jobs") + f() + + @dataclass class PosthogEvent: ip: str @@ -94,13 +109,17 @@ class PosthogEvent: class PluginBaseClass(ABC): @abstractmethod - def process_event(self, event): + def schedule_jobs(self, sender): + pass + + @abstractmethod + def process_event(self, event: PosthogEvent): pass @abstractmethod - def process_person(self, event): + def process_alias(self, event: PosthogEvent): pass @abstractmethod - def process_identify(self, event): + def process_identify(self, event: PosthogEvent): pass diff --git a/posthog/tasks/process_event.py b/posthog/tasks/process_event.py index 33a3882dbe82a..be109725b14ba 100644 --- a/posthog/tasks/process_event.py +++ b/posthog/tasks/process_event.py @@ -10,7 +10,7 @@ from sentry_sdk import capture_exception from posthog.models import Element, Event, Person, Team, User -from posthog.plugins import PosthogEvent, exec_event_plugins +from posthog.plugins import PosthogEvent, exec_plugins def _alias(previous_distinct_id: str, distinct_id: str, team_id: int, retry_if_failed: bool = True,) -> None: @@ -233,7 +233,7 @@ def process_event( timestamp=handle_timestamp(data, now, sent_at), ) - e = exec_event_plugins(plugin_event) + e = exec_plugins(plugin_event) _capture( ip=e.ip, From 632956eff051952209b9d1507ef894b16a61e296 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Wed, 7 Oct 2020 22:47:46 +0200 Subject: [PATCH 007/230] support local plugin paths --- posthog/plugins/__init__.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 57be5f3c0f7b9..2da6a3077906a 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -45,7 +45,10 @@ def import_plugin(plugin): def load_plugins(plugins): for repo in plugins: - download_plugin(repo) + if repo.startswith("http:") or repo.startswith("https:"): + download_plugin(repo) + else: + symlink_plugin(repo) plugins = get_installed_plugins() for plugin in plugins: @@ -70,6 +73,15 @@ def download_plugin(repo): zip_ref.extractall(plugin_path) +def symlink_plugin(path): + real_path = os.path.realpath(path) + path_parts = os.path.split(real_path) + plugin_path = os.path.join(PATH, PLUGIN_PATH, path_parts[-1]) + if os.path.exists(plugin_path) or os.path.islink(plugin_path): + os.unlink(plugin_path) + os.symlink(real_path, plugin_path) + + def get_plugin_modules(): return PluginBaseClass.__subclasses__() @@ -103,6 +115,7 @@ class PosthogEvent: site_url: str event: str distinct_id: str + team_id: int properties: Dict[Any, Any] timestamp: datetime.datetime From 150630680769838f7d514844d6a616a178c8ed76 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Wed, 7 Oct 2020 22:48:30 +0200 Subject: [PATCH 008/230] also ignore symlinks --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4f79b75f1458b..964ff9427cc5f 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,4 @@ yarn-error.log yalc.lock cypress/screenshots/* docker-compose.prod.yml -posthog/plugins/*/ \ No newline at end of file +posthog/plugins/* \ No newline at end of file From eceac05799173e75e7f706ebde1cde13f7032a7e Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Wed, 7 Oct 2020 22:55:35 +0200 Subject: [PATCH 009/230] add positional argument --- posthog/plugins/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 2da6a3077906a..773c2c459c230 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -98,7 +98,7 @@ def exec_plugins(event): def exec_plugin(module, event, method="process_event"): f = getattr(module, method) - event = f(event) + event = f(module, event) return event From a7a10d33225138d7e5cf9dc82d0cf39f6a1cd7a2 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Wed, 7 Oct 2020 23:49:57 +0200 Subject: [PATCH 010/230] fixes --- posthog/plugins/__init__.py | 2 ++ posthog/tasks/process_event.py | 23 ++++++++++++----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 773c2c459c230..84cc079be9374 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -95,6 +95,8 @@ def exec_plugins(event): if event.event == "$create_alias": event = exec_plugin(mod, event, "process_alias") + return event + def exec_plugin(module, event, method="process_event"): f = getattr(module, method) diff --git a/posthog/tasks/process_event.py b/posthog/tasks/process_event.py index be109725b14ba..1abd60becec98 100644 --- a/posthog/tasks/process_event.py +++ b/posthog/tasks/process_event.py @@ -233,14 +233,15 @@ def process_event( timestamp=handle_timestamp(data, now, sent_at), ) - e = exec_plugins(plugin_event) - - _capture( - ip=e.ip, - site_url=e.site_url, - team_id=e.team_id, - event=e.event, - distinct_id=e.distinct_id, - properties=e.properties, - timestamp=handle_timestamp(data, now, sent_at), - ) + event = exec_plugins(plugin_event) + + if event: + _capture( + ip=event.ip, + site_url=event.site_url, + team_id=event.team_id, + event=event.event, + distinct_id=event.distinct_id, + properties=event.properties, + timestamp=handle_timestamp(data, now, sent_at), + ) From 59736dd15fca1039041baf36d95e60edf1264939 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Thu, 8 Oct 2020 01:43:30 +0200 Subject: [PATCH 011/230] small fixes --- posthog/plugins/__init__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 84cc079be9374..4d8622196a030 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -2,6 +2,7 @@ import importlib import os import tempfile +import traceback import zipfile from abc import ABC, abstractmethod from dataclasses import dataclass @@ -39,7 +40,11 @@ def import_plugin(plugin): try: importlib.import_module(plugin_module_name) except Exception as e: - return + print('šŸ”» Can not import plugin "{}"'.format(plugin)) + trace_back = traceback.format_exc() + message = str(e) + " " + str(trace_back) + print(message) + return None return plugin_module_name @@ -55,7 +60,8 @@ def load_plugins(plugins): req_file = os.path.join(ABS_PLUGIN_PATH, plugin, "requirements.txt") install(req_file) module = import_plugin(plugin) - modules.append(module) + if module: + modules.append(module) def download_plugin(repo): From 28fa6f50c325b30036bb180e01288bd8056b774f Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Thu, 8 Oct 2020 11:35:45 +0200 Subject: [PATCH 012/230] config polish --- posthog.json | 11 +++++- posthog/api/capture.py | 2 +- posthog/apps.py | 5 +-- posthog/plugins/__init__.py | 70 +++++++++++++++++++++++++++++-------- yarn.lock | 60 +++++++++++++++++++++++++++++-- 5 files changed, 126 insertions(+), 22 deletions(-) diff --git a/posthog.json b/posthog.json index 5d3d918a7026b..15df2c2d9fdb0 100644 --- a/posthog.json +++ b/posthog.json @@ -1,3 +1,12 @@ { - "plugins": ["https://github.com/fuziontech/helloworldplugin"] + "plugins": [ + { + "url": "https://github.com/fuziontech/helloworldplugin", + "config": { + "api_key": "WAERAWERWAERAWE", + "amount_key": "amount", + "normalized_amount_prop": "" + } + } + ] } diff --git a/posthog/api/capture.py b/posthog/api/capture.py index 310f480f340bd..16bc53526f575 100644 --- a/posthog/api/capture.py +++ b/posthog/api/capture.py @@ -162,7 +162,7 @@ def get_event(request): ), ) - process_event.delay( + process_event( distinct_id=distinct_id, ip=get_ip_address(request), site_url=request.build_absolute_uri("/")[:-1], diff --git a/posthog/apps.py b/posthog/apps.py index c946a15db8dbe..6a39ef9f75985 100644 --- a/posthog/apps.py +++ b/posthog/apps.py @@ -19,10 +19,7 @@ def ready(self): posthoganalytics.personal_api_key = os.environ.get("POSTHOG_PERSONAL_API_KEY") # Load plugins - with open("posthog.json", "r") as f: - conf = json.loads(f.read()) - plugins = conf["plugins"] - load_plugins(plugins) + load_plugins() if settings.DEBUG: # log development server launch to posthog diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 4d8622196a030..45ef4478dd41a 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -1,12 +1,14 @@ import datetime import importlib +import json import os +import re import tempfile import traceback import zipfile from abc import ABC, abstractmethod from dataclasses import dataclass -from typing import Any, Callable, Dict +from typing import Any, Dict, List import pip import requests @@ -17,8 +19,6 @@ PATH = os.path.abspath(os.getcwd()) ABS_PLUGIN_PATH = os.path.join(PATH, PLUGIN_PATH) -modules = [] - def install(reqs): if hasattr(pip, "main"): @@ -48,20 +48,18 @@ def import_plugin(plugin): return plugin_module_name -def load_plugins(plugins): +def load_plugins(): + plugins = get_plugin_config().order for repo in plugins: - if repo.startswith("http:") or repo.startswith("https:"): - download_plugin(repo) + if not repo.path: + download_plugin(repo.url) else: - symlink_plugin(repo) - + symlink_plugin(repo.path) plugins = get_installed_plugins() for plugin in plugins: req_file = os.path.join(ABS_PLUGIN_PATH, plugin, "requirements.txt") install(req_file) - module = import_plugin(plugin) - if module: - modules.append(module) + import_plugin(plugin) def download_plugin(repo): @@ -100,11 +98,34 @@ def exec_plugins(event): event = exec_plugin(mod, event, "process_identify") if event.event == "$create_alias": event = exec_plugin(mod, event, "process_alias") - return event -def exec_plugin(module, event, method="process_event"): +def get_plugin_config(): + with open("posthog.json", "r") as f: + conf = json.loads(f.read()).get("plugins", None) + plugin_configs = PluginConfigs(order=[], dict={}) + for plugin in conf: + ppc = PluginConfig( + path=plugin.get("path", None), url=plugin.get("url", None), config=plugin.get("config", None) + ) + plugin_configs.dict[ppc.name] = ppc + plugin_configs.order.append(ppc) + return plugin_configs + + +def get_module_config(module): + module_name = module.__module__.split(".")[-1] + module_name = re.sub("-main$", "", module_name) + module_name = re.sub("-master$", "", module_name) + plugin_config = get_plugin_config() + module_config = plugin_config.dict[module_name] + return module_config + + +def exec_plugin(Module, event, method="process_event"): + mc = get_module_config(Module) + module = Module(mc) f = getattr(module, method) event = f(module, event) return event @@ -112,11 +133,32 @@ def exec_plugin(module, event, method="process_event"): def schedule_tasks(): mods = get_plugin_modules() - for mod in mods: + for Mod in mods: + mc = get_module_config(Mod) + mod = Mod(mc) f = getattr(mod, "schedule_jobs") f() +@dataclass +class PluginConfig: + url: str + path: str + config: Dict[Any, Any] + + @property + def name(self): + if self.path: + return os.path.split(self.path)[-1] + return self.url.split("/")[-1] + + +@dataclass +class PluginConfigs: + order: List[PluginConfig] + dict: Dict[str, PluginConfig] + + @dataclass class PosthogEvent: ip: str diff --git a/yarn.lock b/yarn.lock index 13c504097cfbe..9403adc61a445 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1081,6 +1081,11 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/css-font-loading-module@0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@types/css-font-loading-module/-/css-font-loading-module-0.0.4.tgz#94a835e27d1af444c65cba88523533c174463d64" + integrity sha512-ENdXf7MW4m9HeDojB2Ukbi7lYMIuQNBHVf98dbzaiG4EEJREBd6oleVAjrLRCrp7dm6CK1mmdmU9tcgF61acbw== + "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -1104,6 +1109,14 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/hoist-non-react-statics@^3.3.0": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/html-minifier-terser@^5.0.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880" @@ -1144,6 +1157,23 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== +"@types/react-dom@^16.9.8": + version "16.9.8" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423" + integrity sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA== + dependencies: + "@types/react" "*" + +"@types/react-redux@^7.1.9": + version "7.1.9" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.9.tgz#280c13565c9f13ceb727ec21e767abe0e9b4aec3" + integrity sha512-mpC0jqxhP4mhmOl3P4ipRsgTgbNofMRXJb08Ms6gekViLj61v1hOZEKWDCyWsdONr6EjEA6ZHXC446wdywDe0w== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + "@types/react-syntax-highlighter@^11.0.4": version "11.0.5" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz#0d546261b4021e1f9d85b50401c0a42acb106087" @@ -1427,6 +1457,11 @@ resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== +"@xstate/fsm@^1.4.0": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@xstate/fsm/-/fsm-1.5.1.tgz#b1ebedc77b11e4956fda0a6429cef986cd17979e" + integrity sha512-t8blLI0e90jCE71yUQHLH5uEljD1v0aC8PKkHUtNWEnXt81EOj7xBjnlwmaHn/8cmE2aSygiBElUpkRZUV9hXA== + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -6409,6 +6444,11 @@ mississippi@^3.0.0: stream-each "^1.1.0" through2 "^2.0.0" +mitt@^1.1.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-1.2.0.tgz#cb24e6569c806e31bd4e3995787fe38a04fdf90d" + integrity sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw== + mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -6925,7 +6965,7 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" -pako@~1.0.5: +pako@^1.0.11, pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== @@ -8331,7 +8371,7 @@ readdirp@~3.4.0: dependencies: picomatch "^2.2.1" -redux@^4.0.5: +redux@^4.0.0, redux@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== @@ -8604,6 +8644,22 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rrweb-snapshot@^0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-0.8.3.tgz#1d29599d8c1c342aef5dd377145daaf07cab0e0a" + integrity sha512-zAAmCg2BHTGxbFoPj9+s1WnBvOIvd1TYf0CegGXbt8PaSqwa8iEfBUsV/zKksnGOAvRZOQ8vVmeVECLXtw/v2w== + +rrweb@^0.9.7: + version "0.9.7" + resolved "https://registry.yarnpkg.com/rrweb/-/rrweb-0.9.7.tgz#6632918dc61f2979d86d2b892810678408e7e11f" + integrity sha512-n3TEe1OyEtOGFvh4D513dR4jKVBlw//flCaFSwNFBreod/WKNRuqyZ7A2SCbY3XJbQ8JRFTfGa1jGsKvhiqTdQ== + dependencies: + "@types/css-font-loading-module" "0.0.4" + "@xstate/fsm" "^1.4.0" + mitt "^1.1.3" + pako "^1.0.11" + rrweb-snapshot "^0.8.2" + rtl-css-js@^1.9.0: version "1.14.0" resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.14.0.tgz#daa4f192a92509e292a0519f4b255e6e3c076b7d" From 1bc6a7a208c21502add03154575be4e5f68407bc Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Thu, 8 Oct 2020 11:59:24 +0200 Subject: [PATCH 013/230] config passed to posthog plugin --- posthog.json | 4 +--- posthog/plugins/__init__.py | 14 ++++++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/posthog.json b/posthog.json index 15df2c2d9fdb0..25b6a9f13dc6d 100644 --- a/posthog.json +++ b/posthog.json @@ -3,9 +3,7 @@ { "url": "https://github.com/fuziontech/helloworldplugin", "config": { - "api_key": "WAERAWERWAERAWE", - "amount_key": "amount", - "normalized_amount_prop": "" + "bar": "foo" } } ] diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 45ef4478dd41a..aeb285d344006 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -6,7 +6,6 @@ import tempfile import traceback import zipfile -from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Any, Dict, List @@ -119,7 +118,7 @@ def get_module_config(module): module_name = re.sub("-main$", "", module_name) module_name = re.sub("-master$", "", module_name) plugin_config = get_plugin_config() - module_config = plugin_config.dict[module_name] + module_config = plugin_config.dict[module_name].config return module_config @@ -127,7 +126,7 @@ def exec_plugin(Module, event, method="process_event"): mc = get_module_config(Module) module = Module(mc) f = getattr(module, method) - event = f(module, event) + event = f(event) return event @@ -170,19 +169,18 @@ class PosthogEvent: timestamp: datetime.datetime -class PluginBaseClass(ABC): - @abstractmethod +class PluginBaseClass: + def __init__(self, config): + self.config = config + def schedule_jobs(self, sender): pass - @abstractmethod def process_event(self, event: PosthogEvent): pass - @abstractmethod def process_alias(self, event: PosthogEvent): pass - @abstractmethod def process_identify(self, event: PosthogEvent): pass From 63813226cfead4b6e47cb11014a1c79b8766fd0f Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Thu, 8 Oct 2020 12:03:33 +0200 Subject: [PATCH 014/230] ooooooops --- posthog/api/capture.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/api/capture.py b/posthog/api/capture.py index 16bc53526f575..310f480f340bd 100644 --- a/posthog/api/capture.py +++ b/posthog/api/capture.py @@ -162,7 +162,7 @@ def get_event(request): ), ) - process_event( + process_event.delay( distinct_id=distinct_id, ip=get_ip_address(request), site_url=request.build_absolute_uri("/")[:-1], From a4c211ec5af859ba7d3fb72481a2424ef85ee19d Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Thu, 8 Oct 2020 12:10:54 +0200 Subject: [PATCH 015/230] symlink fix --- posthog/plugins/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index aeb285d344006..80de365e97f7f 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -148,7 +148,7 @@ class PluginConfig: @property def name(self): if self.path: - return os.path.split(self.path)[-1] + return os.path.split(os.path.realpath(self.path))[-1] return self.url.split("/")[-1] From a10a6071006916f961b13408b5e41c5a7198a8a2 Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Thu, 8 Oct 2020 12:30:49 +0200 Subject: [PATCH 016/230] cleanse dir before loading --- posthog/plugins/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 80de365e97f7f..04eb982e6c4c2 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -3,6 +3,7 @@ import json import os import re +import shutil import tempfile import traceback import zipfile @@ -19,6 +20,13 @@ ABS_PLUGIN_PATH = os.path.join(PATH, PLUGIN_PATH) +def cleanse_plugin_directory(): + for x in os.listdir(ABS_PLUGIN_PATH): + if os.path.isdir(os.path.join(ABS_PLUGIN_PATH, x)) and x != "__pycache__": + dir = os.path.join(ABS_PLUGIN_PATH, x) + shutil.rmtree(dir) + + def install(reqs): if hasattr(pip, "main"): pip.main(["install", "-r", reqs]) @@ -48,6 +56,7 @@ def import_plugin(plugin): def load_plugins(): + cleanse_plugin_directory() plugins = get_plugin_config().order for repo in plugins: if not repo.path: From fc40fe3044d8831c98ee1a27f55263d0cef7dc15 Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Thu, 8 Oct 2020 12:40:45 +0200 Subject: [PATCH 017/230] add cache to plugins --- posthog/plugins/__init__.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 04eb982e6c4c2..e7efc5062dae3 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -10,8 +10,11 @@ from dataclasses import dataclass from typing import Any, Dict, List +import fakeredis # type: ignore import pip +import redis import requests +from django.conf import settings PLUGIN_PATH = os.path.join("posthog", "plugins") URL_TEMPLATE = "{repo}/archive/{branch}.zip" @@ -178,9 +181,31 @@ class PosthogEvent: timestamp: datetime.datetime +class PluginCache: + def __init__(self, plugin_name: str): + self.plugin_name = plugin_name + if settings.TEST: + self.redis = fakeredis.FakeStrictRedis() + elif settings.REDIS_URL: + self.redis = redis.from_url(settings.REDIS_URL, db=0) + + def format_key(self, key): + key = "{plugin_name}_{key}".format(plugin_name=self.plugin_name, key=key) + return key + + def set(self, key: str, value: Any): + key = self.format_key(key) + self.redis.set(key, value) + + def get(self, key) -> Any: + key = self.format_key(key) + return self.redis.get(key) + + class PluginBaseClass: - def __init__(self, config): - self.config = config + def __init__(self, config: PluginConfig): + self.config = config.config + self.cache = PluginCache(plugin_name=config.name) def schedule_jobs(self, sender): pass From 2e29a7221d29239daeee328c08448c357a452f43 Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Thu, 8 Oct 2020 12:50:33 +0200 Subject: [PATCH 018/230] pickle the goods --- posthog/plugins/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index e7efc5062dae3..cb94d18a1eaf5 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -2,6 +2,7 @@ import importlib import json import os +import pickle import re import shutil import tempfile @@ -195,11 +196,14 @@ def format_key(self, key): def set(self, key: str, value: Any): key = self.format_key(key) + value = pickle.dumps(value) self.redis.set(key, value) def get(self, key) -> Any: key = self.format_key(key) - return self.redis.get(key) + str_value = self.redis.get(key) + value = pickle.loads(str_value) + return value class PluginBaseClass: From b5f655214ab75e73981a205733267901d66803a3 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Thu, 8 Oct 2020 12:53:16 +0200 Subject: [PATCH 019/230] unlink symlink --- posthog/plugins/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index cb94d18a1eaf5..5938a22d5085d 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -28,7 +28,10 @@ def cleanse_plugin_directory(): for x in os.listdir(ABS_PLUGIN_PATH): if os.path.isdir(os.path.join(ABS_PLUGIN_PATH, x)) and x != "__pycache__": dir = os.path.join(ABS_PLUGIN_PATH, x) - shutil.rmtree(dir) + if os.path.islink(dir): + os.unlink(dir) + else: + shutil.rmtree(dir) def install(reqs): From ace537524a2011cb7e404d343f1cf4fa12864a09 Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Thu, 8 Oct 2020 12:53:43 +0200 Subject: [PATCH 020/230] pass full config --- posthog/plugins/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index cb94d18a1eaf5..6f73aab48f96f 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -131,7 +131,7 @@ def get_module_config(module): module_name = re.sub("-main$", "", module_name) module_name = re.sub("-master$", "", module_name) plugin_config = get_plugin_config() - module_config = plugin_config.dict[module_name].config + module_config = plugin_config.dict[module_name] return module_config From 4d3375440ac837c96b945548216090fec7565bbc Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Thu, 8 Oct 2020 12:54:49 +0200 Subject: [PATCH 021/230] unlink even if link points to nothing --- posthog/plugins/__init__.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 5938a22d5085d..5beab22b94600 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -26,12 +26,11 @@ def cleanse_plugin_directory(): for x in os.listdir(ABS_PLUGIN_PATH): - if os.path.isdir(os.path.join(ABS_PLUGIN_PATH, x)) and x != "__pycache__": + if os.path.islink(dir): + os.unlink(dir) + elif os.path.isdir(os.path.join(ABS_PLUGIN_PATH, x)) and x != "__pycache__": dir = os.path.join(ABS_PLUGIN_PATH, x) - if os.path.islink(dir): - os.unlink(dir) - else: - shutil.rmtree(dir) + shutil.rmtree(dir) def install(reqs): From a1f70502b7166af66cd96c9a88c0019a5f1037dd Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Thu, 8 Oct 2020 12:55:46 +0200 Subject: [PATCH 022/230] fix fix --- posthog/plugins/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 1d2cb312d86dc..35125cab02b59 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -26,10 +26,10 @@ def cleanse_plugin_directory(): for x in os.listdir(ABS_PLUGIN_PATH): + dir = os.path.join(ABS_PLUGIN_PATH, x) if os.path.islink(dir): os.unlink(dir) - elif os.path.isdir(os.path.join(ABS_PLUGIN_PATH, x)) and x != "__pycache__": - dir = os.path.join(ABS_PLUGIN_PATH, x) + elif os.path.isdir(dir) and x != "__pycache__": shutil.rmtree(dir) From 41e880d7658296851609f0cbca49acdc6a47992a Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Thu, 8 Oct 2020 13:07:06 +0200 Subject: [PATCH 023/230] return none if value is empty --- posthog/plugins/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 504cc5414613a..28981dc177577 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -205,6 +205,8 @@ def set(self, key: str, value: Any): def get(self, key) -> Any: key = self.format_key(key) str_value = self.redis.get(key) + if not str_value: + return None value = pickle.loads(str_value) return value From 9eb7142e284a285b26a4cd461f2536ba93cc43d3 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Thu, 8 Oct 2020 17:42:26 +0200 Subject: [PATCH 024/230] plugin model --- latest_migrations.manifest | 2 +- posthog/api/__init__.py | 2 ++ posthog/api/plugin.py | 42 ++++++++++++++++++++++++++++++ posthog/migrations/0086_plugins.py | 27 +++++++++++++++++++ posthog/models/__init__.py | 1 + posthog/models/plugin.py | 11 ++++++++ 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 posthog/api/plugin.py create mode 100644 posthog/migrations/0086_plugins.py create mode 100644 posthog/models/plugin.py diff --git a/latest_migrations.manifest b/latest_migrations.manifest index c61a52602a221..64dd18e7ea90b 100644 --- a/latest_migrations.manifest +++ b/latest_migrations.manifest @@ -2,7 +2,7 @@ admin: 0003_logentry_add_action_flag_choices auth: 0011_update_proxy_permissions contenttypes: 0002_remove_content_type_name ee: 0002_hook -posthog: 0085_org_models +posthog: 0086_plugins rest_hooks: 0002_swappable_hook_model sessions: 0001_initial social_django: 0008_partial_timestamp diff --git a/posthog/api/__init__.py b/posthog/api/__init__.py index 552e022cfa732..755ac815e25f5 100644 --- a/posthog/api/__init__.py +++ b/posthog/api/__init__.py @@ -19,6 +19,7 @@ paths, person, personal_api_key, + plugin, team_user, ) @@ -44,6 +45,7 @@ def api_not_found(request): router.register(r"dashboard", dashboard.DashboardsViewSet) router.register(r"dashboard_item", dashboard.DashboardItemsViewSet) router.register(r"cohort", cohort.CohortViewSet) +router.register(r"plugin", plugin.PluginViewSet) router.register(r"personal_api_keys", personal_api_key.PersonalAPIKeyViewSet, basename="personal_api_keys") router.register(r"team/user", team_user.TeamUserViewSet) diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py new file mode 100644 index 0000000000000..4c35a64b389a5 --- /dev/null +++ b/posthog/api/plugin.py @@ -0,0 +1,42 @@ +from typing import Any, Dict + +from django.db.models import QuerySet +from rest_framework import request, serializers, viewsets + +from posthog.models import Plugin + + +class PluginSerializer(serializers.ModelSerializer): + class Meta: + model = Plugin + fields = [ + "id", + "name", + "url", + "enabled", + "order", + "config", + ] + + def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: + request = self.context["request"] + plugin = Plugin.objects.create(team=request.user.team, **validated_data) + return plugin + + def update(self, plugin: Plugin, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: # type: ignore + plugin.name = validated_data.get("name", plugin.name) + plugin.url = validated_data.get("url", plugin.url) + plugin.enabled = validated_data.get("enabled", plugin.enabled) + plugin.config = validated_data.get("config", plugin.config) + plugin.order = validated_data.get("order", plugin.order) + plugin.save() + return plugin + + +class PluginViewSet(viewsets.ModelViewSet): + queryset = Plugin.objects.all() + serializer_class = PluginSerializer + + def get_queryset(self) -> QuerySet: + queryset = super().get_queryset() + return queryset.filter(team=self.request.user.team).order_by("order") diff --git a/posthog/migrations/0086_plugins.py b/posthog/migrations/0086_plugins.py new file mode 100644 index 0000000000000..9ed7b8d42b754 --- /dev/null +++ b/posthog/migrations/0086_plugins.py @@ -0,0 +1,27 @@ +# Generated by Django 3.0.7 on 2020-10-08 15:24 + +import django.contrib.postgres.fields.jsonb +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("posthog", "0085_org_models"), + ] + + operations = [ + migrations.CreateModel( + name="Plugin", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("name", models.CharField(blank=True, max_length=200, null=True)), + ("url", models.CharField(blank=True, max_length=800, null=True)), + ("enabled", models.BooleanField(default=False)), + ("order", models.IntegerField(null=True, blank=True)), + ("config", django.contrib.postgres.fields.jsonb.JSONField(default=dict)), + ("team", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="posthog.Team")), + ], + ), + ] diff --git a/posthog/models/__init__.py b/posthog/models/__init__.py index fa3619a57cd1f..e66d914a45e63 100644 --- a/posthog/models/__init__.py +++ b/posthog/models/__init__.py @@ -14,6 +14,7 @@ from .organization import Organization, OrganizationInvite, OrganizationMembership from .person import Person, PersonDistinctId from .personal_api_key import PersonalAPIKey +from .plugin import Plugin from .property import Property from .team import Team from .user import User, UserManager diff --git a/posthog/models/plugin.py b/posthog/models/plugin.py new file mode 100644 index 0000000000000..d87833aaeadf9 --- /dev/null +++ b/posthog/models/plugin.py @@ -0,0 +1,11 @@ +from django.contrib.postgres.fields import JSONField +from django.db import connection, models, transaction + + +class Plugin(models.Model): + team: models.ForeignKey = models.ForeignKey("Team", on_delete=models.CASCADE) + name: models.CharField = models.CharField(max_length=200, null=True, blank=True) + url: models.CharField = models.CharField(max_length=800, null=True, blank=True) + enabled: models.BooleanField = models.BooleanField(default=False) + order: models.IntegerField = models.IntegerField(null=True, blank=True) + config: JSONField = JSONField(default=dict) From 676080357bc44e12d412a59653b81bb6a0d27464 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 9 Oct 2020 00:49:22 +0200 Subject: [PATCH 025/230] plugins scene --- frontend/src/layout/Sidebar.js | 8 + frontend/src/scenes/plugins/Plugins.tsx | 113 +++ frontend/src/scenes/plugins/pluginsLogic.ts | 51 ++ frontend/src/scenes/plugins/types.ts | 12 + frontend/src/scenes/sceneLogic.js | 2 + frontend/src/types.ts | 9 + package.json | 3 +- posthog/api/plugin.py | 10 + yarn.lock | 869 +------------------- 9 files changed, 236 insertions(+), 841 deletions(-) create mode 100644 frontend/src/scenes/plugins/Plugins.tsx create mode 100644 frontend/src/scenes/plugins/pluginsLogic.ts create mode 100644 frontend/src/scenes/plugins/types.ts diff --git a/frontend/src/layout/Sidebar.js b/frontend/src/layout/Sidebar.js index e6c3506f06378..0c1aff48787fe 100644 --- a/frontend/src/layout/Sidebar.js +++ b/frontend/src/layout/Sidebar.js @@ -20,6 +20,7 @@ import { TeamOutlined, LockOutlined, WalletOutlined, + ApiOutlined, } from '@ant-design/icons' import { useActions, useValues } from 'kea' import { Link } from 'lib/components/Link' @@ -63,6 +64,7 @@ const submenuOverride = { annotations: 'settings', billing: 'settings', licenses: 'settings', + plugins: 'settings', } export function Sidebar({ user, sidebarCollapsed, setSidebarCollapsed }) { @@ -260,6 +262,12 @@ export function Sidebar({ user, sidebarCollapsed, setSidebarCollapsed }) { )} + + + + Plugins + + diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx new file mode 100644 index 0000000000000..e60f476ae5cb6 --- /dev/null +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -0,0 +1,113 @@ +import React from 'react' +import { Button, Col, Row, Table } from 'antd' +import { hot } from 'react-hot-loader/root' +import { useValues } from 'kea' +import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { PluginType } from '~/types' +import { LinkOutlined, ToolOutlined } from '@ant-design/icons' +import { PluginRepositoryEntry } from 'scenes/plugins/types' + +export const Plugins = hot(_Plugins) +function _Plugins(): JSX.Element { + const { plugins, pluginsLoading, repositoryLoading, uninstalledPlugins } = useValues(pluginsLogic) + + return ( +
+

Installed Plugins

+ plugin.name} + pagination={{ pageSize: 99999, hideOnSinglePage: true }} + dataSource={Object.values(plugins)} + columns={[ + { + title: 'Plugin', + key: 'name', + render: function RenderPlugin(plugin: PluginType): JSX.Element { + return ( + <> + + + {plugin.name} + + + + {plugin.enabled ? 'Enabled' : 'Disabled'} + + + Visit Site + + + + + ) + }, + }, + { + title: 'Config', + key: 'config', + render: function RenderConfig(plugin: PluginType): JSX.Element { + return ( +
+ {Object.keys(plugin.config).map((configKey) => ( + {configKey} + ))} +
+ ) + }, + }, + { + title: '', + key: 'config', + align: 'right', + render: function RenderConfig(): JSX.Element { + return
plugin.name} + pagination={{ pageSize: 99999, hideOnSinglePage: true }} + dataSource={uninstalledPlugins} + columns={[ + { + title: 'Plugin', + key: 'name', + render: function RenderPlugin(plugin: PluginType): JSX.Element { + return ( + + {plugin.name} + + ) + }, + }, + { + title: 'Description', + key: 'description', + render: function RenderDescription(plugin: PluginRepositoryEntry): JSX.Element { + return
{plugin.description}
+ }, + }, + { + title: '', + key: 'install', + align: 'right', + render: function RenderInstall(): JSX.Element { + return + }, + }, + ]} + loading={pluginsLoading || repositoryLoading} + /> + + ) +} diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts new file mode 100644 index 0000000000000..d68d52b16591f --- /dev/null +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -0,0 +1,51 @@ +import { kea } from 'kea' +import { pluginsLogicType } from 'types/scenes/plugins/pluginsLogicType' +import api from 'lib/api' +import { PluginType } from '~/types' +import { PluginRepositoryEntry } from './types' + +export const pluginsLogic = kea>({ + loaders: { + plugins: [ + {} as Record, + { + loadPlugins: async () => { + const { results } = await api.get('api/plugin') + const plugins: Record = {} + for (const plugin of results as PluginType[]) { + plugins[plugin.name] = plugin + } + return plugins + }, + }, + ], + repository: [ + {} as Record, + { + loadRepository: async () => { + const results = await api.get('api/plugin/repository') + const repository: Record = {} + for (const plugin of results as PluginRepositoryEntry[]) { + repository[plugin.name] = plugin + } + return repository + }, + }, + ], + }, + + selectors: { + uninstalledPlugins: [ + (s) => [s.plugins, s.repository], + (plugins, repository) => { + return Object.keys(repository) + .filter((name) => !plugins[name]) + .map((name) => repository[name]) + }, + ], + }, + + events: ({ actions }) => ({ + afterMount: [actions.loadPlugins, actions.loadRepository], + }), +}) diff --git a/frontend/src/scenes/plugins/types.ts b/frontend/src/scenes/plugins/types.ts new file mode 100644 index 0000000000000..4c769abdc0dc5 --- /dev/null +++ b/frontend/src/scenes/plugins/types.ts @@ -0,0 +1,12 @@ +export interface PluginRepositoryEntryConfig { + name: string + type: string + default: any +} + +export interface PluginRepositoryEntry { + name: string + url: string + description: string + config?: Record +} diff --git a/frontend/src/scenes/sceneLogic.js b/frontend/src/scenes/sceneLogic.js index 8c1a1ccd67c43..8f2e2f03c5337 100644 --- a/frontend/src/scenes/sceneLogic.js +++ b/frontend/src/scenes/sceneLogic.js @@ -26,6 +26,7 @@ export const scenes = { preflight: () => import(/* webpackChunkName: 'preflightCheck' */ './setup/PreflightCheck'), signup: () => import(/* webpackChunkName: 'signup' */ './team/Signup'), billing: () => import(/* webpackChunkName: 'billing' */ './billing/Billing'), + plugins: () => import(/* webpackChunkName: 'plugins' */ './plugins/Plugins'), } /* List of routes that do not require authentication (N.B. add to posthog.urls too) */ @@ -55,6 +56,7 @@ export const routes = { '/annotations': 'annotations', '/team': 'team', '/setup/licenses': 'licenses', + '/setup/plugins': 'plugins', '/preflight': 'preflight', '/signup': 'signup', '/billing': 'billing', diff --git a/frontend/src/types.ts b/frontend/src/types.ts index c4068d40f82ec..82e1ead171b44 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -166,3 +166,12 @@ export interface BillingSubscription { subscription_url: string stripe_checkout_session: string } + +export interface PluginType { + id: number + name: string + url: string + enabled: boolean + order: number + config: Record +} diff --git a/package.json b/package.json index 2564a9e4101ac..8bc7bc0f8bd57 100644 --- a/package.json +++ b/package.json @@ -84,13 +84,14 @@ "cssnano": "^4.1.10", "eslint": "^7.8.0", "eslint-config-prettier": "^6.11.0", + "eslint-plugin-cypress": "^2.11.2", "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-react": "^7.20.3", "file-loader": "^6.1.0", "html-webpack-harddisk-plugin": "^1.0.1", "html-webpack-plugin": "^4.4.1", "husky": "~4.2.5", - "kea-typegen": "^0.3.0", + "kea-typegen": "^0.3.2", "lint-staged": "~10.2.13", "mini-css-extract-plugin": "^0.11.0", "nodemon": "^2.0.4", diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py index 4c35a64b389a5..fc9266ee7432e 100644 --- a/posthog/api/plugin.py +++ b/posthog/api/plugin.py @@ -1,7 +1,11 @@ +import json from typing import Any, Dict +import requests from django.db.models import QuerySet from rest_framework import request, serializers, viewsets +from rest_framework.decorators import action +from rest_framework.response import Response from posthog.models import Plugin @@ -40,3 +44,9 @@ class PluginViewSet(viewsets.ModelViewSet): def get_queryset(self) -> QuerySet: queryset = super().get_queryset() return queryset.filter(team=self.request.user.team).order_by("order") + + @action(methods=["GET"], detail=False) + def repository(self, request: request.Request): + url = "https://raw.githubusercontent.com/PostHog/plugins/main/plugins.json" + plugins = requests.get(url) + return Response(json.loads(plugins.text)) diff --git a/yarn.lock b/yarn.lock index 9403adc61a445..d78606a9d4564 100644 --- a/yarn.lock +++ b/yarn.lock @@ -909,50 +909,6 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" -"@cypress/listr-verbose-renderer@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a" - integrity sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo= - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - -"@cypress/request@^2.88.5": - version "2.88.5" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.5.tgz#8d7ecd17b53a849cfd5ab06d5abe7d84976375d7" - integrity sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -"@cypress/xvfb@^1.2.4": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" - integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== - dependencies: - debug "^3.1.0" - lodash.once "^4.1.1" - "@emotion/is-prop-valid@^0.8.8": version "0.8.8" resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" @@ -1052,13 +1008,6 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@samverschueren/stream-to-observable@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301" - integrity sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ== - dependencies: - any-observable "^0.3.0" - "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -1081,11 +1030,6 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== -"@types/css-font-loading-module@0.0.4": - version "0.0.4" - resolved "https://registry.yarnpkg.com/@types/css-font-loading-module/-/css-font-loading-module-0.0.4.tgz#94a835e27d1af444c65cba88523533c174463d64" - integrity sha512-ENdXf7MW4m9HeDojB2Ukbi7lYMIuQNBHVf98dbzaiG4EEJREBd6oleVAjrLRCrp7dm6CK1mmdmU9tcgF61acbw== - "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -1109,14 +1053,6 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/hoist-non-react-statics@^3.3.0": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" - integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== - dependencies: - "@types/react" "*" - hoist-non-react-statics "^3.3.0" - "@types/html-minifier-terser@^5.0.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880" @@ -1157,23 +1093,6 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== -"@types/react-dom@^16.9.8": - version "16.9.8" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423" - integrity sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA== - dependencies: - "@types/react" "*" - -"@types/react-redux@^7.1.9": - version "7.1.9" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.9.tgz#280c13565c9f13ceb727ec21e767abe0e9b4aec3" - integrity sha512-mpC0jqxhP4mhmOl3P4ipRsgTgbNofMRXJb08Ms6gekViLj61v1hOZEKWDCyWsdONr6EjEA6ZHXC446wdywDe0w== - dependencies: - "@types/hoist-non-react-statics" "^3.3.0" - "@types/react" "*" - hoist-non-react-statics "^3.3.0" - redux "^4.0.0" - "@types/react-syntax-highlighter@^11.0.4": version "11.0.5" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz#0d546261b4021e1f9d85b50401c0a42acb106087" @@ -1189,16 +1108,6 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/sinonjs__fake-timers@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz#681df970358c82836b42f989188d133e218c458e" - integrity sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA== - -"@types/sizzle@^2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" - integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== - "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -1457,11 +1366,6 @@ resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== -"@xstate/fsm@^1.4.0": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@xstate/fsm/-/fsm-1.5.1.tgz#b1ebedc77b11e4956fda0a6429cef986cd17979e" - integrity sha512-t8blLI0e90jCE71yUQHLH5uEljD1v0aC8PKkHUtNWEnXt81EOj7xBjnlwmaHn/8cmE2aSygiBElUpkRZUV9hXA== - "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -1518,7 +1422,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4: version "6.12.4" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== @@ -1550,11 +1454,6 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-escapes@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - ansi-escapes@^4.3.0: version "4.3.1" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" @@ -1572,11 +1471,6 @@ ansi-regex@^2.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" @@ -1587,11 +1481,6 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1659,11 +1548,6 @@ antd@^4.1.1: scroll-into-view-if-needed "^2.2.25" warning "^4.0.3" -any-observable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" - integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== - anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1685,11 +1569,6 @@ aproba@^1.1.1: resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== -arch@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.2.tgz#0c52bbe7344bb4fa260c443d2cbad9c00ff2f0bf" - integrity sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ== - argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1772,18 +1651,6 @@ asn1.js@^5.2.0: minimalistic-assert "^1.0.0" safer-buffer "^2.1.0" -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - assert@^1.1.1: version "1.5.0" resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" @@ -1829,16 +1696,6 @@ async@^2.6.2: dependencies: lodash "^4.17.14" -async@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" - integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" @@ -1857,16 +1714,6 @@ autoprefixer@^9.7.4: postcss "^7.0.32" postcss-value-parser "^4.1.0" -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" - integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== - babel-eslint@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" @@ -1962,13 +1809,6 @@ batch@0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -1991,7 +1831,7 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -bluebird@^3.5.5, bluebird@^3.7.2: +bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -2170,11 +2010,6 @@ browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.8.5: escalade "^3.0.2" node-releases "^1.1.60" -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -2263,11 +2098,6 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" -cachedir@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" - integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== - caller-callsite@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" @@ -2325,22 +2155,6 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001111: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001122.tgz#2c8ff631330d986a07a7ba7125cce77a1373b475" integrity sha512-pxjw28CThdrqfz06nJkpAc5SXM404TXB/h5f4UJX+rrXJKE/1bu/KAILc2AY+O6cQIFtRjV9qOR2vaEp9LDGUA== -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -chalk@^1.0.0, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -2404,11 +2218,6 @@ chartjs-color@^2.1.0: chartjs-color-string "^0.6.0" color-convert "^1.9.3" -check-more-types@^2.24.0: - version "2.24.0" - resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" - integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= - "chokidar@>=2.0.0 <4.0.0", chokidar@^3.2.2, chokidar@^3.4.1: version "3.4.2" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" @@ -2500,20 +2309,6 @@ cli-boxes@^2.2.0: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== -cli-cursor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= - dependencies: - restore-cursor "^1.0.1" - -cli-cursor@^2.0.0, cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= - dependencies: - restore-cursor "^2.0.0" - cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -2521,24 +2316,6 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-table3@~0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" - integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== - dependencies: - object-assign "^4.1.0" - string-width "^2.1.1" - optionalDependencies: - colors "^1.1.2" - -cli-truncate@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" - integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= - dependencies: - slice-ansi "0.0.4" - string-width "^1.0.1" - cli-truncate@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" @@ -2590,11 +2367,6 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -2648,18 +2420,6 @@ colorette@^1.2.1: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== -colors@^1.1.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - comma-separated-tokens@^1.0.0: version "1.0.8" resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" @@ -2680,11 +2440,6 @@ commander@^6.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-6.1.0.tgz#f8d722b78103141006b66f4c7ba1e97315ba75bc" integrity sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA== -common-tags@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" - integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== - commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -2730,7 +2485,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.5.0, concat-stream@^1.6.2: +concat-stream@^1.5.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -2848,7 +2603,7 @@ core-js@3.6.5: resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== -core-util-is@1.0.2, core-util-is@~1.0.0: +core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= @@ -3166,57 +2921,6 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= -cypress-terminal-report@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/cypress-terminal-report/-/cypress-terminal-report-1.4.2.tgz#4eeaf2c6a063b42271ec686aff4ab0d6f7b252a6" - integrity sha512-YfKB1McG/1mxTrSTyCxv103hg+NC4atHLw3rZKfeRO4qyL5OLNLKbC6kMErqlTrKjfb/cR6KZ+yNr2G+i4pIJA== - dependencies: - chalk "^3.0.0" - methods "^1.1.2" - -cypress@^4.11.0: - version "4.12.1" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-4.12.1.tgz#0ead1b9f4c0917d69d8b57f996b6e01fe693b6ec" - integrity sha512-9SGIPEmqU8vuRA6xst2CMTYd9sCFCxKSzrHt0wr+w2iAQMCIIsXsQ5Gplns1sT6LDbZcmLv6uehabAOl3fhc9Q== - dependencies: - "@cypress/listr-verbose-renderer" "^0.4.1" - "@cypress/request" "^2.88.5" - "@cypress/xvfb" "^1.2.4" - "@types/sinonjs__fake-timers" "^6.0.1" - "@types/sizzle" "^2.3.2" - arch "^2.1.2" - bluebird "^3.7.2" - cachedir "^2.3.0" - chalk "^2.4.2" - check-more-types "^2.24.0" - cli-table3 "~0.5.1" - commander "^4.1.1" - common-tags "^1.8.0" - debug "^4.1.1" - eventemitter2 "^6.4.2" - execa "^1.0.0" - executable "^4.1.1" - extract-zip "^1.7.0" - fs-extra "^8.1.0" - getos "^3.2.1" - is-ci "^2.0.0" - is-installed-globally "^0.3.2" - lazy-ass "^1.6.0" - listr "^0.14.3" - lodash "^4.17.19" - log-symbols "^3.0.0" - minimist "^1.2.5" - moment "^2.27.0" - ospath "^1.2.2" - pretty-bytes "^5.3.0" - ramda "~0.26.1" - request-progress "^3.0.0" - supports-color "^7.1.0" - tmp "~0.1.0" - untildify "^4.0.0" - url "^0.11.0" - yauzl "^2.10.0" - d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0: version "1.2.4" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" @@ -3478,18 +3182,6 @@ d3@^5.15.0: d3-voronoi "1" d3-zoom "1" -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -date-fns@^1.27.2: - version "1.30.1" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" - integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== - date-fns@^2.0.1, date-fns@^2.15.0: version "2.16.1" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b" @@ -3500,14 +3192,14 @@ dayjs@^1.8.30: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.34.tgz#d3ad33cc43d6b0f24cb8686b90aad2c653708069" integrity sha512-Olb+E6EoMvdPmAMq2QoucuyZycKHjTlBXmRx8Ada+wGtq4SIXuDCdtoaX4KkK0yjf1fJLnwXQURr8gQKWKaybw== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: +debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -3620,11 +3312,6 @@ del@^4.1.1: pify "^4.0.1" rimraf "^2.6.3" -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - delegate@^3.1.2: version "3.2.0" resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" @@ -3802,14 +3489,6 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - editor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/editor/-/editor-1.0.0.tgz#60c7f87bd62bcc6a894fa8ccd6afb7823a24f742" @@ -3825,11 +3504,6 @@ electron-to-chromium@^1.3.523: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.556.tgz#d2a8fed6b93051c5c27d182c43c7bc4d88b77afb" integrity sha512-g5cGpg6rOCXxyfaLCQIWz9Fx+raFfbZ6sc4QLfvvaiCERBzY6YD6rh5d12QN++bEF1Tm9osYnxP37lbN/92j4A== -elegant-spinner@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" - integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= - elliptic@^6.5.3: version "6.5.3" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" @@ -3976,7 +3650,7 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -3988,10 +3662,10 @@ eslint-config-prettier@^6.11.0: dependencies: get-stdin "^6.0.0" -eslint-plugin-cypress@^2.11.1: - version "2.11.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.11.1.tgz#a945e2774b88211e2c706a059d431e262b5c2862" - integrity sha512-MxMYoReSO5+IZMGgpBZHHSx64zYPSPTpXDwsgW7ChlJTF/sA+obqRbHplxD6sBStE+g4Mi0LCLkG4t9liu//mQ== +eslint-plugin-cypress@^2.11.2: + version "2.11.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.11.2.tgz#a8f3fe7ec840f55e4cea37671f93293e6c3e76a0" + integrity sha512-1SergF1sGbVhsf7MYfOLiBhdOg6wqyeV9pXUAIDIffYTGMN3dTBQS9nFAzhLsHhO+Bn0GaVM1Ecm71XUidQ7VA== dependencies: globals "^11.12.0" @@ -4148,11 +3822,6 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -eventemitter2@^6.4.2: - version "6.4.3" - resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.3.tgz#35c563619b13f3681e7eb05cbdaf50f56ba58820" - integrity sha512-t0A2msp6BzOf+QAcI6z9XMktLj52OjGQg+8SJH6v5+3uxNpWYRR3wQmfA+6xtMU9kOC59qk9licus5dYcrYkMQ== - eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -4206,18 +3875,6 @@ execa@^4.0.3: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -executable@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" - integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== - dependencies: - pify "^2.2.0" - -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= - expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -4289,11 +3946,6 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -4308,26 +3960,6 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extract-zip@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== - dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" - yauzl "^2.10.0" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - fast-deep-equal@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -4379,33 +4011,11 @@ faye-websocket@~0.11.1: dependencies: websocket-driver ">=0.5.1" -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - figgy-pudding@^3.5.1: version "3.5.2" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== -figures@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= - dependencies: - escape-string-regexp "^1.0.5" - figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -4536,20 +4146,6 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - format@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" @@ -4580,15 +4176,6 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" @@ -4671,20 +4258,6 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= -getos@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" - integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== - dependencies: - async "^3.2.0" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" @@ -4810,7 +4383,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== @@ -4825,26 +4398,6 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -5121,15 +4674,6 @@ http-proxy@^1.17.0: follow-redirects "^1.0.0" requires-port "^1.0.0" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -5248,11 +4792,6 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= - indent-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" @@ -5512,13 +5051,6 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -5548,7 +5080,7 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== -is-installed-globally@^0.3.1, is-installed-globally@^0.3.2: +is-installed-globally@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== @@ -5588,13 +5120,6 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-observable@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" - integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA== - dependencies: - symbol-observable "^1.1.0" - is-path-cwd@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" @@ -5631,11 +5156,6 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-promise@^2.1.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" - integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== - is-reference@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" @@ -5689,7 +5209,7 @@ is-symbol@^1.0.2: dependencies: has-symbols "^1.0.1" -is-typedarray@^1.0.0, is-typedarray@~1.0.0: +is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= @@ -5731,11 +5251,6 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" @@ -5754,11 +5269,6 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -5789,21 +5299,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - json2mq@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" @@ -5830,23 +5330,6 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - jsx-ast-utils@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz#1114a4c1209481db06c690c2b4f488cc665f657e" @@ -5872,10 +5355,10 @@ kea-router@^0.4.0: dependencies: url-pattern "^1.0.3" -kea-typegen@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/kea-typegen/-/kea-typegen-0.3.0.tgz#fa15842f11c889a49231ad665e7b9682697eea05" - integrity sha512-Ch5AhNzYvYrehBOsC4SfYw9oav50I7hQw5B6eAJnulkQpd625rvS86EVsTncLRDIEUUUmywkTGGKOQs7ii59Cg== +kea-typegen@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/kea-typegen/-/kea-typegen-0.3.2.tgz#2ff669627af430b8c5fe15a0b4dd02341c4cd37e" + integrity sha512-AGSox31u/esOkYwOvD/RATE/WJvl62ximPWGWCMPxNol+k8nItqYPApIz6rOboRHtooGrKNn50s9NSbAZFgq8Q== dependencies: "@wessberg/ts-clone-node" "0.3.8" prettier "^2.0.5" @@ -5939,11 +5422,6 @@ latest-version@^5.0.0: dependencies: package-json "^6.3.0" -lazy-ass@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" - integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= - leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -5990,35 +5468,6 @@ lint-staged@~10.2.13: string-argv "0.3.1" stringify-object "^3.3.0" -listr-silent-renderer@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" - integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= - -listr-update-renderer@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2" - integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA== - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^2.3.0" - strip-ansi "^3.0.1" - -listr-verbose-renderer@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db" - integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw== - dependencies: - chalk "^2.4.1" - cli-cursor "^2.1.0" - date-fns "^1.27.2" - figures "^2.0.0" - listr2@^2.6.0: version "2.6.2" resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.6.2.tgz#4912eb01e1e2dd72ec37f3895a56bf2622d6f36a" @@ -6033,21 +5482,6 @@ listr2@^2.6.0: rxjs "^6.6.2" through "^2.3.8" -listr@^0.14.3: - version "0.14.3" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" - integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== - dependencies: - "@samverschueren/stream-to-observable" "^0.3.0" - is-observable "^1.1.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.5.0" - listr-verbose-renderer "^0.5.0" - p-map "^2.0.0" - rxjs "^6.3.3" - loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" @@ -6111,11 +5545,6 @@ lodash.memoize@^4.1.2: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= -lodash.once@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= - lodash.take@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.take/-/lodash.take-4.1.1.tgz#0b4146dcb7a70c6153495187fc10b12b71fefadf" @@ -6136,20 +5565,6 @@ lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== -log-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" - integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= - dependencies: - chalk "^1.0.0" - -log-symbols@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" - integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== - dependencies: - chalk "^2.4.2" - log-symbols@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" @@ -6157,15 +5572,6 @@ log-symbols@^4.0.0: dependencies: chalk "^4.0.0" -log-update@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" - integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg= - dependencies: - ansi-escapes "^3.0.0" - cli-cursor "^2.0.0" - wrap-ansi "^3.0.1" - log-update@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" @@ -6304,7 +5710,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -methods@^1.1.2, methods@~1.1.2: +methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= @@ -6349,7 +5755,7 @@ mime-db@1.44.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: +mime-types@~2.1.17, mime-types@~2.1.24: version "2.1.27" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== @@ -6366,11 +5772,6 @@ mime@^2.4.4: resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1" integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA== -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -6444,11 +5845,6 @@ mississippi@^3.0.0: stream-each "^1.1.0" through2 "^2.0.0" -mitt@^1.1.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mitt/-/mitt-1.2.0.tgz#cb24e6569c806e31bd4e3995787fe38a04fdf90d" - integrity sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw== - mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -6457,14 +5853,14 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" -moment@^2.10.2, moment@^2.24.0, moment@^2.25.3, moment@^2.27.0: +moment@^2.10.2, moment@^2.24.0, moment@^2.25.3: version "2.27.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d" integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== @@ -6708,16 +6104,6 @@ num2fraction@^1.2.2: resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -6840,18 +6226,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= - -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= - dependencies: - mimic-fn "^1.0.0" - onetime@^5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" @@ -6895,11 +6269,6 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -ospath@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" - integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= - p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -6965,7 +6334,7 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" -pako@^1.0.11, pako@~1.0.5: +pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== @@ -7124,11 +6493,6 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -7139,7 +6503,7 @@ picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1, picomatch@^2.2.2: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -pify@^2.0.0, pify@^2.2.0: +pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= @@ -7578,11 +6942,6 @@ prettier@^2.0.5, prettier@^2.1.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.1.tgz#d9485dd5e499daa6cb547023b87a6cf51bee37d6" integrity sha512-9bY+5ZWCfqj3ghYBLxApy2zf6m+NJo5GzmLTpr9FsApsfjriNnS2dahWReHMi7qNPhhHl9SYHJs2cHZLgexNIw== -pretty-bytes@^5.3.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.4.1.tgz#cd89f79bbcef21e3d21eb0da68ffe93f803e884b" - integrity sha512-s1Iam6Gwz3JI5Hweaz4GoCD1WUNUIyzePFy5+Js2hjwGVt2Z79wNN+ZKOZ2vB6C+Xs6njyB84Z1IthQg8d9LxA== - pretty-error@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" @@ -7647,11 +7006,6 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= -psl@^1.1.28: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - pstree.remy@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" @@ -7704,7 +7058,7 @@ punycode@^1.2.4: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -punycode@^2.1.0, punycode@^2.1.1: +punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== @@ -7726,11 +7080,6 @@ qs@6.7.0: resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - query-string@^4.1.0: version "4.3.4" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" @@ -7770,11 +7119,6 @@ raf@^3.4.0, raf@^3.4.1: dependencies: performance-now "^2.1.0" -ramda@~0.26.1: - version "0.26.1" - resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" - integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== - randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -8371,7 +7715,7 @@ readdirp@~3.4.0: dependencies: picomatch "^2.2.1" -redux@^4.0.0, redux@^4.0.5: +redux@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== @@ -8502,13 +7846,6 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -request-progress@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" - integrity sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4= - dependencies: - throttleit "^1.0.0" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -8578,22 +7915,6 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -8644,22 +7965,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rrweb-snapshot@^0.8.2: - version "0.8.3" - resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-0.8.3.tgz#1d29599d8c1c342aef5dd377145daaf07cab0e0a" - integrity sha512-zAAmCg2BHTGxbFoPj9+s1WnBvOIvd1TYf0CegGXbt8PaSqwa8iEfBUsV/zKksnGOAvRZOQ8vVmeVECLXtw/v2w== - -rrweb@^0.9.7: - version "0.9.7" - resolved "https://registry.yarnpkg.com/rrweb/-/rrweb-0.9.7.tgz#6632918dc61f2979d86d2b892810678408e7e11f" - integrity sha512-n3TEe1OyEtOGFvh4D513dR4jKVBlw//flCaFSwNFBreod/WKNRuqyZ7A2SCbY3XJbQ8JRFTfGa1jGsKvhiqTdQ== - dependencies: - "@types/css-font-loading-module" "0.0.4" - "@xstate/fsm" "^1.4.0" - mitt "^1.1.3" - pako "^1.0.11" - rrweb-snapshot "^0.8.2" - rtl-css-js@^1.9.0: version "1.14.0" resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.14.0.tgz#daa4f192a92509e292a0519f4b255e6e3c076b7d" @@ -8679,7 +7984,7 @@ rw@1: resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q= -rxjs@^6.3.3, rxjs@^6.5.2, rxjs@^6.6.2: +rxjs@^6.5.2, rxjs@^6.6.2: version "6.6.2" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== @@ -8703,7 +8008,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -8969,11 +8274,6 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= - slice-ansi@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" @@ -9189,21 +8489,6 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - ssri@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" @@ -9310,23 +8595,6 @@ string-convert@^0.2.0: resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" integrity sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c= -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -9403,13 +8671,6 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -9482,11 +8743,6 @@ stylis@3.5.0: resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.0.tgz#016fa239663d77f868fef5b67cf201c4b7c701e1" integrity sha512-pP7yXN6dwMzAR29Q0mBrabPCe0/mNO1MSr93bhay+hcZondvMMTpeGyd8nbhYJdyperNT2DRxONQuUGcJr5iPw== -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -9527,7 +8783,7 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" -symbol-observable@^1.1.0, symbol-observable@^1.2.0: +symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== @@ -9586,11 +8842,6 @@ throttle-debounce@^2.1.0: resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.3.0.tgz#fd31865e66502071e411817e241465b3e9c372e2" integrity sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ== -throttleit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" - integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= - through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -9631,13 +8882,6 @@ tinycolor2@^1.4.1: resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8" integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g= -tmp@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" - integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== - dependencies: - rimraf "^2.6.3" - to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -9702,14 +8946,6 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -9737,18 +8973,6 @@ tty-browserify@0.0.0: resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -9867,11 +9091,6 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -9890,11 +9109,6 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -untildify@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" - integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== - upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" @@ -10039,15 +9253,6 @@ vendors@^1.0.0: resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" @@ -10261,14 +9466,6 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" -wrap-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" - integrity sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo= - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -10390,14 +9587,6 @@ yargs@^15.4.0: y18n "^4.0.0" yargs-parser "^18.1.2" -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - zxcvbn@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30" From caac3d16c6f6fe393781061f58a670a0d3397e07 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 9 Oct 2020 00:53:38 +0200 Subject: [PATCH 026/230] add config schema to plugins --- frontend/src/types.ts | 8 ++++++++ posthog/api/plugin.py | 1 + posthog/migrations/0086_plugins.py | 1 + posthog/models/plugin.py | 1 + 4 files changed, 11 insertions(+) diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 82e1ead171b44..11225af4eb15a 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -174,4 +174,12 @@ export interface PluginType { enabled: boolean order: number config: Record + configSchema: Record< + string, + { + name: string + type: string + default: any + } + > } diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py index fc9266ee7432e..179055b047434 100644 --- a/posthog/api/plugin.py +++ b/posthog/api/plugin.py @@ -20,6 +20,7 @@ class Meta: "enabled", "order", "config", + "configSchema", ] def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: diff --git a/posthog/migrations/0086_plugins.py b/posthog/migrations/0086_plugins.py index 9ed7b8d42b754..d6ba06fa18de3 100644 --- a/posthog/migrations/0086_plugins.py +++ b/posthog/migrations/0086_plugins.py @@ -21,6 +21,7 @@ class Migration(migrations.Migration): ("enabled", models.BooleanField(default=False)), ("order", models.IntegerField(null=True, blank=True)), ("config", django.contrib.postgres.fields.jsonb.JSONField(default=dict)), + ("configSchema", django.contrib.postgres.fields.jsonb.JSONField(default=dict)), ("team", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="posthog.Team")), ], ), diff --git a/posthog/models/plugin.py b/posthog/models/plugin.py index d87833aaeadf9..60a944660ec79 100644 --- a/posthog/models/plugin.py +++ b/posthog/models/plugin.py @@ -9,3 +9,4 @@ class Plugin(models.Model): enabled: models.BooleanField = models.BooleanField(default=False) order: models.IntegerField = models.IntegerField(null=True, blank=True) config: JSONField = JSONField(default=dict) + configSchema: JSONField = JSONField(default=dict) From 74aa457681d8fd753176968daee739682945b96a Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 9 Oct 2020 01:06:39 +0200 Subject: [PATCH 027/230] install plugins --- frontend/src/scenes/plugins/Plugins.tsx | 11 +++++-- frontend/src/scenes/plugins/pluginsLogic.ts | 32 +++++++++++++++++++-- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index e60f476ae5cb6..3d33572db54fe 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -1,7 +1,7 @@ import React from 'react' import { Button, Col, Row, Table } from 'antd' import { hot } from 'react-hot-loader/root' -import { useValues } from 'kea' +import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { PluginType } from '~/types' import { LinkOutlined, ToolOutlined } from '@ant-design/icons' @@ -10,6 +10,7 @@ import { PluginRepositoryEntry } from 'scenes/plugins/types' export const Plugins = hot(_Plugins) function _Plugins(): JSX.Element { const { plugins, pluginsLoading, repositoryLoading, uninstalledPlugins } = useValues(pluginsLogic) + const { installPlugin } = useActions(pluginsLogic) return (
@@ -101,8 +102,12 @@ function _Plugins(): JSX.Element { title: '', key: 'install', align: 'right', - render: function RenderInstall(): JSX.Element { - return + render: function RenderInstall(plugin: PluginRepositoryEntry): JSX.Element { + return ( + + ) }, }, ]} diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index d68d52b16591f..e68b7b34caeac 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -5,7 +5,7 @@ import { PluginType } from '~/types' import { PluginRepositoryEntry } from './types' export const pluginsLogic = kea>({ - loaders: { + loaders: ({ values }) => ({ plugins: [ {} as Record, { @@ -17,6 +17,34 @@ export const pluginsLogic = kea { + const { plugins } = values + const nextOrder = Math.max( + Object.values(plugins) + .map((p) => p.order || 1) + .sort() + .reverse()[0] || 0, + Object.keys(plugins).length + 1 + ) + + const config: Record = {} + if (repositoryEntry.config) { + for (const [key, { default: def }] of Object.entries(repositoryEntry.config)) { + config[key] = def || '' + } + } + + const response = await api.create('api/plugin', { + name: repositoryEntry.name, + url: repositoryEntry.url, + enabled: false, + order: nextOrder, + config: config, + configSchema: repositoryEntry.config, + }) + + return { ...plugins, [response.name]: response } + }, }, ], repository: [ @@ -32,7 +60,7 @@ export const pluginsLogic = kea Date: Fri, 9 Oct 2020 09:33:10 +0200 Subject: [PATCH 028/230] save descriptions --- frontend/src/scenes/plugins/pluginsLogic.ts | 1 + posthog/api/plugin.py | 1 + posthog/migrations/0086_plugins.py | 1 + posthog/models/plugin.py | 1 + 4 files changed, 4 insertions(+) diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index e68b7b34caeac..7cc8b0216087d 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -36,6 +36,7 @@ export const pluginsLogic = kea Date: Fri, 9 Oct 2020 09:34:31 +0200 Subject: [PATCH 029/230] show descriptions --- frontend/src/scenes/plugins/Plugins.tsx | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index 3d33572db54fe..bd76741e4a058 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -46,16 +46,10 @@ function _Plugins(): JSX.Element { }, }, { - title: 'Config', - key: 'config', - render: function RenderConfig(plugin: PluginType): JSX.Element { - return ( -
- {Object.keys(plugin.config).map((configKey) => ( - {configKey} - ))} -
- ) + title: 'Description', + key: 'description', + render: function RenderDescription(plugin: PluginRepositoryEntry): JSX.Element { + return
{plugin.description}
}, }, { @@ -68,6 +62,7 @@ function _Plugins(): JSX.Element { }, ]} loading={pluginsLoading} + locale={{ emptyText: 'No Plugins Installed!' }} />
@@ -112,6 +107,7 @@ function _Plugins(): JSX.Element { }, ]} loading={pluginsLoading || repositoryLoading} + locale={{ emptyText: 'All Plugins Installed!' }} />
) From 025860f3c0a76183362daf551fcc16a5c8eb8593 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 9 Oct 2020 10:02:32 +0200 Subject: [PATCH 030/230] edit plugin --- frontend/src/scenes/plugins/PluginConfig.tsx | 44 ++++++++++++++++++++ frontend/src/scenes/plugins/Plugins.tsx | 16 +++++-- frontend/src/scenes/plugins/pluginsLogic.ts | 17 ++++++++ frontend/src/types.ts | 1 + 4 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 frontend/src/scenes/plugins/PluginConfig.tsx diff --git a/frontend/src/scenes/plugins/PluginConfig.tsx b/frontend/src/scenes/plugins/PluginConfig.tsx new file mode 100644 index 0000000000000..119e7b814a8ff --- /dev/null +++ b/frontend/src/scenes/plugins/PluginConfig.tsx @@ -0,0 +1,44 @@ +import React from 'react' +import { useActions, useValues } from 'kea' +import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { Form, Input, Modal, Switch } from 'antd' + +export function PluginConfig(): JSX.Element { + const { editingPlugin, pluginsLoading } = useValues(pluginsLogic) + const { editPlugin, saveEditedPlugin } = useActions(pluginsLogic) + const [form] = Form.useForm() + + return ( + editPlugin(null)} + okText="Save" + onOk={() => form.submit()} + confirmLoading={pluginsLoading} + > + {editingPlugin ? ( +
+

{editingPlugin.name}

+

{editingPlugin.description}

+ +
+ + + + {Object.keys(editingPlugin.configSchema).map((configKey) => ( + + + + ))} + +
+ ) : null} +
+ ) +} diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index bd76741e4a058..c1322954b3fb2 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -6,11 +6,12 @@ import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { PluginType } from '~/types' import { LinkOutlined, ToolOutlined } from '@ant-design/icons' import { PluginRepositoryEntry } from 'scenes/plugins/types' +import { PluginConfig } from 'scenes/plugins/PluginConfig' export const Plugins = hot(_Plugins) function _Plugins(): JSX.Element { const { plugins, pluginsLoading, repositoryLoading, uninstalledPlugins } = useValues(pluginsLogic) - const { installPlugin } = useActions(pluginsLogic) + const { installPlugin, editPlugin } = useActions(pluginsLogic) return (
@@ -48,7 +49,7 @@ function _Plugins(): JSX.Element { { title: 'Description', key: 'description', - render: function RenderDescription(plugin: PluginRepositoryEntry): JSX.Element { + render: function RenderDescription(plugin: PluginType): JSX.Element { return
{plugin.description}
}, }, @@ -56,8 +57,14 @@ function _Plugins(): JSX.Element { title: '', key: 'config', align: 'right', - render: function RenderConfig(): JSX.Element { - return
) } diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index 7cc8b0216087d..d65b52efae752 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -5,6 +5,11 @@ import { PluginType } from '~/types' import { PluginRepositoryEntry } from './types' export const pluginsLogic = kea>({ + actions: { + editPlugin: (name: string | null) => ({ name }), + saveEditedPlugin: (plugin: PluginType) => ({ plugin }), + }, + loaders: ({ values }) => ({ plugins: [ {} as Record, @@ -46,6 +51,10 @@ export const pluginsLogic = kea { + console.log(plugin) + return values.plugins + }, }, ], repository: [ @@ -63,6 +72,10 @@ export const pluginsLogic = kea name }], + }, + selectors: { uninstalledPlugins: [ (s) => [s.plugins, s.repository], @@ -72,6 +85,10 @@ export const pluginsLogic = kea repository[name]) }, ], + editingPlugin: [ + (s) => [s.editingPluginName, s.plugins], + (editingPluginName, plugins) => (editingPluginName ? plugins[editingPluginName] : null), + ], }, events: ({ actions }) => ({ diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 11225af4eb15a..795f129a4c02f 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -170,6 +170,7 @@ export interface BillingSubscription { export interface PluginType { id: number name: string + description: string url: string enabled: boolean order: number From 87f88d16c65e745ac8396b1efddd52add0a6e397 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 9 Oct 2020 10:08:08 +0200 Subject: [PATCH 031/230] save plugin config --- frontend/src/scenes/plugins/PluginConfig.tsx | 5 +++- frontend/src/scenes/plugins/pluginsLogic.ts | 25 ++++++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/frontend/src/scenes/plugins/PluginConfig.tsx b/frontend/src/scenes/plugins/PluginConfig.tsx index 119e7b814a8ff..2d4d672d6ab1f 100644 --- a/frontend/src/scenes/plugins/PluginConfig.tsx +++ b/frontend/src/scenes/plugins/PluginConfig.tsx @@ -11,7 +11,10 @@ export function PluginConfig(): JSX.Element { return ( editPlugin(null)} + onCancel={() => { + editPlugin(null) + form.resetFields() + }} okText="Save" onOk={() => form.submit()} confirmLoading={pluginsLoading} diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index d65b52efae752..9bb19956435ab 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -7,7 +7,7 @@ import { PluginRepositoryEntry } from './types' export const pluginsLogic = kea>({ actions: { editPlugin: (name: string | null) => ({ name }), - saveEditedPlugin: (plugin: PluginType) => ({ plugin }), + saveEditedPlugin: (pluginConfig: Record) => ({ pluginConfig }), }, loaders: ({ values }) => ({ @@ -51,9 +51,21 @@ export const pluginsLogic = kea { - console.log(plugin) - return values.plugins + saveEditedPlugin: async ({ pluginConfig }) => { + const { plugins, editingPlugin } = values + + if (!editingPlugin) { + return plugins + } + + const { __enabled: enabled, ...config } = pluginConfig + + const response = await api.update(`api/plugin/${editingPlugin.id}`, { + enabled, + config, + }) + + return { ...plugins, [response.name]: response } }, }, ], @@ -73,7 +85,10 @@ export const pluginsLogic = kea name }], + editingPluginName: [ + null as string | null, + { editPlugin: (_, { name }) => name, saveEditedPluginSuccess: () => null }, + ], }, selectors: { From 347cfc89e9fdd3124bf1dd0ea503dff7ebc38a46 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 9 Oct 2020 16:53:29 +0200 Subject: [PATCH 032/230] plugin modal --- .../{PluginConfig.tsx => PluginModal.tsx} | 42 +++++++++---------- frontend/src/scenes/plugins/Plugins.tsx | 20 ++++++--- 2 files changed, 36 insertions(+), 26 deletions(-) rename frontend/src/scenes/plugins/{PluginConfig.tsx => PluginModal.tsx} (51%) diff --git a/frontend/src/scenes/plugins/PluginConfig.tsx b/frontend/src/scenes/plugins/PluginModal.tsx similarity index 51% rename from frontend/src/scenes/plugins/PluginConfig.tsx rename to frontend/src/scenes/plugins/PluginModal.tsx index 2d4d672d6ab1f..d456c69b61b44 100644 --- a/frontend/src/scenes/plugins/PluginConfig.tsx +++ b/frontend/src/scenes/plugins/PluginModal.tsx @@ -1,37 +1,37 @@ -import React from 'react' +import React, { useEffect } from 'react' import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { Form, Input, Modal, Switch } from 'antd' -export function PluginConfig(): JSX.Element { +export function PluginModal(): JSX.Element { const { editingPlugin, pluginsLoading } = useValues(pluginsLogic) const { editPlugin, saveEditedPlugin } = useActions(pluginsLogic) const [form] = Form.useForm() + useEffect(() => { + if (editingPlugin) { + form.setFieldsValue({ ...(editingPlugin.config || {}), __enabled: editingPlugin.enabled }) + } else { + form.resetFields() + } + }, [editingPlugin?.name]) + return ( { - editPlugin(null) - form.resetFields() - }} okText="Save" onOk={() => form.submit()} + onCancel={() => editPlugin(null)} confirmLoading={pluginsLoading} > - {editingPlugin ? ( -
-

{editingPlugin.name}

-

{editingPlugin.description}

+
+ {editingPlugin ? ( +
+

{editingPlugin.name}

+

{editingPlugin.description}

- - + {Object.keys(editingPlugin.configSchema).map((configKey) => ( @@ -39,9 +39,9 @@ export function PluginConfig(): JSX.Element { ))} - -
- ) : null} +
+ ) : null} +
) } diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index c1322954b3fb2..8aa660900512b 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -4,9 +4,9 @@ import { hot } from 'react-hot-loader/root' import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { PluginType } from '~/types' -import { LinkOutlined, ToolOutlined } from '@ant-design/icons' +import { GithubOutlined, CheckOutlined, ToolOutlined, PauseOutlined } from '@ant-design/icons' import { PluginRepositoryEntry } from 'scenes/plugins/types' -import { PluginConfig } from 'scenes/plugins/PluginConfig' +import { PluginModal } from 'scenes/plugins/PluginModal' export const Plugins = hot(_Plugins) function _Plugins(): JSX.Element { @@ -35,10 +35,20 @@ function _Plugins(): JSX.Element { -
{plugin.enabled ? 'Enabled' : 'Disabled'} + + {plugin.enabled ? ( +
+ Enabled +
+ ) : ( +
+ Disabled +
+ )} + - Visit Site + Repository @@ -116,7 +126,7 @@ function _Plugins(): JSX.Element { loading={pluginsLoading || repositoryLoading} locale={{ emptyText: 'All Plugins Installed!' }} /> - + ) } From b0ad18704512f7e2196f1e63fe821ef30771789c Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 9 Oct 2020 17:10:52 +0200 Subject: [PATCH 033/230] uninstall plugins --- frontend/src/scenes/plugins/PluginModal.tsx | 24 +++++++++++++++++++-- frontend/src/scenes/plugins/Plugins.tsx | 4 ++-- frontend/src/scenes/plugins/pluginsLogic.ts | 17 ++++++++++++++- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/frontend/src/scenes/plugins/PluginModal.tsx b/frontend/src/scenes/plugins/PluginModal.tsx index d456c69b61b44..4cffc7930b2f9 100644 --- a/frontend/src/scenes/plugins/PluginModal.tsx +++ b/frontend/src/scenes/plugins/PluginModal.tsx @@ -1,11 +1,12 @@ import React, { useEffect } from 'react' import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { Form, Input, Modal, Switch } from 'antd' +import { Button, Form, Input, Modal, Popconfirm, Switch } from 'antd' +import { DeleteOutlined } from '@ant-design/icons' export function PluginModal(): JSX.Element { const { editingPlugin, pluginsLoading } = useValues(pluginsLogic) - const { editPlugin, saveEditedPlugin } = useActions(pluginsLogic) + const { editPlugin, saveEditedPlugin, uninstallPlugin } = useActions(pluginsLogic) const [form] = Form.useForm() useEffect(() => { @@ -24,6 +25,25 @@ export function PluginModal(): JSX.Element { onOk={() => form.submit()} onCancel={() => editPlugin(null)} confirmLoading={pluginsLoading} + footer={ + <> + uninstallPlugin(editingPlugin.name) : () => {}} + okText="Yes" + cancelText="No" + > + + + + + + } >
{editingPlugin ? ( diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index 8aa660900512b..2e7c1763da696 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -37,11 +37,11 @@ function _Plugins(): JSX.Element {
{plugin.enabled ? ( -
+
Enabled
) : ( -
+
Disabled
)} diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index 9bb19956435ab..98b2e709b5457 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -8,6 +8,7 @@ export const pluginsLogic = kea ({ name }), saveEditedPlugin: (pluginConfig: Record) => ({ pluginConfig }), + uninstallPlugin: (name: string) => ({ name }), }, loaders: ({ values }) => ({ @@ -67,6 +68,16 @@ export const pluginsLogic = kea { + const { plugins, editingPlugin } = values + if (!editingPlugin) { + return plugins + } + + await api.delete(`api/plugin/${editingPlugin.id}`) + const { [editingPlugin.name]: _discard, ...rest } = plugins // eslint-disable-line + return rest + }, }, ], repository: [ @@ -87,7 +98,11 @@ export const pluginsLogic = kea name, saveEditedPluginSuccess: () => null }, + { + editPlugin: (_, { name }) => name, + saveEditedPluginSuccess: () => null, + uninstallPluginSuccess: () => null, + }, ], }, From ebedf5b97bc82ab274eb407d46e21333ea470d33 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 9 Oct 2020 17:17:07 +0200 Subject: [PATCH 034/230] UX cleanup --- frontend/src/scenes/plugins/PluginModal.tsx | 6 ++++- frontend/src/scenes/plugins/Plugins.tsx | 26 +++++++++++++-------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/frontend/src/scenes/plugins/PluginModal.tsx b/frontend/src/scenes/plugins/PluginModal.tsx index 4cffc7930b2f9..f997cfacec53d 100644 --- a/frontend/src/scenes/plugins/PluginModal.tsx +++ b/frontend/src/scenes/plugins/PluginModal.tsx @@ -55,7 +55,11 @@ export function PluginModal(): JSX.Element { {Object.keys(editingPlugin.configSchema).map((configKey) => ( - + ))} diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index 2e7c1763da696..8fc72e26422a5 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -1,10 +1,10 @@ import React from 'react' -import { Button, Col, Row, Table } from 'antd' +import { Button, Col, Row, Table, Tooltip } from 'antd' import { hot } from 'react-hot-loader/root' import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { PluginType } from '~/types' -import { GithubOutlined, CheckOutlined, ToolOutlined, PauseOutlined } from '@ant-design/icons' +import { DownloadOutlined, GithubOutlined, CheckOutlined, ToolOutlined, PauseOutlined } from '@ant-design/icons' import { PluginRepositoryEntry } from 'scenes/plugins/types' import { PluginModal } from 'scenes/plugins/PluginModal' @@ -69,11 +69,13 @@ function _Plugins(): JSX.Element { align: 'right', render: function RenderConfig(plugin: PluginType): JSX.Element { return ( - + +
+ + + + + + + + ) +} diff --git a/frontend/src/scenes/plugins/InstalledPlugins.tsx b/frontend/src/scenes/plugins/InstalledPlugins.tsx new file mode 100644 index 0000000000000..7b0fba5ce8df7 --- /dev/null +++ b/frontend/src/scenes/plugins/InstalledPlugins.tsx @@ -0,0 +1,84 @@ +import React from 'react' +import { Button, Col, Row, Table, Tooltip } from 'antd' +import { useActions, useValues } from 'kea' +import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { PluginType } from '~/types' +import { GithubOutlined, CheckOutlined, ToolOutlined, PauseOutlined } from '@ant-design/icons' + +export function InstalledPlugins(): JSX.Element { + const { plugins, pluginsLoading } = useValues(pluginsLogic) + const { editPlugin } = useActions(pluginsLogic) + + return ( +
+

Installed Plugins

+
plugin.name} + pagination={{ pageSize: 99999, hideOnSinglePage: true }} + dataSource={Object.values(plugins)} + columns={[ + { + title: 'Plugin', + key: 'name', + render: function RenderPlugin(plugin: PluginType): JSX.Element { + return ( + <> + + + {plugin.name} + + + + + {plugin.enabled ? ( +
+ Enabled +
+ ) : ( +
+ Disabled +
+ )} + + + + Repository + + + + + ) + }, + }, + { + title: 'Description', + key: 'description', + render: function RenderDescription(plugin: PluginType): JSX.Element { + return
{plugin.description}
+ }, + }, + { + title: '', + key: 'config', + align: 'right', + render: function RenderConfig(plugin: PluginType): JSX.Element { + return ( + +
plugin.name} - pagination={{ pageSize: 99999, hideOnSinglePage: true }} - dataSource={Object.values(plugins)} - columns={[ - { - title: 'Plugin', - key: 'name', - render: function RenderPlugin(plugin: PluginType): JSX.Element { - return ( - <> - - - {plugin.name} - - - - - {plugin.enabled ? ( -
- Enabled -
- ) : ( -
- Disabled -
- )} - - - - Repository - - - - - ) - }, - }, - { - title: 'Description', - key: 'description', - render: function RenderDescription(plugin: PluginType): JSX.Element { - return
{plugin.description}
- }, - }, - { - title: '', - key: 'config', - align: 'right', - render: function RenderConfig(plugin: PluginType): JSX.Element { - return ( - -
plugin.name} - pagination={{ pageSize: 99999, hideOnSinglePage: true }} - dataSource={uninstalledPlugins} - columns={[ - { - title: 'Plugin', - key: 'name', - render: function RenderPlugin(plugin: PluginType): JSX.Element { - return ( - - {plugin.name} - - ) - }, - }, - { - title: 'Description', - key: 'description', - render: function RenderDescription(plugin: PluginRepositoryEntry): JSX.Element { - return
{plugin.description}
- }, - }, - { - title: '', - key: 'install', - align: 'right', - render: function RenderInstall(plugin: PluginRepositoryEntry): JSX.Element { - return ( - -
plugin.name} + pagination={{ pageSize: 99999, hideOnSinglePage: true }} + dataSource={uninstalledPlugins} + columns={[ + { + title: 'Plugin', + key: 'name', + render: function RenderPlugin(plugin: PluginType): JSX.Element { + return ( + + {plugin.name} + + ) + }, + }, + { + title: 'Description', + key: 'description', + render: function RenderDescription(plugin: PluginRepositoryEntry): JSX.Element { + return
{plugin.description}
+ }, + }, + { + title: '', + key: 'install', + align: 'right', + render: function RenderInstall(plugin: PluginRepositoryEntry): JSX.Element { + return ( + + - + setCustomPluginUrl(e.target.value)} + placeholder="https://github.com/user/repo" + /> - + + {customPluginError ?

{customPluginError}

: null} ) } diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index a044cf4df4d3d..47bf0c7cfdd9d 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -9,6 +9,9 @@ export const pluginsLogic = kea ({ name }), saveEditedPlugin: (pluginConfig: Record) => ({ pluginConfig }), uninstallPlugin: (name: string) => ({ name }), + setCustomPluginUrl: (customPluginUrl: string) => ({ customPluginUrl }), + installCustomPlugin: (customPluginUrl: string) => ({ customPluginUrl }), + setCustomPluginError: (customPluginError: string) => ({ customPluginError }), }, loaders: ({ values }) => ({ @@ -105,6 +108,30 @@ export const pluginsLogic = kea Object.keys(plugins).pop() || null, }, ], + customPluginUrl: [ + '', + { + setCustomPluginUrl: (_, { customPluginUrl }) => customPluginUrl, + installPluginSuccess: () => '', + }, + ], + customPluginError: [ + null as null | string, + { + setCustomPluginError: (_, { customPluginError }) => customPluginError, + setCustomPluginUrl: () => null, + installCustomPlugin: () => null, + }, + ], + installingCustomPlugin: [ + false, + { + installCustomPlugin: () => true, + setCustomPluginError: () => false, + installPluginFailure: () => false, + installPluginSuccess: () => false, + }, + ], }, selectors: { @@ -125,4 +152,42 @@ export const pluginsLogic = kea ({ afterMount: [actions.loadPlugins, actions.loadRepository], }), + + listeners: ({ actions, values }) => ({ + installCustomPlugin: async ({ customPluginUrl }) => { + const match = customPluginUrl.match(/https?:\/\/(www\.|)github.com\/([^\/]+)\/([^\/]+)\/?$/) + if (!match) { + actions.setCustomPluginError('Must be in the format: http://github.com/user/repo') + return + } + const [, , user, repo] = match + + const urls = [ + `https://raw.githubusercontent.com/${user}/${repo}/main/plugin.json`, + `https://raw.githubusercontent.com/${user}/${repo}/master/plugin.json`, + ] + + const promises = urls.map((url) => + window + .fetch(url) + .then((response) => response?.json()) + .catch(() => null) + ) + + const responses = await Promise.all(promises) + const response = responses.find((r) => r) + + if (!response) { + actions.setCustomPluginError(`Could not find plugin.json in repository: ${customPluginUrl}`) + return + } + + if (Object.values(values.plugins).find((p) => p.name === response.name)) { + actions.setCustomPluginError(`Plugin with the name "${response.name}" already installed!`) + return + } + + actions.installPlugin(response as PluginRepositoryEntry) + }, + }), }) From 25dc309cf822c6b12cceb33671e72c113e0c6085 Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Fri, 9 Oct 2020 18:30:56 +0200 Subject: [PATCH 039/230] rework backend for model plugins --- posthog/models/plugin.py | 2 +- posthog/plugins/__init__.py | 309 +++++++++++++++++++++--------------- 2 files changed, 179 insertions(+), 132 deletions(-) diff --git a/posthog/models/plugin.py b/posthog/models/plugin.py index a4af2a73f91c0..578c14fd56125 100644 --- a/posthog/models/plugin.py +++ b/posthog/models/plugin.py @@ -1,5 +1,5 @@ from django.contrib.postgres.fields import JSONField -from django.db import connection, models, transaction +from django.db import models class Plugin(models.Model): diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 854cdbe4e6c80..0706c54e5fa93 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -17,6 +17,9 @@ import requests from django.conf import settings +from posthog.models.plugin import Plugin +from posthog.models.team import Team + PLUGIN_PATH = os.path.join("posthog", "plugins") URL_TEMPLATE = "{repo}/archive/{branch}.zip" DEFAULT_BRANCHES = ["main", "master"] @@ -24,141 +27,17 @@ ABS_PLUGIN_PATH = os.path.join(PATH, PLUGIN_PATH) -def cleanse_plugin_directory(): - for x in os.listdir(ABS_PLUGIN_PATH): - dir = os.path.join(ABS_PLUGIN_PATH, x) - if os.path.islink(dir): - os.unlink(dir) - elif os.path.isdir(dir) and x != "__pycache__": - shutil.rmtree(dir) - - -def install(reqs): - if hasattr(pip, "main"): - pip.main(["install", "-r", reqs]) - else: - pip._internal.main(["install", "-r", reqs]) - - -def get_installed_plugins(): - plugins = [ - x for x in os.listdir(ABS_PLUGIN_PATH) if os.path.isdir(os.path.join(ABS_PLUGIN_PATH, x)) and x != "__pycache__" - ] - return plugins - - -def import_plugin(plugin): - plugin_root = ".".join(os.path.split(PLUGIN_PATH)) - plugin_module_name = plugin_root + "." + plugin - try: - importlib.import_module(plugin_module_name) - except Exception as e: - print('šŸ”» Can not import plugin "{}"'.format(plugin)) - trace_back = traceback.format_exc() - message = str(e) + " " + str(trace_back) - print(message) - return None - return plugin_module_name - - -def load_plugins(): - cleanse_plugin_directory() - plugins = get_plugin_config().order - for repo in plugins: - if not repo.path: - download_plugin(repo.url) - else: - symlink_plugin(repo.path) - plugins = get_installed_plugins() - for plugin in plugins: - req_file = os.path.join(ABS_PLUGIN_PATH, plugin, "requirements.txt") - install(req_file) - import_plugin(plugin) - - -def download_plugin(repo): - for branch in DEFAULT_BRANCHES: - try: - url = URL_TEMPLATE.format(repo=repo, branch=branch) - r = requests.get(url) - break - except requests.RequestException: - continue - with tempfile.TemporaryFile() as f: - f.write(r.content) - with zipfile.ZipFile(f, "r") as zip_ref: - plugin_path = os.path.join(PATH, PLUGIN_PATH) - zip_ref.extractall(plugin_path) - - -def symlink_plugin(path): - real_path = os.path.realpath(path) - path_parts = os.path.split(real_path) - plugin_path = os.path.join(PATH, PLUGIN_PATH, path_parts[-1]) - if os.path.exists(plugin_path) or os.path.islink(plugin_path): - os.unlink(plugin_path) - os.symlink(real_path, plugin_path) - - -def get_plugin_modules(): - return PluginBaseClass.__subclasses__() - - -def exec_plugins(event): - mods = get_plugin_modules() - for mod in mods: - event = exec_plugin(mod, event, "process_event") - if event.event == "$identify": - event = exec_plugin(mod, event, "process_identify") - if event.event == "$create_alias": - event = exec_plugin(mod, event, "process_alias") - return event - - -def get_plugin_config(): - with open("posthog.json", "r") as f: - conf = json.loads(f.read()).get("plugins", None) - plugin_configs = PluginConfigs(order=[], dict={}) - for plugin in conf: - ppc = PluginConfig( - path=plugin.get("path", None), url=plugin.get("url", None), config=plugin.get("config", None) - ) - plugin_configs.dict[ppc.name] = ppc - plugin_configs.order.append(ppc) - return plugin_configs - - -def get_module_config(module): - module_name = module.__module__.split(".")[-1] - module_name = re.sub("-main$", "", module_name) - module_name = re.sub("-master$", "", module_name) - plugin_config = get_plugin_config() - module_config = plugin_config.dict[module_name] - return module_config - - -def exec_plugin(Module, event, method="process_event"): - mc = get_module_config(Module) - module = Module(mc) - f = getattr(module, method) - event = f(event) - return event - - -def schedule_tasks(): - mods = get_plugin_modules() - for Mod in mods: - mc = get_module_config(Mod) - mod = Mod(mc) - f = getattr(mod, "schedule_jobs") - f() - - @dataclass class PluginConfig: url: str path: str config: Dict[Any, Any] + configSchema: Dict[Any, Any] + order: int + enabled: bool + display_name: str = "" + team: int = 0 + description: str = "" @property def name(self): @@ -169,7 +48,7 @@ def name(self): @dataclass class PluginConfigs: - order: List[PluginConfig] + ordered: List[PluginConfig] dict: Dict[str, PluginConfig] @@ -226,3 +105,171 @@ def process_alias(self, event: PosthogEvent): def process_identify(self, event: PosthogEvent): pass + + +class Plugins: + def __init__(self): + self.load_plugins() + self.plugin_configs = self.get_plugin_config() + self.cleanse_plugin_directory() + self.plugins = self.plugin_configs.ordered + + def load_plugins(self): + self.cleanse_plugin_directory() + self.plugins = self.get_plugin_config().ordered + for repo in self.plugins: + if not repo.path: + self.download_plugin(repo.url) + else: + self.symlink_plugin(repo.path) + plugins = self.get_installed_plugins() + for plugin in plugins: + req_file = os.path.join(ABS_PLUGIN_PATH, plugin, "requirements.txt") + self.install(req_file) + self.import_plugin(plugin) + + @staticmethod + def cleanse_plugin_directory(): + for x in os.listdir(ABS_PLUGIN_PATH): + dir = os.path.join(ABS_PLUGIN_PATH, x) + if os.path.islink(dir): + os.unlink(dir) + elif os.path.isdir(dir) and x != "__pycache__": + shutil.rmtree(dir) + + @staticmethod + def install(reqs): + if hasattr(pip, "main"): + pip.main(["install", "-r", reqs]) + else: + pip._internal.main(["install", "-r", reqs]) + + @staticmethod + def get_installed_plugins(): + plugins = [ + x + for x in os.listdir(ABS_PLUGIN_PATH) + if os.path.isdir(os.path.join(ABS_PLUGIN_PATH, x)) and x != "__pycache__" + ] + return plugins + + @staticmethod + def import_plugin(plugin): + plugin_root = ".".join(os.path.split(PLUGIN_PATH)) + plugin_module_name = plugin_root + "." + plugin + try: + importlib.import_module(plugin_module_name) + except Exception as e: + print('šŸ”» Can not import plugin "{}"'.format(plugin)) + trace_back = traceback.format_exc() + message = str(e) + " " + str(trace_back) + print(message) + return None + return plugin_module_name + + @staticmethod + def download_plugin(repo): + for branch in DEFAULT_BRANCHES: + try: + url = URL_TEMPLATE.format(repo=repo, branch=branch) + r = requests.get(url) + break + except requests.RequestException: + continue + with tempfile.TemporaryFile() as f: + f.write(r.content) + with zipfile.ZipFile(f, "r") as zip_ref: + plugin_path = os.path.join(PATH, PLUGIN_PATH) + zip_ref.extractall(plugin_path) + + def symlink_plugin(self, path): + real_path = os.path.realpath(path) + path_parts = os.path.split(real_path) + plugin_path = os.path.join(PATH, PLUGIN_PATH, path_parts[-1]) + if os.path.exists(plugin_path) or os.path.islink(plugin_path): + os.unlink(plugin_path) + os.symlink(real_path, plugin_path) + + @staticmethod + def get_plugin_modules(): + return PluginBaseClass.__subclasses__() + + def exec_plugins(self, event): + mods = self.get_plugin_modules() + for mod in mods: + event = self.exec_plugin(mod, event, "process_event") + if event.event == "$identify": + event = self.exec_plugin(mod, event, "process_identify") + if event.event == "$create_alias": + event = self.exec_plugin(mod, event, "process_alias") + return event + + def update_plugin_config(self): + self.plugin_configs = self.get_plugin_config() + + @staticmethod + def get_plugin_config(team: Team = None) -> PluginConfigs: + # load plugins from json config + with open("posthog.json", "r") as f: + json_conf = json.loads(f.read()).get("plugins", None) + plugin_configs = PluginConfigs(ordered=[], dict={}) + for plugin in json_conf: + path = plugin.get("path", None) + url = plugin.get("url", None) + name = plugin.get("name", None) + config = plugin.get("config", None) + order = plugin.get("order", 1000) + team = plugin.get("team", None) + enabled = plugin.get("enabled", True) + if not name: + if path: + name = os.path.split(os.path.realpath(plugin["path"]))[-1] + else: + name = url.split("/")[-1] + ppc = PluginConfig( + path=path, url=url, config=config, display_name=name, order=order, team=team, enabled=enabled + ) + plugin_configs.dict[ppc.name] = ppc + plugin_configs.ordered.append(ppc) + + # Load plugins from model + if team: + plugins = Plugin.objects.filter(team=Team) + else: + plugins = Plugin.objects.all() + for plugin in plugins: + ppc = PluginConfig( + path=None, + url=plugin.url, + config=plugin.config, + display_name=plugin.name, + order=plugin.order, + team=plugin.team, + enabled=plugin.enabled, + ) + plugin_configs.dict[ppc.name] = ppc + plugin_configs.ordered.append(ppc) + plugin_configs.ordered.sort(key=lambda x: x.order) + return plugin_configs + + def get_module_config(self, module): + module_name = module.__module__.split(".")[-1] + module_name = re.sub("-main$", "", module_name) + module_name = re.sub("-master$", "", module_name) + module_config = self.plugin_configs.dict[module_name] + return module_config + + def exec_plugin(self, Module, event, method="process_event"): + mc = self.get_module_config(Module) + module = Module(mc) + f = getattr(module, method) + event = f(event) + return event + + def schedule_tasks(self): + mods = self.get_plugin_modules() + for Mod in mods: + mc = self.get_module_config(Mod) + mod = Mod(mc) + f = getattr(mod, "schedule_jobs") + f() From f069cd709a18232f41ffdfd18c2de84c21ed4722 Mon Sep 17 00:00:00 2001 From: James Greenhill Date: Fri, 9 Oct 2020 18:41:26 +0200 Subject: [PATCH 040/230] Plugins on models --- posthog/apps.py | 4 ++-- posthog/plugins/__init__.py | 41 ++++++++++++++++++++++------------ posthog/tasks/process_event.py | 5 +++-- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/posthog/apps.py b/posthog/apps.py index 6a39ef9f75985..1a396d2bfa3ba 100644 --- a/posthog/apps.py +++ b/posthog/apps.py @@ -5,7 +5,7 @@ from django.apps import AppConfig from django.conf import settings -from posthog.plugins import load_plugins +from posthog.plugins import Plugins from posthog.utils import get_git_branch, get_git_commit, get_machine_id from posthog.version import VERSION @@ -19,7 +19,7 @@ def ready(self): posthoganalytics.personal_api_key = os.environ.get("POSTHOG_PERSONAL_API_KEY") # Load plugins - load_plugins() + Plugins() if settings.DEBUG: # log development server launch to posthog diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 0706c54e5fa93..fe7cf6ecb5918 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -9,7 +9,7 @@ import traceback import zipfile from dataclasses import dataclass -from typing import Any, Dict, List +from typing import Any, Dict, List, Optional import fakeredis # type: ignore import pip @@ -19,6 +19,7 @@ from posthog.models.plugin import Plugin from posthog.models.team import Team +from posthog.utils import SingletonDecorator PLUGIN_PATH = os.path.join("posthog", "plugins") URL_TEMPLATE = "{repo}/archive/{branch}.zip" @@ -29,12 +30,12 @@ @dataclass class PluginConfig: - url: str - path: str + url: Optional[str] + path: Optional[str] config: Dict[Any, Any] - configSchema: Dict[Any, Any] - order: int - enabled: bool + config_schema: Optional[Dict[Any, Any]] + enabled: bool = True + order: int = 100 display_name: str = "" team: int = 0 description: str = "" @@ -107,7 +108,7 @@ def process_identify(self, event: PosthogEvent): pass -class Plugins: +class _Plugins: def __init__(self): self.load_plugins() self.plugin_configs = self.get_plugin_config() @@ -131,11 +132,11 @@ def load_plugins(self): @staticmethod def cleanse_plugin_directory(): for x in os.listdir(ABS_PLUGIN_PATH): - dir = os.path.join(ABS_PLUGIN_PATH, x) - if os.path.islink(dir): - os.unlink(dir) - elif os.path.isdir(dir) and x != "__pycache__": - shutil.rmtree(dir) + pdir = os.path.join(ABS_PLUGIN_PATH, x) + if os.path.islink(pdir): + os.unlink(pdir) + elif os.path.isdir(pdir) and x != "__pycache__": + shutil.rmtree(pdir) @staticmethod def install(reqs): @@ -182,7 +183,8 @@ def download_plugin(repo): plugin_path = os.path.join(PATH, PLUGIN_PATH) zip_ref.extractall(plugin_path) - def symlink_plugin(self, path): + @staticmethod + def symlink_plugin(path): real_path = os.path.realpath(path) path_parts = os.path.split(real_path) plugin_path = os.path.join(PATH, PLUGIN_PATH, path_parts[-1]) @@ -227,7 +229,14 @@ def get_plugin_config(team: Team = None) -> PluginConfigs: else: name = url.split("/")[-1] ppc = PluginConfig( - path=path, url=url, config=config, display_name=name, order=order, team=team, enabled=enabled + path=path, + url=url, + config=config, + config_schema=None, + display_name=name, + order=order, + team=team, + enabled=enabled, ) plugin_configs.dict[ppc.name] = ppc plugin_configs.ordered.append(ppc) @@ -242,6 +251,7 @@ def get_plugin_config(team: Team = None) -> PluginConfigs: path=None, url=plugin.url, config=plugin.config, + config_schema=plugin.configSchema, display_name=plugin.name, order=plugin.order, team=plugin.team, @@ -273,3 +283,6 @@ def schedule_tasks(self): mod = Mod(mc) f = getattr(mod, "schedule_jobs") f() + + +Plugins = SingletonDecorator(_Plugins) diff --git a/posthog/tasks/process_event.py b/posthog/tasks/process_event.py index 1abd60becec98..799f110e763b6 100644 --- a/posthog/tasks/process_event.py +++ b/posthog/tasks/process_event.py @@ -10,7 +10,7 @@ from sentry_sdk import capture_exception from posthog.models import Element, Event, Person, Team, User -from posthog.plugins import PosthogEvent, exec_plugins +from posthog.plugins import Plugins, PosthogEvent def _alias(previous_distinct_id: str, distinct_id: str, team_id: int, retry_if_failed: bool = True,) -> None: @@ -233,7 +233,8 @@ def process_event( timestamp=handle_timestamp(data, now, sent_at), ) - event = exec_plugins(plugin_event) + plugins = Plugins() + event = plugins.exec_plugins(plugin_event) if event: _capture( From e3bc924f918708fa6a6933337855df7ee3b691d0 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sat, 10 Oct 2020 01:41:30 +0200 Subject: [PATCH 041/230] simple reload pubsub --- posthog/api/plugin.py | 3 +++ posthog/apps.py | 3 ++- posthog/cache.py | 13 ++++++++----- posthog/plugins/__init__.py | 19 +++++++++++++++++++ 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py index 8db7648abecc1..ec90b8c7c4c45 100644 --- a/posthog/api/plugin.py +++ b/posthog/api/plugin.py @@ -8,6 +8,7 @@ from rest_framework.response import Response from posthog.models import Plugin +from posthog.plugins import publish_reload_command class PluginSerializer(serializers.ModelSerializer): @@ -27,6 +28,7 @@ class Meta: def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: request = self.context["request"] plugin = Plugin.objects.create(team=request.user.team, **validated_data) + publish_reload_command() return plugin def update(self, plugin: Plugin, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: # type: ignore @@ -36,6 +38,7 @@ def update(self, plugin: Plugin, validated_data: Dict, *args: Any, **kwargs: Any plugin.config = validated_data.get("config", plugin.config) plugin.order = validated_data.get("order", plugin.order) plugin.save() + publish_reload_command() return plugin diff --git a/posthog/apps.py b/posthog/apps.py index 6a39ef9f75985..c7b33a303aabc 100644 --- a/posthog/apps.py +++ b/posthog/apps.py @@ -5,7 +5,7 @@ from django.apps import AppConfig from django.conf import settings -from posthog.plugins import load_plugins +from posthog.plugins import load_plugins, start_reload_pubsub from posthog.utils import get_git_branch, get_git_commit, get_machine_id from posthog.version import VERSION @@ -20,6 +20,7 @@ def ready(self): # Load plugins load_plugins() + start_reload_pubsub() if settings.DEBUG: # log development server launch to posthog diff --git a/posthog/cache.py b/posthog/cache.py index e22ae0b96cec1..f33682f3eaad9 100644 --- a/posthog/cache.py +++ b/posthog/cache.py @@ -4,12 +4,15 @@ import redis from django.conf import settings -redis_instance = None # type: Optional[redis.Redis] -if settings.TEST: - redis_instance = fakeredis.FakeStrictRedis() -elif settings.REDIS_URL: - redis_instance = redis.from_url(settings.REDIS_URL, db=0) +def get_redis_instance() -> Optional[redis.Redis]: + if settings.TEST: + return fakeredis.FakeStrictRedis() + elif settings.REDIS_URL: + return redis.from_url(settings.REDIS_URL, db=0) + + +redis_instance = get_redis_instance() # type: Optional[redis.Redis] def get_cache_key(team_id: int, key: str) -> str: diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 854cdbe4e6c80..2ec0ce1463250 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -17,11 +17,14 @@ import requests from django.conf import settings +from posthog.cache import get_redis_instance + PLUGIN_PATH = os.path.join("posthog", "plugins") URL_TEMPLATE = "{repo}/archive/{branch}.zip" DEFAULT_BRANCHES = ["main", "master"] PATH = os.path.abspath(os.getcwd()) ABS_PLUGIN_PATH = os.path.join(PATH, PLUGIN_PATH) +REDIS_INSTANCE = get_redis_instance() def cleanse_plugin_directory(): @@ -154,6 +157,22 @@ def schedule_tasks(): f() +def reload_plugins(message): + print("!!! RELOAD PLUGINS") + load_plugins() + + +def start_reload_pubsub(): + print("!!! START RELOAD PUBSUB") + p = REDIS_INSTANCE.pubsub() + p.subscribe(**{"plugin-reload-channel": reload_plugins}) + p.run_in_thread(sleep_time=1, daemon=True) + + +def publish_reload_command(): + REDIS_INSTANCE.publish("plugin-reload-channel", "yeah!") + + @dataclass class PluginConfig: url: str From 5c6300d56793e21156c8ea61a9881615952fc554 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sat, 10 Oct 2020 02:11:08 +0200 Subject: [PATCH 042/230] fix apps not installed --- posthog/plugins/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 46e2848a66511..8377055c27ca0 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -18,8 +18,6 @@ from django.conf import settings from posthog.cache import get_redis_instance -from posthog.models.plugin import Plugin -from posthog.models.team import Team from posthog.utils import SingletonDecorator PLUGIN_PATH = os.path.join("posthog", "plugins") @@ -227,7 +225,7 @@ def update_plugin_config(self): self.plugin_configs = self.get_plugin_config() @staticmethod - def get_plugin_config(team: Team = None) -> PluginConfigs: + def get_plugin_config(team: any = None) -> PluginConfigs: # load plugins from json config with open("posthog.json", "r") as f: json_conf = json.loads(f.read()).get("plugins", None) @@ -258,6 +256,9 @@ def get_plugin_config(team: Team = None) -> PluginConfigs: plugin_configs.dict[ppc.name] = ppc plugin_configs.ordered.append(ppc) + from posthog.models.plugin import Plugin + from posthog.models.team import Team + # Load plugins from model if team: plugins = Plugin.objects.filter(team=Team) From 5c5caf422fda96d7cc2623cefb05f9e803155625 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sat, 10 Oct 2020 02:11:17 +0200 Subject: [PATCH 043/230] fix master/main issue --- posthog/plugins/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 8377055c27ca0..ffa75e1e61646 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -189,6 +189,8 @@ def download_plugin(repo): try: url = URL_TEMPLATE.format(repo=repo, branch=branch) r = requests.get(url) + if not r.ok: + continue break except requests.RequestException: continue From a4caa01730e3de83fe8546a9b9a240dcf9b01a93 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sat, 10 Oct 2020 02:16:36 +0200 Subject: [PATCH 044/230] fix reload command --- posthog/plugins/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index ffa75e1e61646..7cdeb8a443ad8 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -127,7 +127,7 @@ def start_reload_pubsub(self): p.run_in_thread(sleep_time=1, daemon=True) @staticmethod - def publish_reload_command(self): + def publish_reload_command(): REDIS_INSTANCE.publish("plugin-reload-channel", "yeah!") def load_plugins(self): From 1227d8bc3615a429c78180f8eabae1f71fc54f30 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sat, 10 Oct 2020 12:30:41 +0200 Subject: [PATCH 045/230] use the github api to get the default branch --- frontend/src/scenes/plugins/pluginsLogic.ts | 36 +++++++++++---------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index 47bf0c7cfdd9d..d80a53138dd8b 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -157,37 +157,39 @@ export const pluginsLogic = kea { const match = customPluginUrl.match(/https?:\/\/(www\.|)github.com\/([^\/]+)\/([^\/]+)\/?$/) if (!match) { - actions.setCustomPluginError('Must be in the format: http://github.com/user/repo') + actions.setCustomPluginError('Must be in the format: https://github.com/user/repo') return } const [, , user, repo] = match - const urls = [ - `https://raw.githubusercontent.com/${user}/${repo}/main/plugin.json`, - `https://raw.githubusercontent.com/${user}/${repo}/master/plugin.json`, - ] + const repoUrl = `https://api.github.com/repos/${user}/${repo}` + const repoDetails = await window + .fetch(repoUrl) + .then((response) => response?.json()) + .catch(() => null) - const promises = urls.map((url) => - window - .fetch(url) - .then((response) => response?.json()) - .catch(() => null) - ) + if (!repoDetails) { + actions.setCustomPluginError(`Could not find repository: ${customPluginUrl}`) + return + } - const responses = await Promise.all(promises) - const response = responses.find((r) => r) + const jsonUrl = `https://raw.githubusercontent.com/${user}/${repo}/${repoDetails['default_branch']}/plugin.json` + const json = await window + .fetch(jsonUrl) + .then((response) => response?.json()) + .catch(() => null) - if (!response) { + if (!json) { actions.setCustomPluginError(`Could not find plugin.json in repository: ${customPluginUrl}`) return } - if (Object.values(values.plugins).find((p) => p.name === response.name)) { - actions.setCustomPluginError(`Plugin with the name "${response.name}" already installed!`) + if (Object.values(values.plugins).find((p) => p.name === json.name)) { + actions.setCustomPluginError(`Plugin with the name "${json.name}" already installed!`) return } - actions.installPlugin(response as PluginRepositoryEntry) + actions.installPlugin(json as PluginRepositoryEntry) }, }), }) From a018abf070157fcb9c649163149f021b5c1b1c8b Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sat, 10 Oct 2020 12:55:36 +0200 Subject: [PATCH 046/230] init plugins only if not running migrate/makemigrations --- posthog/apps.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/posthog/apps.py b/posthog/apps.py index 1a396d2bfa3ba..ca422d51e871b 100644 --- a/posthog/apps.py +++ b/posthog/apps.py @@ -1,5 +1,5 @@ -import json import os +import sys import posthoganalytics from django.apps import AppConfig @@ -18,8 +18,9 @@ def ready(self): posthoganalytics.api_key = "sTMFPsFhdP1Ssg" posthoganalytics.personal_api_key = os.environ.get("POSTHOG_PERSONAL_API_KEY") - # Load plugins - Plugins() + # Load plugins, except under "migrate/makemigrations" as those init the Plugins model which might not be there + if not "makemigrations" in sys.argv and not "migrate" in sys.argv: + Plugins() if settings.DEBUG: # log development server launch to posthog From c53f0b69082307015bf4bcd5c7937d5153e7242f Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sat, 10 Oct 2020 13:44:37 +0200 Subject: [PATCH 047/230] store plugins zip archives in postgres --- frontend/src/scenes/plugins/pluginsLogic.ts | 11 ++++++---- frontend/src/scenes/plugins/types.ts | 1 + latest_migrations.manifest | 2 +- posthog/api/plugin.py | 23 ++++++++++++--------- posthog/migrations/0087_pluginarchive.py | 17 +++++++++++++++ posthog/models/plugin.py | 2 ++ 6 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 posthog/migrations/0087_pluginarchive.py diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index d80a53138dd8b..9a82f54064183 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -47,6 +47,7 @@ export const pluginsLogic = kea [s.plugins], (plugins) => Object.values(plugins).sort((a, b) => a.order - b.order)], uninstalledPlugins: [ (s) => [s.plugins, s.repository], (plugins, repository) => { @@ -163,7 +165,7 @@ export const pluginsLogic = kea | null = await window .fetch(repoUrl) .then((response) => response?.json()) .catch(() => null) @@ -173,8 +175,9 @@ export const pluginsLogic = kea response?.json()) .catch(() => null) @@ -189,7 +192,7 @@ export const pluginsLogic = kea } diff --git a/latest_migrations.manifest b/latest_migrations.manifest index 64dd18e7ea90b..c2f8c98dfab83 100644 --- a/latest_migrations.manifest +++ b/latest_migrations.manifest @@ -2,7 +2,7 @@ admin: 0003_logentry_add_action_flag_choices auth: 0011_update_proxy_permissions contenttypes: 0002_remove_content_type_name ee: 0002_hook -posthog: 0086_plugins +posthog: 0087_pluginarchive rest_hooks: 0002_swappable_hook_model sessions: 0001_initial social_django: 0008_partial_timestamp diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py index f801983777ba6..007947842cec5 100644 --- a/posthog/api/plugin.py +++ b/posthog/api/plugin.py @@ -14,19 +14,11 @@ class PluginSerializer(serializers.ModelSerializer): class Meta: model = Plugin - fields = [ - "id", - "name", - "description", - "url", - "enabled", - "order", - "config", - "configSchema", - ] + fields = ["id", "name", "description", "url", "enabled", "order", "config", "configSchema", "tag"] def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: request = self.context["request"] + validated_data["archive"] = self._download_github_zip(validated_data["url"], validated_data["tag"]) plugin = Plugin.objects.create(team=request.user.team, **validated_data) Plugins().publish_reload_command() return plugin @@ -37,10 +29,21 @@ def update(self, plugin: Plugin, validated_data: Dict, *args: Any, **kwargs: Any plugin.enabled = validated_data.get("enabled", plugin.enabled) plugin.config = validated_data.get("config", plugin.config) plugin.order = validated_data.get("order", plugin.order) + if plugin.tag != validated_data.get("tag", plugin.tag): + plugin.tag = validated_data.get("tag", plugin.tag) + plugin.archive = self._download_github_zip(plugin.url, plugin.tag) plugin.save() Plugins().publish_reload_command() return plugin + def _download_github_zip(self, repo: str, tag: str): + URL_TEMPLATE = "{repo}/archive/{tag}.zip" + url = URL_TEMPLATE.format(repo=repo, tag=tag) + response = requests.get(url) + if not response.ok: + raise Exception("Could not download archive from GitHub") + return response.content + class PluginViewSet(viewsets.ModelViewSet): queryset = Plugin.objects.all() diff --git a/posthog/migrations/0087_pluginarchive.py b/posthog/migrations/0087_pluginarchive.py new file mode 100644 index 0000000000000..7aa8cff238d6d --- /dev/null +++ b/posthog/migrations/0087_pluginarchive.py @@ -0,0 +1,17 @@ +# Generated by Django 3.0.7 on 2020-10-10 10:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("posthog", "0086_plugins"), + ] + + operations = [ + migrations.AddField(model_name="plugin", name="archive", field=models.BinaryField(blank=True),), + migrations.AddField( + model_name="plugin", name="tag", field=models.CharField(blank=True, max_length=200, null=True), + ), + ] diff --git a/posthog/models/plugin.py b/posthog/models/plugin.py index 578c14fd56125..2995a4487f0ea 100644 --- a/posthog/models/plugin.py +++ b/posthog/models/plugin.py @@ -11,3 +11,5 @@ class Plugin(models.Model): order: models.IntegerField = models.IntegerField(null=True, blank=True) config: JSONField = JSONField(default=dict) configSchema: JSONField = JSONField(default=dict) + tag: models.CharField = models.CharField(max_length=200, null=True, blank=True) + archive: models.BinaryField = models.BinaryField(blank=True) From 8463924cb920d0380cea2419348bebe40613e873 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sat, 10 Oct 2020 14:03:03 +0200 Subject: [PATCH 048/230] tag plugins to specific versions --- frontend/src/scenes/plugins/CustomPlugin.tsx | 6 +++-- .../src/scenes/plugins/InstalledPlugins.tsx | 22 +++++++++++++++---- frontend/src/scenes/plugins/Repository.tsx | 8 +++---- frontend/src/scenes/plugins/pluginsLogic.ts | 15 ++++++++----- frontend/src/types.ts | 1 + posthog/api/plugin.py | 1 + 6 files changed, 38 insertions(+), 15 deletions(-) diff --git a/frontend/src/scenes/plugins/CustomPlugin.tsx b/frontend/src/scenes/plugins/CustomPlugin.tsx index c9375149f596c..5e61b0ee4d550 100644 --- a/frontend/src/scenes/plugins/CustomPlugin.tsx +++ b/frontend/src/scenes/plugins/CustomPlugin.tsx @@ -4,7 +4,7 @@ import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' export function CustomPlugin(): JSX.Element { - const { customPluginUrl, customPluginError, installingCustomPlugin } = useValues(pluginsLogic) + const { customPluginUrl, customPluginError, loading, installingCustomPlugin } = useValues(pluginsLogic) const { setCustomPluginUrl, installCustomPlugin } = useActions(pluginsLogic) return ( @@ -18,13 +18,15 @@ export function CustomPlugin(): JSX.Element { setCustomPluginUrl(e.target.value)} placeholder="https://github.com/user/repo" /> - - Repository + + {trimTag(plugin.tag)} @@ -76,7 +90,7 @@ export function InstalledPlugins(): JSX.Element { }, }, ]} - loading={pluginsLoading} + loading={loading} locale={{ emptyText: 'No Plugins Installed!' }} /> diff --git a/frontend/src/scenes/plugins/Repository.tsx b/frontend/src/scenes/plugins/Repository.tsx index 478d0eaf7a1d0..927402736468c 100644 --- a/frontend/src/scenes/plugins/Repository.tsx +++ b/frontend/src/scenes/plugins/Repository.tsx @@ -7,8 +7,8 @@ import { DownloadOutlined } from '@ant-design/icons' import { PluginRepositoryEntry } from 'scenes/plugins/types' export function Repository(): JSX.Element { - const { pluginsLoading, repositoryLoading, uninstalledPlugins } = useValues(pluginsLogic) - const { installPlugin } = useActions(pluginsLogic) + const { loading, repositoryLoading, uninstalledPlugins } = useValues(pluginsLogic) + const { installCustomPlugin } = useActions(pluginsLogic) return (
@@ -47,7 +47,7 @@ export function Repository(): JSX.Element {
diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index 9a82f54064183..ce7e65711e75b 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -149,6 +149,11 @@ export const pluginsLogic = kea [s.editingPluginName, s.plugins], (editingPluginName, plugins) => (editingPluginName ? plugins[editingPluginName] : null), ], + loading: [ + (s) => [s.installingCustomPlugin, s.pluginsLoading, s.repositoryLoading], + (installingCustomPlugin, pluginsLoading, repositoryLoading) => + installingCustomPlugin || pluginsLoading || repositoryLoading, + ], }, events: ({ actions }) => ({ @@ -164,18 +169,18 @@ export const pluginsLogic = kea | null = await window - .fetch(repoUrl) + const repoCommitsUrl = `https://api.github.com/repos/${user}/${repo}/commits` + const repoCommits: Record[] | null = await window + .fetch(repoCommitsUrl) .then((response) => response?.json()) .catch(() => null) - if (!repoDetails) { + if (!repoCommits || repoCommits.length === 0) { actions.setCustomPluginError(`Could not find repository: ${customPluginUrl}`) return } - const tag: string = repoDetails['default_branch'] + const tag: string = repoCommits[0].sha const jsonUrl = `https://raw.githubusercontent.com/${user}/${repo}/${tag}/plugin.json` const json: PluginRepositoryEntry | null = await window .fetch(jsonUrl) diff --git a/frontend/src/types.ts b/frontend/src/types.ts index b3016271157ea..af0fb663b4095 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -179,6 +179,7 @@ export interface PluginType { name: string description: string url: string + tag: string enabled: boolean order: number config: Record diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py index 007947842cec5..dd9f1d53837be 100644 --- a/posthog/api/plugin.py +++ b/posthog/api/plugin.py @@ -31,6 +31,7 @@ def update(self, plugin: Plugin, validated_data: Dict, *args: Any, **kwargs: Any plugin.order = validated_data.get("order", plugin.order) if plugin.tag != validated_data.get("tag", plugin.tag): plugin.tag = validated_data.get("tag", plugin.tag) + plugin.configSchema = validated_data.get("configSchema", plugin.configSchema) plugin.archive = self._download_github_zip(plugin.url, plugin.tag) plugin.save() Plugins().publish_reload_command() From ffd9246497e24c30c295b13cbdc7a8a11179b6c8 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sat, 10 Oct 2020 16:40:49 +0200 Subject: [PATCH 049/230] save plugins in pluginConfig --- frontend/src/scenes/plugins/CustomPlugin.tsx | 10 +- .../src/scenes/plugins/InstalledPlugins.tsx | 16 +- frontend/src/scenes/plugins/PluginModal.tsx | 9 +- frontend/src/scenes/plugins/Repository.tsx | 4 +- frontend/src/scenes/plugins/pluginsLogic.ts | 252 ++++++++++-------- frontend/src/scenes/plugins/types.ts | 8 +- frontend/src/types.ts | 9 +- latest_migrations.manifest | 2 +- posthog/api/__init__.py | 1 + posthog/api/plugin.py | 47 ++-- posthog/migrations/0088_pluginconfig.py | 30 +++ posthog/models/__init__.py | 2 +- posthog/models/plugin.py | 12 +- posthog/plugins/__init__.py | 34 +-- 14 files changed, 263 insertions(+), 173 deletions(-) create mode 100644 posthog/migrations/0088_pluginconfig.py diff --git a/frontend/src/scenes/plugins/CustomPlugin.tsx b/frontend/src/scenes/plugins/CustomPlugin.tsx index 5e61b0ee4d550..aec295bc968ed 100644 --- a/frontend/src/scenes/plugins/CustomPlugin.tsx +++ b/frontend/src/scenes/plugins/CustomPlugin.tsx @@ -4,8 +4,8 @@ import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' export function CustomPlugin(): JSX.Element { - const { customPluginUrl, customPluginError, loading, installingCustomPlugin } = useValues(pluginsLogic) - const { setCustomPluginUrl, installCustomPlugin } = useActions(pluginsLogic) + const { customPluginUrl, pluginError, loading } = useValues(pluginsLogic) + const { setCustomPluginUrl, installPlugin } = useActions(pluginsLogic) return (
@@ -25,16 +25,16 @@ export function CustomPlugin(): JSX.Element {
- {customPluginError ?

{customPluginError}

: null} + {pluginError ?

{pluginError}

: null} ) } diff --git a/frontend/src/scenes/plugins/InstalledPlugins.tsx b/frontend/src/scenes/plugins/InstalledPlugins.tsx index 031aaeef557b2..1ea81fe222c94 100644 --- a/frontend/src/scenes/plugins/InstalledPlugins.tsx +++ b/frontend/src/scenes/plugins/InstalledPlugins.tsx @@ -2,8 +2,8 @@ import React from 'react' import { Button, Col, Row, Table, Tooltip } from 'antd' import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { PluginType } from '~/types' import { GithubOutlined, CheckOutlined, ToolOutlined, PauseOutlined } from '@ant-design/icons' +import { PluginTypeWithConfig } from 'scenes/plugins/types' function trimTag(tag: string): string { if (tag.match(/^[a-f0-9]{40}$/)) { @@ -16,7 +16,7 @@ function trimTag(tag: string): string { } export function InstalledPlugins(): JSX.Element { - const { plugins, loading } = useValues(pluginsLogic) + const { installedPlugins, loading } = useValues(pluginsLogic) const { editPlugin } = useActions(pluginsLogic) return ( @@ -27,12 +27,12 @@ export function InstalledPlugins(): JSX.Element { size="small" rowKey={(plugin) => plugin.name} pagination={{ pageSize: 99999, hideOnSinglePage: true }} - dataSource={Object.values(plugins)} + dataSource={installedPlugins} columns={[ { title: 'Plugin', key: 'name', - render: function RenderPlugin(plugin: PluginType): JSX.Element { + render: function RenderPlugin(plugin: PluginTypeWithConfig): JSX.Element { return ( <> @@ -42,7 +42,7 @@ export function InstalledPlugins(): JSX.Element { - {plugin.enabled ? ( + {plugin.pluginConfig?.enabled ? (
Enabled
@@ -69,7 +69,7 @@ export function InstalledPlugins(): JSX.Element { { title: 'Description', key: 'description', - render: function RenderDescription(plugin: PluginType): JSX.Element { + render: function RenderDescription(plugin: PluginTypeWithConfig): JSX.Element { return
{plugin.description}
}, }, @@ -77,13 +77,13 @@ export function InstalledPlugins(): JSX.Element { title: '', key: 'config', align: 'right', - render: function RenderConfig(plugin: PluginType): JSX.Element { + render: function RenderConfig(plugin: PluginTypeWithConfig): JSX.Element { return ( - + {user?.plugin_access?.install && ( + uninstallPlugin(editingPlugin.name) : () => {}} + okText="Yes" + cancelText="No" + > + + + )} - - {trimTag(plugin.tag)} - + {!plugin.url?.startsWith('file:') && ( + + {trimTag(plugin.tag)} + + )} diff --git a/frontend/src/scenes/plugins/PluginModal.tsx b/frontend/src/scenes/plugins/PluginModal.tsx index 4056425982d26..f03d2a99d5ed9 100644 --- a/frontend/src/scenes/plugins/PluginModal.tsx +++ b/frontend/src/scenes/plugins/PluginModal.tsx @@ -32,7 +32,7 @@ export function PluginModal(): JSX.Element { confirmLoading={pluginsLoading} footer={ <> - {user?.plugin_access?.install && ( + {user?.plugin_access?.install && !editingPlugin?.from_json && ( } + if (!user?.plugin_access?.view) { + useEffect(() => { + window.location.href = '/' + }, []) + return
+ } + return (
diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index 4ab7be212885e..aecf09a3157fb 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -159,7 +159,7 @@ export const pluginsLogic = kea< { setCustomPluginUrl: () => null, installPlugin: () => null, - installPluginFailure: (_, { error }) => error, + installPluginFailure: (_, { error }) => error || '', }, ], pluginConfigs: { diff --git a/posthog.json b/posthog.json index ec244354036db..784e1b8d8f8ac 100644 --- a/posthog.json +++ b/posthog.json @@ -3,10 +3,6 @@ { "name": "posthog-currency-normalization-plugin", "path": "../posthog-currency-normalization-plugin/" - }, - { - "name": "posthog-maxmind-plugin", - "path": "../posthog-maxmind-plugin/" } ], "globalPlugins": { diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py index d5ba60e2a2abe..9fd931914f1f6 100644 --- a/posthog/api/plugin.py +++ b/posthog/api/plugin.py @@ -15,7 +15,7 @@ class PluginSerializer(serializers.ModelSerializer): class Meta: model = Plugin - fields = ["id", "name", "description", "url", "config_schema", "tag"] + fields = ["id", "name", "description", "url", "config_schema", "tag", "from_json"] def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: if not settings.INSTALL_PLUGINS_FROM_WEB: @@ -23,6 +23,8 @@ def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: if len(Plugin.objects.filter(name=validated_data["name"])) > 0: raise APIException('Plugin with name "{}" already installed!'.format(validated_data["name"])) validated_data["archive"] = download_plugin_github_zip(validated_data["url"], validated_data["tag"]) + if "from_json" in validated_data: # prevent hackery + del validated_data["from_json"] plugin = Plugin.objects.create(from_web=True, **validated_data) Plugins().publish_reload_command() return plugin @@ -47,6 +49,12 @@ class PluginViewSet(viewsets.ModelViewSet): queryset = Plugin.objects.all() serializer_class = PluginSerializer + def get_queryset(self): + queryset = super().get_queryset() + if not settings.INSTALL_PLUGINS_FROM_WEB: + return queryset.none() + return queryset + @action(methods=["GET"], detail=False) def repository(self, request: request.Request): if not settings.INSTALL_PLUGINS_FROM_WEB: @@ -94,6 +102,12 @@ class PluginConfigViewSet(viewsets.ModelViewSet): queryset = PluginConfig.objects.all() serializer_class = PluginConfigSerializer + def get_queryset(self): + queryset = super().get_queryset() + if not settings.CONFIGURE_PLUGINS_FROM_WEB: + return queryset.none() + return queryset.filter(team_id=self.request.user.team.pk) + # we don't use this endpoint, but have something anyway to prevent team leakage def destroy(self, request: request.Request, pk=None) -> Response: # type: ignore if not settings.CONFIGURE_PLUGINS_FROM_WEB: diff --git a/posthog/settings.py b/posthog/settings.py index 7250bdc2acebd..b433c0b286bf6 100644 --- a/posthog/settings.py +++ b/posthog/settings.py @@ -68,7 +68,7 @@ def print_warning(warning_lines: Sequence[str]): else: JS_URL = os.environ.get("JS_URL", "") -INSTALL_PLUGINS_FROM_WEB = get_bool_from_env("INSTALL_PLUGINS_FROM_WEB", False) +INSTALL_PLUGINS_FROM_WEB = get_bool_from_env("INSTALL_PLUGINS_FROM_WEB", True) CONFIGURE_PLUGINS_FROM_WEB = get_bool_from_env("CONFIGURE_PLUGINS_FROM_WEB", True) # This is set as a cross-domain cookie with a random value. From 355fc847b7237a0af2705da76a86116c0e745797 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Thu, 15 Oct 2020 14:42:39 +0200 Subject: [PATCH 086/230] remove posthog.json from git --- .gitignore | 1 + posthog.json | 17 ----------------- 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 posthog.json diff --git a/.gitignore b/.gitignore index 2b4466b007e4c..f60b28cd25a06 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ yarn-error.log yalc.lock cypress/screenshots/* docker-compose.prod.yml +posthog.json diff --git a/posthog.json b/posthog.json deleted file mode 100644 index 784e1b8d8f8ac..0000000000000 --- a/posthog.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "plugins": [ - { - "name": "posthog-currency-normalization-plugin", - "path": "../posthog-currency-normalization-plugin/" - } - ], - "globalPlugins": { - "posthog-maxmind-plugin": { - "geoip_path": "/Users/marius/Projects/PostHog/GeoLite2-City.mmdb" - }, - "posthog-currency-normalization-plugin": { - "openexchangerates_api_key": "34afa399dc2b41cfa8a68273ff159624", - "normalized_currency": "EUR" - } - } -} From c210068e10837ca81477b46194fb10ef4d1ad6ee Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Thu, 15 Oct 2020 14:56:06 +0200 Subject: [PATCH 087/230] test config schema from json --- posthog/plugins/test/test_sync.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/posthog/plugins/test/test_sync.py b/posthog/plugins/test/test_sync.py index 3c7dcd04bb26b..d1f10b218b486 100644 --- a/posthog/plugins/test/test_sync.py +++ b/posthog/plugins/test/test_sync.py @@ -82,6 +82,7 @@ def test_load_plugin_local(self, mock_get): self.assertEqual(plugin.from_json, True) self.assertEqual(plugin.from_web, False) self.assertEqual(plugin.archive, None) + self.assertEqual(plugin.config_schema["bar"]["type"], "string") self.assertEqual(plugin.tag, "") with plugins_in_posthog_json([]) as filename: @@ -116,6 +117,7 @@ def test_load_plugin_local_if_exists_from_app(self, mock_get): self.assertEqual(plugin.from_web, True) self.assertEqual(plugin.archive, None) self.assertEqual(plugin.tag, "") + self.assertEqual(plugin.config_schema["bar"]["type"], "string") with plugins_in_posthog_json([]) as filename: sync_posthog_json_plugins(raise_errors=True, filename=filename) @@ -130,6 +132,7 @@ def test_load_plugin_local_if_exists_from_app(self, mock_get): self.assertEqual(plugin.from_web, True) self.assertEqual(plugin.archive, None) self.assertEqual(plugin.tag, "") + self.assertEqual(plugin.config_schema["bar"]["type"], "string") def test_load_plugin_http(self, mock_get): self.assertEqual(len(Plugin.objects.all()), 0) @@ -156,6 +159,7 @@ def test_load_plugin_http(self, mock_get): self.assertEqual(plugin.from_web, False) self.assertEqual(bytes(plugin.archive), base64.b64decode(HELLO_WORLD_PLUGIN)) self.assertEqual(plugin.tag, "3c4c77e7d7878e87be3c2373b658c74ec3085f49") + self.assertEqual(plugin.config_schema["bar"]["type"], "string") with plugins_in_posthog_json([]) as filename: sync_posthog_json_plugins(raise_errors=True, filename=filename) @@ -197,6 +201,7 @@ def test_load_plugin_http_if_exists_from_app(self, mock_get): self.assertEqual(plugin.from_web, True) self.assertEqual(bytes(plugin.archive), base64.b64decode(HELLO_WORLD_PLUGIN)) self.assertEqual(plugin.tag, "3c4c77e7d7878e87be3c2373b658c74ec3085f49") + self.assertEqual(plugin.config_schema["bar"]["type"], "string") with plugins_in_posthog_json([]) as filename: sync_posthog_json_plugins(raise_errors=True, filename=filename) @@ -211,6 +216,7 @@ def test_load_plugin_http_if_exists_from_app(self, mock_get): self.assertEqual(plugin.from_web, True) self.assertEqual(bytes(plugin.archive), base64.b64decode(HELLO_WORLD_PLUGIN)) self.assertEqual(plugin.tag, "3c4c77e7d7878e87be3c2373b658c74ec3085f49") + self.assertEqual(plugin.config_schema["bar"]["type"], "string") def test_load_plugin_local_to_http_and_back(self, mock_get): with extracted_base64_zip(HELLO_WORLD_PLUGIN) as plugin_path: @@ -234,6 +240,7 @@ def test_load_plugin_local_to_http_and_back(self, mock_get): self.assertEqual(plugin.from_web, False) self.assertEqual(bytes(plugin.archive), base64.b64decode(HELLO_WORLD_PLUGIN)) self.assertEqual(plugin.tag, "3c4c77e7d7878e87be3c2373b658c74ec3085f49") + self.assertEqual(plugin.config_schema["bar"]["type"], "string") with plugins_in_posthog_json([{"name": "helloworldplugin", "path": plugin_path,}]) as filename: sync_posthog_json_plugins(raise_errors=True, filename=filename) @@ -247,6 +254,7 @@ def test_load_plugin_local_to_http_and_back(self, mock_get): self.assertEqual(plugin.from_web, False) self.assertEqual(plugin.archive, None) self.assertEqual(plugin.tag, "") + self.assertEqual(plugin.config_schema["bar"]["type"], "string") with plugins_in_posthog_json( [ @@ -268,3 +276,4 @@ def test_load_plugin_local_to_http_and_back(self, mock_get): self.assertEqual(plugin.from_web, False) self.assertEqual(bytes(plugin.archive), base64.b64decode(HELLO_WORLD_PLUGIN)) self.assertEqual(plugin.tag, "3c4c77e7d7878e87be3c2373b658c74ec3085f49") + self.assertEqual(plugin.config_schema["bar"]["type"], "string") From 5dbcf645ecea3ca7f9281e4a6dbea6ef3c016bf9 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Thu, 15 Oct 2020 15:01:08 +0200 Subject: [PATCH 088/230] if you can install via the web, you can also configure --- frontend/src/scenes/plugins/PluginModal.tsx | 4 +++- posthog/settings.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/scenes/plugins/PluginModal.tsx b/frontend/src/scenes/plugins/PluginModal.tsx index f03d2a99d5ed9..713977e7e9af7 100644 --- a/frontend/src/scenes/plugins/PluginModal.tsx +++ b/frontend/src/scenes/plugins/PluginModal.tsx @@ -11,6 +11,8 @@ export function PluginModal(): JSX.Element { const { editPlugin, savePluginConfig, uninstallPlugin } = useActions(pluginsLogic) const [form] = Form.useForm() + const canDelete = user?.plugin_access?.install && !editingPlugin?.from_json + useEffect(() => { if (editingPlugin) { form.setFieldsValue({ @@ -32,7 +34,7 @@ export function PluginModal(): JSX.Element { confirmLoading={pluginsLoading} footer={ <> - {user?.plugin_access?.install && !editingPlugin?.from_json && ( + {canDelete && ( Date: Thu, 15 Oct 2020 15:07:31 +0200 Subject: [PATCH 089/230] remove separate view access --- frontend/src/layout/Sidebar.js | 2 +- frontend/src/scenes/plugins/Plugins.tsx | 2 +- posthog/api/plugin.py | 2 +- posthog/api/user.py | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/layout/Sidebar.js b/frontend/src/layout/Sidebar.js index e73bb0751b4f1..8d70f2ee7bdce 100644 --- a/frontend/src/layout/Sidebar.js +++ b/frontend/src/layout/Sidebar.js @@ -282,7 +282,7 @@ export function Sidebar({ user, sidebarCollapsed, setSidebarCollapsed }) { )} - {user.plugin_access?.view && ( + {user.plugin_access?.configure && ( Plugins diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index cf9bdf4bf1d3e..92f83a14efab6 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -15,7 +15,7 @@ function _Plugins(): JSX.Element { return
} - if (!user?.plugin_access?.view) { + if (!user?.plugin_access?.configure) { useEffect(() => { window.location.href = '/' }, []) diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py index 9fd931914f1f6..76ff61c04d5d0 100644 --- a/posthog/api/plugin.py +++ b/posthog/api/plugin.py @@ -51,7 +51,7 @@ class PluginViewSet(viewsets.ModelViewSet): def get_queryset(self): queryset = super().get_queryset() - if not settings.INSTALL_PLUGINS_FROM_WEB: + if not settings.INSTALL_PLUGINS_FROM_WEB and not settings.CONFIGURE_PLUGINS_FROM_WEB: return queryset.none() return queryset diff --git a/posthog/api/user.py b/posthog/api/user.py index 54abc7a33b1c2..dd579d6c0c36a 100644 --- a/posthog/api/user.py +++ b/posthog/api/user.py @@ -94,7 +94,6 @@ def user(request): "is_multi_tenancy": getattr(settings, "MULTI_TENANCY", False), "ee_available": request.user.ee_available, "plugin_access": { - "view": True, "install": settings.INSTALL_PLUGINS_FROM_WEB, "configure": settings.CONFIGURE_PLUGINS_FROM_WEB, }, From 4ee561bc80f461f0536e7986690813ac430c04d7 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Thu, 15 Oct 2020 15:10:22 +0200 Subject: [PATCH 090/230] title as "Plugins" instead of "Installed Plugins" if we can't install ourselves and don't see the repository --- frontend/src/scenes/plugins/InstalledPlugins.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/scenes/plugins/InstalledPlugins.tsx b/frontend/src/scenes/plugins/InstalledPlugins.tsx index f6cf45004d17a..5e2ee0bf35fad 100644 --- a/frontend/src/scenes/plugins/InstalledPlugins.tsx +++ b/frontend/src/scenes/plugins/InstalledPlugins.tsx @@ -4,6 +4,7 @@ import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { GithubOutlined, CheckOutlined, ToolOutlined, PauseOutlined } from '@ant-design/icons' import { PluginTypeWithConfig } from 'scenes/plugins/types' +import { userLogic } from 'scenes/userLogic' function trimTag(tag: string): string { if (tag.match(/^[a-f0-9]{40}$/)) { @@ -16,12 +17,15 @@ function trimTag(tag: string): string { } export function InstalledPlugins(): JSX.Element { + const { user } = useValues(userLogic) const { installedPlugins, loading } = useValues(pluginsLogic) const { editPlugin } = useActions(pluginsLogic) + const canInstall = user?.plugin_access?.install + return (
-

Installed Plugins

+

{canInstall ? 'Installed Plugins' : 'Plugins'}

Date: Fri, 16 Oct 2020 11:21:30 +0200 Subject: [PATCH 091/230] add self.team to plugin base class --- posthog/plugins/plugins.py | 1 + 1 file changed, 1 insertion(+) diff --git a/posthog/plugins/plugins.py b/posthog/plugins/plugins.py index c2b45b7f3ea42..d8578b9be050b 100644 --- a/posthog/plugins/plugins.py +++ b/posthog/plugins/plugins.py @@ -58,6 +58,7 @@ def get(self, key) -> Any: class PluginBaseClass: def __init__(self, config: "TeamPlugin"): self.config = config.config + self.team = config.team self.cache = PluginCache(plugin_name=config.name) def schedule_jobs(self, sender): From 4002fd4dcfdd4395e57dc2ff89aa039d7dbadeb4 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 16 Oct 2020 12:11:41 +0200 Subject: [PATCH 092/230] add instance_init method --- posthog/plugins/plugins.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/posthog/plugins/plugins.py b/posthog/plugins/plugins.py index d8578b9be050b..3d7ca550fdf7f 100644 --- a/posthog/plugins/plugins.py +++ b/posthog/plugins/plugins.py @@ -60,6 +60,14 @@ def __init__(self, config: "TeamPlugin"): self.config = config.config self.team = config.team self.cache = PluginCache(plugin_name=config.name) + self.team_init() + + @staticmethod + def instance_init(): + pass + + def team_init(self): + pass def schedule_jobs(self, sender): pass @@ -234,6 +242,7 @@ def load_plugins(self): plugin=item[1], module=module, ) + item[1].instance_init() if found_plugin: if local_plugin: From 5329efd94d21d727344f6f2e491ac00b0ea21748 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 16 Oct 2020 12:20:31 +0200 Subject: [PATCH 093/230] refactor into files --- posthog/plugins/__init__.py | 4 +- posthog/plugins/cache.py | 31 ++++++++++++ posthog/plugins/models.py | 70 ++++++++++++++++++++++++++ posthog/plugins/plugins.py | 99 ++----------------------------------- 4 files changed, 107 insertions(+), 97 deletions(-) create mode 100644 posthog/plugins/cache.py create mode 100644 posthog/plugins/models.py diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index c81d34cdf3a4e..b7e24d0b87f84 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -1,3 +1,5 @@ -from .plugins import PluginBaseClass, PluginCache, PluginModule, Plugins, PosthogEvent, TeamPlugin +from .cache import PluginCache +from .models import PluginBaseClass, PluginModule, PosthogEvent, TeamPlugin +from .plugins import Plugins from .sync import sync_posthog_json_plugins from .utils import download_plugin_github_zip diff --git a/posthog/plugins/cache.py b/posthog/plugins/cache.py new file mode 100644 index 0000000000000..6889ddb76623d --- /dev/null +++ b/posthog/plugins/cache.py @@ -0,0 +1,31 @@ +import pickle +from typing import Any + +from posthog.cache import get_redis_instance + + +class PluginCache: + def __init__(self, scope: str): + self.scope = scope + self.redis = get_redis_instance() + + def format_key(self, key): + key = "{scope}_{key}".format(scope=self.scope, key=key) + return key + + def set(self, key: str, value: Any): + if not self.redis: + raise Exception("Redis not configured!") + key = self.format_key(key) + value = pickle.dumps(value) + self.redis.set(key, value) + + def get(self, key) -> Any: + if not self.redis: + raise Exception("Redis not configured!") + key = self.format_key(key) + str_value = self.redis.get(key) + if not str_value: + return None + value = pickle.loads(str_value) + return value diff --git a/posthog/plugins/models.py b/posthog/plugins/models.py new file mode 100644 index 0000000000000..e79520cb3c050 --- /dev/null +++ b/posthog/plugins/models.py @@ -0,0 +1,70 @@ +import datetime +from dataclasses import dataclass +from types import ModuleType +from typing import Any, Dict, List, Optional, Type + +from .cache import PluginCache + + +@dataclass +class PosthogEvent: + ip: str + site_url: str + event: str + distinct_id: str + team_id: int + properties: Dict[Any, Any] + timestamp: datetime.datetime + + +# Contains metadata and the python module for a plugin +@dataclass +class PluginModule: + id: int # id in the Plugin model + name: str # name in the Plugin model + url: str # url in the Plugin model, can be https: or file: + tag: str # tag in the Plugin model + module_name: str # name of the module, "posthog.plugins.plugin_{id}_{name}_{tag}" + plugin_path: str # path of the local folder or the temporary .zip file for github + requirements: List[str] # requirements.txt split into lines + module: ModuleType # python module + plugin: Type["PluginBaseClass"] # plugin base class extracted from the exports in the module + + +# Contains per-team config for a plugin +@dataclass +class TeamPlugin: + team: int # team id + plugin: int # plugin id + name: str # plugin name + tag: str # plugin tag + config: Dict[str, Any] # config from the DB + loaded_class: Optional["PluginBaseClass"] # link to the class + plugin_module: PluginModule # link to the module + + +class PluginBaseClass: + def __init__(self, config: TeamPlugin): + self.config = config.config + self.team = config.team + self.cache = PluginCache(scope="{}/{}".format(config.name, config.team)) + self.team_init() + + @staticmethod + def instance_init(): + pass + + def team_init(self): + pass + + def schedule_jobs(self, sender): + pass + + def process_event(self, event: PosthogEvent): + pass + + def process_alias(self, event: PosthogEvent): + pass + + def process_identify(self, event: PosthogEvent): + pass diff --git a/posthog/plugins/plugins.py b/posthog/plugins/plugins.py index 3d7ca550fdf7f..95bb9fecf4713 100644 --- a/posthog/plugins/plugins.py +++ b/posthog/plugins/plugins.py @@ -1,111 +1,18 @@ -import datetime import importlib import importlib.util import inspect import os -import pickle import tempfile import zipimport -from dataclasses import dataclass -from types import ModuleType -from typing import Any, Dict, List, Optional, Type +from typing import Dict, List, Optional from zipfile import ZipFile from posthog.cache import get_redis_instance from posthog.utils import SingletonDecorator -REDIS_INSTANCE = get_redis_instance() - - -@dataclass -class PosthogEvent: - ip: str - site_url: str - event: str - distinct_id: str - team_id: int - properties: Dict[Any, Any] - timestamp: datetime.datetime - +from .models import PluginBaseClass, PluginModule, TeamPlugin -class PluginCache: - def __init__(self, plugin_name: str): - self.plugin_name = plugin_name - self.redis = get_redis_instance() - - def format_key(self, key): - key = "{plugin_name}_{key}".format(plugin_name=self.plugin_name, key=key) - return key - - def set(self, key: str, value: Any): - if not self.redis: - raise Exception("Redis not configured!") - key = self.format_key(key) - value = pickle.dumps(value) - self.redis.set(key, value) - - def get(self, key) -> Any: - if not self.redis: - raise Exception("Redis not configured!") - key = self.format_key(key) - str_value = self.redis.get(key) - if not str_value: - return None - value = pickle.loads(str_value) - return value - - -class PluginBaseClass: - def __init__(self, config: "TeamPlugin"): - self.config = config.config - self.team = config.team - self.cache = PluginCache(plugin_name=config.name) - self.team_init() - - @staticmethod - def instance_init(): - pass - - def team_init(self): - pass - - def schedule_jobs(self, sender): - pass - - def process_event(self, event: PosthogEvent): - pass - - def process_alias(self, event: PosthogEvent): - pass - - def process_identify(self, event: PosthogEvent): - pass - - -# Contains metadata and the python module for a plugin -@dataclass -class PluginModule: - id: int # id in the Plugin model - name: str # name in the Plugin model - url: str # url in the Plugin model, can be https: or file: - tag: str # tag in the Plugin model - module_name: str # name of the module, "posthog.plugins.plugin_{id}_{name}_{tag}" - plugin_path: str # path of the local folder or the temporary .zip file for github - requirements: List[str] # requirements.txt split into lines - module: ModuleType # python module - plugin: Type[PluginBaseClass] # plugin base class extracted from the exports in the module - - -# Contains per-team config for a plugin -@dataclass -class TeamPlugin: - team: int # team id - plugin: int # plugin id - name: str # plugin name - tag: str # plugin tag - config: Dict[str, Any] # config from the DB - loaded_class: Optional[PluginBaseClass] # link to the class - plugin_module: PluginModule # link to the module +REDIS_INSTANCE = get_redis_instance() class _Plugins: From bd3ea3ca18c8a6dd4613e6a95f29b802ceda3238 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 16 Oct 2020 13:27:04 +0200 Subject: [PATCH 094/230] sync global plugin config from json --- posthog/migrations/0090_plugins.py | 2 +- posthog/models/plugin.py | 2 +- posthog/plugins/plugins.py | 3 +- posthog/plugins/sync.py | 50 ++++++++++++++ posthog/plugins/test/test_sync.py | 104 ++++++++++++++++++++++++++++- 5 files changed, 156 insertions(+), 5 deletions(-) diff --git a/posthog/migrations/0090_plugins.py b/posthog/migrations/0090_plugins.py index 2bcd8255cd7db..8bc81a7743571 100644 --- a/posthog/migrations/0090_plugins.py +++ b/posthog/migrations/0090_plugins.py @@ -30,7 +30,7 @@ class Migration(migrations.Migration): name="PluginConfig", fields=[ ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), - ("team", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="posthog.Team")), + ("team", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="posthog.Team", null=True)), ("plugin", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="posthog.Plugin")), ("enabled", models.BooleanField(default=False)), ("order", models.IntegerField(blank=True, null=True)), diff --git a/posthog/models/plugin.py b/posthog/models/plugin.py index 68324ef0b5239..e3aa3c8939329 100644 --- a/posthog/models/plugin.py +++ b/posthog/models/plugin.py @@ -14,7 +14,7 @@ class Plugin(models.Model): class PluginConfig(models.Model): - team: models.ForeignKey = models.ForeignKey("Team", on_delete=models.CASCADE) + team: models.ForeignKey = models.ForeignKey("Team", on_delete=models.CASCADE, null=True) plugin: models.ForeignKey = models.ForeignKey("Plugin", on_delete=models.CASCADE) enabled: models.BooleanField = models.BooleanField(default=False) order: models.IntegerField = models.IntegerField(null=True, blank=True) diff --git a/posthog/plugins/plugins.py b/posthog/plugins/plugins.py index 95bb9fecf4713..1f96461c6cfa5 100644 --- a/posthog/plugins/plugins.py +++ b/posthog/plugins/plugins.py @@ -19,7 +19,7 @@ class _Plugins: def __init__(self): from posthog.models.plugin import Plugin, PluginConfig - from .sync import sync_posthog_json_plugins + from .sync import sync_global_plugin_config, sync_posthog_json_plugins self.redis = get_redis_instance() self.plugins: List[Plugin] = [] # type not loaded yet @@ -28,6 +28,7 @@ def __init__(self): self.plugins_by_team: Dict[int, List[TeamPlugin]] = {} sync_posthog_json_plugins() + sync_global_plugin_config() self.load_plugins() self.load_plugin_configs() diff --git a/posthog/plugins/sync.py b/posthog/plugins/sync.py index 36398296f1cf1..8879a089fabc9 100644 --- a/posthog/plugins/sync.py +++ b/posthog/plugins/sync.py @@ -134,3 +134,53 @@ def print_or_raise(msg, raise_errors): if raise_errors: raise Exception(msg) print("šŸ”»šŸ”» {}".format(msg)) + + +def sync_global_plugin_config(filename="posthog.json"): + from posthog.models.plugin import Plugin, PluginConfig + + posthog_json = load_json_file(filename) + + # get all plugins with global configs from posthog.json + json_plugin_configs = {} + if posthog_json and posthog_json.get("plugins", None): + for plugin in posthog_json["plugins"]: + global_config = plugin.get("global", None) + if global_config: + json_plugin_configs[plugin["name"]] = global_config + + # what plugins actually exist in the db? + db_plugins = {} + for plugin in Plugin.objects.all(): + db_plugins[plugin.name] = plugin + + # get all global plugins configs from the db... delete if not in posthog.json or plugin not installed + db_plugin_configs = {} + for plugin_config in list(PluginConfig.objects.filter(team=None)): + name = plugin_config.plugin.name + if not json_plugin_configs.get(name, None) or not db_plugins.get(name, None): + plugin_config.delete() + continue + db_plugin_configs[name] = plugin_config + + # add new and update changed configs into the db + for name, plugin_json in json_plugin_configs.items(): + enabled = plugin_json.get("enabled", False) + order = plugin_json.get("order", 0) + config = plugin_json.get("config", {}) + + db_plugin_config = db_plugin_configs.get(name, None) + if db_plugin_config: + if ( + db_plugin_config.enabled != enabled + or db_plugin_config.order != order + or json.dumps(db_plugin_config.config) != json.dumps(config) + ): + db_plugin_config.enabled = enabled + db_plugin_config.order = order + db_plugin_config.config = config + db_plugin_config.save() + elif db_plugins.get(name, None): + PluginConfig.objects.create( + team=None, plugin=db_plugins[name], enabled=enabled, order=order, config=config, + ) diff --git a/posthog/plugins/test/test_sync.py b/posthog/plugins/test/test_sync.py index d1f10b218b486..5519559e2a321 100644 --- a/posthog/plugins/test/test_sync.py +++ b/posthog/plugins/test/test_sync.py @@ -8,8 +8,8 @@ from unittest import mock from posthog.api.test.base import BaseTest -from posthog.models import Plugin -from posthog.plugins.sync import sync_posthog_json_plugins +from posthog.models import Plugin, PluginConfig +from posthog.plugins.sync import sync_global_plugin_config, sync_posthog_json_plugins from .plugin_archives import HELLO_WORLD_PLUGIN @@ -277,3 +277,103 @@ def test_load_plugin_local_to_http_and_back(self, mock_get): self.assertEqual(bytes(plugin.archive), base64.b64decode(HELLO_WORLD_PLUGIN)) self.assertEqual(plugin.tag, "3c4c77e7d7878e87be3c2373b658c74ec3085f49") self.assertEqual(plugin.config_schema["bar"]["type"], "string") + + def test_sync_global_config(self, mock_get): + self.assertEqual(len(Plugin.objects.all()), 0) + + with plugins_in_posthog_json( + [ + { + "name": "helloworldplugin", + "url": "https://github.com/PostHog/helloworldplugin/", + "tag": "3c4c77e7d7878e87be3c2373b658c74ec3085f49", + } + ] + ) as filename: + sync_posthog_json_plugins(raise_errors=True, filename=filename) + sync_global_plugin_config(filename=filename) + self.assertEqual(len(Plugin.objects.all()), 1) + self.assertEqual(len(PluginConfig.objects.all()), 0) + + with plugins_in_posthog_json( + [ + { + "name": "helloworldplugin", + "url": "https://github.com/PostHog/helloworldplugin/", + "tag": "3c4c77e7d7878e87be3c2373b658c74ec3085f49", + "global": {"enabled": True, "order": 2, "config": {"bar": "foo"}}, + } + ] + ) as filename: + sync_posthog_json_plugins(raise_errors=True, filename=filename) + sync_global_plugin_config(filename=filename) + self.assertEqual(len(Plugin.objects.all()), 1) + self.assertEqual(len(PluginConfig.objects.all()), 1) + plugin_config = PluginConfig.objects.get() + self.assertEqual(plugin_config.team, None) + self.assertEqual(plugin_config.plugin, Plugin.objects.get()) + self.assertEqual(plugin_config.enabled, True) + self.assertEqual(plugin_config.config["bar"], "foo") + self.assertEqual(plugin_config.order, 2) + + with plugins_in_posthog_json( + [ + { + "name": "helloworldplugin", + "url": "https://github.com/PostHog/helloworldplugin/", + "tag": "3c4c77e7d7878e87be3c2373b658c74ec3085f49", + "global": {"enabled": False, "order": 3, "config": {"bar": "foop"}}, + } + ] + ) as filename: + sync_posthog_json_plugins(raise_errors=True, filename=filename) + sync_global_plugin_config(filename=filename) + self.assertEqual(len(Plugin.objects.all()), 1) + self.assertEqual(len(PluginConfig.objects.all()), 1) + plugin_config = PluginConfig.objects.get() + self.assertEqual(plugin_config.team, None) + self.assertEqual(plugin_config.plugin, Plugin.objects.get()) + self.assertEqual(plugin_config.enabled, False) + self.assertEqual(plugin_config.config["bar"], "foop") + self.assertEqual(plugin_config.order, 3) + + with plugins_in_posthog_json([]) as filename: + sync_posthog_json_plugins(raise_errors=True, filename=filename) + sync_global_plugin_config(filename=filename) + self.assertEqual(len(Plugin.objects.all()), 0) + self.assertEqual(len(PluginConfig.objects.all()), 0) + + with plugins_in_posthog_json( + [ + { + "name": "helloworldplugin", + "url": "https://github.com/PostHog/helloworldplugin/", + "tag": "3c4c77e7d7878e87be3c2373b658c74ec3085f49", + "global": {"enabled": False, "order": 3, "config": {"bar": "foop"}}, + } + ] + ) as filename: + sync_posthog_json_plugins(raise_errors=True, filename=filename) + sync_global_plugin_config(filename=filename) + self.assertEqual(len(Plugin.objects.all()), 1) + self.assertEqual(len(PluginConfig.objects.all()), 1) + plugin_config = PluginConfig.objects.get() + self.assertEqual(plugin_config.team, None) + self.assertEqual(plugin_config.plugin, Plugin.objects.get()) + self.assertEqual(plugin_config.enabled, False) + self.assertEqual(plugin_config.config["bar"], "foop") + self.assertEqual(plugin_config.order, 3) + + with plugins_in_posthog_json( + [ + { + "name": "helloworldplugin", + "url": "https://github.com/PostHog/helloworldplugin/", + "tag": "3c4c77e7d7878e87be3c2373b658c74ec3085f49", + } + ] + ) as filename: + sync_posthog_json_plugins(raise_errors=True, filename=filename) + sync_global_plugin_config(filename=filename) + self.assertEqual(len(Plugin.objects.all()), 1) + self.assertEqual(len(PluginConfig.objects.all()), 0) From 5277078f42baba1419cde05aaa5db7fea4035277 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 16 Oct 2020 14:01:30 +0200 Subject: [PATCH 095/230] make global plugins work, add test --- posthog/plugins/models.py | 1 + posthog/plugins/plugins.py | 38 +++++++-- posthog/plugins/test/test_plugins.py | 115 +++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 8 deletions(-) diff --git a/posthog/plugins/models.py b/posthog/plugins/models.py index e79520cb3c050..89c587449315b 100644 --- a/posthog/plugins/models.py +++ b/posthog/plugins/models.py @@ -36,6 +36,7 @@ class PluginModule: class TeamPlugin: team: int # team id plugin: int # plugin id + order: int # plugin order name: str # plugin name tag: str # plugin tag config: Dict[str, Any] # config from the DB diff --git a/posthog/plugins/plugins.py b/posthog/plugins/plugins.py index 1f96461c6cfa5..95807ca693e1b 100644 --- a/posthog/plugins/plugins.py +++ b/posthog/plugins/plugins.py @@ -7,6 +7,8 @@ from typing import Dict, List, Optional from zipfile import ZipFile +from django.db.models import F + from posthog.cache import get_redis_instance from posthog.utils import SingletonDecorator @@ -179,7 +181,9 @@ def unregister_plugin(self, id): def load_plugin_configs(self): from posthog.models.plugin import PluginConfig - self.plugin_configs = list(PluginConfig.objects.filter(enabled=True).order_by("team", "order").all()) + self.plugin_configs = list( + PluginConfig.objects.filter(enabled=True).order_by(F("team_id").desc(nulls_first=True), "order").all() + ) self.plugins_by_team = {} for plugin_config in self.plugin_configs: @@ -195,6 +199,7 @@ def load_plugin_configs(self): team_plugin = TeamPlugin( team=plugin_config.team_id, plugin=plugin_config.plugin_id, + order=plugin_config.order, name=plugin_module.name, tag=plugin_module.tag, config=plugin_config.config, @@ -208,6 +213,18 @@ def load_plugin_configs(self): print('šŸ”»šŸ”»šŸ”» Error loading plugin "{}" for team {}'.format(plugin_module.name, plugin_config.team_id)) print(e) + # if we have global plugins, add them to the team plugins list for all teams that have team plugins + global_plugins = self.plugins_by_team.get(None, None) + if global_plugins and len(global_plugins) > 0: + for team, plugins in self.plugins_by_team.items(): + team_plugin_keys = {} + for plugin in plugins: + team_plugin_keys[plugin.name] = True + for plugin in global_plugins: + if not team_plugin_keys.get(plugin.name, None): + plugins.append(plugin) + plugins.sort(key=order_by_order) + def install_requirements(self, plugin_name, requirements): if len(requirements) > 0: print('Loading requirements for plugin "{}": {}'.format(plugin_name, requirements)) @@ -228,14 +245,15 @@ def install_requirement(self, requirement): def exec_plugins(self, event, team_id): team_plugins = self.plugins_by_team.get(team_id, None) + global_plugins = self.plugins_by_team.get(None, []) + plugins_to_run = team_plugins if team_plugins else global_plugins - if team_plugins: - for plugin in team_plugins: - event = self.exec_plugin(plugin.loaded_class, event, "process_event") - if event.event == "$identify": - event = self.exec_plugin(plugin.loaded_class, event, "process_identify") - if event.event == "$create_alias": - event = self.exec_plugin(plugin.loaded_class, event, "process_alias") + for plugin in plugins_to_run: + event = self.exec_plugin(plugin.loaded_class, event, "process_event") + if event.event == "$identify": + event = self.exec_plugin(plugin.loaded_class, event, "process_identify") + if event.event == "$create_alias": + event = self.exec_plugin(plugin.loaded_class, event, "process_alias") return event @@ -265,3 +283,7 @@ def publish_reload_command(self, team_id: Optional[int] = None): Plugins = SingletonDecorator(_Plugins) + + +def order_by_order(e): + return e.order diff --git a/posthog/plugins/test/test_plugins.py b/posthog/plugins/test/test_plugins.py index 15e0552c74ff6..58840329db3fc 100644 --- a/posthog/plugins/test/test_plugins.py +++ b/posthog/plugins/test/test_plugins.py @@ -94,3 +94,118 @@ def test_load_plugin(self): events = Event.objects.all() self.assertEqual(len(events), 4) self.assertEqual(events[3].properties.get("bar", None), None) + + def test_load_global_plugin(self): + Person.objects.create(team=self.team, distinct_ids=["plugin_test_distinct_id"]) + + self._create_event() + + event = Event.objects.get() + self.assertEqual(event.event, "$pageview") + self.assertEqual(event.properties.get("bar", None), None) + + plugin = Plugin.objects.create( + name="helloworldplugin", + description="Hello World Plugin that runs in test mode", + url="https://github.com/PostHog/helloworldplugin", + config_schema={ + "bar": {"name": "What's in the bar?", "type": "string", "default": "baz", "required": False} + }, + tag="3c4c77e7d7878e87be3c2373b658c74ec3085f49", + archive=base64.b64decode(HELLO_WORLD_PLUGIN), + from_web=True, + from_json=False, + ) + plugin_config = PluginConfig.objects.create( + team=None, plugin=plugin, enabled=True, order=0, config={"bar": "foo"}, + ) + + self.assertEqual(Plugin.objects.count(), 1) + self.assertEqual(PluginConfig.objects.count(), 1) + + Plugins().reload_plugins() + + self._create_event() + + events = Event.objects.all() + self.assertEqual(len(events), 2) + self.assertEqual(events[0].properties.get("hello", None), None) + self.assertEqual(events[0].properties.get("bar", None), None) + self.assertEqual(events[1].properties.get("hello", None), "world") + self.assertEqual(events[1].properties.get("bar", None), "foo") + + plugin_config.config["bar"] = "foobar" + plugin_config.save() + + Plugins().reload_plugins() + + self._create_event() + + events = Event.objects.all() + self.assertEqual(len(events), 3) + self.assertEqual(events[2].properties.get("bar", None), "foobar") + + plugin_config.delete() + plugin.delete() + + Plugins().reload_plugins() + + self._create_event() + + events = Event.objects.all() + self.assertEqual(len(events), 4) + self.assertEqual(events[3].properties.get("bar", None), None) + + def test_local_plugin_takes_precedence(self): + self.assertEqual(Plugin.objects.count(), 0) + Person.objects.create(team=self.team, distinct_ids=["plugin_test_distinct_id"]) + + self._create_event() + + event = Event.objects.get() + self.assertEqual(event.event, "$pageview") + self.assertEqual(event.properties.get("bar", None), None) + + plugin = Plugin.objects.create( + name="helloworldplugin", + description="Hello World Plugin that runs in test mode", + url="https://github.com/PostHog/helloworldplugin", + config_schema={ + "bar": {"name": "What's in the bar?", "type": "string", "default": "baz", "required": False} + }, + tag="3c4c77e7d7878e87be3c2373b658c74ec3085f49", + archive=base64.b64decode(HELLO_WORLD_PLUGIN), + from_web=True, + from_json=False, + ) + self.assertEqual(Plugin.objects.count(), 1) + local_plugin_config = PluginConfig.objects.create( + team=self.team, plugin=plugin, enabled=True, order=2, config={"bar": "foo_local"}, + ) + global_plugin_config = PluginConfig.objects.create( + team=None, plugin=plugin, enabled=True, order=1, config={"bar": "foo_global"}, + ) + + self.assertEqual(Plugin.objects.count(), 1) + self.assertEqual(PluginConfig.objects.count(), 2) + + Plugins().reload_plugins() + + self._create_event() + + events = Event.objects.all() + self.assertEqual(len(events), 2) + self.assertEqual(events[0].properties.get("hello", None), None) + self.assertEqual(events[0].properties.get("bar", None), None) + self.assertEqual(events[1].properties.get("hello", None), "world") + self.assertEqual(events[1].properties.get("bar", None), "foo_local") + + local_plugin_config.delete() + + Plugins().reload_plugins() + + self._create_event() + + events = Event.objects.all() + self.assertEqual(len(events), 3) + self.assertEqual(events[2].properties.get("bar", None), "foo_global") From 90c175b80cde2340034a1df88b469f8c2af0fd5b Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 16 Oct 2020 14:34:05 +0200 Subject: [PATCH 096/230] global plugins in interface, make them take precedence over local plugins --- .../src/scenes/plugins/InstalledPlugins.tsx | 9 +++++---- frontend/src/scenes/plugins/pluginsLogic.ts | 13 +++++++++++-- frontend/src/types.ts | 1 + posthog/api/plugin.py | 14 ++++++++++++++ posthog/plugins/plugins.py | 18 ++++++++++-------- posthog/plugins/test/test_plugins.py | 2 +- 6 files changed, 42 insertions(+), 15 deletions(-) diff --git a/frontend/src/scenes/plugins/InstalledPlugins.tsx b/frontend/src/scenes/plugins/InstalledPlugins.tsx index 5e2ee0bf35fad..0017fc6470dba 100644 --- a/frontend/src/scenes/plugins/InstalledPlugins.tsx +++ b/frontend/src/scenes/plugins/InstalledPlugins.tsx @@ -48,7 +48,8 @@ export function InstalledPlugins(): JSX.Element { {plugin.pluginConfig?.enabled ? (
- Enabled + {' '} + {plugin.pluginConfig?.global ? 'Globally Enabled' : 'Enabled'}
) : (
@@ -83,8 +84,8 @@ export function InstalledPlugins(): JSX.Element { title: '', key: 'config', align: 'right', - render: function RenderConfig(plugin: PluginTypeWithConfig): JSX.Element { - return ( + render: function RenderConfig(plugin: PluginTypeWithConfig): JSX.Element | null { + return !plugin.pluginConfig?.global ? (
- setCustomPluginUrl(e.target.value)} - placeholder="https://github.com/user/repo" - /> - - - - - - {pluginError ?

{pluginError}

: null} + + + setCustomPluginUrl(e.target.value)} + placeholder="https://github.com/user/repo" + /> + + + + + + {pluginError ?

{pluginError}

: null} + ) } diff --git a/frontend/src/scenes/plugins/InstalledPlugins.tsx b/frontend/src/scenes/plugins/InstalledPlugins.tsx index f4ea5f93b0dcf..817ef557b3d2b 100644 --- a/frontend/src/scenes/plugins/InstalledPlugins.tsx +++ b/frontend/src/scenes/plugins/InstalledPlugins.tsx @@ -1,106 +1,46 @@ import React from 'react' -import { Button, Col, Row, Table, Tooltip } from 'antd' +import { Button, Col, Row } from 'antd' import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { GithubOutlined, CheckOutlined, ToolOutlined, PauseOutlined } from '@ant-design/icons' -import { PluginTypeWithConfig } from 'scenes/plugins/types' +import { PlusOutlined } from '@ant-design/icons' +import { PluginCard, PluginLoading } from './PluginCard' import { userLogic } from 'scenes/userLogic' -function trimTag(tag: string): string { - // show the short 7-char tag if it's a 40-char github hash - if (tag.match(/^[a-f0-9]{40}$/)) { - return tag.substring(0, 7) - } - if (tag.length >= 20) { - return tag.substring(0, 17) + '...' - } - return tag -} - export function InstalledPlugins(): JSX.Element { - const { user } = useValues(userLogic) const { installedPlugins, loading } = useValues(pluginsLogic) - const { editPlugin } = useActions(pluginsLogic) - - const canInstall = user?.plugin_access?.install + const { setPluginTab } = useActions(pluginsLogic) + const { user } = useValues(userLogic) return (
-

{canInstall ? 'Installed Plugins' : 'Plugins'}

-
plugin.name} - pagination={{ pageSize: 99999, hideOnSinglePage: true }} - dataSource={installedPlugins} - columns={[ - { - title: 'Plugin', - key: 'name', - render: function RenderPlugin(plugin: PluginTypeWithConfig): JSX.Element { +

Installed {!loading && <>({installedPlugins.length})}

+ {user?.plugin_access?.install && ( +
+ +
+ )} + + {(!loading || installedPlugins.length > 0) && ( + <> + {installedPlugins.map((plugin) => { return ( - <> - - - {plugin.name} - - - - - {plugin.pluginConfig?.enabled ? ( -
- {' '} - {plugin.pluginConfig?.global ? 'Globally Enabled' : 'Enabled'} -
- ) : ( -
- Disabled -
- )} - - - {!plugin.url?.startsWith('file:') && ( - - {trimTag(plugin.tag)} - - )} - - - + ) - }, - }, - { - title: 'Description', - key: 'description', - render: function RenderDescription(plugin: PluginTypeWithConfig): JSX.Element { - return
{plugin.description}
- }, - }, - { - title: '', - key: 'config', - align: 'right', - render: function RenderConfig(plugin: PluginTypeWithConfig): JSX.Element | null { - return !plugin.pluginConfig?.global ? ( - -
You don't have any plugins installed yet.} + + )} + {loading && installedPlugins.length === 0 && } + ) } diff --git a/frontend/src/scenes/plugins/PluginCard.tsx b/frontend/src/scenes/plugins/PluginCard.tsx new file mode 100644 index 0000000000000..02a44c7496fa1 --- /dev/null +++ b/frontend/src/scenes/plugins/PluginCard.tsx @@ -0,0 +1,115 @@ +import { Col, Card, Button, Switch, Popconfirm, Skeleton } from 'antd' +import { useActions } from 'kea' +import React from 'react' +import { pluginsLogic } from './pluginsLogic' +import { ellipsis } from 'lib/utils' +import { PluginConfigType } from '~/types' +import { PlusOutlined } from '@ant-design/icons' +import { Link } from 'lib/components/Link' +import { PluginImage } from './PluginImage' + +interface PluginCardType { + name: string + description: string + url: string + pluginConfig?: PluginConfigType + pluginId?: number +} + +export function PluginCard({ name, description, url, pluginConfig, pluginId }: PluginCardType): JSX.Element { + const { editPlugin, toggleEnabled, installPlugin } = useActions(pluginsLogic) + + const canConfigure = pluginId && !pluginConfig?.global + const handleClick = (): void => { + if (canConfigure) { + editPlugin(pluginId || null) + } + } + + const switchDisabled = (pluginConfig && pluginConfig.global) || !pluginConfig || !pluginConfig.id + + return ( + + + +
+ {name} +
+
{ellipsis(description, 180)}
+
+
{ + if (!switchDisabled) e.stopPropagation() + }} + > + {pluginConfig && ( + toggleEnabled({ id: pluginConfig.id, enabled: !pluginConfig.enabled })} + okText="Yes" + cancelText="No" + disabled={switchDisabled} + > +
+ + {pluginConfig.global && ( + + Globally enabled + + )} +
+
+ )} + {!pluginConfig && ( + <> + + Learn more + + + )} +
+
+ {canConfigure && } + {!pluginId && ( + + )} +
+
+
+ + ) +} + +export function PluginLoading(): JSX.Element { + return ( + <> + {[1, 2, 3, 4].map((i) => { + return ( + + +
+ +
+ + +
+ + ) + })} + + ) +} diff --git a/frontend/src/scenes/plugins/PluginImage.tsx b/frontend/src/scenes/plugins/PluginImage.tsx new file mode 100644 index 0000000000000..87830830ddc45 --- /dev/null +++ b/frontend/src/scenes/plugins/PluginImage.tsx @@ -0,0 +1,40 @@ +import { Card } from 'antd' +import { parseGithubRepoURL } from 'lib/utils' +import React, { useEffect, useState } from 'react' +import imgPluginDefault from 'public/plugin-default.svg' + +export function PluginImage({ url }: { url: string }): JSX.Element { + const [state, setState] = useState({ image: imgPluginDefault }) + + useEffect(() => { + if (url.includes('github.com')) { + const { user, repo } = parseGithubRepoURL(url) + setState({ ...state, image: `https://raw.githubusercontent.com/${user}/${repo}/main/logo.png` }) + } + }, []) + + return ( + + setState({ ...state, image: imgPluginDefault })} + /> + + ) +} diff --git a/frontend/src/scenes/plugins/PluginModal.tsx b/frontend/src/scenes/plugins/PluginModal.tsx index 713977e7e9af7..eaeaad424f753 100644 --- a/frontend/src/scenes/plugins/PluginModal.tsx +++ b/frontend/src/scenes/plugins/PluginModal.tsx @@ -1,9 +1,13 @@ import React, { useEffect } from 'react' import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { Button, Form, Input, Modal, Popconfirm, Switch } from 'antd' -import { DeleteOutlined } from '@ant-design/icons' +import { Button, Form, Input, Popconfirm, Switch } from 'antd' +import { DeleteOutlined, ArrowRightOutlined } from '@ant-design/icons' import { userLogic } from 'scenes/userLogic' +import { PluginImage } from './PluginImage' +import { ellipsis } from 'lib/utils' +import { Link } from 'lib/components/Link' +import { Drawer } from 'lib/components/Drawer' export function PluginModal(): JSX.Element { const { user } = useValues(userLogic) @@ -25,44 +29,72 @@ export function PluginModal(): JSX.Element { }, [editingPlugin?.name]) return ( - form.submit()} - onCancel={() => editPlugin(null)} - confirmLoading={pluginsLoading} + onClose={() => editPlugin(null)} + width={480} + title={editingPlugin?.name} footer={ <> - {canDelete && ( - uninstallPlugin(editingPlugin.name) : () => {}} - okText="Yes" - cancelText="No" - > - + + )} + +
+ - - )} - - + +
+ } > {editingPlugin ? (
-

{editingPlugin.name}

-

{editingPlugin.description}

- - - - +
+
+ +
+
+ {ellipsis(editingPlugin.description, 140)} +
+ + View plugin + +
+
+
+
+ Enabled + + + +
+

+ Configuration +

{Object.keys(editingPlugin.config_schema).map((configKey) => ( ) : null} - + ) } diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index 92f83a14efab6..3b2073e7cd5dd 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -4,12 +4,17 @@ import { PluginModal } from 'scenes/plugins/PluginModal' import { CustomPlugin } from 'scenes/plugins/CustomPlugin' import { Repository } from 'scenes/plugins/Repository' import { InstalledPlugins } from 'scenes/plugins/InstalledPlugins' -import { useValues } from 'kea' +import { useActions, useValues } from 'kea' import { userLogic } from 'scenes/userLogic' +import { pluginsLogic } from './pluginsLogic' +import { Tabs } from 'antd' export const Plugins = hot(_Plugins) function _Plugins(): JSX.Element { const { user } = useValues(userLogic) + const { pluginTab } = useValues(pluginsLogic) + const { setPluginTab } = useActions(pluginsLogic) + const { TabPane } = Tabs if (!user) { return
@@ -24,19 +29,23 @@ function _Plugins(): JSX.Element { return (
- - - {user.plugin_access?.install ? ( - <> -
-
- -
-
- - - ) : null} +

Plugins

+
+ Plugins enable you to extend PostHog's core functionality. Examples include, normalizing your revenue + information to a single currency, adding geographical information to your events, etc. +
+ setPluginTab(activeKey)}> + + + + {user.plugin_access?.install && ( + + + + + )} +
) diff --git a/frontend/src/scenes/plugins/Repository.tsx b/frontend/src/scenes/plugins/Repository.tsx index 57f13f2d9b2b0..72f1d9dbe8653 100644 --- a/frontend/src/scenes/plugins/Repository.tsx +++ b/frontend/src/scenes/plugins/Repository.tsx @@ -1,63 +1,37 @@ import React from 'react' -import { Button, Table, Tooltip } from 'antd' -import { useActions, useValues } from 'kea' +import { Col, Row } from 'antd' +import { useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { PluginType } from '~/types' -import { DownloadOutlined } from '@ant-design/icons' -import { PluginRepositoryEntry } from 'scenes/plugins/types' +import { PluginCard, PluginLoading } from './PluginCard' export function Repository(): JSX.Element { const { loading, repositoryLoading, uninstalledPlugins } = useValues(pluginsLogic) - const { installPlugin } = useActions(pluginsLogic) return (
-

Plugin Repository

-
plugin.name} - pagination={{ pageSize: 99999, hideOnSinglePage: true }} - dataSource={uninstalledPlugins} - columns={[ - { - title: 'Plugin', - key: 'name', - render: function RenderPlugin(plugin: PluginType): JSX.Element { +

Available

+ + {((!loading && !repositoryLoading) || uninstalledPlugins.length > 0) && ( + <> + {uninstalledPlugins.map((plugin) => { return ( - - {plugin.name} - + ) - }, - }, - { - title: 'Description', - key: 'description', - render: function RenderDescription(plugin: PluginRepositoryEntry): JSX.Element { - return
{plugin.description}
- }, - }, - { - title: '', - key: 'install', - align: 'right', - render: function RenderInstall(plugin: PluginRepositoryEntry): JSX.Element { - return ( - -
+ You have already installed all available plugins from the official repository! + + )} + + )} + + {(loading || repositoryLoading) && uninstalledPlugins.length === 0 && } ) } diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index 5b806bf5a321b..2548c77281a21 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -3,6 +3,7 @@ import { pluginsLogicType } from 'types/scenes/plugins/pluginsLogicType' import api from 'lib/api' import { PluginConfigType, PluginType } from '~/types' import { PluginRepositoryEntry, PluginTypeWithConfig } from './types' +import { parseGithubRepoURL } from 'lib/utils' import { userLogic } from 'scenes/userLogic' export const pluginsLogic = kea< @@ -14,6 +15,7 @@ export const pluginsLogic = kea< installPlugin: (pluginUrl: string, isCustom: boolean = false) => ({ pluginUrl, isCustom }), uninstallPlugin: (name: string) => ({ name }), setCustomPluginUrl: (customPluginUrl: string) => ({ customPluginUrl }), + setPluginTab: (tab: string) => ({ tab }), }, loaders: ({ values }) => ({ @@ -31,11 +33,7 @@ export const pluginsLogic = kea< installPlugin: async ({ pluginUrl }) => { const { plugins } = values - const match = pluginUrl.match(/https?:\/\/(www\.|)github.com\/([^\/]+)\/([^\/]+)\/?$/) - if (!match) { - throw new Error('Must be in the format: https://github.com/user/repo') - } - const [, , user, repo] = match + const { user, repo } = parseGithubRepoURL(pluginUrl) const repoCommitsUrl = `https://api.github.com/repos/${user}/${repo}/commits` const repoCommits: Record[] | null = await window @@ -130,6 +128,13 @@ export const pluginsLogic = kea< return { ...pluginConfigs, [response.plugin]: response } }, + toggleEnabled: async ({ id, enabled }) => { + const { pluginConfigs } = values + const response = await api.update(`api/plugin_config/${id}`, { + enabled, + }) + return { ...pluginConfigs, [response.plugin]: response } + }, }, ], repository: [ @@ -183,6 +188,12 @@ export const pluginsLogic = kea< return newPluginConfigs }, }, + pluginTab: [ + 'installed', + { + setPluginTab: (_, { tab }) => tab, + }, + ], }, selectors: { diff --git a/frontend/src/style.scss b/frontend/src/style.scss index e910646527248..2e2b8be166a17 100644 --- a/frontend/src/style.scss +++ b/frontend/src/style.scss @@ -504,3 +504,16 @@ label.disabled { background-color: #f7a501; } } + +.Papercups-toggleButtonContainer { + transition: bottom 0.5s ease-out; +} + +.drawer-open { + .Papercups-toggleButtonContainer { + bottom: 60px !important; + } + #bottom-notice { + height: 0px !important; + } +} From 2a2b4441577bc0bd61c769c6edcd887aefb649b9 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 01:24:03 +0100 Subject: [PATCH 186/230] add plugins opt in toggle to project/plugins --- frontend/src/scenes/plugins/OptInPlugins.tsx | 27 +++++++++++++++ frontend/src/scenes/plugins/Plugins.tsx | 33 ++++++++++++------- frontend/src/types.ts | 1 + latest_migrations.manifest | 2 +- posthog/api/user.py | 2 ++ .../migrations/0098_team_plugins_opt_in.py | 14 ++++++++ posthog/models/team.py | 1 + 7 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 frontend/src/scenes/plugins/OptInPlugins.tsx create mode 100644 posthog/migrations/0098_team_plugins_opt_in.py diff --git a/frontend/src/scenes/plugins/OptInPlugins.tsx b/frontend/src/scenes/plugins/OptInPlugins.tsx new file mode 100644 index 0000000000000..242fc63e95939 --- /dev/null +++ b/frontend/src/scenes/plugins/OptInPlugins.tsx @@ -0,0 +1,27 @@ +import React from 'react' +import { useActions, useValues } from 'kea' +import { Switch } from 'antd' +import { userLogic } from 'scenes/userLogic' + +export function OptInPlugins(): JSX.Element { + const { userUpdateRequest } = useActions(userLogic) + const { user } = useValues(userLogic) + + return ( +
+ { + userUpdateRequest({ team: { plugins_opt_in: checked } }) + }} + defaultChecked={user?.team?.plugins_opt_in} + /> + +
+ ) +} diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index 3b2073e7cd5dd..2d3bf390df50e 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -8,6 +8,7 @@ import { useActions, useValues } from 'kea' import { userLogic } from 'scenes/userLogic' import { pluginsLogic } from './pluginsLogic' import { Tabs } from 'antd' +import { OptInPlugins } from 'scenes/plugins/OptInPlugins' export const Plugins = hot(_Plugins) function _Plugins(): JSX.Element { @@ -35,18 +36,26 @@ function _Plugins(): JSX.Element { information to a single currency, adding geographical information to your events, etc. - setPluginTab(activeKey)}> - - - - {user.plugin_access?.install && ( - - - - - )} - - +
+ +
+ + {user.team.plugins_opt_in ? ( + <> + setPluginTab(activeKey)}> + + + + {user.plugin_access?.install && ( + + + + + )} + + + + ) : null} ) } diff --git a/frontend/src/types.ts b/frontend/src/types.ts index ee7056a66e330..e354a6229c85d 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -64,6 +64,7 @@ export interface TeamType { opt_out_capture: boolean slack_incoming_webhook: string session_recording_opt_in: boolean + plugins_opt_in: boolean ingested_event: boolean } diff --git a/latest_migrations.manifest b/latest_migrations.manifest index d7259e4d725b2..3edb85894534c 100644 --- a/latest_migrations.manifest +++ b/latest_migrations.manifest @@ -2,7 +2,7 @@ admin: 0003_logentry_add_action_flag_choices auth: 0011_update_proxy_permissions contenttypes: 0002_remove_content_type_name ee: 0002_hook -posthog: 0097_plugin_url_field +posthog: 0098_team_plugins_opt_in rest_hooks: 0002_swappable_hook_model sessions: 0001_initial social_django: 0008_partial_timestamp diff --git a/posthog/api/user.py b/posthog/api/user.py index 652ff7bb6d809..41f6ec01c5f27 100644 --- a/posthog/api/user.py +++ b/posthog/api/user.py @@ -45,6 +45,7 @@ def user(request): team.slack_incoming_webhook = data["team"].get("slack_incoming_webhook", team.slack_incoming_webhook) team.anonymize_ips = data["team"].get("anonymize_ips", team.anonymize_ips) team.session_recording_opt_in = data["team"].get("session_recording_opt_in", team.session_recording_opt_in) + team.plugins_opt_in = data["team"].get("plugins_opt_in", team.plugins_opt_in) team.completed_snippet_onboarding = data["team"].get( "completed_snippet_onboarding", team.completed_snippet_onboarding, ) @@ -121,6 +122,7 @@ def user(request): "event_properties_numerical": team.event_properties_numerical, "completed_snippet_onboarding": team.completed_snippet_onboarding, "session_recording_opt_in": team.session_recording_opt_in, + "plugins_opt_in": team.plugins_opt_in, "ingested_event": team.ingested_event, }, "teams": teams, diff --git a/posthog/migrations/0098_team_plugins_opt_in.py b/posthog/migrations/0098_team_plugins_opt_in.py new file mode 100644 index 0000000000000..ae6f10f518561 --- /dev/null +++ b/posthog/migrations/0098_team_plugins_opt_in.py @@ -0,0 +1,14 @@ +# Generated by Django 3.0.7 on 2020-10-31 23:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("posthog", "0097_plugin_url_field"), + ] + + operations = [ + migrations.AddField(model_name="team", name="plugins_opt_in", field=models.BooleanField(default=False),), + ] diff --git a/posthog/models/team.py b/posthog/models/team.py index d333d906e1b6b..cda28e03e5d35 100644 --- a/posthog/models/team.py +++ b/posthog/models/team.py @@ -115,6 +115,7 @@ class Team(models.Model): ingested_event: models.BooleanField = models.BooleanField(default=False) uuid: models.UUIDField = models.UUIDField(default=UUIDT, editable=False, unique=True) session_recording_opt_in: models.BooleanField = models.BooleanField(default=False) + plugins_opt_in: models.BooleanField = models.BooleanField(default=False) # DEPRECATED: replaced with env variable OPT_OUT_CAPTURE and User field anonymized_data # However, we still honor teams that have set this previously From 55cb955882d582e5c7cfb739913ac770f024d7c7 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 01:40:34 +0100 Subject: [PATCH 187/230] choose pipeline based on team setting --- ee/clickhouse/process_event.py | 15 +++++++++++++-- posthog/api/capture.py | 14 ++++++++------ posthog/tasks/process_event.py | 11 ----------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/ee/clickhouse/process_event.py b/ee/clickhouse/process_event.py index 982f07ef3fa60..1cf6d361520f2 100644 --- a/ee/clickhouse/process_event.py +++ b/ee/clickhouse/process_event.py @@ -78,7 +78,7 @@ def _capture_ee( if check_ee_enabled(): - @shared_task(ignore_result=True) + @shared_task(name="process_event_ee", ignore_result=True) def process_event_ee( distinct_id: str, ip: str, site_url: str, data: dict, team_id: int, now: str, sent_at: Optional[str], ) -> None: @@ -117,12 +117,23 @@ def process_event_ee( else: - @shared_task(ignore_result=True) + @shared_task(name="process_event_ee", ignore_result=True) def process_event_ee(*args, **kwargs) -> None: # Noop if ee is not enabled return +@shared_task(name="process_event_ee_with_plugins", ignore_result=True) +def process_event_ee_with_plugins( + distinct_id: str, ip: str, site_url: str, data: dict, team_id: int, now: str, sent_at: Optional[str] +) -> None: + # If settings.PLUGINS_ENABLED, this task will be sent to the "posthog-plugins" queue, handled by the nodejs process. + # If we're here, it means nodejs plugins are disabled. Pass the event along. + process_event_ee( + distinct_id=distinct_id, ip=ip, site_url=site_url, data=data, team_id=team_id, now=now, sent_at=sent_at + ) + + def log_event( distinct_id: str, ip: str, diff --git a/posthog/api/capture.py b/posthog/api/capture.py index 8b70d4f97ea8c..7e3660adf462c 100644 --- a/posthog/api/capture.py +++ b/posthog/api/capture.py @@ -9,6 +9,7 @@ from django.views.decorators.csrf import csrf_exempt from posthog.auth import PersonalAPIKeyAuthentication +from posthog.celery import app as celery_app from posthog.ee import check_ee_enabled from posthog.models import Team from posthog.tasks.process_event import process_event_with_plugins @@ -162,14 +163,16 @@ def get_event(request): ), ) - event_queue = "process_event" + task_name = "process_event_ee" if check_ee_enabled() else "process_event" celery_queue = settings.CELERY_DEFAULT_QUEUE - if check_ee_enabled(): - event_queue += "_ee" + if team.plugins_opt_in: - event_queue += "_with_plugins" + task_name += "_with_plugins" celery_queue = settings.PLUGINS_CELERY_QUEUE - process_event_with_plugins.apply_async( + + celery_app.send_task( + name=task_name, + queue=celery_queue, args=[ distinct_id, get_ip_address(request), @@ -179,7 +182,6 @@ def get_event(request): now.isoformat(), sent_at, ], - queue=celery_queue, ) if check_ee_enabled(): diff --git a/posthog/tasks/process_event.py b/posthog/tasks/process_event.py index 355ae13a86479..a37ee43628e6b 100644 --- a/posthog/tasks/process_event.py +++ b/posthog/tasks/process_event.py @@ -23,17 +23,6 @@ def process_event_with_plugins( ) -@shared_task(name="process_event_ee_with_plugins", ignore_result=True) -def process_event_ee_with_plugins( - distinct_id: str, ip: str, site_url: str, data: dict, team_id: int, now: str, sent_at: Optional[str] -) -> None: - # If settings.PLUGINS_ENABLED, this task will be sent to the "posthog-plugins" queue, handled by the nodejs process. - # If we're here, it means nodejs plugins are disabled. Pass the event along. - process_event( - distinct_id=distinct_id, ip=ip, site_url=site_url, data=data, team_id=team_id, now=now, sent_at=sent_at - ) - - def _alias(previous_distinct_id: str, distinct_id: str, team_id: int, retry_if_failed: bool = True,) -> None: old_person: Optional[Person] = None new_person: Optional[Person] = None From 88ce5bcee62ba077b11b68f2f067c3963557cfbd Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 02:32:26 +0100 Subject: [PATCH 188/230] add "beta" --- frontend/src/scenes/plugins/Plugins.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index 2d3bf390df50e..a94d1e3a4cd8c 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -30,7 +30,9 @@ function _Plugins(): JSX.Element { return (
-

Plugins

+

+ Plugins BETA! +

Plugins enable you to extend PostHog's core functionality. Examples include, normalizing your revenue information to a single currency, adding geographical information to your events, etc. From 163176729cefe3e34ed260cf940607d3a98c9931 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 17:01:27 +0100 Subject: [PATCH 189/230] plugin opt-in opt-out pages --- frontend/src/scenes/plugins/OptInPlugins.tsx | 51 ++++++++++++------- frontend/src/scenes/plugins/OptOutPlugins.tsx | 25 +++++++++ frontend/src/scenes/plugins/Plugins.tsx | 16 +++--- 3 files changed, 66 insertions(+), 26 deletions(-) create mode 100644 frontend/src/scenes/plugins/OptOutPlugins.tsx diff --git a/frontend/src/scenes/plugins/OptInPlugins.tsx b/frontend/src/scenes/plugins/OptInPlugins.tsx index 242fc63e95939..273b7ff0c3633 100644 --- a/frontend/src/scenes/plugins/OptInPlugins.tsx +++ b/frontend/src/scenes/plugins/OptInPlugins.tsx @@ -1,27 +1,44 @@ -import React from 'react' -import { useActions, useValues } from 'kea' -import { Switch } from 'antd' +import React, { useState } from 'react' +import { useActions } from 'kea' +import { Button, Checkbox } from 'antd' +import { ApiOutlined } from '@ant-design/icons' import { userLogic } from 'scenes/userLogic' export function OptInPlugins(): JSX.Element { const { userUpdateRequest } = useActions(userLogic) - const { user } = useValues(userLogic) + const [optIn, setOptIn] = useState(false) return (
- { - userUpdateRequest({ team: { plugins_opt_in: checked } }) - }} - defaultChecked={user?.team?.plugins_opt_in} - /> - +
+ Plugins enable you to extend PostHog's core functionality. For example by adding geographical + information to your events, normalizing your revenue information to a single currency, etc. +
+
+ Plugins are currently an experimental feature that you must opt in to. +
+
+ Plugins support requires the cooperation of the main posthog application and a new nodejs based{' '} + + posthog-plugin-server + + . In case the plugin server is not properly configured, you might experience data loss. Proceed + at your own risk or wait a few weeks until we're out of beta. +
+
+ setOptIn(!optIn)}> + I understand the risks and I'm not worried about potentially losing a few events. + +
+
+ +
) } diff --git a/frontend/src/scenes/plugins/OptOutPlugins.tsx b/frontend/src/scenes/plugins/OptOutPlugins.tsx new file mode 100644 index 0000000000000..41d8595e35966 --- /dev/null +++ b/frontend/src/scenes/plugins/OptOutPlugins.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import { useActions } from 'kea' +import { Button, Popconfirm } from 'antd' +import { ApiOutlined } from '@ant-design/icons' +import { userLogic } from 'scenes/userLogic' + +export function OptOutPlugins(): JSX.Element { + const { userUpdateRequest } = useActions(userLogic) + + return ( +
+ userUpdateRequest({ team: { plugins_opt_in: false } })} + onCancel={() => null} + okText="Yes" + cancelText="No" + > + + +
+ ) +} diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index a94d1e3a4cd8c..c2f8e59a4b313 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -9,6 +9,7 @@ import { userLogic } from 'scenes/userLogic' import { pluginsLogic } from './pluginsLogic' import { Tabs } from 'antd' import { OptInPlugins } from 'scenes/plugins/OptInPlugins' +import { OptOutPlugins } from 'scenes/plugins/OptOutPlugins' export const Plugins = hot(_Plugins) function _Plugins(): JSX.Element { @@ -30,17 +31,10 @@ function _Plugins(): JSX.Element { return (
+ {user.team.plugins_opt_in ? : null}

Plugins BETA!

-
- Plugins enable you to extend PostHog's core functionality. Examples include, normalizing your revenue - information to a single currency, adding geographical information to your events, etc. -
- -
- -
{user.team.plugins_opt_in ? ( <> @@ -57,7 +51,11 @@ function _Plugins(): JSX.Element { - ) : null} + ) : ( +
+ +
+ )}
) } From 5570556d1e6f6ca476d0e973abb082a64fa89bc8 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 17:08:32 +0100 Subject: [PATCH 190/230] adjust install button --- .../src/scenes/plugins/InstalledPlugins.tsx | 20 +++++++++++-------- frontend/src/scenes/plugins/Plugins.tsx | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/frontend/src/scenes/plugins/InstalledPlugins.tsx b/frontend/src/scenes/plugins/InstalledPlugins.tsx index 817ef557b3d2b..f94ad54891a10 100644 --- a/frontend/src/scenes/plugins/InstalledPlugins.tsx +++ b/frontend/src/scenes/plugins/InstalledPlugins.tsx @@ -13,14 +13,18 @@ export function InstalledPlugins(): JSX.Element { return (
-

Installed {!loading && <>({installedPlugins.length})}

- {user?.plugin_access?.install && ( -
- -
- )} + +

+ Installed {!loading || installedPlugins.length > 0 ? `(${installedPlugins.length})` : ''} +

+ {user?.plugin_access?.install && ( +
+ +
+ )} +
{(!loading || installedPlugins.length > 0) && ( <> diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index c2f8e59a4b313..5c94f2f3f34bb 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -33,7 +33,7 @@ function _Plugins(): JSX.Element {
{user.team.plugins_opt_in ? : null}

- Plugins BETA! + Plugins BETA

{user.team.plugins_opt_in ? ( From 2f3d4f9149015f94560ec247f957778cb096d4f2 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 17:13:33 +0100 Subject: [PATCH 191/230] remove tasks that are never called, remove PLUGINS_ENABLED global key --- ee/clickhouse/process_event.py | 11 ----------- posthog/api/capture.py | 1 - posthog/settings.py | 3 +-- posthog/tasks/process_event.py | 11 ----------- 4 files changed, 1 insertion(+), 25 deletions(-) diff --git a/ee/clickhouse/process_event.py b/ee/clickhouse/process_event.py index 1cf6d361520f2..04e27cede2795 100644 --- a/ee/clickhouse/process_event.py +++ b/ee/clickhouse/process_event.py @@ -123,17 +123,6 @@ def process_event_ee(*args, **kwargs) -> None: return -@shared_task(name="process_event_ee_with_plugins", ignore_result=True) -def process_event_ee_with_plugins( - distinct_id: str, ip: str, site_url: str, data: dict, team_id: int, now: str, sent_at: Optional[str] -) -> None: - # If settings.PLUGINS_ENABLED, this task will be sent to the "posthog-plugins" queue, handled by the nodejs process. - # If we're here, it means nodejs plugins are disabled. Pass the event along. - process_event_ee( - distinct_id=distinct_id, ip=ip, site_url=site_url, data=data, team_id=team_id, now=now, sent_at=sent_at - ) - - def log_event( distinct_id: str, ip: str, diff --git a/posthog/api/capture.py b/posthog/api/capture.py index 7e3660adf462c..56fc6f29beedc 100644 --- a/posthog/api/capture.py +++ b/posthog/api/capture.py @@ -12,7 +12,6 @@ from posthog.celery import app as celery_app from posthog.ee import check_ee_enabled from posthog.models import Team -from posthog.tasks.process_event import process_event_with_plugins from posthog.utils import cors_response, get_ip_address, load_data_from_request if settings.EE_AVAILABLE: diff --git a/posthog/settings.py b/posthog/settings.py index fb6e4366c869f..c76801e615d55 100644 --- a/posthog/settings.py +++ b/posthog/settings.py @@ -69,8 +69,7 @@ def print_warning(warning_lines: Sequence[str]): else: JS_URL = os.environ.get("JS_URL", "") -PLUGINS_ENABLED = get_bool_from_env("PLUGINS_ENABLED", False) -PLUGINS_INSTALL_FROM_WEB = PLUGINS_ENABLED and get_bool_from_env("PLUGINS_INSTALL_FROM_WEB", True) +PLUGINS_INSTALL_FROM_WEB = get_bool_from_env("PLUGINS_INSTALL_FROM_WEB", True) PLUGINS_CONFIGURE_FROM_WEB = PLUGINS_INSTALL_FROM_WEB or get_bool_from_env("PLUGINS_CONFIGURE_FROM_WEB", True) PLUGINS_CELERY_QUEUE = os.environ.get("PLUGINS_CELERY_QUEUE", "posthog-plugins") PLUGINS_RELOAD_PUBSUB_CHANNEL = os.environ.get("PLUGINS_RELOAD_PUBSUB_CHANNEL", "reload-plugins") diff --git a/posthog/tasks/process_event.py b/posthog/tasks/process_event.py index a37ee43628e6b..0add3a4d7c178 100644 --- a/posthog/tasks/process_event.py +++ b/posthog/tasks/process_event.py @@ -12,17 +12,6 @@ from posthog.models import Element, Event, Person, SessionRecordingEvent, Team, User -@shared_task(name="process_event_with_plugins", ignore_result=True) -def process_event_with_plugins( - distinct_id: str, ip: str, site_url: str, data: dict, team_id: int, now: str, sent_at: Optional[str] -) -> None: - # If settings.PLUGINS_ENABLED, this task will be sent to the "posthog-plugins" queue, handled by the nodejs process. - # If we're here, it means nodejs plugins are disabled. Pass the event along. - process_event( - distinct_id=distinct_id, ip=ip, site_url=site_url, data=data, team_id=team_id, now=now, sent_at=sent_at - ) - - def _alias(previous_distinct_id: str, distinct_id: str, team_id: int, retry_if_failed: bool = True,) -> None: old_person: Optional[Person] = None new_person: Optional[Person] = None From 5c6d58022a4999b163ad11b8fd01290dc9a4f11b Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 17:22:06 +0100 Subject: [PATCH 192/230] fix responsive card display --- frontend/src/scenes/plugins/PluginCard.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/scenes/plugins/PluginCard.tsx b/frontend/src/scenes/plugins/PluginCard.tsx index 02a44c7496fa1..6a68313a7d420 100644 --- a/frontend/src/scenes/plugins/PluginCard.tsx +++ b/frontend/src/scenes/plugins/PluginCard.tsx @@ -30,13 +30,16 @@ export function PluginCard({ name, description, url, pluginConfig, pluginId }: P return (
@@ -99,7 +102,7 @@ export function PluginLoading(): JSX.Element { <> {[1, 2, 3, 4].map((i) => { return ( - +
From 29dd1509a9e1e241c0203ec884a7bf15df06ab5e Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 17:32:31 +0100 Subject: [PATCH 193/230] fix typo and drawer width --- frontend/src/scenes/plugins/OptInPlugins.tsx | 2 +- .../src/scenes/plugins/{PluginModal.tsx => PluginDrawer.tsx} | 4 ++-- frontend/src/scenes/plugins/Plugins.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename frontend/src/scenes/plugins/{PluginModal.tsx => PluginDrawer.tsx} (98%) diff --git a/frontend/src/scenes/plugins/OptInPlugins.tsx b/frontend/src/scenes/plugins/OptInPlugins.tsx index 273b7ff0c3633..0a424e12ad794 100644 --- a/frontend/src/scenes/plugins/OptInPlugins.tsx +++ b/frontend/src/scenes/plugins/OptInPlugins.tsx @@ -18,7 +18,7 @@ export function OptInPlugins(): JSX.Element { Plugins are currently an experimental feature that you must opt in to.
- Plugins support requires the cooperation of the main posthog application and a new nodejs based{' '} + Plugin support requires the cooperation of the main posthog application and a new nodejs based{' '} posthog-plugin-server diff --git a/frontend/src/scenes/plugins/PluginModal.tsx b/frontend/src/scenes/plugins/PluginDrawer.tsx similarity index 98% rename from frontend/src/scenes/plugins/PluginModal.tsx rename to frontend/src/scenes/plugins/PluginDrawer.tsx index eaeaad424f753..2195f7da185b8 100644 --- a/frontend/src/scenes/plugins/PluginModal.tsx +++ b/frontend/src/scenes/plugins/PluginDrawer.tsx @@ -9,7 +9,7 @@ import { ellipsis } from 'lib/utils' import { Link } from 'lib/components/Link' import { Drawer } from 'lib/components/Drawer' -export function PluginModal(): JSX.Element { +export function PluginDrawer(): JSX.Element { const { user } = useValues(userLogic) const { editingPlugin, pluginsLoading } = useValues(pluginsLogic) const { editPlugin, savePluginConfig, uninstallPlugin } = useActions(pluginsLogic) @@ -33,7 +33,7 @@ export function PluginModal(): JSX.Element { forceRender={true} visible={!!editingPlugin} onClose={() => editPlugin(null)} - width={480} + width="min(90vw, 420px)" title={editingPlugin?.name} footer={ <> diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index 5c94f2f3f34bb..45d85fbd055cf 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react' import { hot } from 'react-hot-loader/root' -import { PluginModal } from 'scenes/plugins/PluginModal' +import { PluginDrawer } from 'scenes/plugins/PluginDrawer' import { CustomPlugin } from 'scenes/plugins/CustomPlugin' import { Repository } from 'scenes/plugins/Repository' import { InstalledPlugins } from 'scenes/plugins/InstalledPlugins' @@ -49,7 +49,7 @@ function _Plugins(): JSX.Element { )} - + ) : (
From 42409835c9cfe9d569c2d9aedd9403252e60d1ec Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 17:33:28 +0100 Subject: [PATCH 194/230] skeleton fixes --- frontend/src/scenes/plugins/PluginCard.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/scenes/plugins/PluginCard.tsx b/frontend/src/scenes/plugins/PluginCard.tsx index 6a68313a7d420..01c0b2afb3ad0 100644 --- a/frontend/src/scenes/plugins/PluginCard.tsx +++ b/frontend/src/scenes/plugins/PluginCard.tsx @@ -100,7 +100,7 @@ export function PluginCard({ name, description, url, pluginConfig, pluginId }: P export function PluginLoading(): JSX.Element { return ( <> - {[1, 2, 3, 4].map((i) => { + {[1, 2, 3].map((i) => { return (
@@ -108,7 +108,7 @@ export function PluginLoading(): JSX.Element { - + ) From d4ddb8ab8e36181bb9b3722e9f5cf0ecdb6165a0 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 17:34:50 +0100 Subject: [PATCH 195/230] typo --- frontend/src/scenes/plugins/CustomPlugin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/scenes/plugins/CustomPlugin.tsx b/frontend/src/scenes/plugins/CustomPlugin.tsx index aeb8712033b98..1fd49a1da1d54 100644 --- a/frontend/src/scenes/plugins/CustomPlugin.tsx +++ b/frontend/src/scenes/plugins/CustomPlugin.tsx @@ -13,7 +13,7 @@ export function CustomPlugin(): JSX.Element {

Install Custom Plugin

To install a third-party or custom plugin, please paste the plugin's repository below.{' '} - Warning: Only install plugin from trusted sources. + Warning: Only install plugins from trusted sources.

From a6f48b9c902c464a57f813d2d3669295e492c005 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 17:45:17 +0100 Subject: [PATCH 196/230] use "posthog-plugin-server" npm package --- bin/{plugins-server => plugin-server} | 2 +- plugins/package.json | 4 +- plugins/yarn.lock | 73 +++++++++++++++++++++++++-- 3 files changed, 73 insertions(+), 6 deletions(-) rename bin/{plugins-server => plugin-server} (91%) diff --git a/bin/plugins-server b/bin/plugin-server similarity index 91% rename from bin/plugins-server rename to bin/plugin-server index 341de039d877d..a3f513032f97b 100755 --- a/bin/plugins-server +++ b/bin/plugin-server @@ -1,4 +1,4 @@ #!/bin/bash KEYS="DATABASE_URL REDIS_URL PLUGINS_CELERY_QUEUE CELERY_DEFAULT_QUEUE BASE_DIR PLUGINS_RELOAD_PUBSUB_CHANNEL" CONF=`python manage.py print_settings --format json --indent 0 $KEYS 2> /dev/null | tr -d '\n' | sed 's/\n$//'` -cd plugins && yarn --frozen-lockfile --silent && yarn --silent start start --config "$CONF" +cd plugins && yarn --frozen-lockfile --silent && yarn --silent start --config "$CONF" diff --git a/plugins/package.json b/plugins/package.json index 7a74caad5e739..019352f06a5bf 100644 --- a/plugins/package.json +++ b/plugins/package.json @@ -4,9 +4,9 @@ "license": "MIT", "private": true, "dependencies": { - "posthog-plugins": "^0.0.4" + "posthog-plugin-server": "^0.0.4" }, "scripts": { - "start": "posthog-plugins" + "start": "posthog-plugin-server start" } } diff --git a/plugins/yarn.lock b/plugins/yarn.lock index 68167b7e8e2fe..d7390ffbec278 100644 --- a/plugins/yarn.lock +++ b/plugins/yarn.lock @@ -45,6 +45,13 @@ "@types/node" "*" form-data "^3.0.0" +"@types/node-schedule@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/node-schedule/-/node-schedule-1.3.1.tgz#6785ea71b12b0b8899c3fce0650b2ef5a7ea9d1e" + integrity sha512-xAY/ZATrThUkMElSDfOk+5uXprCrV6c6GQ5gTw3U04qPS6NofE1dhOUW+yrOF2UyrUiAax/Zc4WtagrbPAN3Tw== + dependencies: + "@types/node" "*" + "@types/node@*", "@types/node@^14.14.5": version "14.14.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.5.tgz#e92d3b8f76583efa26c1a63a21c9d3c1143daa29" @@ -319,6 +326,14 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cron-parser@^2.7.3: + version "2.16.3" + resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.16.3.tgz#acb8e405eed1733aac542fdf604cb7c1daf0204a" + integrity sha512-XNJBD1QLFeAMUkZtZQuncAAOgJFWNhBdIbwgD22hZxrcWOImBFMKgPC66GzaXpyoJs7UvYLLgPH/8BRk/7gbZg== + dependencies: + is-nan "^1.3.0" + moment-timezone "^0.5.31" + cross-spawn@^7.0.0: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -352,6 +367,13 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -543,6 +565,13 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-nan@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.0.tgz#85d1f5482f7051c2019f5673ccebdb06f3b0db03" + integrity sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ== + dependencies: + define-properties "^1.1.3" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -657,6 +686,11 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" +long-timeout@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/long-timeout/-/long-timeout-0.1.1.tgz#9721d788b47e0bcb5a24c2e2bee1a0da55dab514" + integrity sha1-lyHXiLR+C8taJMLivuGg2lXatRQ= + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -692,6 +726,18 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +moment-timezone@^0.5.31: + version "0.5.31" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.31.tgz#9c40d8c5026f0c7ab46eda3d63e49c155148de05" + integrity sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA== + dependencies: + moment ">= 2.9.0" + +"moment@>= 2.9.0": + version "2.29.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" + integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -707,6 +753,15 @@ node-fetch@^2.6.1: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== +node-schedule@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/node-schedule/-/node-schedule-1.3.2.tgz#d774b383e2a6f6ade59eecc62254aea07cd758cb" + integrity sha512-GIND2pHMHiReSZSvS6dpZcDH7pGPGFfWBIEud6S00Q8zEIzAs9ommdyRK1ZbQt8y1LyZsJYZgPnyi7gpU2lcdw== + dependencies: + cron-parser "^2.7.3" + long-timeout "0.1.1" + sorted-array-functions "^1.0.0" + normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -719,6 +774,11 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" +object-keys@^1.0.12: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -898,15 +958,16 @@ posthog-js-lite@^0.0.5: resolved "https://registry.yarnpkg.com/posthog-js-lite/-/posthog-js-lite-0.0.5.tgz#984a619190d1c4ef003cb81194d0a6276491d2e8" integrity sha512-xHVZ9qbBdoqK7G1JVA4k6nheWu50aiEwjJIPQw7Zhi705aX5wr2W8zxdmvtadeEE5qbJNg+/uZ7renOQoqcbJw== -posthog-plugins@^0.0.4: +posthog-plugin-server@^0.0.4: version "0.0.4" - resolved "https://registry.yarnpkg.com/posthog-plugins/-/posthog-plugins-0.0.4.tgz#d6d6d10bed10b22fc19a3fb943512e3366386fee" - integrity sha512-q+A7FYcsSZjfJOL23t1BCnGmkhb4w5xIJaP1lo3kJPHYviTHh00XXayP9DsjS37qtKLaD2ETZrouky9dfftGBw== + resolved "https://registry.yarnpkg.com/posthog-plugin-server/-/posthog-plugin-server-0.0.4.tgz#5798938cfaa0c2b07fc263ba2b89dd20cd4caf76" + integrity sha512-SJrOqp1efVafuRI6OZ8A1fswY9OqVH8mDgG3ev8FUP1nmyI0juEIGlIwNxWRG4GHbFW0m1j2D1qViz8tegvmIg== dependencies: "@types/adm-zip" "^0.4.33" "@types/ioredis" "^4.17.6" "@types/node" "^14.14.5" "@types/node-fetch" "^2.5.7" + "@types/node-schedule" "^1.3.1" "@types/pg" "^7.14.5" "@types/yargs" "^15.0.9" adm-zip "^0.4.16" @@ -915,6 +976,7 @@ posthog-plugins@^0.0.4: ioredis "^4.19.0" lint-staged ">=10" node-fetch "^2.6.1" + node-schedule "^1.3.2" pg "^8.4.1" posthog-js-lite "^0.0.5" prettier "^2.1.2" @@ -1077,6 +1139,11 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +sorted-array-functions@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz#8605695563294dffb2c9796d602bd8459f7a0dd5" + integrity sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA== + source-map-support@^0.5.17: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" From 028ef6a7ebf745a4f92ee1040a0904df28d7520b Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 17:50:27 +0100 Subject: [PATCH 197/230] "posthog-plugin-server" doc --- bin/docker-preview | 2 +- bin/docker-worker-plugins | 2 +- bin/start-worker | 2 +- plugins/README.md | 6 ++++++ 4 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 plugins/README.md diff --git a/bin/docker-preview b/bin/docker-preview index 841bef8d7a452..ab903845dab28 100755 --- a/bin/docker-preview +++ b/bin/docker-preview @@ -10,5 +10,5 @@ export REDIS_URL=redis://localhost /etc/init.d/redis-server start > /dev/null DEBUG=1 celery -A posthog worker --loglevel=info & -DEBUG=1 ./bin/plugins-server & +DEBUG=1 ./bin/plugin-server & DEBUG=1 gunicorn posthog.wsgi --config gunicorn.config.py --bind 0.0.0.0:8000 --log-file - -e DEBUG=1 -e DATABASE_URL=postgres://posthog:posthog@localhost:5432/posthog diff --git a/bin/docker-worker-plugins b/bin/docker-worker-plugins index 5b4295bc11baf..c6b8951791244 100755 --- a/bin/docker-worker-plugins +++ b/bin/docker-worker-plugins @@ -1,4 +1,4 @@ #!/bin/bash set -e -./bin/plugins-server +./bin/plugin-server diff --git a/bin/start-worker b/bin/start-worker index d69e4b9fd8c05..310177ef579bf 100755 --- a/bin/start-worker +++ b/bin/start-worker @@ -8,4 +8,4 @@ trap 'kill $(jobs -p)' EXIT celery -A posthog worker -B --scheduler redbeat.RedBeatScheduler & # start celery plugin worker -./bin/plugins-server +./bin/plugin-server diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 0000000000000..6757a44e860ae --- /dev/null +++ b/plugins/README.md @@ -0,0 +1,6 @@ +# posthog-plugin-server + +This folder contains a custom `package.json` which installs the latest [`posthog-plugin-server`](https://github.com/PostHog/posthog-plugin-server) package. + +The plugin server is started by running `bin/plugin-server` directly or indirectly as part of `bin/start-worker` or `bin/docker-worker`. + From 86abcdeee676ca2b67c47a5883c0cc1fa8f4e039 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sun, 1 Nov 2020 18:16:41 +0100 Subject: [PATCH 198/230] require the plugin server to be online before enabling plugins --- frontend/src/scenes/plugins/OptInPlugins.tsx | 40 +++++++++++++++++--- posthog/api/plugin.py | 17 +++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/frontend/src/scenes/plugins/OptInPlugins.tsx b/frontend/src/scenes/plugins/OptInPlugins.tsx index 0a424e12ad794..7fffc7fef793d 100644 --- a/frontend/src/scenes/plugins/OptInPlugins.tsx +++ b/frontend/src/scenes/plugins/OptInPlugins.tsx @@ -1,12 +1,28 @@ -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import { useActions } from 'kea' -import { Button, Checkbox } from 'antd' -import { ApiOutlined } from '@ant-design/icons' +import { Button, Checkbox, Spin } from 'antd' +import { ApiOutlined, CheckOutlined, WarningOutlined } from '@ant-design/icons' import { userLogic } from 'scenes/userLogic' +import api from 'lib/api' export function OptInPlugins(): JSX.Element { const { userUpdateRequest } = useActions(userLogic) const [optIn, setOptIn] = useState(false) + const [serverStatus, setServerStatus] = useState('loading') + + useEffect(() => { + async function setStatus(): Promise { + try { + const response = await api.get('api/plugin/status') + setServerStatus(response.status) + } catch (e) { + setServerStatus('offline') + } + } + setStatus() + const interval = window.setInterval(setStatus, 5000) + return () => window.clearInterval(interval) + }, []) return (
@@ -26,14 +42,28 @@ export function OptInPlugins(): JSX.Element { at your own risk or wait a few weeks until we're out of beta.
- setOptIn(!optIn)}> + Plugin server:{' '} + {serverStatus === 'loading' ? ( + + ) : serverStatus === 'online' ? ( + + Online + + ) : ( + + Offline + + )} +
+
+ setOptIn(!optIn)} disabled={serverStatus !== 'online'}> I understand the risks and I'm not worried about potentially losing a few events.
+ ) : null} +
+ {error.name ? {error.name}: : ''} + {error.message} +
+ {error.stack ? ( + + {error.stack} + + ) : null} + + } + trigger="click" + placement="bottom" + > + + ERROR + + + ) +} diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index e8c8b8791926e..7bbb10fe9b829 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -16,6 +16,7 @@ export const pluginsLogic = kea< uninstallPlugin: (name: string) => ({ name }), setCustomPluginUrl: (customPluginUrl: string) => ({ customPluginUrl }), setPluginTab: (tab: string) => ({ tab }), + resetPluginConfigError: (id: number) => ({ id }), }, loaders: ({ values }) => ({ @@ -135,6 +136,13 @@ export const pluginsLogic = kea< }) return { ...pluginConfigs, [response.plugin]: response } }, + resetPluginConfigError: async ({ id }) => { + const { pluginConfigs } = values + const response = await api.update(`api/plugin_config/${id}`, { + error: null, + }) + return { ...pluginConfigs, [response.plugin]: response } + }, }, ], repository: [ diff --git a/frontend/src/types.ts b/frontend/src/types.ts index e354a6229c85d..e32510d7e073c 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -227,9 +227,7 @@ export interface PluginType { config_schema: Record from_json: boolean from_web: boolean - error?: { - message: string - } + error?: PluginErrorType } export interface PluginConfigType { @@ -239,7 +237,12 @@ export interface PluginConfigType { order: number config: Record global?: boolean - error?: { - message: string - } + error?: PluginErrorType +} + +export interface PluginErrorType { + message: string + time: string + stack?: string + name?: string } diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py index 2dc4881b1234b..d6639e54cc7d0 100644 --- a/posthog/api/plugin.py +++ b/posthog/api/plugin.py @@ -85,7 +85,7 @@ def destroy(self, request: request.Request, *args, **kwargs) -> Response: class PluginConfigSerializer(serializers.ModelSerializer): class Meta: model = PluginConfig - fields = ["id", "plugin", "enabled", "order", "config"] + fields = ["id", "plugin", "enabled", "order", "config", "error"] read_only_fields = ["id"] def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> PluginConfig: @@ -131,6 +131,7 @@ def global_plugins(self, request: request.Request): for plugin_config in plugin_configs: plugin = PluginConfigSerializer(plugin_config).data plugin["config"] = None + plugin["error"] = None response.append(plugin) return Response(response) From 3bb81d9a88ec550898990435350ee86f3940f8f6 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 00:51:51 +0100 Subject: [PATCH 203/230] stop clicks if clicking on error --- frontend/src/lib/utils.tsx | 7 +++++++ frontend/src/scenes/plugins/PluginCard.tsx | 7 +++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/utils.tsx b/frontend/src/lib/utils.tsx index b4f757605f628..52d8679544f9f 100644 --- a/frontend/src/lib/utils.tsx +++ b/frontend/src/lib/utils.tsx @@ -548,3 +548,10 @@ export function parseGithubRepoURL(url: string): Record { const [, , user, repo] = match return { user, repo } } + +export function someParentMatchesSelector(element: HTMLElement, selector: string): boolean { + if (element.matches(selector)) { + return true + } + return element.parentElement ? someParentMatchesSelector(element.parentElement, selector) : false +} diff --git a/frontend/src/scenes/plugins/PluginCard.tsx b/frontend/src/scenes/plugins/PluginCard.tsx index 30f05e2078d35..9df343425b18b 100644 --- a/frontend/src/scenes/plugins/PluginCard.tsx +++ b/frontend/src/scenes/plugins/PluginCard.tsx @@ -2,7 +2,7 @@ import { Col, Card, Button, Switch, Popconfirm, Skeleton } from 'antd' import { useActions } from 'kea' import React from 'react' import { pluginsLogic } from './pluginsLogic' -import { ellipsis } from 'lib/utils' +import { ellipsis, someParentMatchesSelector } from 'lib/utils' import { PluginConfigType, PluginErrorType } from '~/types' import { PlusOutlined } from '@ant-design/icons' import { Link } from 'lib/components/Link' @@ -22,7 +22,10 @@ export function PluginCard({ name, description, url, pluginConfig, pluginId, err const { editPlugin, toggleEnabled, installPlugin, resetPluginConfigError } = useActions(pluginsLogic) const canConfigure = pluginId && !pluginConfig?.global - const handleClick = (): void => { + const handleClick = (e: React.MouseEvent): void => { + if (someParentMatchesSelector(e.target as HTMLElement, '.ant-popover,.ant-tag')) { + return + } if (canConfigure) { editPlugin(pluginId || null) } From 2f20225a40b6e2452f6227e0162f35ce27318b88 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 00:57:44 +0100 Subject: [PATCH 204/230] show plugin errors --- posthog/api/plugin.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py index d6639e54cc7d0..2f76c8b76d122 100644 --- a/posthog/api/plugin.py +++ b/posthog/api/plugin.py @@ -1,10 +1,11 @@ import json -from typing import Any, Dict +from typing import Any, Dict, Optional import requests from dateutil import parser from dateutil.relativedelta import relativedelta from django.conf import settings +from django.contrib.postgres.fields import JSONField from django.utils.timezone import now from rest_framework import request, serializers, viewsets from rest_framework.decorators import action @@ -19,8 +20,12 @@ class PluginSerializer(serializers.ModelSerializer): class Meta: model = Plugin - fields = ["id", "name", "description", "url", "config_schema", "tag", "from_json"] - read_only_fields = ["id", "from_json"] + fields = ["id", "name", "description", "url", "config_schema", "tag", "error", "from_json"] + read_only_fields = ["id", "error", "from_json"] + + def get_error(self, plugin: Plugin) -> Optional[JSONField]: + if plugin.error and settings.PLUGINS_INSTALL_FROM_WEB: + return plugin.error def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: if not settings.PLUGINS_INSTALL_FROM_WEB: From 6a44cebcd8ff6af7c7ef01f3e6273d88b73217b9 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 01:02:50 +0100 Subject: [PATCH 205/230] loading indicators --- frontend/src/scenes/plugins/PluginCard.tsx | 10 ++++++++-- frontend/src/scenes/plugins/PluginDrawer.tsx | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/frontend/src/scenes/plugins/PluginCard.tsx b/frontend/src/scenes/plugins/PluginCard.tsx index 9df343425b18b..643a9a00b2fb1 100644 --- a/frontend/src/scenes/plugins/PluginCard.tsx +++ b/frontend/src/scenes/plugins/PluginCard.tsx @@ -1,5 +1,5 @@ import { Col, Card, Button, Switch, Popconfirm, Skeleton } from 'antd' -import { useActions } from 'kea' +import { useActions, useValues } from 'kea' import React from 'react' import { pluginsLogic } from './pluginsLogic' import { ellipsis, someParentMatchesSelector } from 'lib/utils' @@ -20,6 +20,7 @@ interface PluginCardProps { export function PluginCard({ name, description, url, pluginConfig, pluginId, error }: PluginCardProps): JSX.Element { const { editPlugin, toggleEnabled, installPlugin, resetPluginConfigError } = useActions(pluginsLogic) + const { loading } = useValues(pluginsLogic) const canConfigure = pluginId && !pluginConfig?.global const handleClick = (e: React.MouseEvent): void => { @@ -99,7 +100,12 @@ export function PluginCard({ name, description, url, pluginConfig, pluginId, err
{canConfigure && } {!pluginId && ( - )} diff --git a/frontend/src/scenes/plugins/PluginDrawer.tsx b/frontend/src/scenes/plugins/PluginDrawer.tsx index 46a74625babd4..c29d440541076 100644 --- a/frontend/src/scenes/plugins/PluginDrawer.tsx +++ b/frontend/src/scenes/plugins/PluginDrawer.tsx @@ -11,7 +11,7 @@ import { Drawer } from 'lib/components/Drawer' export function PluginDrawer(): JSX.Element { const { user } = useValues(userLogic) - const { editingPlugin, pluginsLoading } = useValues(pluginsLogic) + const { editingPlugin, loading } = useValues(pluginsLogic) const { editPlugin, savePluginConfig, uninstallPlugin } = useActions(pluginsLogic) const [form] = Form.useForm() @@ -57,7 +57,7 @@ export function PluginDrawer(): JSX.Element { -
From 1186081d16a0c782fe3bfb75c1cb866179f39d98 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 01:06:02 +0100 Subject: [PATCH 206/230] reload plugins when opting in/out --- posthog/api/user.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/posthog/api/user.py b/posthog/api/user.py index 41f6ec01c5f27..121c5a132dfbf 100644 --- a/posthog/api/user.py +++ b/posthog/api/user.py @@ -18,6 +18,7 @@ from posthog.auth import authenticate_secondarily from posthog.models import Event, Team, User +from posthog.plugins import reload_plugins_on_workers from posthog.version import VERSION @@ -45,6 +46,8 @@ def user(request): team.slack_incoming_webhook = data["team"].get("slack_incoming_webhook", team.slack_incoming_webhook) team.anonymize_ips = data["team"].get("anonymize_ips", team.anonymize_ips) team.session_recording_opt_in = data["team"].get("session_recording_opt_in", team.session_recording_opt_in) + if data["team"].get("plugins_opt_in", "nope") != "nope": + reload_plugins_on_workers() team.plugins_opt_in = data["team"].get("plugins_opt_in", team.plugins_opt_in) team.completed_snippet_onboarding = data["team"].get( "completed_snippet_onboarding", team.completed_snippet_onboarding, From 21086990029c2b9e582d9fe0762e8c9b63ec3824 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 01:10:28 +0100 Subject: [PATCH 207/230] nicer beta tag --- frontend/src/scenes/plugins/Plugins.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/scenes/plugins/Plugins.tsx b/frontend/src/scenes/plugins/Plugins.tsx index 9d819504aff32..19aa5b5385992 100644 --- a/frontend/src/scenes/plugins/Plugins.tsx +++ b/frontend/src/scenes/plugins/Plugins.tsx @@ -7,7 +7,7 @@ import { InstalledPlugins } from 'scenes/plugins/InstalledPlugins' import { useActions, useValues } from 'kea' import { userLogic } from 'scenes/userLogic' import { pluginsLogic } from './pluginsLogic' -import { Tabs } from 'antd' +import { Tabs, Tag } from 'antd' import { OptInPlugins } from 'scenes/plugins/OptInPlugins' import { OptOutPlugins } from 'scenes/plugins/OptOutPlugins' @@ -33,7 +33,10 @@ function _Plugins(): JSX.Element {
{user.team.plugins_opt_in ? : null}

- Plugins BETA + Plugins{' '} + + BETA +

{user.team.plugins_opt_in ? ( From df8a894a084a846eb01c6e467c1c401f2b91b11e Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 01:12:04 +0100 Subject: [PATCH 208/230] add frontend type --- posthog/models/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/posthog/models/plugin.py b/posthog/models/plugin.py index 530e5b261dde7..4bfd17073207d 100644 --- a/posthog/models/plugin.py +++ b/posthog/models/plugin.py @@ -20,7 +20,7 @@ class Plugin(models.Model): archive: models.BinaryField = models.BinaryField(blank=True, null=True) from_json: models.BooleanField = models.BooleanField(default=False) from_web: models.BooleanField = models.BooleanField(default=False) - # Error installing or configuring this plugin + # Error installing or configuring this plugin (frontend: PluginErrorType) # - e.g: "could not find plugin.json" / "syntax error in index.js") # - error = { message: "Could not find plugin.json", time: "iso-string", ...meta } error: JSONField = JSONField(default=None, null=True) @@ -32,7 +32,7 @@ class PluginConfig(models.Model): enabled: models.BooleanField = models.BooleanField(default=False) order: models.IntegerField = models.IntegerField(null=True, blank=True) config: JSONField = JSONField(default=dict) - # Error when running this plugin on an event + # Error when running this plugin on an event (frontend: PluginErrorType) # - e.g: "undefined is not a function on index.js line 23" # - error = { message: "Exception in processEvent()", time: "iso-string", ...meta } error: JSONField = JSONField(default=None, null=True) From 3f782fb2b312b0e86bf6d01c1fb756ffa5f2bec8 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 01:13:19 +0100 Subject: [PATCH 209/230] fix mypy error --- posthog/api/plugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py index 2f76c8b76d122..6539ea85fa52b 100644 --- a/posthog/api/plugin.py +++ b/posthog/api/plugin.py @@ -26,6 +26,7 @@ class Meta: def get_error(self, plugin: Plugin) -> Optional[JSONField]: if plugin.error and settings.PLUGINS_INSTALL_FROM_WEB: return plugin.error + return None def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: if not settings.PLUGINS_INSTALL_FROM_WEB: From 24e582ae1a76f7a5f7bb608ddfad99c4be239acc Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 01:16:11 +0100 Subject: [PATCH 210/230] fix test --- posthog/api/test/test_capture.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/posthog/api/test/test_capture.py b/posthog/api/test/test_capture.py index 2477224447f09..9763376e2e440 100644 --- a/posthog/api/test/test_capture.py +++ b/posthog/api/test/test_capture.py @@ -43,7 +43,7 @@ def _to_arguments(self, patch_process_event_with_plugins: Any) -> dict: } @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_capture_event(self, patch_process_event_with_plugins): data = { "event": "$autocapture", @@ -80,7 +80,7 @@ def test_capture_event(self, patch_process_event_with_plugins): ) @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_personal_api_key(self, patch_process_event_with_plugins): key = PersonalAPIKey(label="X", user=self.user, team=self.team) key.save() @@ -119,7 +119,7 @@ def test_personal_api_key(self, patch_process_event_with_plugins): ) @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_multiple_events(self, patch_process_event_with_plugins): self.client.post( "/track/", @@ -136,7 +136,7 @@ def test_multiple_events(self, patch_process_event_with_plugins): self.assertEqual(patch_process_event_with_plugins.call_count, 2) @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_emojis_in_text(self, patch_process_event_with_plugins): self.team.api_token = "xp9qT2VLY76JJg" self.team.save() @@ -155,7 +155,7 @@ def test_emojis_in_text(self, patch_process_event_with_plugins): ) @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_incorrect_padding(self, patch_process_event_with_plugins): response = self.client.get( "/e/?data=eyJldmVudCI6IndoYXRldmVmciIsInByb3BlcnRpZXMiOnsidG9rZW4iOiJ0b2tlbjEyMyIsImRpc3RpbmN0X2lkIjoiYXNkZiJ9fQ", @@ -166,7 +166,7 @@ def test_incorrect_padding(self, patch_process_event_with_plugins): self.assertEqual(patch_process_event_with_plugins.call_args[1]["args"][3]["event"], "whatevefr") @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_empty_request_returns_an_error(self, patch_process_event_with_plugins): """ Empty requests that fail silently cause confusion as to whether they were successful or not. @@ -183,7 +183,7 @@ def test_empty_request_returns_an_error(self, patch_process_event_with_plugins): self.assertEqual(patch_process_event_with_plugins.call_count, 0) @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_batch(self, patch_process_event_with_plugins): data = {"type": "capture", "event": "user signed up", "distinct_id": "2"} response = self.client.post( @@ -204,7 +204,7 @@ def test_batch(self, patch_process_event_with_plugins): ) @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_batch_gzip_header(self, patch_process_event_with_plugins): data = { "api_key": self.team.api_token, @@ -234,7 +234,7 @@ def test_batch_gzip_header(self, patch_process_event_with_plugins): ) @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_batch_gzip_param(self, patch_process_event_with_plugins): data = { "api_key": self.team.api_token, @@ -263,7 +263,7 @@ def test_batch_gzip_param(self, patch_process_event_with_plugins): ) @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_batch_lzstring(self, patch_process_event_with_plugins): data = { "api_key": self.team.api_token, @@ -332,7 +332,7 @@ def test_batch_distinct_id_not_set(self): self.assertEqual(response.json()["message"], "You need to set user distinct ID field `distinct_id`.") @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_engage(self, patch_process_event_with_plugins): response = self.client.get( "/engage/?data=%s" @@ -361,7 +361,7 @@ def test_engage(self, patch_process_event_with_plugins): ) @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_python_library(self, patch_process_event_with_plugins): self.client.post( "/track/", @@ -374,7 +374,7 @@ def test_python_library(self, patch_process_event_with_plugins): self.assertEqual(arguments["team_id"], self.team.pk) @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_base64_decode_variations(self, patch_process_event_with_plugins): base64 = "eyJldmVudCI6IiRwYWdldmlldyIsInByb3BlcnRpZXMiOnsiZGlzdGluY3RfaWQiOiJlZWVlZWVlZ8+lZWVlZWUifX0=" dict = self._dict_from_b64(base64) @@ -400,7 +400,7 @@ def test_base64_decode_variations(self, patch_process_event_with_plugins): self.assertEqual(arguments["distinct_id"], "eeeeeeegĻ„eeeee") @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_js_library_underscore_sent_at(self, patch_process_event_with_plugins): now = timezone.now() tomorrow = now + timedelta(days=1, hours=2) @@ -430,7 +430,7 @@ def test_js_library_underscore_sent_at(self, patch_process_event_with_plugins): self.assertEqual(arguments["data"]["timestamp"], tomorrow.isoformat()) @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_long_distinct_id(self, patch_process_event_with_plugins): now = timezone.now() tomorrow = now + timedelta(days=1, hours=2) @@ -451,7 +451,7 @@ def test_long_distinct_id(self, patch_process_event_with_plugins): self.assertEqual(len(arguments["distinct_id"]), 200) @patch("posthog.models.team.TEAM_CACHE", {}) - @patch("posthog.tasks.process_event.process_event_with_plugins.apply_async") + @patch("posthog.api.capture.celery_app.send_task") def test_sent_at_field(self, patch_process_event_with_plugins): now = timezone.now() tomorrow = now + timedelta(days=1, hours=2) From c7380f08a079e0ce94d5617df66e69995a2d2860 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 01:39:58 +0100 Subject: [PATCH 211/230] disable plugins if MULTI_TENANCY --- posthog/api/plugin.py | 25 +++++++++++++++---------- posthog/api/user.py | 7 ++----- posthog/plugins/__init__.py | 1 + posthog/plugins/access.py | 9 +++++++++ posthog/settings.py | 5 +++-- 5 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 posthog/plugins/access.py diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py index 6539ea85fa52b..2c235c465593c 100644 --- a/posthog/api/plugin.py +++ b/posthog/api/plugin.py @@ -13,7 +13,12 @@ from rest_framework.response import Response from posthog.models import Plugin, PluginConfig -from posthog.plugins import download_plugin_github_zip, reload_plugins_on_workers +from posthog.plugins import ( + can_configure_plugins_via_api, + can_install_plugins_via_api, + download_plugin_github_zip, + reload_plugins_on_workers, +) from posthog.redis import get_client @@ -24,12 +29,12 @@ class Meta: read_only_fields = ["id", "error", "from_json"] def get_error(self, plugin: Plugin) -> Optional[JSONField]: - if plugin.error and settings.PLUGINS_INSTALL_FROM_WEB: + if plugin.error and can_install_plugins_via_api(): return plugin.error return None def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: - if not settings.PLUGINS_INSTALL_FROM_WEB: + if not can_install_plugins_via_api(): raise ValidationError("Plugin installation via the web is disabled!") if len(Plugin.objects.filter(name=validated_data["name"])) > 0: raise ValidationError('Plugin with name "{}" already installed!'.format(validated_data["name"])) @@ -53,17 +58,17 @@ class PluginViewSet(viewsets.ModelViewSet): def get_queryset(self): queryset = super().get_queryset() if self.action == "get" or self.action == "list": # type: ignore - if settings.PLUGINS_INSTALL_FROM_WEB or settings.PLUGINS_CONFIGURE_FROM_WEB: + if can_install_plugins_via_api() or can_configure_plugins_via_api(): return queryset else: - if settings.PLUGINS_INSTALL_FROM_WEB: + if can_install_plugins_via_api(): # block update/delete for plugins that come from posthog.json return queryset.filter(from_json=False) return queryset.none() @action(methods=["GET"], detail=False) def repository(self, request: request.Request): - if not settings.PLUGINS_INSTALL_FROM_WEB: + if not can_install_plugins_via_api(): raise ValidationError("Plugin installation via the web is disabled!") url = "https://raw.githubusercontent.com/PostHog/plugins/main/repository.json" plugins = requests.get(url) @@ -71,7 +76,7 @@ def repository(self, request: request.Request): @action(methods=["GET"], detail=False) def status(self, request: request.Request): - if not settings.PLUGINS_INSTALL_FROM_WEB: + if not can_install_plugins_via_api(): raise ValidationError("Plugin installation via the web is disabled!") ping = get_client().get("@posthog-plugin-server/ping") @@ -95,7 +100,7 @@ class Meta: read_only_fields = ["id"] def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> PluginConfig: - if not settings.PLUGINS_CONFIGURE_FROM_WEB: + if not can_configure_plugins_via_api(): raise ValidationError("Plugin configuration via the web is disabled!") request = self.context["request"] validated_data["team"] = request.user.team @@ -116,7 +121,7 @@ class PluginConfigViewSet(viewsets.ModelViewSet): def get_queryset(self): queryset = super().get_queryset() - if settings.PLUGINS_CONFIGURE_FROM_WEB: + if can_configure_plugins_via_api(): return queryset.filter(team_id=self.request.user.team.pk) return queryset.none() @@ -129,7 +134,7 @@ def destroy(self, request: request.Request, pk=None) -> Response: # type: ignor @action(methods=["GET"], detail=False) def global_plugins(self, request: request.Request): - if not settings.PLUGINS_CONFIGURE_FROM_WEB: + if not can_configure_plugins_via_api(): return Response([]) response = [] diff --git a/posthog/api/user.py b/posthog/api/user.py index 121c5a132dfbf..72baea579c166 100644 --- a/posthog/api/user.py +++ b/posthog/api/user.py @@ -18,7 +18,7 @@ from posthog.auth import authenticate_secondarily from posthog.models import Event, Team, User -from posthog.plugins import reload_plugins_on_workers +from posthog.plugins import can_configure_plugins_via_api, can_install_plugins_via_api, reload_plugins_on_workers from posthog.version import VERSION @@ -134,10 +134,7 @@ def user(request): "posthog_version": VERSION, "is_multi_tenancy": getattr(settings, "MULTI_TENANCY", False), "ee_available": user.ee_available, - "plugin_access": { - "install": settings.PLUGINS_INSTALL_FROM_WEB, - "configure": settings.PLUGINS_CONFIGURE_FROM_WEB, - }, + "plugin_access": {"install": can_install_plugins_via_api(), "configure": can_configure_plugins_via_api()}, } ) diff --git a/posthog/plugins/__init__.py b/posthog/plugins/__init__.py index 0b67868b0c7c3..81482e127eadd 100644 --- a/posthog/plugins/__init__.py +++ b/posthog/plugins/__init__.py @@ -1,3 +1,4 @@ +from .access import can_configure_plugins_via_api, can_install_plugins_via_api from .reload import reload_plugins_on_workers from .sync import sync_plugin_config from .utils import download_plugin_github_zip diff --git a/posthog/plugins/access.py b/posthog/plugins/access.py new file mode 100644 index 0000000000000..306f46358cc09 --- /dev/null +++ b/posthog/plugins/access.py @@ -0,0 +1,9 @@ +from django.conf import settings + + +def can_install_plugins_via_api(): + return settings.PLUGINS_INSTALL_VIA_API and not getattr(settings, "MULTI_TENANCY", False) + + +def can_configure_plugins_via_api(): + return settings.PLUGINS_CONFIGURE_VIA_API and not getattr(settings, "MULTI_TENANCY", False) diff --git a/posthog/settings.py b/posthog/settings.py index c76801e615d55..844934460b772 100644 --- a/posthog/settings.py +++ b/posthog/settings.py @@ -69,8 +69,9 @@ def print_warning(warning_lines: Sequence[str]): else: JS_URL = os.environ.get("JS_URL", "") -PLUGINS_INSTALL_FROM_WEB = get_bool_from_env("PLUGINS_INSTALL_FROM_WEB", True) -PLUGINS_CONFIGURE_FROM_WEB = PLUGINS_INSTALL_FROM_WEB or get_bool_from_env("PLUGINS_CONFIGURE_FROM_WEB", True) +PLUGINS_INSTALL_VIA_API = get_bool_from_env("PLUGINS_INSTALL_VIA_API", True) +PLUGINS_CONFIGURE_VIA_API = PLUGINS_INSTALL_VIA_API or get_bool_from_env("PLUGINS_CONFIGURE_VIA_API", True) + PLUGINS_CELERY_QUEUE = os.environ.get("PLUGINS_CELERY_QUEUE", "posthog-plugins") PLUGINS_RELOAD_PUBSUB_CHANNEL = os.environ.get("PLUGINS_RELOAD_PUBSUB_CHANNEL", "reload-plugins") From 1c8d1c67be39fe6e713d4e54b430f1a353c0a071 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 01:45:07 +0100 Subject: [PATCH 212/230] upgrade plugin-server version --- plugins/package.json | 2 +- plugins/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/package.json b/plugins/package.json index 019352f06a5bf..c4dabbafaab6e 100644 --- a/plugins/package.json +++ b/plugins/package.json @@ -4,7 +4,7 @@ "license": "MIT", "private": true, "dependencies": { - "posthog-plugin-server": "^0.0.4" + "posthog-plugin-server": "^0.0.5" }, "scripts": { "start": "posthog-plugin-server start" diff --git a/plugins/yarn.lock b/plugins/yarn.lock index d7390ffbec278..be371cd5b2b2b 100644 --- a/plugins/yarn.lock +++ b/plugins/yarn.lock @@ -958,10 +958,10 @@ posthog-js-lite@^0.0.5: resolved "https://registry.yarnpkg.com/posthog-js-lite/-/posthog-js-lite-0.0.5.tgz#984a619190d1c4ef003cb81194d0a6276491d2e8" integrity sha512-xHVZ9qbBdoqK7G1JVA4k6nheWu50aiEwjJIPQw7Zhi705aX5wr2W8zxdmvtadeEE5qbJNg+/uZ7renOQoqcbJw== -posthog-plugin-server@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/posthog-plugin-server/-/posthog-plugin-server-0.0.4.tgz#5798938cfaa0c2b07fc263ba2b89dd20cd4caf76" - integrity sha512-SJrOqp1efVafuRI6OZ8A1fswY9OqVH8mDgG3ev8FUP1nmyI0juEIGlIwNxWRG4GHbFW0m1j2D1qViz8tegvmIg== +posthog-plugin-server@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/posthog-plugin-server/-/posthog-plugin-server-0.0.5.tgz#dcdd51b9891ecc4edc99c15bd3e510a32f45bcaa" + integrity sha512-8mR1ztVlMBXWuH+OBuKFAEyF0I9ou1ak7KlWpq4mWTZ7nS6VocbrK+yOtlbiQohN+Vj9hocHS4dMdQVlPfncgw== dependencies: "@types/adm-zip" "^0.4.33" "@types/ioredis" "^4.17.6" From 15a1ab99955f38f934e0df73d1bc74dff5cec608 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 02:16:45 +0100 Subject: [PATCH 213/230] save event with plugin error --- frontend/src/scenes/plugins/PluginError.tsx | 33 ++++++++++++--------- frontend/src/types.ts | 1 + 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/frontend/src/scenes/plugins/PluginError.tsx b/frontend/src/scenes/plugins/PluginError.tsx index 47ad982a3ba8c..b1d190d3d72e3 100644 --- a/frontend/src/scenes/plugins/PluginError.tsx +++ b/frontend/src/scenes/plugins/PluginError.tsx @@ -4,6 +4,23 @@ import React from 'react' import { ClearOutlined } from '@ant-design/icons' import { PluginErrorType } from '~/types' +function CodeBlock({ children }: { children: React.ReactNode }): JSX.Element { + return ( + + {children} + + ) +} + export function PluginError({ error, reset }: { error: PluginErrorType; reset?: () => void }): JSX.Element | null { if (!error) { return null @@ -22,20 +39,8 @@ export function PluginError({ error, reset }: { error: PluginErrorType; reset?: {error.name ? {error.name}: : ''} {error.message}
- {error.stack ? ( - - {error.stack} - - ) : null} + {error.stack ? {error.stack} : null} + {error.event ? {JSON.stringify(error.event, null, 2)} : null} } trigger="click" diff --git a/frontend/src/types.ts b/frontend/src/types.ts index e32510d7e073c..d84c1f922c38c 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -245,4 +245,5 @@ export interface PluginErrorType { time: string stack?: string name?: string + event?: Record } From 799d7c9e5cfc6c0804ac9704da6208fcce078ffb Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 02:21:46 +0100 Subject: [PATCH 214/230] upgrade plugin server --- plugins/package.json | 2 +- plugins/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/package.json b/plugins/package.json index c4dabbafaab6e..33b775a40111f 100644 --- a/plugins/package.json +++ b/plugins/package.json @@ -4,7 +4,7 @@ "license": "MIT", "private": true, "dependencies": { - "posthog-plugin-server": "^0.0.5" + "posthog-plugin-server": "^0.0.6" }, "scripts": { "start": "posthog-plugin-server start" diff --git a/plugins/yarn.lock b/plugins/yarn.lock index be371cd5b2b2b..1f848b4e9f4a8 100644 --- a/plugins/yarn.lock +++ b/plugins/yarn.lock @@ -958,10 +958,10 @@ posthog-js-lite@^0.0.5: resolved "https://registry.yarnpkg.com/posthog-js-lite/-/posthog-js-lite-0.0.5.tgz#984a619190d1c4ef003cb81194d0a6276491d2e8" integrity sha512-xHVZ9qbBdoqK7G1JVA4k6nheWu50aiEwjJIPQw7Zhi705aX5wr2W8zxdmvtadeEE5qbJNg+/uZ7renOQoqcbJw== -posthog-plugin-server@^0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/posthog-plugin-server/-/posthog-plugin-server-0.0.5.tgz#dcdd51b9891ecc4edc99c15bd3e510a32f45bcaa" - integrity sha512-8mR1ztVlMBXWuH+OBuKFAEyF0I9ou1ak7KlWpq4mWTZ7nS6VocbrK+yOtlbiQohN+Vj9hocHS4dMdQVlPfncgw== +posthog-plugin-server@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/posthog-plugin-server/-/posthog-plugin-server-0.0.6.tgz#1cc6ca6807a4c0c59311080f9f3022c2d78e6e28" + integrity sha512-wNrasi2+Va7HJq2Af7BqDKb5BntGt7gXPCBICVjjU/TXyitBLgwaL60m0lMFS+WAFXpviyzkRhgZmPlimIkecg== dependencies: "@types/adm-zip" "^0.4.33" "@types/ioredis" "^4.17.6" From 3ce9c7ae48d694296e17297f8aa94842742148cc Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Mon, 2 Nov 2020 10:26:37 +0000 Subject: [PATCH 215/230] squashed & optimized migrations --- latest_migrations.manifest | 2 +- posthog/migrations/0096_plugins.py | 25 +++++++++++-------- posthog/migrations/0097_plugin_url_field.py | 20 --------------- .../migrations/0098_team_plugins_opt_in.py | 14 ----------- 4 files changed, 15 insertions(+), 46 deletions(-) delete mode 100644 posthog/migrations/0097_plugin_url_field.py delete mode 100644 posthog/migrations/0098_team_plugins_opt_in.py diff --git a/latest_migrations.manifest b/latest_migrations.manifest index 3edb85894534c..d447d50076e31 100644 --- a/latest_migrations.manifest +++ b/latest_migrations.manifest @@ -2,7 +2,7 @@ admin: 0003_logentry_add_action_flag_choices auth: 0011_update_proxy_permissions contenttypes: 0002_remove_content_type_name ee: 0002_hook -posthog: 0098_team_plugins_opt_in +posthog: 0096_auto_20201102_1021 rest_hooks: 0002_swappable_hook_model sessions: 0001_initial social_django: 0008_partial_timestamp diff --git a/posthog/migrations/0096_plugins.py b/posthog/migrations/0096_plugins.py index 993ef404e6d08..8dfe0bcc334a2 100644 --- a/posthog/migrations/0096_plugins.py +++ b/posthog/migrations/0096_plugins.py @@ -1,7 +1,6 @@ -# Generated by Django 3.0.7 on 2020-10-08 15:24 - import django.contrib.postgres.fields.jsonb import django.db.models.deletion +import posthog.models.plugin from django.db import migrations, models @@ -15,28 +14,32 @@ class Migration(migrations.Migration): migrations.CreateModel( name="Plugin", fields=[ - ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID",),), ("name", models.CharField(blank=True, max_length=200, null=True)), ("description", models.TextField(blank=True, null=True)), - ("url", models.CharField(blank=True, max_length=800, null=True)), - ("config_schema", django.contrib.postgres.fields.jsonb.JSONField(default=dict)), + ("url", posthog.models.plugin.FileURLField(blank=True, max_length=800, null=True),), + ("config_schema", django.contrib.postgres.fields.jsonb.JSONField(default=dict),), ("tag", models.CharField(blank=True, max_length=200, null=True)), ("archive", models.BinaryField(blank=True, null=True)), ("from_json", models.BooleanField(default=False)), ("from_web", models.BooleanField(default=False)), - ("error", django.contrib.postgres.fields.jsonb.JSONField(default=None, null=True)), + ("error", django.contrib.postgres.fields.jsonb.JSONField(default=None, null=True),), ], ), migrations.CreateModel( name="PluginConfig", fields=[ - ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), - ("team", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="posthog.Team", null=True)), - ("plugin", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="posthog.Plugin")), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID",),), ("enabled", models.BooleanField(default=False)), ("order", models.IntegerField(blank=True, null=True)), - ("config", django.contrib.postgres.fields.jsonb.JSONField(default=dict)), - ("error", django.contrib.postgres.fields.jsonb.JSONField(default=None, null=True)), + ("config", django.contrib.postgres.fields.jsonb.JSONField(default=dict),), + ("error", django.contrib.postgres.fields.jsonb.JSONField(default=None, null=True),), + ("plugin", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="posthog.Plugin"),), + ( + "team", + models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to="posthog.Team",), + ), ], ), + migrations.AddField(model_name="team", name="plugins_opt_in", field=models.BooleanField(default=False),), ] diff --git a/posthog/migrations/0097_plugin_url_field.py b/posthog/migrations/0097_plugin_url_field.py deleted file mode 100644 index 8e07b9bf3960f..0000000000000 --- a/posthog/migrations/0097_plugin_url_field.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-30 13:03 - -from django.db import migrations - -import posthog.models.plugin - - -class Migration(migrations.Migration): - - dependencies = [ - ("posthog", "0096_plugins"), - ] - - operations = [ - migrations.AlterField( - model_name="plugin", - name="url", - field=posthog.models.plugin.FileURLField(blank=True, max_length=800, null=True), - ), - ] diff --git a/posthog/migrations/0098_team_plugins_opt_in.py b/posthog/migrations/0098_team_plugins_opt_in.py deleted file mode 100644 index ae6f10f518561..0000000000000 --- a/posthog/migrations/0098_team_plugins_opt_in.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-31 23:55 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("posthog", "0097_plugin_url_field"), - ] - - operations = [ - migrations.AddField(model_name="team", name="plugins_opt_in", field=models.BooleanField(default=False),), - ] From bf90b5a97715b8d96a1c82dea4bd720de09e5a69 Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Mon, 2 Nov 2020 10:28:49 +0000 Subject: [PATCH 216/230] remove unused import --- posthog/api/plugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/posthog/api/plugin.py b/posthog/api/plugin.py index 2c235c465593c..3ebd30d82e612 100644 --- a/posthog/api/plugin.py +++ b/posthog/api/plugin.py @@ -4,7 +4,6 @@ import requests from dateutil import parser from dateutil.relativedelta import relativedelta -from django.conf import settings from django.contrib.postgres.fields import JSONField from django.utils.timezone import now from rest_framework import request, serializers, viewsets From 59623d50ef39ae59d3e21e7ac242b905433a3578 Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Mon, 2 Nov 2020 11:04:09 +0000 Subject: [PATCH 217/230] updates opt-in copy & hides tech details for cloud version --- frontend/src/scenes/plugins/OptInPlugins.tsx | 62 +++++++++++--------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/frontend/src/scenes/plugins/OptInPlugins.tsx b/frontend/src/scenes/plugins/OptInPlugins.tsx index 7fffc7fef793d..70045f21a067c 100644 --- a/frontend/src/scenes/plugins/OptInPlugins.tsx +++ b/frontend/src/scenes/plugins/OptInPlugins.tsx @@ -1,12 +1,13 @@ import React, { useEffect, useState } from 'react' -import { useActions } from 'kea' +import { useActions, useValues } from 'kea' import { Button, Checkbox, Spin } from 'antd' -import { ApiOutlined, CheckOutlined, WarningOutlined } from '@ant-design/icons' +import { CheckOutlined, WarningOutlined } from '@ant-design/icons' import { userLogic } from 'scenes/userLogic' import api from 'lib/api' export function OptInPlugins(): JSX.Element { const { userUpdateRequest } = useActions(userLogic) + const { user } = useValues(userLogic) const [optIn, setOptIn] = useState(false) const [serverStatus, setServerStatus] = useState('loading') @@ -31,42 +32,49 @@ export function OptInPlugins(): JSX.Element { information to your events, normalizing your revenue information to a single currency, etc.
- Plugins are currently an experimental feature that you must opt in to. -
-
- Plugin support requires the cooperation of the main posthog application and a new nodejs based{' '} - - posthog-plugin-server - - . In case the plugin server is not properly configured, you might experience data loss. Proceed - at your own risk or wait a few weeks until we're out of beta. -
-
- Plugin server:{' '} - {serverStatus === 'loading' ? ( - - ) : serverStatus === 'online' ? ( - - Online - - ) : ( - - Offline - - )} + Plugins are currently in an experimental stage. You must opt-in to use them in{' '} + each project.
+ {!user?.is_multi_tenancy && ( + <> +
+ Plugin support requires the cooperation of the main PostHog application and the new NodeJS-based{' '} + + posthog-plugin-server + + . In case the plugin server is not properly configured, you might experience data loss. + If you do not wish to take this risk we recommend waiting a few weeks until this functionality + is fully released. +
+
+ Plugin server:{' '} + {serverStatus === 'loading' ? ( + + ) : serverStatus === 'online' ? ( + + Online + + ) : ( + + Offline + + )} +
+ + )}
setOptIn(!optIn)} disabled={serverStatus !== 'online'}> - I understand the risks and I'm not worried about potentially losing a few events. + I understand the risks and wish to try this beta feature now for {user?.team.name}.
From 6c80b44d1189850ed7b0f0d696707efa099312a8 Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Mon, 2 Nov 2020 11:49:39 +0000 Subject: [PATCH 218/230] fix cypress tests --- cypress/integration/projectSettings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/integration/projectSettings.js b/cypress/integration/projectSettings.js index bea7eda710e26..3acafa3b0560e 100644 --- a/cypress/integration/projectSettings.js +++ b/cypress/integration/projectSettings.js @@ -1,6 +1,6 @@ describe('Setup', () => { it('Setup loaded', () => { - cy.get('[data-attr=menu-item-project-settings]').click() + cy.get('[data-attr=menu-item-project]').click() cy.get('[data-attr=layout-content]').should('exist') }) @@ -16,7 +16,7 @@ describe('Setup', () => { }) }) cy.reload(true) - cy.get('[data-attr=menu-item-project-settings]').click() + cy.get('[data-attr=menu-item-project]').click() cy.get('[data-attr=app-url-suggestion]').click() cy.get('[data-attr=app-url-item]').should('contain', '/demo') }) From 407168b1a120ee4619a5fbf580f6c032be6cfd14 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 13:27:35 +0100 Subject: [PATCH 219/230] compare with None --- posthog/api/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/api/user.py b/posthog/api/user.py index 72baea579c166..a6c59dc6ee902 100644 --- a/posthog/api/user.py +++ b/posthog/api/user.py @@ -46,7 +46,7 @@ def user(request): team.slack_incoming_webhook = data["team"].get("slack_incoming_webhook", team.slack_incoming_webhook) team.anonymize_ips = data["team"].get("anonymize_ips", team.anonymize_ips) team.session_recording_opt_in = data["team"].get("session_recording_opt_in", team.session_recording_opt_in) - if data["team"].get("plugins_opt_in", "nope") != "nope": + if data["team"].get("plugins_opt_in", None) != None: reload_plugins_on_workers() team.plugins_opt_in = data["team"].get("plugins_opt_in", team.plugins_opt_in) team.completed_snippet_onboarding = data["team"].get( From 284bddcc4fb85344d02984a340de7e52b3b55231 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 13:39:22 +0100 Subject: [PATCH 220/230] change plugins url and add redirect --- frontend/src/layout/Sidebar.js | 2 +- frontend/src/scenes/sceneLogic.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/layout/Sidebar.js b/frontend/src/layout/Sidebar.js index 87ed877e1b5a6..f6cc5e1179e13 100644 --- a/frontend/src/layout/Sidebar.js +++ b/frontend/src/layout/Sidebar.js @@ -263,7 +263,7 @@ function _Sidebar({ user, sidebarCollapsed, setSidebarCollapsed }) { Plugins - + )} diff --git a/frontend/src/scenes/sceneLogic.js b/frontend/src/scenes/sceneLogic.js index 556673e70e45b..cbdf008c18fac 100644 --- a/frontend/src/scenes/sceneLogic.js +++ b/frontend/src/scenes/sceneLogic.js @@ -40,6 +40,7 @@ export const unauthenticatedRoutes = ['preflightCheck', 'signup'] export const redirects = { '/': '/insights', + '/plugins': '/project/plugins', } export const routes = { @@ -60,6 +61,7 @@ export const routes = { '/feature_flags': 'featureFlags', '/annotations': 'annotations', '/project/settings': 'projectSettings', + '/project/plugins': 'plugins', '/organization/settings': 'organizationSettings', '/organization/members': 'organizationMembers', '/organization/invites': 'organizationInvites', @@ -67,7 +69,6 @@ export const routes = { '/instance/licenses': 'instanceLicenses', '/instance/status': 'instanceStatus', '/me/settings': 'mySettings', - '/plugins': 'plugins', '/preflight': 'preflightCheck', '/signup': 'signup', '/ingestion': 'ingestion', From a345be78f93848a0eab0e4be532acc8151f405a7 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 13:42:55 +0100 Subject: [PATCH 221/230] remove ellipsis --- frontend/src/lib/utils.tsx | 4 ---- frontend/src/scenes/plugins/PluginCard.tsx | 4 ++-- frontend/src/scenes/plugins/PluginDrawer.tsx | 3 +-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/frontend/src/lib/utils.tsx b/frontend/src/lib/utils.tsx index 52d8679544f9f..6bde06bd12c19 100644 --- a/frontend/src/lib/utils.tsx +++ b/frontend/src/lib/utils.tsx @@ -536,10 +536,6 @@ export function sampleSingle(items: T[]): T[] { return [items[Math.floor(Math.random() * items.length)]] } -export function ellipsis(input: string, max: number): string { - return input.length > max ? input.substr(0, max) + '...' : input -} - export function parseGithubRepoURL(url: string): Record { const match = url.match(/https?:\/\/(www\.|)github.com\/([^\/]+)\/([^\/]+)\/?$/) if (!match) { diff --git a/frontend/src/scenes/plugins/PluginCard.tsx b/frontend/src/scenes/plugins/PluginCard.tsx index 643a9a00b2fb1..0a723ebfd262d 100644 --- a/frontend/src/scenes/plugins/PluginCard.tsx +++ b/frontend/src/scenes/plugins/PluginCard.tsx @@ -2,7 +2,7 @@ import { Col, Card, Button, Switch, Popconfirm, Skeleton } from 'antd' import { useActions, useValues } from 'kea' import React from 'react' import { pluginsLogic } from './pluginsLogic' -import { ellipsis, someParentMatchesSelector } from 'lib/utils' +import { someParentMatchesSelector } from 'lib/utils' import { PluginConfigType, PluginErrorType } from '~/types' import { PlusOutlined } from '@ant-design/icons' import { Link } from 'lib/components/Link' @@ -60,7 +60,7 @@ export function PluginCard({ name, description, url, pluginConfig, pluginId, err
{name}
-
{ellipsis(description, 180)}
+
{description}
- {ellipsis(editingPlugin.description, 140)} + {editingPlugin.description}
View plugin From 8d1de95e77315745858ce676fb5c4c9e8373f069 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 13:48:28 +0100 Subject: [PATCH 222/230] use code snippet in plugin errors --- .../ingestion/frameworks/CodeSnippet.tsx | 4 ++- frontend/src/scenes/plugins/PluginError.tsx | 30 +++++++------------ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/frontend/src/scenes/ingestion/frameworks/CodeSnippet.tsx b/frontend/src/scenes/ingestion/frameworks/CodeSnippet.tsx index 8013a30fcf712..4620b06c22d16 100644 --- a/frontend/src/scenes/ingestion/frameworks/CodeSnippet.tsx +++ b/frontend/src/scenes/ingestion/frameworks/CodeSnippet.tsx @@ -74,16 +74,18 @@ export interface CodeSnippetProps { language?: Language wrap?: boolean actions?: Action[] + style?: React.CSSProperties } export function CodeSnippet({ children, language = Language.Text, wrap = false, + style = {}, actions, }: CodeSnippetProps): JSX.Element { return ( -
+
{actions && actions.map(({ Icon, callback, popconfirmProps }, index) => diff --git a/frontend/src/scenes/plugins/PluginError.tsx b/frontend/src/scenes/plugins/PluginError.tsx index b1d190d3d72e3..0fa3304903204 100644 --- a/frontend/src/scenes/plugins/PluginError.tsx +++ b/frontend/src/scenes/plugins/PluginError.tsx @@ -3,23 +3,7 @@ import moment from 'moment' import React from 'react' import { ClearOutlined } from '@ant-design/icons' import { PluginErrorType } from '~/types' - -function CodeBlock({ children }: { children: React.ReactNode }): JSX.Element { - return ( - - {children} - - ) -} +import { CodeSnippet, Language } from 'scenes/ingestion/frameworks/CodeSnippet' export function PluginError({ error, reset }: { error: PluginErrorType; reset?: () => void }): JSX.Element | null { if (!error) { @@ -39,8 +23,16 @@ export function PluginError({ error, reset }: { error: PluginErrorType; reset?: {error.name ? {error.name}: : ''} {error.message}
- {error.stack ? {error.stack} : null} - {error.event ? {JSON.stringify(error.event, null, 2)} : null} + {error.stack ? ( + + {error.stack} + + ) : null} + {error.event ? ( + + {JSON.stringify(error.event, null, 2)} + + ) : null} } trigger="click" From 4a6c00e2e7a28415da81af0586c36aa1af9318a0 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 13:51:00 +0100 Subject: [PATCH 223/230] change github regex --- frontend/src/lib/utils.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/utils.tsx b/frontend/src/lib/utils.tsx index 6bde06bd12c19..6cd0efc8932a1 100644 --- a/frontend/src/lib/utils.tsx +++ b/frontend/src/lib/utils.tsx @@ -537,11 +537,11 @@ export function sampleSingle(items: T[]): T[] { } export function parseGithubRepoURL(url: string): Record { - const match = url.match(/https?:\/\/(www\.|)github.com\/([^\/]+)\/([^\/]+)\/?$/) + const match = url.match(/^https?:\/\/(?:www\.)?github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\/?$/) if (!match) { throw new Error('Must be in the format: https://github.com/user/repo') } - const [, , user, repo] = match + const [, user, repo] = match return { user, repo } } From 4888e6e2bbdd1fc4510d3b10d477eb4226230b03 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 13:55:10 +0100 Subject: [PATCH 224/230] fix loading flickering on installing plugins --- frontend/src/scenes/plugins/Repository.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/src/scenes/plugins/Repository.tsx b/frontend/src/scenes/plugins/Repository.tsx index 72f1d9dbe8653..1c1ff8278b7a4 100644 --- a/frontend/src/scenes/plugins/Repository.tsx +++ b/frontend/src/scenes/plugins/Repository.tsx @@ -5,13 +5,13 @@ import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { PluginCard, PluginLoading } from './PluginCard' export function Repository(): JSX.Element { - const { loading, repositoryLoading, uninstalledPlugins } = useValues(pluginsLogic) + const { repositoryLoading, uninstalledPlugins } = useValues(pluginsLogic) return (

Available

- {((!loading && !repositoryLoading) || uninstalledPlugins.length > 0) && ( + {(!repositoryLoading || uninstalledPlugins.length > 0) && ( <> {uninstalledPlugins.map((plugin) => { return ( @@ -31,7 +31,11 @@ export function Repository(): JSX.Element { )} - {(loading || repositoryLoading) && uninstalledPlugins.length === 0 && } + {repositoryLoading && uninstalledPlugins.length === 0 && ( + + + + )}
) } From 4a8d7eabad19b3f4db40d1b4dc544dd5f63f857b Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 13:58:29 +0100 Subject: [PATCH 225/230] add comment to plugin archive --- posthog/plugins/test/plugin_archives.py | 1 + 1 file changed, 1 insertion(+) diff --git a/posthog/plugins/test/plugin_archives.py b/posthog/plugins/test/plugin_archives.py index c57a7987e54cb..9c0db8c3c1410 100644 --- a/posthog/plugins/test/plugin_archives.py +++ b/posthog/plugins/test/plugin_archives.py @@ -1,3 +1,4 @@ +# https://github.com/PostHog/helloworldplugin in a base64 encoded zip file HELLO_WORLD_PLUGIN = ( "3c4c77e7d7878e87be3c2373b658c74ec3085f49", "UEsDBAoAAAAAAGNLSVEAAAAAAAAAAAAAAAA6AAkAaGVsbG93b3JsZHBsdWdpbi0zYzRjNzdlN2Q3ODc4ZTg3YmUzYzIzNzNiNjU4Yzc0ZWMzMDg1ZjQ5L1VUBQABW4+AX1BLAwQKAAAACABjS0lRHKl1qccDAAAHBwAARAAJAGhlbGxvd29ybGRwbHVnaW4tM2M0Yzc3ZTdkNzg3OGU4N2JlM2MyMzczYjY1OGM3NGVjMzA4NWY0OS8uZ2l0aWdub3JlVVQFAAFbj4BfbVTfj9w0EH7PX2HpkJBOl0QCigo8tb2rONSWE215QWjlOJPEt47H2M7uhb+eb5xdbot4WO94fnvyfXOlXq+ZasNzsI561SoO2c727yLfvnunBuhTtduF1Wgz0W7XVtdNWP8w3P9ZXX8V1sY4nVJVXak3ip4y+WTZJzglFuWtTTnabsnQImXQZq9H68eqeVjzxL7qFuv6turpQI5DTeOYcEMUTj56x7qHYlM325+zXTm+/66tgo4ZqrRFHHRsq+NE5KALNtRFrmfKutdZw2/SkdpQStdnx2vJW1s/MEpYn7J2GEZjhnEzVe9ffbh/e/fxkzzoYb0/eUTc1Oe0QF5VnijRNi2FEuoYbcYwVLcqrbZ6KploQ1ZD5BnKTHNwOpNk6WhgBF3kVmUuSfJirHSjEiuNKyvrH8lkhfdQyzBHJZ0nnGLMDXqetbcDpSxfIZCRtp8TOx5TmQ2EJj/lIvfkCEDIk011byMKcFyLFbGfvc1oN2V8QMMHinokFSkwRl9NeXZQYnKZn3D6cp7dnqXmGrJAqPKcSLKl5ml21b92uVxv7gViN5vYTGuQZyYrAMAkEbkriVrp7VPUPmGMJ9DNLLFc2r591H5klfIyDD9CjfdWjo12O9TPwGBCuqrvmvSXs5m+vRDrR16i107SvAW+9+csBR3eoHZzpA7Ap5zq7Vlw/WiiDuvZt0nlWgxhsv5J9WyWmXwu7QLeJrW7E/4Lsl6LjDdnHUfKRfnLIk+O6gNn6pj3AGhYfbdDRbMPjI9euHd/YlOILBjc9TToxSGF3bC3M+wHOzZbO2ElfyjDFBpgysLZYrBBLECkemUMxx5TEsyFNeh2M169+OHlDXCmbAIGsDnwoB7roiDTuKUHjG2QJjBws4dSnQoAOz5Hdk3J/zMfQfl4Iw5Ggzs8wME53XEs84FlUJM+SAtCFDBkrgXOdrBG9RSkrjcWhOP4xb3kP0V6ViZySvU5hUpLEOTenB6rZr2qE+m/zJonDZqx/zqrI8f9jZTxgJZkPwfIzxPJAC5j8cTLIZSve/egXrz85ie1JDhjLVAzNmq0eVo6oH5ub9FwX//6hr3niBU1OD6WxbvtTEqye2XNgqrxBLLKlEtHOtcJkOgXRxe6Jti+wA/h73WesH6jVN+WOpaD8G6DxJ0/2Mhe0JmwZwUeBznxw1ot592H31u5N53eb7pNKvhegVsF9JXddOYXKFAMJ325nmVE/caB/icmQn3hNe+FKP/hTptA02Jd0X8j53kpNL3cmsckFHsWC8GwY/OKooU9slwCVG31D1BLAwQKAAAACABjS0lRwW6wcnYCAAAwBAAAQQAJAGhlbGxvd29ybGRwbHVnaW4tM2M0Yzc3ZTdkNzg3OGU4N2JlM2MyMzczYjY1OGM3NGVjMzA4NWY0OS9MSUNFTlNFVVQFAAFbj4BfXVJLb+IwEL77V4w4tVLUrXrcmyEGvJvEkWPKcgyJIV6FGMVmUf/9zgTabldCijyP7zXk0kDmGjsEy9jCn99Gd+wiPDSP8PL88gw/6pMNsBqtHTrX94yVdjy5EJwfwAXo7Gj3b3Ac6yHaNoEDDoI/QNPV49EmED3Uwxuc7Rhwwe9j7QY3HKGGBrkYTsYOYYI/xGs9WhxuoQ7BN65GPGh9cznZIdaR+A6uRy0PsbMwq+4bs8eJpLV1z9wA1HtvwdXFzl8ijDbE0TWEkYAbmv7Skob3du9O7s5A61MAgSHoJaAD0pnAybfuQF872Tpf9r0LXQKtI+j9JWIxUHFKMiEf3/wIwWJkiOBQ9+T1U900Q9LPFGi8RxSocu386asTF9jhMg5Iaaed1mNkE+Nv20Sq0PjB972/krXGD60jR+E7YwZb9d7/sZOX230HH1HqTQId4Px51XsrdHXfw97eA0NejLf+x85I9CHi4V3dw9mPE9//Np+Qfy2gUkuz5VqArKDU6lWmIoUZr/A9S2ArzVptDOCE5oXZgVoCL3bwUxZpAuJXqUVVgdJM5mUmBdZkscg2qSxWMMe9QuGfWObSIKhRQIR3KCkqAsuFXqzxyecyk2aXsKU0BWEulQYOJddGLjYZ11BudKkqgfQpwhayWGpkEbkozBOyYg3EKz6gWvMsIyrGN6hekz5YqHKn5WptYK2yVGBxLlAZn2fiRoWmFhmXeQIpz/lKTFsKUTSjsZs62K4FlYiP429hpCrIxkIVRuMzQZfafKxuZSUS4FpWFMhSqzxhFCduqAkE9wpxQ6Go4ctFcITem0p8AEIqeIZYFS2TxffhJ/YXUEsDBAoAAAAIAGNLSVEf4ES4IwAAAC4AAABDAAkAaGVsbG93b3JsZHBsdWdpbi0zYzRjNzdlN2Q3ODc4ZTg3YmUzYzIzNzNiNjU4Yzc0ZWMzMDg1ZjQ5L1JFQURNRS5tZFVUBQABW4+AX1NWyEjNyckvzy/KSSnIKU3PzOMKyC8uychPh0gogGUUoFIAUEsDBAoAAAAIAGNLSVH7pxdtngAAAA0BAABFAAkAaGVsbG93b3JsZHBsdWdpbi0zYzRjNzdlN2Q3ODc4ZTg3YmUzYzIzNzNiNjU4Yzc0ZWMzMDg1ZjQ5L19faW5pdF9fLnB5VVQFAAFbj4BffY3BCsIwDIbvfYrQ0wZjDzDwouy+u4jMLd0KXVOSTn18104EPRgIJH/+749hWiCQxJmmOrh1sl7ALoE4QpfXYy94cr1IBd3ua+/oo1JqSCq0z34JDndz8cOUDQCorWFEA4FpQJErpoBC0JkK8tx8RZdNJlLla71xATlalLOe0TnSFziAfhC7Uf/x3nrOzvSpHsgbO73FD8QYV/Y7q15QSwMECgAAAAgAY0tJUR6z6b+vAAAAHQEAAEUACQBoZWxsb3dvcmxkcGx1Z2luLTNjNGM3N2U3ZDc4NzhlODdiZTNjMjM3M2I2NThjNzRlYzMwODVmNDkvcGx1Z2luLmpzb25VVAUAAVuPgF9tjjEPwiAQhff+iksXF2P3LiYO6ujWmZYrkFCuwhGjTf+7gDF1cGB47/seuaUCqJ2YsG6h1mgtPchbOduojKv3mUZvC2SeQ9s0yrCO/WGgqblR4Cup5v9OYhi8mdmQy/uLR2RgjdBlE4STcCYCASfhP4uB3GhUkpeUUu4T+IafMzsteBfAuPJbko5lXhx+zsUJ7I1TWy9xFNFyRr14bb3HezQeZQKjsAFLv1b5rdUbUEsDBAoAAAAAAGNLSVGjCLPpEgAAABIAAABKAAkAaGVsbG93b3JsZHBsdWdpbi0zYzRjNzdlN2Q3ODc4ZTg3YmUzYzIzNzNiNjU4Yzc0ZWMzMDg1ZjQ5L3JlcXVpcmVtZW50cy50eHRVVAUAAVuPgF9tYXJzaG1hbGxvdz09My44LjBQSwECAAAKAAAAAABjS0lRAAAAAAAAAAAAAAAAOgAJAAAAAAAAABAAAAAAAAAAaGVsbG93b3JsZHBsdWdpbi0zYzRjNzdlN2Q3ODc4ZTg3YmUzYzIzNzNiNjU4Yzc0ZWMzMDg1ZjQ5L1VUBQABW4+AX1BLAQIAAAoAAAAIAGNLSVEcqXWpxwMAAAcHAABEAAkAAAAAAAEAAAAAAGEAAABoZWxsb3dvcmxkcGx1Z2luLTNjNGM3N2U3ZDc4NzhlODdiZTNjMjM3M2I2NThjNzRlYzMwODVmNDkvLmdpdGlnbm9yZVVUBQABW4+AX1BLAQIAAAoAAAAIAGNLSVHBbrBydgIAADAEAABBAAkAAAAAAAEAAAAAAJMEAABoZWxsb3dvcmxkcGx1Z2luLTNjNGM3N2U3ZDc4NzhlODdiZTNjMjM3M2I2NThjNzRlYzMwODVmNDkvTElDRU5TRVVUBQABW4+AX1BLAQIAAAoAAAAIAGNLSVEf4ES4IwAAAC4AAABDAAkAAAAAAAEAAAAAAHEHAABoZWxsb3dvcmxkcGx1Z2luLTNjNGM3N2U3ZDc4NzhlODdiZTNjMjM3M2I2NThjNzRlYzMwODVmNDkvUkVBRE1FLm1kVVQFAAFbj4BfUEsBAgAACgAAAAgAY0tJUfunF22eAAAADQEAAEUACQAAAAAAAQAAAAAA/gcAAGhlbGxvd29ybGRwbHVnaW4tM2M0Yzc3ZTdkNzg3OGU4N2JlM2MyMzczYjY1OGM3NGVjMzA4NWY0OS9fX2luaXRfXy5weVVUBQABW4+AX1BLAQIAAAoAAAAIAGNLSVEes+m/rwAAAB0BAABFAAkAAAAAAAEAAAAAAAgJAABoZWxsb3dvcmxkcGx1Z2luLTNjNGM3N2U3ZDc4NzhlODdiZTNjMjM3M2I2NThjNzRlYzMwODVmNDkvcGx1Z2luLmpzb25VVAUAAVuPgF9QSwECAAAKAAAAAABjS0lRowiz6RIAAAASAAAASgAJAAAAAAABAAAAAAAjCgAAaGVsbG93b3JsZHBsdWdpbi0zYzRjNzdlN2Q3ODc4ZTg3YmUzYzIzNzNiNjU4Yzc0ZWMzMDg1ZjQ5L3JlcXVpcmVtZW50cy50eHRVVAUAAVuPgF9QSwUGAAAAAAcABwBXAwAApgoAACgAM2M0Yzc3ZTdkNzg3OGU4N2JlM2MyMzczYjY1OGM3NGVjMzA4NWY0OQ==", From cc2f4ec39d21cc25871052687a5397d51548d976 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 14:00:09 +0100 Subject: [PATCH 226/230] fix python style --- posthog/api/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/api/user.py b/posthog/api/user.py index a6c59dc6ee902..9cc63b86d05ae 100644 --- a/posthog/api/user.py +++ b/posthog/api/user.py @@ -46,7 +46,7 @@ def user(request): team.slack_incoming_webhook = data["team"].get("slack_incoming_webhook", team.slack_incoming_webhook) team.anonymize_ips = data["team"].get("anonymize_ips", team.anonymize_ips) team.session_recording_opt_in = data["team"].get("session_recording_opt_in", team.session_recording_opt_in) - if data["team"].get("plugins_opt_in", None) != None: + if data["team"].get("plugins_opt_in") is not None: reload_plugins_on_workers() team.plugins_opt_in = data["team"].get("plugins_opt_in", team.plugins_opt_in) team.completed_snippet_onboarding = data["team"].get( From 1cd4bcb88b77f0d52690c67b2673e2b5daca3529 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 14:39:11 +0100 Subject: [PATCH 227/230] remove pip-tools (relic from the python plugin era) --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index af9b5907d6c66..61b7d70139265 100644 --- a/requirements.txt +++ b/requirements.txt @@ -67,7 +67,6 @@ pytz==2019.3 redis==3.4.1 requests==2.22.0 requests-oauthlib==1.3.0 -pip-tools==5.0.0 ruamel.yaml==0.16.10 ruamel.yaml.clib==0.2.0 sentry-sdk==0.16.5 From f2b3efc695d08bab1e194587af19c4773fab21d4 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 14:40:06 +0100 Subject: [PATCH 228/230] hard pin plugin server version --- plugins/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/package.json b/plugins/package.json index 33b775a40111f..dbc1b38a82350 100644 --- a/plugins/package.json +++ b/plugins/package.json @@ -4,7 +4,7 @@ "license": "MIT", "private": true, "dependencies": { - "posthog-plugin-server": "^0.0.6" + "posthog-plugin-server": "0.0.6" }, "scripts": { "start": "posthog-plugin-server start" From 9add77c49ee29c7b0a91d94bed7010b50a3f921b Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 14:44:15 +0100 Subject: [PATCH 229/230] remove copying of posthog.json from dev dockerfile (breaks if file doesn't exist, copied later anyway) --- dev.Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/dev.Dockerfile b/dev.Dockerfile index c3351af80c239..e51be4fc98dd0 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -21,7 +21,6 @@ RUN mkdir /code/requirements/ COPY requirements/dev.txt /code/requirements/ RUN pip install -r requirements/dev.txt --compile -COPY posthog.json /code/ COPY package.json /code/ COPY yarn.lock /code/ COPY webpack.config.js /code/ From 0fcb4e6d59f9a2edb1fa8fc2232fd4cbe7b879d9 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Mon, 2 Nov 2020 14:50:17 +0100 Subject: [PATCH 230/230] update lockfile --- plugins/yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/yarn.lock b/plugins/yarn.lock index 1f848b4e9f4a8..1adacf790406b 100644 --- a/plugins/yarn.lock +++ b/plugins/yarn.lock @@ -958,7 +958,7 @@ posthog-js-lite@^0.0.5: resolved "https://registry.yarnpkg.com/posthog-js-lite/-/posthog-js-lite-0.0.5.tgz#984a619190d1c4ef003cb81194d0a6276491d2e8" integrity sha512-xHVZ9qbBdoqK7G1JVA4k6nheWu50aiEwjJIPQw7Zhi705aX5wr2W8zxdmvtadeEE5qbJNg+/uZ7renOQoqcbJw== -posthog-plugin-server@^0.0.6: +posthog-plugin-server@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/posthog-plugin-server/-/posthog-plugin-server-0.0.6.tgz#1cc6ca6807a4c0c59311080f9f3022c2d78e6e28" integrity sha512-wNrasi2+Va7HJq2Af7BqDKb5BntGt7gXPCBICVjjU/TXyitBLgwaL60m0lMFS+WAFXpviyzkRhgZmPlimIkecg==