def update(self, plugin_config: PluginConfig, validated_data: Dict, *args: Any, **kwargs: Any) -> PluginConfig: # type: ignore self._fix_formdata_config_json(validated_data) validated_data.pop("plugin", None) response = super().update(plugin_config, validated_data) self._update_plugin_attachments(plugin_config) reload_plugins_on_workers() return response
def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> PluginConfig: 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 plugin_config = super().create(validated_data) reload_plugins_on_workers() return plugin_config
def update(self, plugin: Plugin, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: # type: ignore if not can_install_plugins_via_api(self.context["organization_id"]): raise ValidationError("Plugin upgrades via the web are disabled!") if plugin.plugin_type != Plugin.PluginType.SOURCE: validated_data = self._update_validated_data_from_url(validated_data, validated_data["url"]) response = super().update(plugin, validated_data) reload_plugins_on_workers() return response
def destroy(self, request: request.Request, *args, **kwargs) -> Response: instance = self.get_object() if instance.is_global: raise ValidationError( "This plugin is marked as global! Make it local before uninstallation" ) self.perform_destroy(instance) reload_plugins_on_workers() return Response(status=status.HTTP_204_NO_CONTENT)
def update(self, plugin: Plugin, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: # type: ignore if not can_install_plugins_via_api(): raise ValidationError("Plugin upgrades via the web are disabled!") validated_data = self._get_validated_data_for_url( validated_data["url"]) response = super().update(plugin, validated_data) reload_plugins_on_workers() return response
def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> PluginConfig: if not can_configure_plugins_via_api(Team.objects.get(id=self.context["team_id"]).organization_id): raise ValidationError("Plugin configuration via the web is disabled!") request = self.context["request"] validated_data["team"] = Team.objects.get(id=self.context["team_id"]) self._fix_formdata_config_json(validated_data) plugin_config = super().create(validated_data) self._update_plugin_attachments(plugin_config) reload_plugins_on_workers() return plugin_config
def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: validated_data["url"] = self.initial_data.get("url", None) if not can_install_plugins_via_api(self.context["organization_id"]): raise ValidationError("Plugin installation via the web is disabled!") if validated_data.get("plugin_type", None) != Plugin.PluginType.SOURCE: self._update_validated_data_from_url(validated_data, validated_data["url"]) self._raise_if_plugin_installed(validated_data["url"]) validated_data["organization_id"] = self.context["organization_id"] plugin = super().create(validated_data) reload_plugins_on_workers() return plugin
def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: if not can_install_plugins_via_api(self.context["organization_id"]): raise ValidationError("Plugin installation via the web is disabled!") if validated_data.get("plugin_type", None) != Plugin.PluginType.SOURCE: self._update_validated_data_from_url(validated_data, validated_data["url"]) if len(Plugin.objects.filter(name=validated_data["name"])) > 0: raise ValidationError('Plugin with name "{}" already installed!'.format(validated_data["name"])) validated_data["organization_id"] = self.context["organization_id"] plugin = super().create(validated_data) reload_plugins_on_workers() return plugin
def update(self, plugin: Plugin, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: # type: ignore context_organization = self.context.get( "organization") or Organization.objects.get( id=self.context["organization_id"]) if ("is_global" in validated_data and context_organization.plugins_access_level < Organization.PluginsAccessLevel.ROOT): raise PermissionDenied( "This organization can't manage global plugins!") response = super().update(plugin, validated_data) reload_plugins_on_workers() return response
def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> PluginConfig: if not can_configure_plugins( Team.objects.get(id=self.context["team_id"]).organization_id): raise ValidationError( "Plugin configuration is not available for the current organization!" ) validated_data["team"] = Team.objects.get(id=self.context["team_id"]) self._fix_formdata_config_json(validated_data) plugin_config = super().create(validated_data) self._update_plugin_attachments(plugin_config) reload_plugins_on_workers() return plugin_config
def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: if not can_install_plugins_via_api(): raise ValidationError( "Plugin installation via the web is disabled!") validated_data = self._get_validated_data_for_url( validated_data["url"]) if len(Plugin.objects.filter(name=validated_data["name"])) > 0: raise ValidationError( 'Plugin with name "{}" already installed!'.format( validated_data["name"])) plugin = super().create(validated_data) reload_plugins_on_workers() return plugin
def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: validated_data["url"] = self.initial_data.get("url", None) if validated_data.get("is_global") and not can_globally_manage_plugins( self.context["organization_id"]): raise PermissionDenied( "This organization can't manage global plugins!") if validated_data.get("plugin_type", None) != Plugin.PluginType.SOURCE: self._update_validated_data_from_url(validated_data, validated_data["url"]) self._raise_if_plugin_installed(validated_data["url"], self.context["organization_id"]) validated_data["organization_id"] = self.context["organization_id"] plugin = super().create(validated_data) reload_plugins_on_workers() return plugin
def upgrade(self, request: request.Request, **kwargs): plugin: Plugin = self.get_object() organization = self.organization if plugin.organization != organization: raise NotFound() if not can_install_plugins(self.organization, plugin.organization_id): raise PermissionDenied( "Plugin upgrading is not available for the current organization!" ) serializer = PluginSerializer(plugin, context={"organization": organization}) validated_data = {} if plugin.plugin_type != Plugin.PluginType.SOURCE: validated_data = PluginSerializer._update_validated_data_from_url( {}, plugin.url) serializer.update(plugin, validated_data) reload_plugins_on_workers() return Response(serializer.data)
def update(self, plugin_config: PluginConfig, validated_data: Dict, *args: Any, **kwargs: Any) -> PluginConfig: # type: ignore self._fix_formdata_config_json(validated_data) validated_data.pop("plugin", None) # Keep old value for secret fields if no new value in the request secret_fields = _get_secret_fields_for_plugin(plugin_config.plugin) if "config" in validated_data: for key in secret_fields: if validated_data["config"].get( key) is None: # explicitly checking None to allow "" validated_data["config"][key] = plugin_config.config.get( key) response = super().update(plugin_config, validated_data) self._update_plugin_attachments(plugin_config) reload_plugins_on_workers() return response
def rearrange(self, request: request.Request, **kwargs): if not can_configure_plugins_via_api(self.team.organization_id): raise ValidationError( "Plugin configuration via the web is disabled!") orders = request.data.get("orders", {}) did_save = False plugin_configs = PluginConfig.objects.filter(team_id=self.team.pk, enabled=True) plugin_configs_dict = {p.plugin_id: p for p in plugin_configs} for plugin_id, order in orders.items(): plugin_config = plugin_configs_dict.get(int(plugin_id), None) if plugin_config and plugin_config.order != order: plugin_config.order = order plugin_config.save() did_save = True if did_save: reload_plugins_on_workers() return Response(PluginConfigSerializer(plugin_configs, many=True).data)
def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: if not can_install_plugins_via_api(): raise ValidationError("Plugin installation via the web is disabled!") local_plugin = validated_data.get("url", "").startswith("file:") if local_plugin: plugin_path = validated_data["url"][5:] json_path = os.path.join(plugin_path, "plugin.json") json = load_json_file(json_path) if not json: raise ValidationError("Could not load plugin.json from: {}".format(json_path)) validated_data["name"] = json.get("name", json_path.split("/")[-2]) validated_data["description"] = json.get("description", "") validated_data["config_schema"] = json.get("config", {}) else: parsed_url = parse_url(validated_data["url"], get_latest_if_none=True) if parsed_url: validated_data["url"] = parsed_url["root_url"] validated_data["tag"] = parsed_url.get("version", parsed_url.get("tag", None)) validated_data["archive"] = download_plugin_archive(validated_data["url"], validated_data["tag"]) plugin_json = get_json_from_archive(validated_data["archive"], "plugin.json") if not plugin_json: raise ValidationError("Could not find plugin.json in the plugin") validated_data["name"] = plugin_json["name"] validated_data["description"] = plugin_json.get("description", "") validated_data["config_schema"] = plugin_json.get("config", {}) else: raise ValidationError("Must be a GitHub repository or a NPM package URL!") if len(Plugin.objects.filter(name=validated_data["name"])) > 0: raise ValidationError('Plugin with name "{}" already installed!'.format(validated_data["name"])) validated_data["from_web"] = True plugin = super().create(validated_data) reload_plugins_on_workers() return plugin
def user(request): """ DEPRECATED: This endpoint (/api/user/) has been deprecated in favor of /api/v2/user/ and will be removed soon. """ organization: Optional[Organization] = request.user.organization organizations = list( request.user.organizations.order_by("-created_at").values( "name", "id")) team: Optional[Team] = request.user.team teams = list( request.user.teams.order_by("-created_at").values("name", "id")) user = cast(User, request.user) if request.method == "PATCH": data = json.loads(request.body) if team is not None and "team" in data: team.app_urls = data["team"].get("app_urls", team.app_urls) 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.session_recording_retention_period_days = data["team"].get( "session_recording_retention_period_days", team.session_recording_retention_period_days, ) 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( "completed_snippet_onboarding", team.completed_snippet_onboarding, ) team.test_account_filters = data["team"].get( "test_account_filters", team.test_account_filters) team.timezone = data["team"].get("timezone", team.timezone) team.save() if "user" in data: try: user.current_organization = user.organizations.get( id=data["user"]["current_organization_id"]) assert user.organization is not None, "Organization should have been just set" user.current_team = user.organization.teams.first() except (KeyError, ValueError): pass except ObjectDoesNotExist: return JsonResponse( {"detail": "Organization not found for user."}, status=404) except KeyError: pass except ObjectDoesNotExist: return JsonResponse( {"detail": "Organization not found for user."}, status=404) if user.organization is not None: try: user.current_team = user.organization.teams.get( id=int(data["user"]["current_team_id"])) except (KeyError, TypeError): pass except ValueError: return JsonResponse( {"detail": "Team ID must be an integer."}, status=400) except ObjectDoesNotExist: return JsonResponse( { "detail": "Team not found for user's current organization." }, status=404) user.email_opt_in = data["user"].get("email_opt_in", user.email_opt_in) user.anonymize_data = data["user"].get("anonymize_data", user.anonymize_data) user.toolbar_mode = data["user"].get("toolbar_mode", user.toolbar_mode) user.save() user_identify.identify_task.delay(user_id=user.id) return JsonResponse({ "deprecation": "Endpoint has been deprecated. Please use `/api/v2/user/`.", "id": user.pk, "distinct_id": user.distinct_id, "name": user.first_name, "email": user.email, "email_opt_in": user.email_opt_in, "anonymize_data": user.anonymize_data, "toolbar_mode": user.toolbar_mode, "organization": None if organization is None else { "id": organization.id, "name": organization.name, "billing_plan": organization.billing_plan, "available_features": organization.available_features, "plugins_access_level": organization.plugins_access_level, "created_at": organization.created_at, "updated_at": organization.updated_at, "teams": [{ "id": team.id, "name": team.name } for team in organization.teams.all().only("id", "name")], }, "organizations": organizations, "team": None if team is None else { "id": team.id, "name": team.name, "app_urls": team.app_urls, "api_token": team.api_token, "anonymize_ips": team.anonymize_ips, "slack_incoming_webhook": team.slack_incoming_webhook, "event_names": team.event_names, "event_names_with_usage": team.get_latest_event_names_with_usage(), "event_properties": team.event_properties, "event_properties_numerical": team.event_properties_numerical, "event_properties_with_usage": team.get_latest_event_properties_with_usage(), "completed_snippet_onboarding": team.completed_snippet_onboarding, "session_recording_opt_in": team.session_recording_opt_in, "session_recording_retention_period_days": team.session_recording_retention_period_days, "plugins_opt_in": team.plugins_opt_in, "ingested_event": team.ingested_event, "is_demo": team.is_demo, "test_account_filters": team.test_account_filters, "timezone": team.timezone, "data_attributes": team.data_attributes, }, "teams": teams, "has_password": user.has_usable_password(), "opt_out_capture": os.environ.get("OPT_OUT_CAPTURE"), "posthog_version": VERSION, "is_multi_tenancy": getattr(settings, "MULTI_TENANCY", False), "ee_available": settings.EE_AVAILABLE, "ee_enabled": is_ee_enabled(), "email_service_available": is_email_available(with_absolute_urls=True), "is_debug": getattr(settings, "DEBUG", False), "is_staff": user.is_staff, "is_impersonated": is_impersonated_session(request), "is_event_property_usage_enabled": getattr(settings, "ASYNC_EVENT_PROPERTY_USAGE", False), "is_async_event_action_mapping_enabled": getattr(settings, "ASYNC_EVENT_ACTION_MAPPING", False), })
def user(request): organization = request.user.organization organizations = list( request.user.organizations.order_by("-created_at").values( "name", "id")) team = request.user.team teams = list( request.user.teams.order_by("-created_at").values("name", "id")) user = cast(User, request.user) if request.method == "PATCH": data = json.loads(request.body) if "team" in data: team.app_urls = data["team"].get("app_urls", team.app_urls) team.opt_out_capture = data["team"].get("opt_out_capture", team.opt_out_capture) 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") 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( "completed_snippet_onboarding", team.completed_snippet_onboarding, ) team.save() if "user" in data: try: user.current_organization = user.organizations.get( id=data["user"]["current_organization_id"]) user.current_team = user.organization.teams.first() except KeyError: pass except ObjectDoesNotExist: return JsonResponse( {"detail": "Organization not found for user."}, status=404) except KeyError: pass except ObjectDoesNotExist: return JsonResponse( {"detail": "Organization not found for user."}, status=404) try: user.current_team = user.organization.teams.get( id=int(data["user"]["current_team_id"])) except (KeyError, TypeError): pass except ValueError: return JsonResponse({"detail": "Team ID must be an integer."}, status=400) except ObjectDoesNotExist: return JsonResponse( { "detail": "Team not found for user's current organization." }, status=404) user.email_opt_in = data["user"].get("email_opt_in", user.email_opt_in) user.anonymize_data = data["user"].get("anonymize_data", user.anonymize_data) user.toolbar_mode = data["user"].get("toolbar_mode", user.toolbar_mode) posthoganalytics.identify( user.distinct_id, { "email_opt_in": user.email_opt_in, "anonymize_data": user.anonymize_data, "email": user.email if not user.anonymize_data else None, "is_signed_up": True, "toolbar_mode": user.toolbar_mode, "billing_plan": user.organization.billing_plan, "is_team_unique_user": (team.users.count() == 1), "team_setup_complete": (team.completed_snippet_onboarding and team.ingested_event), }, ) user.save() return JsonResponse({ "id": user.pk, "distinct_id": user.distinct_id, "name": user.first_name, "email": user.email, "email_opt_in": user.email_opt_in, "anonymize_data": user.anonymize_data, "toolbar_mode": user.toolbar_mode, "organization": { "id": organization.id, "name": organization.name, "billing_plan": organization.billing_plan, "available_features": organization.available_features, "created_at": organization.created_at, "updated_at": organization.updated_at, "teams": [{ "id": team.id, "name": team.name } for team in organization.teams.all().only("id", "name")], }, "organizations": organizations, "team": team and { "id": team.id, "name": team.name, "app_urls": team.app_urls, "api_token": team.api_token, "opt_out_capture": team.opt_out_capture, "anonymize_ips": team.anonymize_ips, "slack_incoming_webhook": team.slack_incoming_webhook, "event_names": team.event_names, "event_properties": team.event_properties, "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, "has_password": user.has_usable_password(), "opt_out_capture": os.environ.get("OPT_OUT_CAPTURE"), "posthog_version": VERSION, "is_multi_tenancy": getattr(settings, "MULTI_TENANCY", False), "ee_available": user.ee_available, "plugin_access": { "install": can_install_plugins_via_api(), "configure": can_configure_plugins_via_api() }, })
def update(self, plugin_config: PluginConfig, validated_data: Dict, *args: Any, **kwargs: Any) -> PluginConfig: # type: ignore validated_data.pop("plugin", None) response = super().update(plugin_config, validated_data) reload_plugins_on_workers() return response
def update(self, plugin: Plugin, validated_data: Dict, *args: Any, **kwargs: Any) -> Plugin: # type: ignore validated_data["archive"] = download_plugin_archive(plugin.url, plugin.tag) response = super().update(plugin, validated_data) reload_plugins_on_workers() return response
def user(request): organization: Optional[Organization] = request.user.organization organizations = list( request.user.organizations.order_by("-created_at").values( "name", "id")) team: Optional[Team] = request.user.team teams = list( request.user.teams.order_by("-created_at").values("name", "id")) user = cast(User, request.user) if request.method == "PATCH": data = json.loads(request.body) if team is not None and "team" in data: team.app_urls = data["team"].get("app_urls", team.app_urls) team.opt_out_capture = data["team"].get("opt_out_capture", team.opt_out_capture) 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.session_recording_retention_period_days = data["team"].get( "session_recording_retention_period_days", team.session_recording_retention_period_days) 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( "completed_snippet_onboarding", team.completed_snippet_onboarding, ) team.save() if "user" in data: try: user.current_organization = user.organizations.get( id=data["user"]["current_organization_id"]) assert user.organization is not None, "Organization should have been just set" user.current_team = user.organization.teams.first() except (KeyError, ValueError): pass except ObjectDoesNotExist: return JsonResponse( {"detail": "Organization not found for user."}, status=404) except KeyError: pass except ObjectDoesNotExist: return JsonResponse( {"detail": "Organization not found for user."}, status=404) if user.organization is not None: try: user.current_team = user.organization.teams.get( id=int(data["user"]["current_team_id"])) except (KeyError, TypeError): pass except ValueError: return JsonResponse( {"detail": "Team ID must be an integer."}, status=400) except ObjectDoesNotExist: return JsonResponse( { "detail": "Team not found for user's current organization." }, status=404) user.email_opt_in = data["user"].get("email_opt_in", user.email_opt_in) user.anonymize_data = data["user"].get("anonymize_data", user.anonymize_data) user.toolbar_mode = data["user"].get("toolbar_mode", user.toolbar_mode) user.save() user_identify.identify_task.delay(user_id=user.id) return JsonResponse({ "id": user.pk, "distinct_id": user.distinct_id, "name": user.first_name, "email": user.email, "email_opt_in": user.email_opt_in, "anonymize_data": user.anonymize_data, "toolbar_mode": user.toolbar_mode, "organization": None if organization is None else { "id": organization.id, "name": organization.name, "billing_plan": organization.billing_plan, "available_features": organization.available_features, "created_at": organization.created_at, "updated_at": organization.updated_at, "teams": [{ "id": team.id, "name": team.name } for team in organization.teams.all().only("id", "name")], }, "organizations": organizations, "team": None if team is None else { "id": team.id, "name": team.name, "app_urls": team.app_urls, "api_token": team.api_token, "opt_out_capture": team.opt_out_capture, "anonymize_ips": team.anonymize_ips, "slack_incoming_webhook": team.slack_incoming_webhook, "event_names": team.event_names, "event_names_with_usage": team.event_names_with_usage or [{ "event": event, "volume": None, "usage_count": None } for event in team.event_names], "event_properties": team.event_properties, "event_properties_numerical": team.event_properties_numerical, "event_properties_with_usage": team.event_properties_with_usage or [{ "key": key, "volume": None, "usage_count": None } for key in team.event_properties], "completed_snippet_onboarding": team.completed_snippet_onboarding, "session_recording_opt_in": team.session_recording_opt_in, "session_recording_retention_period_days": team.session_recording_retention_period_days, "plugins_opt_in": team.plugins_opt_in, "ingested_event": team.ingested_event, }, "teams": teams, "has_password": user.has_usable_password(), "opt_out_capture": os.environ.get("OPT_OUT_CAPTURE"), "posthog_version": VERSION, "is_multi_tenancy": getattr(settings, "MULTI_TENANCY", False), "ee_available": settings.EE_AVAILABLE, "ee_enabled": is_ee_enabled(), "email_service_available": is_email_available(with_absolute_urls=True), "is_debug": getattr(settings, "DEBUG", False), "is_staff": user.is_staff, "is_impersonated": is_impersonated_session(request), "plugin_access": { "install": can_install_plugins_via_api(user.organization), "configure": can_configure_plugins_via_api(user.organization), }, })
def destroy(self, request: request.Request, *args, **kwargs) -> Response: response = super().destroy(request, *args, **kwargs) reload_plugins_on_workers() return response