├── .dockerignore
├── .editorconfig
├── .githooks
└── pre-commit
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ └── release-template.md
└── workflows
│ ├── build-image.yml
│ ├── build-rolling-image.yml
│ ├── docs.yml
│ ├── go.yml
│ ├── linter.yml
│ ├── load-test.yml
│ └── release.yml
├── .gitignore
├── .publisher.yml
├── .vscode
└── settings.json
├── CHANGELOG.md
├── CLA.md
├── CONTRIBUTING.md
├── Dockerfile.dev
├── ENT-CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── RELEASE.md
├── VERSION
├── api
├── api.go
├── dashboard_integration_test.go
├── filter_integration_test.go
├── handlers
│ ├── auth.go
│ ├── configuration.go
│ ├── dashboard.go
│ ├── delivery_attempt.go
│ ├── endpoint.go
│ ├── event.go
│ ├── event_delivery.go
│ ├── event_types.go
│ ├── filter.go
│ ├── handlers.go
│ ├── license.go
│ ├── main.go
│ ├── meta_event.go
│ ├── middleware.go
│ ├── organisation.go
│ ├── organisation_invite.go
│ ├── organisation_member.go
│ ├── portal_link.go
│ ├── project.go
│ ├── security.go
│ ├── source.go
│ ├── subscription.go
│ └── user.go
├── ingest.go
├── ingest_integration_test.go
├── ingest_test.go
├── migrations.go
├── migrations
│ ├── v20240101
│ │ ├── common.go
│ │ ├── create_endpoint_migration.go
│ │ ├── create_endpoint_migration_test.go
│ │ ├── get_endpoint_migration.go
│ │ ├── get_endpoints_migration.go
│ │ └── update_endpoint_migration.go
│ └── v20240401
│ │ ├── common.go
│ │ ├── create_endpoint_migration.go
│ │ ├── create_endpoint_migration_test.go
│ │ ├── get_endpoint_migration.go
│ │ ├── get_endpoints_migration.go
│ │ └── update_endpoint_migration.go
├── models
│ ├── configuration.go
│ ├── endpoint.go
│ ├── event.go
│ ├── event_delivery.go
│ ├── event_types.go
│ ├── filter.go
│ ├── meta_event.go
│ ├── models.go
│ ├── portal_link.go
│ ├── project.go
│ ├── source.go
│ ├── source_test.go
│ ├── sso.go
│ ├── subscription.go
│ └── user.go
├── policies
│ ├── organisation_policy.go
│ ├── organisation_policy_test.go
│ ├── policy.go
│ ├── policy_test.go
│ ├── project_policy.go
│ └── project_policy_test.go
├── portal_api_integration_test.go
├── public_integration_test.go
├── server_suite_test.go
├── testdata
│ ├── Auth_Config
│ │ ├── basic-convoy.json
│ │ ├── full-convoy-with-all-realms.json
│ │ ├── full-convoy-with-jwt-realm.json
│ │ ├── full-convoy-with-native-auth-realm.json
│ │ ├── full-convoy.json
│ │ ├── jwt-convoy-signup-disabled.json
│ │ ├── jwt-convoy-signup-enabled.json
│ │ ├── jwt-convoy.json
│ │ ├── no-auth-convoy.json
│ │ └── none-convoy.json
│ ├── TestApplicationHandler_BatchRetryEventDelivery
│ │ ├── should_batch_retry_all_successfully.golden
│ │ ├── should_batch_retry_one_successfully.golden
│ │ ├── should_fail_to_find_app_endpoint.golden
│ │ ├── should_fail_to_load_event_deliveries.golden
│ │ ├── should_fail_to_update_app_endpoints_status.golden
│ │ ├── should_fail_to_update_status_of_event_delivery.golden
│ │ └── should_fail_to_write_to_queue.golden
│ ├── TestApplicationHandler_CountAffectedEventDeliveries
│ │ ├── should_count_affected_event_deliveries_successfully.golden
│ │ └── should_fail_to_count_affected_event_deliveries.golden
│ ├── TestApplicationHandler_CreateAPIKey
│ │ ├── create_api_key.golden
│ │ ├── create_api_key_without_expires_at_field.golden
│ │ ├── invalid_expiry_date.golden
│ │ └── invalid_role.golden
│ ├── TestApplicationHandler_CreateApp
│ │ ├── invalid_request.golden
│ │ ├── invalid_request_-_no_app_name.golden
│ │ ├── should_fail_for_application_name_not_unique.golden
│ │ ├── should_fail_to_check_if_application_name_is_unique.golden
│ │ └── valid_application.golden
│ ├── TestApplicationHandler_CreateAppEndpoint
│ │ ├── should_error_for_invalid_rate_limit_duration.golden
│ │ └── valid_endpoint.golden
│ ├── TestApplicationHandler_CreateAppPortalAPIKey
│ │ ├── app_id_does_not_belong_to_group.golden
│ │ └── create_api_key.golden
│ ├── TestApplicationHandler_CreateGroup
│ │ ├── invalid_request_-_no_group_hash_field.golden
│ │ ├── invalid_request_-_no_group_header_field.golden
│ │ ├── invalid_request_-_no_group_interval_seconds_field.golden
│ │ ├── invalid_request_-_no_group_name.golden
│ │ ├── invalid_request_-_no_group_retry_limit_field.golden
│ │ ├── invalid_request_-_no_group_strategy_type_field.golden
│ │ ├── invalid_request_-_unsupported_group_hash_field.golden
│ │ ├── invalid_request_-_unsupported_group_strategy_type.golden
│ │ ├── should_fail_to_create_group.golden
│ │ └── valid_group.golden
│ ├── TestApplicationHandler_DeleteGroup
│ │ ├── failed_group_apps_delete.golden
│ │ ├── failed_group_delete.golden
│ │ ├── failed_group_events_delete.golden
│ │ └── valid_group_delete.golden
│ ├── TestApplicationHandler_ForceResendEventDelivery
│ │ ├── should_error_for_discarded_event_status.golden
│ │ ├── should_error_for_endpoint_not_found.golden
│ │ ├── should_error_for_failed_to_fetch_deliveries.golden
│ │ ├── should_error_for_failed_to_update_event_delivery_status.golden
│ │ ├── should_error_for_inactive_endpoint.golden
│ │ ├── should_error_for_malformed_body.golden
│ │ └── should_force_resend_all_successfully.golden
│ ├── TestApplicationHandler_GetAPIKeyByID
│ │ ├── should_fail_to_find_api_key.golden
│ │ └── should_find_api_key.golden
│ ├── TestApplicationHandler_GetAPIKeys
│ │ ├── should_fail_to_load_api_keys.golden
│ │ └── should_load_api_keys.golden
│ ├── TestApplicationHandler_GetApp
│ │ ├── app_not_found.golden
│ │ └── valid_application.golden
│ ├── TestApplicationHandler_GetAppEndpoint
│ │ └── should_get_application_endpoint.golden
│ ├── TestApplicationHandler_GetAppEndpoints
│ │ └── should_get_application_endpoints.golden
│ ├── TestApplicationHandler_GetApps
│ │ ├── should_fail_to_fetch_applications.golden
│ │ └── valid_applications.golden
│ ├── TestApplicationHandler_GetGroup
│ │ ├── group_not_found.golden
│ │ ├── should_fail_to_count_group_apps.golden
│ │ ├── should_fail_to_count_group_messages.golden
│ │ └── valid_group.golden
│ ├── TestApplicationHandler_GetGroups
│ │ ├── should_error_for_invalid_group_access.golden
│ │ ├── should_fail_to_fetch_groups.golden
│ │ ├── should_fetch_user_groups.golden
│ │ └── valid_groups.golden
│ ├── TestApplicationHandler_RevokeAPIKey
│ │ ├── revoke_api_key.golden
│ │ └── should_error_for_revoke_api_key.golden
│ ├── TestApplicationHandler_UpdateAPIKey
│ │ ├── invalid_role.golden
│ │ └── update_api_key.golden
│ ├── TestApplicationHandler_UpdateApp
│ │ ├── invalid_request.golden
│ │ ├── invalid_request_-_no_app_name.golden
│ │ ├── should_error_for_duplicate_app_name.golden
│ │ ├── valid_application_update.golden
│ │ ├── valid_request_-_disable_application.golden
│ │ ├── valid_request_-_enable_disabled_application.golden
│ │ ├── valid_request_-_update_secret.golden
│ │ └── valid_request_-_update_support_email.golden
│ ├── TestApplicationHandler_UpdateAppEndpoint
│ │ ├── invalid_request.golden
│ │ ├── should_error_for_endpoint_not_found.golden
│ │ ├── should_error_for_invalid_rate_limit_duration.golden
│ │ └── valid_application.golden
│ ├── TestApplicationHandler_UpdateAppEndpoint_InvalidRequest
│ │ ├── invalid_request.golden
│ │ └── valid_application.golden
│ ├── TestApplicationHandler_UpdateGroup
│ │ ├── invalid_request.golden
│ │ ├── invalid_request_-_no_group_name.golden
│ │ ├── should_fail_to_update_group.golden
│ │ └── valid_group_update.golden
│ ├── TestDashboardIntegrationTestSuiteTest
│ │ └── TestGetDashboardSummary
│ │ │ ├── should_error_for_empty_startDate.golden
│ │ │ ├── should_error_for_invalid_startDate.golden
│ │ │ ├── should_error_for_invalid_type.golden
│ │ │ ├── should_error_for_startDate_greater_than_endDate.golden
│ │ │ ├── should_fetch_daily_dashboard_summary.golden
│ │ │ ├── should_fetch_monthly_dashboard_summary.golden
│ │ │ ├── should_fetch_weekly_dashboard_summary.golden
│ │ │ └── should_fetch_yearly_dashboard_summary.golden
│ ├── TestRequirePermission_Basic
│ │ ├── authorization_failed.golden
│ │ ├── credentials_not_provided.golden
│ │ ├── invalid_basic_credentials.golden
│ │ ├── invalid_credentials.golden
│ │ └── valid_credentials.golden
│ ├── TestRequirePermission_Noop
│ │ ├── authorization_failed.golden
│ │ ├── credentials_not_provided.golden
│ │ ├── invalid_credentials.golden
│ │ └── valid_credentials.golden
│ ├── Test_applicationHandler_DeleteApp
│ │ ├── should_delete_app.golden
│ │ └── should_fail_to_delete_app.golden
│ ├── Test_applicationHandler_DeleteAppEndpoint
│ │ ├── should_delete_app_endpoint.golden
│ │ ├── should_error_for_endpoint_not_found.golden
│ │ └── should_fail_to_delete_app_endpoint.golden
│ ├── Test_applicationHandler_GetPaginatedApps
│ │ └── valid_groups.golden
│ ├── Test_fetchAuthConfig
│ │ └── successful_auth_fetch.golden
│ ├── Test_resendEventDelivery
│ │ ├── invalid__resend_-_pending_endpoint.golden
│ │ ├── invalid_resend_-_event_not_failed.golden
│ │ ├── invalid_resend_-_event_successful.golden
│ │ ├── valid_resend_-_previously_failed_-_active_endpoint.golden
│ │ └── valid_resend_-_previously_failed_and_inactive_endpoint.golden
│ └── Test_resendMessage
│ │ ├── invalid__resend_-_pending_endpoint.golden
│ │ ├── invalid_resend_-_event_not_failed.golden
│ │ ├── invalid_resend_-_event_successful.golden
│ │ ├── valid_resend_-_previously_failed_-_active_endpoint.golden
│ │ └── valid_resend_-_previously_failed_and_inactive_endpoint.golden
├── testdb
│ ├── filter.go
│ └── seed.go
├── types
│ └── types.go
└── ui
│ └── build
│ └── go_test_stub.txt
├── auth
├── credential.go
├── realm.go
├── realm
│ ├── file
│ │ ├── file_realm.go
│ │ └── file_realm_test.go
│ ├── jwt
│ │ ├── jwt.go
│ │ ├── jwt_realm.go
│ │ ├── jwt_realm_test.go
│ │ └── jwt_test.go
│ ├── native
│ │ ├── native_realm.go
│ │ └── native_realm_test.go
│ ├── noop
│ │ └── noop_realm.go
│ └── portal
│ │ └── portal_realm.go
├── realm_chain
│ ├── realm_chain.go
│ └── realm_chain_test.go
└── role.go
├── cache
├── cache.go
├── memory
│ ├── client.go
│ └── client_test.go
├── noop
│ └── noop.go
└── redis
│ ├── client.go
│ └── client_test.go
├── cmd
├── agent
│ └── agent.go
├── bootstrap
│ └── bootstrap.go
├── config
│ └── config.go
├── ff
│ └── feature_flags.go
├── hooks
│ └── hooks.go
├── ingest
│ └── ingest.go
├── main.go
├── migrate
│ └── migrate.go
├── openapi
│ └── openapi.go
├── retry
│ └── retry.go
├── server
│ └── server.go
├── stream
│ └── stream.go
├── utils
│ ├── init_encryption.go
│ ├── partition.go
│ ├── revert_encryption.go
│ ├── rotate_key.go
│ └── utils.go
├── version
│ └── version.go
└── worker
│ └── worker.go
├── config
├── algo
│ └── algo.go
├── ca_cert.go
├── ca_cert_test.go
├── config.go
├── config_test.go
├── realm.go
└── testdata
│ ├── Config
│ ├── empty-api-key-auth-group-name.json
│ ├── empty-api-key.json
│ ├── empty-redis-dsn.json
│ ├── empty-ssl-cert-file.json
│ ├── empty-ssl-key-file.json
│ ├── no-port-convoy.json
│ ├── too-large-max-response-size-convoy.json
│ ├── unknown-strategy-type.json
│ ├── valid-convoy-redis-cluster.json
│ ├── valid-convoy.json
│ └── zero-max-response-size-convoy.json
│ └── Test_ConfigurationFromEnvironment
│ └── convoy.json
├── configs
├── caddyfile
├── convoy.templ.json
├── docker-compose.templ.yml
└── local
│ ├── conf
│ ├── .env
│ └── userlists.txt
│ ├── convoy.json
│ ├── docker-compose.yml
│ └── start.sh
├── const.go
├── convoy-logo.svg
├── convoy.env.example
├── convoy.go
├── convoy.json.example
├── database
├── database.go
├── hooks
│ └── hooks.go
├── listener
│ ├── endpoint_listener.go
│ ├── event_delivery_listener.go
│ └── project_listener.go
├── postgres
│ ├── api_key.go
│ ├── api_key_test.go
│ ├── batch_retry.go
│ ├── configuration.go
│ ├── configuration_test.go
│ ├── delivery_attempts.go
│ ├── delivery_attempts_test.go
│ ├── device.go
│ ├── device_test.go
│ ├── endpoint.go
│ ├── endpoint_test.go
│ ├── event.go
│ ├── event_delivery.go
│ ├── event_delivery_test.go
│ ├── event_test.go
│ ├── event_types.go
│ ├── export.go
│ ├── filter.go
│ ├── job.go
│ ├── job_test.go
│ ├── meta_event.go
│ ├── meta_event_test.go
│ ├── organisation.go
│ ├── organisation_invite.go
│ ├── organisation_invite_test.go
│ ├── organisation_member.go
│ ├── organisation_member_test.go
│ ├── organisation_test.go
│ ├── portal_link.go
│ ├── portal_link_test.go
│ ├── postgres.go
│ ├── postgres_collector.go
│ ├── postgres_test.go
│ ├── project.go
│ ├── project_test.go
│ ├── source.go
│ ├── source_test.go
│ ├── subscription.go
│ ├── subscription_test.go
│ ├── users.go
│ └── users_test.go
└── sqlite3
│ └── sqlite3.go
├── datastore
├── batch_retry.go
├── filter.go
├── filter_test.go
├── models.go
├── models_test.go
├── repository.go
└── testdata
│ ├── applications.yml
│ └── organisations.yml
├── deploy
├── vm-deploy.sh
└── vm-upgrade.sh
├── docker-compose.dep.yml
├── docker-compose.dev.yml
├── docs
├── docs.go
├── swagger.json
├── swagger.yaml
└── v3
│ ├── openapi3.json
│ └── openapi3.yaml
├── ee
└── VERSION
├── generate.go
├── go.mod
├── go.sum
├── immune-test-files
├── test-app-endpoints.json
├── test-apps.json
├── test-groups.json
└── test-push-events.json
├── internal
├── email
│ ├── email.go
│ ├── email_test.go
│ └── templates
│ │ ├── endpoint.update.html
│ │ ├── organisation.invite.html
│ │ ├── reset.password.html
│ │ ├── twitter.source.html
│ │ └── user.verify.email.html
├── notifications
│ └── notifications.go
├── pkg
│ ├── cli
│ │ └── cli.go
│ ├── crc
│ │ ├── crc.go
│ │ └── crc_test.go
│ ├── dedup
│ │ └── dedup.go
│ ├── exporter
│ │ └── exporter.go
│ ├── fflag
│ │ ├── fflag.go
│ │ └── fflag_test.go
│ ├── keys
│ │ ├── encrypter_init.go
│ │ ├── encrypter_revert.go
│ │ ├── encrypter_rotate.go
│ │ ├── hcpvault.go
│ │ ├── hcpvault_test.go
│ │ ├── keys.go
│ │ ├── local.go
│ │ └── vault.go
│ ├── license
│ │ ├── keygen
│ │ │ ├── community.go
│ │ │ ├── community_test.go
│ │ │ ├── feature.go
│ │ │ ├── keygen.go
│ │ │ └── keygen_test.go
│ │ ├── license.go
│ │ └── noop
│ │ │ └── noop.go
│ ├── limiter
│ │ ├── limiter.go
│ │ ├── pg
│ │ │ ├── client.go
│ │ │ └── client_test.go
│ │ └── redis
│ │ │ ├── client.go
│ │ │ └── client_test.go
│ ├── loader
│ │ ├── loader.go
│ │ └── loader_test.go
│ ├── memorystore
│ │ ├── memorystore.go
│ │ ├── row.go
│ │ ├── table.go
│ │ └── table_test.go
│ ├── metrics
│ │ ├── data_plane.go
│ │ └── metrics.go
│ ├── middleware
│ │ └── middleware.go
│ ├── migrator
│ │ └── migrator.go
│ ├── object-store
│ │ ├── objectstore.go
│ │ ├── onprem.go
│ │ └── s3.go
│ ├── openapi
│ │ ├── converter.go
│ │ ├── converter_test.go
│ │ ├── testdata
│ │ │ ├── test-2.0.yml
│ │ │ ├── test-3.0.yml
│ │ │ └── test-3.1.yml
│ │ ├── validator.go
│ │ └── validator_test.go
│ ├── pubsub
│ │ ├── amqp
│ │ │ └── client.go
│ │ ├── google
│ │ │ └── client.go
│ │ ├── ingest.go
│ │ ├── kafka
│ │ │ └── client.go
│ │ ├── pubsub.go
│ │ ├── pubsub_test.go
│ │ ├── source_loader.go
│ │ ├── source_loader_test.go
│ │ ├── sqs
│ │ │ └── client.go
│ │ └── verifier.go
│ ├── rdb
│ │ └── rdb.go
│ ├── retention
│ │ └── retention.go
│ ├── server
│ │ └── server.go
│ ├── smtp
│ │ ├── smtp.go
│ │ └── smtp_test.go
│ ├── socket
│ │ ├── client.go
│ │ ├── client_test.go
│ │ ├── handlers.go
│ │ ├── handlers_test.go
│ │ ├── server.go
│ │ └── socket.go
│ └── tracer
│ │ ├── datadog.go
│ │ ├── noop.go
│ │ ├── otel.go
│ │ ├── sentry.go
│ │ └── tracer.go
└── telemetry
│ ├── mixpanel.go
│ ├── posthog.go
│ ├── telemetry.go
│ └── tracker.go
├── loadtest
├── convoy.js
└── setup.sh
├── mocks
├── cache.go
├── database.go
├── dedup.go
├── license.go
├── limiter.go
├── pubsub.go
├── queue.go
├── repository.go
├── searcher.go
├── smtp.go
├── socket.go
├── table.go
└── tracer.go
├── net
├── dispatcher.go
└── dispatcher_test.go
├── pkg
├── circuit_breaker
│ ├── circuit_breaker.go
│ ├── circuit_breaker_collector.go
│ ├── circuit_breaker_manager.go
│ ├── circuit_breaker_manager_test.go
│ ├── circuit_breaker_test.go
│ ├── config.go
│ ├── config_test.go
│ ├── store.go
│ └── store_test.go
├── clock
│ ├── clock.go
│ └── clock_test.go
├── compare
│ ├── compare.go
│ └── compare_test.go
├── flatten
│ ├── flat.go
│ ├── flat_test.go
│ ├── gh_event.json
│ ├── gh_event_flat.json
│ └── types.go
├── httpheader
│ ├── httpheader.go
│ └── httpheader_test.go
├── log
│ ├── log.go
│ └── std.go
├── models
│ └── webhook.go
├── msgpack
│ └── msgpack.go
├── signature
│ ├── signature.go
│ └── signature_test.go
├── transform
│ ├── printer.go
│ ├── transform.go
│ └── transform_test.go
├── url
│ ├── url_query.go
│ └── url_query_test.go
└── verifier
│ ├── verifier.go
│ └── verifier_test.go
├── plugin.go
├── plugins
├── add_headers_plugin.go
└── add_headers_plugin_test.go
├── prometheus
└── prometheus.yml
├── queue
├── queue.go
├── redis
│ ├── client.go
│ ├── client_test.go
│ └── queue_collector.go
└── testdata
│ └── convoy_redis.json
├── release.Dockerfile
├── retrystrategies
├── default.go
├── default_test.go
├── exponentialBackoff.go
├── exponentialBackoff_test.go
├── retry.go
└── retry_test.go
├── scripts
├── build.sh
├── integration-test.sh
├── release.sh
├── replica-set.sh
└── ui.sh
├── services
├── activate_endpoint.go
├── batch_replay_event.go
├── batch_replay_event_test.go
├── batch_retry_event_delivery.go
├── batch_retry_event_delivery_test.go
├── cancel_invite.go
├── cancel_invite_test.go
├── create_api_key.go
├── create_api_key_test.go
├── create_broadcast_event.go
├── create_broadcast_event_test.go
├── create_configuration.go
├── create_configuration_test.go
├── create_dynamic_event.go
├── create_dynamic_event_test.go
├── create_endpoint.go
├── create_endpoint_api_key.go
├── create_endpoint_api_key_test.go
├── create_endpoint_test.go
├── create_fanout_event.go
├── create_fanout_event_test.go
├── create_meta_event.go
├── create_meta_event_test.go
├── create_organisation.go
├── create_organisation_test.go
├── create_personal_api_key.go
├── create_portal_link.go
├── create_portal_link_endpoint.go
├── create_portal_link_test.go
├── create_source.go
├── create_source_test.go
├── create_subscription.go
├── create_subscription_test.go
├── device_service.go
├── device_service_test.go
├── errors.go
├── expire_secret.go
├── expire_secret_test.go
├── find_user_by_invite_token.go
├── find_user_by_invite_token_test.go
├── force_resend_event_delivery.go
├── force_resend_event_delivery_test.go
├── generate_password_reset_token.go
├── generate_password_reset_token_test.go
├── import_openapi_spec.go
├── import_openapi_spec_test.go
├── invite_user.go
├── invite_user_test.go
├── login_sso.go
├── login_user.go
├── login_user_test.go
├── logout_user.go
├── logout_user_test.go
├── meta_event_service.go
├── meta_event_service_test.go
├── organisation_member_service.go
├── organisation_member_service_test.go
├── pause_endpoint.go
├── pause_endpoint_test.go
├── process_invite.go
├── process_invite_test.go
├── project_service.go
├── project_service_test.go
├── refresh_user_login_token.go
├── refresh_user_login_token_test.go
├── regenerate_project_api_key.go
├── regenerate_project_api_key_test.go
├── register_user.go
├── register_user_test.go
├── replay_event.go
├── replay_event_test.go
├── resend_invite.go
├── resend_invite_test.go
├── resend_user_email_verification.go
├── resend_user_email_verification_test.go
├── reset_password.go
├── reset_password_test.go
├── retry_event_delivery.go
├── retry_event_delivery_test.go
├── revoke_api_key.go
├── revoke_api_key_test.go
├── testdata
│ ├── basic-config.json
│ └── basic-convoy.json
├── update_api_key.go
├── update_api_key_test.go
├── update_configuration.go
├── update_configuration_test.go
├── update_endpoint.go
├── update_endpoint_test.go
├── update_organisation.go
├── update_organisation_test.go
├── update_portal_link.go
├── update_portal_link_test.go
├── update_source.go
├── update_source_test.go
├── update_subscription.go
├── update_subscription_test.go
├── update_user.go
├── update_user_password.go
├── update_user_password_test.go
├── update_user_test.go
├── verify_user_email.go
└── verify_user_email_test.go
├── slim.Dockerfile
├── sql
├── 1677078479.sql
├── 1677770163.sql
├── 1679836136.sql
├── 1681821969.sql
├── 1684504109.sql
├── 1684884904.sql
├── 1684918027.sql
├── 1684929840.sql
├── 1685202737.sql
├── 1686048402.sql
├── 1686656160.sql
├── 1692024707.sql
├── 1692105853.sql
├── 1692699318.sql
├── 1693908172.sql
├── 1698074481.sql
├── 1698683940.sql
├── 1704372039.sql
├── 1705562731.sql
├── 1705575999.sql
├── 1708434555.sql
├── 1709568783.sql
├── 1709729972.sql
├── 1710685343.sql
├── 1710763531.sql
├── 1715118159.sql
├── 1715849591.sql
├── 1716983708.sql
├── 1717504961.sql
├── 1718819653.sql
├── 1719521272.sql
├── 1719521273.sql
├── 1721127636.sql
├── 1724236900.sql
├── 1724932863.sql
├── 1729585620.sql
├── 1729709223.sql
├── 1729941918.sql
├── 1730027360.sql
├── 1730719365.sql
├── 1733488138.sql
├── 1733518672.sql
├── 1735652782.sql
├── 1736807673.sql
├── 1739961493.sql
├── 1740483415.sql
├── 1742902597.sql
├── 1745514784.sql
└── 1746876407.sql
├── test
└── docker-compose.yml
├── testcon
├── direct_event_test.go
├── docker_e2e_integration_test.go
├── docker_e2e_integration_test_helper.go
├── fanout_event_test.go
├── manifest
│ ├── counter.go
│ ├── endpoint.go
│ └── event.go
└── testdata
│ ├── convoy-docker.json
│ ├── convoy-host.json
│ └── docker-compose-test.yml
├── testdata
└── test-2.0.yml
├── type.go
├── util
├── crypto.go
├── crypto_test.go
├── db.go
├── endpoint.go
├── endpoint_test.go
├── header.go
├── json.go
├── name.go
├── response.go
├── slice.go
├── strings.go
├── strings_test.go
└── validator.go
├── v3gen
└── main.go
├── web
└── ui
│ └── dashboard
│ ├── .browserslistrc
│ ├── .editorconfig
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .storybook
│ ├── main.js
│ ├── preview.js
│ ├── tsconfig.json
│ └── typings.d.ts
│ ├── README.md
│ ├── angular.json
│ ├── documentation.json
│ ├── karma.conf.js
│ ├── package.json
│ ├── post-build-css.js
│ ├── purgecss.config.js
│ ├── src
│ ├── app
│ │ ├── app-routing.module.ts
│ │ ├── app.component.html
│ │ ├── app.component.scss
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── components
│ │ │ ├── badge
│ │ │ │ ├── badge.component.html
│ │ │ │ ├── badge.component.scss
│ │ │ │ ├── badge.component.spec.ts
│ │ │ │ └── badge.component.ts
│ │ │ ├── button
│ │ │ │ ├── button.component.html
│ │ │ │ ├── button.component.scss
│ │ │ │ ├── button.component.spec.ts
│ │ │ │ └── button.component.ts
│ │ │ ├── card
│ │ │ │ ├── card.component.spec.ts
│ │ │ │ └── card.component.ts
│ │ │ ├── chart
│ │ │ │ ├── chart.component.html
│ │ │ │ ├── chart.component.scss
│ │ │ │ ├── chart.component.spec.ts
│ │ │ │ └── chart.component.ts
│ │ │ ├── components.module.ts
│ │ │ ├── copy-button
│ │ │ │ ├── copy-button.component.html
│ │ │ │ ├── copy-button.component.scss
│ │ │ │ ├── copy-button.component.spec.ts
│ │ │ │ └── copy-button.component.ts
│ │ │ ├── date-picker
│ │ │ │ ├── date-picker.component.html
│ │ │ │ ├── date-picker.component.scss
│ │ │ │ ├── date-picker.component.spec.ts
│ │ │ │ └── date-picker.component.ts
│ │ │ ├── dialog
│ │ │ │ ├── dialog.directive.spec.ts
│ │ │ │ └── dialog.directive.ts
│ │ │ ├── dropdown-container
│ │ │ │ ├── dropdown-container.component.html
│ │ │ │ ├── dropdown-container.component.scss
│ │ │ │ ├── dropdown-container.component.spec.ts
│ │ │ │ └── dropdown-container.component.ts
│ │ │ ├── dropdown
│ │ │ │ ├── dropdown.component.html
│ │ │ │ ├── dropdown.component.scss
│ │ │ │ ├── dropdown.component.spec.ts
│ │ │ │ └── dropdown.component.ts
│ │ │ ├── empty-state
│ │ │ │ ├── empty-state.component.html
│ │ │ │ ├── empty-state.component.scss
│ │ │ │ ├── empty-state.component.spec.ts
│ │ │ │ └── empty-state.component.ts
│ │ │ ├── file-input
│ │ │ │ ├── file-input.component.html
│ │ │ │ ├── file-input.component.scss
│ │ │ │ ├── file-input.component.spec.ts
│ │ │ │ └── file-input.component.ts
│ │ │ ├── form-loader
│ │ │ │ ├── form-loader.component.spec.ts
│ │ │ │ └── form-loader.component.ts
│ │ │ ├── github-star
│ │ │ │ ├── github-star.component.html
│ │ │ │ ├── github-star.component.scss
│ │ │ │ ├── github-star.component.spec.ts
│ │ │ │ └── github-star.component.ts
│ │ │ ├── input
│ │ │ │ ├── input.component.scss
│ │ │ │ ├── input.component.spec.ts
│ │ │ │ └── input.component.ts
│ │ │ ├── list-item
│ │ │ │ ├── list-item.component.spec.ts
│ │ │ │ └── list-item.component.ts
│ │ │ ├── multi-input
│ │ │ │ ├── multi-input.component.html
│ │ │ │ ├── multi-input.component.scss
│ │ │ │ ├── multi-input.component.spec.ts
│ │ │ │ └── multi-input.component.ts
│ │ │ ├── notification-modal
│ │ │ │ ├── notification-modal.component.html
│ │ │ │ ├── notification-modal.component.scss
│ │ │ │ ├── notification-modal.component.spec.ts
│ │ │ │ └── notification-modal.component.ts
│ │ │ ├── notification
│ │ │ │ ├── notification.component.html
│ │ │ │ ├── notification.component.scss
│ │ │ │ ├── notification.component.spec.ts
│ │ │ │ └── notification.component.ts
│ │ │ ├── overlay
│ │ │ │ ├── overlay.directive.spec.ts
│ │ │ │ └── overlay.directive.ts
│ │ │ ├── page
│ │ │ │ ├── page.component.spec.ts
│ │ │ │ └── page.component.ts
│ │ │ ├── radio
│ │ │ │ ├── radio.component.html
│ │ │ │ ├── radio.component.scss
│ │ │ │ ├── radio.component.spec.ts
│ │ │ │ └── radio.component.ts
│ │ │ ├── select
│ │ │ │ ├── select.component.html
│ │ │ │ ├── select.component.scss
│ │ │ │ ├── select.component.spec.ts
│ │ │ │ └── select.component.ts
│ │ │ ├── skeleton-loader
│ │ │ │ ├── skeleton-loader.component.html
│ │ │ │ ├── skeleton-loader.component.scss
│ │ │ │ ├── skeleton-loader.component.spec.ts
│ │ │ │ └── skeleton-loader.component.ts
│ │ │ ├── table
│ │ │ │ ├── table.component.spec.ts
│ │ │ │ └── table.component.ts
│ │ │ ├── tag
│ │ │ │ ├── tag.component.spec.ts
│ │ │ │ └── tag.component.ts
│ │ │ ├── time-picker
│ │ │ │ ├── time-picker.component.html
│ │ │ │ ├── time-picker.component.scss
│ │ │ │ ├── time-picker.component.spec.ts
│ │ │ │ └── time-picker.component.ts
│ │ │ ├── toggle
│ │ │ │ ├── toggle.component.html
│ │ │ │ ├── toggle.component.scss
│ │ │ │ ├── toggle.component.spec.ts
│ │ │ │ └── toggle.component.ts
│ │ │ └── tooltip
│ │ │ │ ├── tooltip.component.html
│ │ │ │ ├── tooltip.component.scss
│ │ │ │ ├── tooltip.component.spec.ts
│ │ │ │ └── tooltip.component.ts
│ │ ├── guards
│ │ │ └── iframe
│ │ │ │ ├── iframe.guard.spec.ts
│ │ │ │ └── iframe.guard.ts
│ │ ├── interceptor
│ │ │ ├── http.interceptor.spec.ts
│ │ │ └── http.interceptor.ts
│ │ ├── models
│ │ │ ├── device.model.ts
│ │ │ ├── endpoint.model.ts
│ │ │ ├── event.model.ts
│ │ │ ├── filter.model.ts
│ │ │ ├── flipt.model.ts
│ │ │ ├── global.model.ts
│ │ │ ├── organisation.model.ts
│ │ │ ├── project.model.ts
│ │ │ ├── source.model.ts
│ │ │ ├── subscription.ts
│ │ │ └── user.model.ts
│ │ ├── pipes
│ │ │ ├── formatSeconds
│ │ │ │ ├── format-seconds.pipe.spec.ts
│ │ │ │ └── format-seconds.pipe.ts
│ │ │ ├── role
│ │ │ │ ├── role.pipe.spec.ts
│ │ │ │ └── role.pipe.ts
│ │ │ ├── source-value
│ │ │ │ ├── source-value.module.ts
│ │ │ │ ├── source-value.pipe.spec.ts
│ │ │ │ └── source-value.pipe.ts
│ │ │ └── status-color
│ │ │ │ ├── status-color.module.ts
│ │ │ │ ├── status-color.pipe.spec.ts
│ │ │ │ └── status-color.pipe.ts
│ │ ├── portal
│ │ │ ├── create-portal-endpoint
│ │ │ │ ├── create-portal-endpoint.component.html
│ │ │ │ ├── create-portal-endpoint.component.scss
│ │ │ │ └── create-portal-endpoint.component.ts
│ │ │ ├── create-portal-transform-function
│ │ │ │ ├── create-portal-transform-function.component.html
│ │ │ │ ├── create-portal-transform-function.component.scss
│ │ │ │ ├── create-portal-transform-function.component.spec.ts
│ │ │ │ └── create-portal-transform-function.component.ts
│ │ │ ├── endpoints
│ │ │ │ ├── endpoints.component.html
│ │ │ │ ├── endpoints.component.scss
│ │ │ │ ├── endpoints.component.spec.ts
│ │ │ │ └── endpoints.component.ts
│ │ │ ├── event-catalog
│ │ │ │ ├── event-catalog.component.html
│ │ │ │ ├── event-catalog.component.scss
│ │ │ │ ├── event-catalog.component.spec.ts
│ │ │ │ └── event-catalog.component.ts
│ │ │ ├── event-deliveries
│ │ │ │ ├── event-deliveries.component.html
│ │ │ │ ├── event-deliveries.component.scss
│ │ │ │ ├── event-deliveries.component.spec.ts
│ │ │ │ └── event-deliveries.component.ts
│ │ │ ├── event-delivery
│ │ │ │ ├── event-delivery.component.html
│ │ │ │ ├── event-delivery.component.scss
│ │ │ │ ├── event-delivery.component.spec.ts
│ │ │ │ └── event-delivery.component.ts
│ │ │ ├── portal.component.html
│ │ │ ├── portal.component.scss
│ │ │ ├── portal.component.spec.ts
│ │ │ ├── portal.component.ts
│ │ │ ├── portal.service.spec.ts
│ │ │ ├── portal.service.ts
│ │ │ └── subscriptions
│ │ │ │ ├── subscriptions.component.html
│ │ │ │ ├── subscriptions.component.scss
│ │ │ │ ├── subscriptions.component.spec.ts
│ │ │ │ └── subscriptions.component.ts
│ │ ├── private
│ │ │ ├── components
│ │ │ │ ├── config-button
│ │ │ │ │ ├── config-button.component.html
│ │ │ │ │ ├── config-button.component.spec.ts
│ │ │ │ │ └── config-button.component.ts
│ │ │ │ ├── create-endpoint
│ │ │ │ │ ├── create-endpoint.component.html
│ │ │ │ │ ├── create-endpoint.component.scss
│ │ │ │ │ ├── create-endpoint.component.spec.ts
│ │ │ │ │ ├── create-endpoint.component.ts
│ │ │ │ │ ├── create-endpoint.service.spec.ts
│ │ │ │ │ └── create-endpoint.service.ts
│ │ │ │ ├── create-portal-link
│ │ │ │ │ ├── create-portal-link.component.html
│ │ │ │ │ ├── create-portal-link.component.scss
│ │ │ │ │ ├── create-portal-link.component.spec.ts
│ │ │ │ │ ├── create-portal-link.component.ts
│ │ │ │ │ ├── create-portal-link.service.spec.ts
│ │ │ │ │ └── create-portal-link.service.ts
│ │ │ │ ├── create-project-component
│ │ │ │ │ ├── create-project-component.component.html
│ │ │ │ │ ├── create-project-component.component.scss
│ │ │ │ │ ├── create-project-component.component.spec.ts
│ │ │ │ │ ├── create-project-component.component.ts
│ │ │ │ │ ├── create-project-component.module.ts
│ │ │ │ │ ├── create-project-component.service.spec.ts
│ │ │ │ │ └── create-project-component.service.ts
│ │ │ │ ├── create-source
│ │ │ │ │ ├── create-source.component.html
│ │ │ │ │ ├── create-source.component.scss
│ │ │ │ │ ├── create-source.component.spec.ts
│ │ │ │ │ ├── create-source.component.ts
│ │ │ │ │ ├── create-source.module.ts
│ │ │ │ │ ├── create-source.service.spec.ts
│ │ │ │ │ ├── create-source.service.ts
│ │ │ │ │ └── source-url
│ │ │ │ │ │ ├── source-url.component.html
│ │ │ │ │ │ ├── source-url.component.spec.ts
│ │ │ │ │ │ └── source-url.component.ts
│ │ │ │ ├── create-subscription-filter
│ │ │ │ │ ├── create-subscription-filter.component.html
│ │ │ │ │ ├── create-subscription-filter.component.scss
│ │ │ │ │ ├── create-subscription-filter.component.spec.ts
│ │ │ │ │ └── create-subscription-filter.component.ts
│ │ │ │ ├── create-subscription
│ │ │ │ │ ├── create-subscription.component.html
│ │ │ │ │ ├── create-subscription.component.scss
│ │ │ │ │ ├── create-subscription.component.spec.ts
│ │ │ │ │ ├── create-subscription.component.ts
│ │ │ │ │ ├── create-subscription.module.ts
│ │ │ │ │ ├── create-subscription.service.spec.ts
│ │ │ │ │ ├── create-subscription.service.ts
│ │ │ │ │ └── filter.service.ts
│ │ │ │ ├── create-transform-function
│ │ │ │ │ ├── create-transform-function.component.html
│ │ │ │ │ ├── create-transform-function.component.scss
│ │ │ │ │ ├── create-transform-function.component.spec.ts
│ │ │ │ │ └── create-transform-function.component.ts
│ │ │ │ ├── delete-modal
│ │ │ │ │ ├── delete-modal.component.html
│ │ │ │ │ ├── delete-modal.component.scss
│ │ │ │ │ ├── delete-modal.component.spec.ts
│ │ │ │ │ └── delete-modal.component.ts
│ │ │ │ ├── endpoints-filter
│ │ │ │ │ ├── endpoints-filter.component.html
│ │ │ │ │ ├── endpoints-filter.component.spec.ts
│ │ │ │ │ └── endpoints-filter.component.ts
│ │ │ │ ├── enterprise
│ │ │ │ │ ├── enterprise.directive.spec.ts
│ │ │ │ │ └── enterprise.directive.ts
│ │ │ │ ├── event-delivery-filter
│ │ │ │ │ ├── event-delivery-filter.component.html
│ │ │ │ │ ├── event-delivery-filter.component.scss
│ │ │ │ │ ├── event-delivery-filter.component.spec.ts
│ │ │ │ │ └── event-delivery-filter.component.ts
│ │ │ │ ├── loader
│ │ │ │ │ ├── loader.component.html
│ │ │ │ │ ├── loader.component.scss
│ │ │ │ │ ├── loader.component.spec.ts
│ │ │ │ │ ├── loader.component.ts
│ │ │ │ │ └── loader.module.ts
│ │ │ │ ├── monaco
│ │ │ │ │ ├── monaco.component.html
│ │ │ │ │ ├── monaco.component.scss
│ │ │ │ │ ├── monaco.component.spec.ts
│ │ │ │ │ ├── monaco.component.ts
│ │ │ │ │ ├── monaco.service.spec.ts
│ │ │ │ │ └── monaco.service.ts
│ │ │ │ ├── pagination
│ │ │ │ │ ├── pagination.component.html
│ │ │ │ │ ├── pagination.component.scss
│ │ │ │ │ ├── pagination.component.spec.ts
│ │ │ │ │ └── pagination.component.ts
│ │ │ │ ├── permission
│ │ │ │ │ ├── permission.directive.spec.ts
│ │ │ │ │ └── permission.directive.ts
│ │ │ │ ├── prism
│ │ │ │ │ ├── prism.component.html
│ │ │ │ │ ├── prism.component.scss
│ │ │ │ │ ├── prism.component.spec.ts
│ │ │ │ │ ├── prism.component.ts
│ │ │ │ │ └── prism.module.ts
│ │ │ │ ├── sdk-documentation
│ │ │ │ │ ├── sdk-documentation.component.html
│ │ │ │ │ ├── sdk-documentation.component.scss
│ │ │ │ │ ├── sdk-documentation.component.spec.ts
│ │ │ │ │ └── sdk-documentation.component.ts
│ │ │ │ ├── table-loader
│ │ │ │ │ ├── table-loader.component.html
│ │ │ │ │ ├── table-loader.component.scss
│ │ │ │ │ ├── table-loader.component.spec.ts
│ │ │ │ │ ├── table-loader.component.ts
│ │ │ │ │ └── table-loader.module.ts
│ │ │ │ ├── token-modal
│ │ │ │ │ ├── token-modal.component.html
│ │ │ │ │ ├── token-modal.component.scss
│ │ │ │ │ ├── token-modal.component.spec.ts
│ │ │ │ │ └── token-modal.component.ts
│ │ │ │ └── verify-email
│ │ │ │ │ ├── verify-email.component.html
│ │ │ │ │ ├── verify-email.component.scss
│ │ │ │ │ ├── verify-email.component.spec.ts
│ │ │ │ │ ├── verify-email.component.ts
│ │ │ │ │ ├── verify-email.service.spec.ts
│ │ │ │ │ └── verify-email.service.ts
│ │ │ ├── pages
│ │ │ │ ├── account
│ │ │ │ │ ├── account.component.html
│ │ │ │ │ ├── account.component.scss
│ │ │ │ │ ├── account.component.spec.ts
│ │ │ │ │ ├── account.component.ts
│ │ │ │ │ ├── account.module.ts
│ │ │ │ │ ├── account.service.spec.ts
│ │ │ │ │ ├── account.service.ts
│ │ │ │ │ ├── personal-settings
│ │ │ │ │ │ ├── personal-settings.component.html
│ │ │ │ │ │ ├── personal-settings.component.scss
│ │ │ │ │ │ ├── personal-settings.component.spec.ts
│ │ │ │ │ │ └── personal-settings.component.ts
│ │ │ │ │ ├── profile-settings
│ │ │ │ │ │ ├── profile-settings.component.html
│ │ │ │ │ │ ├── profile-settings.component.scss
│ │ │ │ │ │ ├── profile-settings.component.spec.ts
│ │ │ │ │ │ └── profile-settings.component.ts
│ │ │ │ │ └── security-settings
│ │ │ │ │ │ ├── security-settings.component.html
│ │ │ │ │ │ ├── security-settings.component.scss
│ │ │ │ │ │ ├── security-settings.component.spec.ts
│ │ │ │ │ │ └── security-settings.component.ts
│ │ │ │ ├── app
│ │ │ │ │ ├── app.component.html
│ │ │ │ │ ├── app.component.scss
│ │ │ │ │ ├── app.component.spec.ts
│ │ │ │ │ ├── app.component.ts
│ │ │ │ │ └── app.module.ts
│ │ │ │ ├── create-project
│ │ │ │ │ ├── create-project.component.html
│ │ │ │ │ ├── create-project.component.scss
│ │ │ │ │ ├── create-project.component.spec.ts
│ │ │ │ │ ├── create-project.component.ts
│ │ │ │ │ └── create-project.module.ts
│ │ │ │ ├── dashboard
│ │ │ │ │ ├── dashboard.component.html
│ │ │ │ │ ├── dashboard.component.scss
│ │ │ │ │ ├── dashboard.component.spec.ts
│ │ │ │ │ ├── dashboard.component.ts
│ │ │ │ │ └── dashboard.module.ts
│ │ │ │ ├── onboarding
│ │ │ │ │ ├── onboarding.component.html
│ │ │ │ │ ├── onboarding.component.scss
│ │ │ │ │ ├── onboarding.component.spec.ts
│ │ │ │ │ └── onboarding.component.ts
│ │ │ │ ├── project
│ │ │ │ │ ├── endpoints
│ │ │ │ │ │ ├── endpoint-secret
│ │ │ │ │ │ │ ├── endpoint-secret.component.html
│ │ │ │ │ │ │ ├── endpoint-secret.component.scss
│ │ │ │ │ │ │ ├── endpoint-secret.component.spec.ts
│ │ │ │ │ │ │ └── endpoint-secret.component.ts
│ │ │ │ │ │ ├── endpoints.component.html
│ │ │ │ │ │ ├── endpoints.component.scss
│ │ │ │ │ │ ├── endpoints.component.spec.ts
│ │ │ │ │ │ ├── endpoints.component.ts
│ │ │ │ │ │ ├── endpoints.service.spec.ts
│ │ │ │ │ │ └── endpoints.service.ts
│ │ │ │ │ ├── event-logs
│ │ │ │ │ │ ├── event-logs.component.html
│ │ │ │ │ │ ├── event-logs.component.scss
│ │ │ │ │ │ ├── event-logs.component.spec.ts
│ │ │ │ │ │ ├── event-logs.component.ts
│ │ │ │ │ │ ├── event-logs.service.spec.ts
│ │ │ │ │ │ └── event-logs.service.ts
│ │ │ │ │ ├── events
│ │ │ │ │ │ ├── event-deliveries
│ │ │ │ │ │ │ ├── event-deliveries.component.html
│ │ │ │ │ │ │ ├── event-deliveries.component.scss
│ │ │ │ │ │ │ ├── event-deliveries.component.spec.ts
│ │ │ │ │ │ │ ├── event-deliveries.component.ts
│ │ │ │ │ │ │ └── event-deliveries.module.ts
│ │ │ │ │ │ ├── event-delivery-details-page
│ │ │ │ │ │ │ ├── event-delivery-details-page.component.html
│ │ │ │ │ │ │ ├── event-delivery-details-page.component.scss
│ │ │ │ │ │ │ ├── event-delivery-details-page.component.spec.ts
│ │ │ │ │ │ │ ├── event-delivery-details-page.component.ts
│ │ │ │ │ │ │ └── event-delivery-details-page.module.ts
│ │ │ │ │ │ ├── event-delivery-details
│ │ │ │ │ │ │ ├── event-delivery-details.component.html
│ │ │ │ │ │ │ ├── event-delivery-details.component.scss
│ │ │ │ │ │ │ ├── event-delivery-details.component.spec.ts
│ │ │ │ │ │ │ ├── event-delivery-details.component.ts
│ │ │ │ │ │ │ ├── event-delivery-details.module.ts
│ │ │ │ │ │ │ ├── event-delivery-details.service.spec.ts
│ │ │ │ │ │ │ └── event-delivery-details.service.ts
│ │ │ │ │ │ ├── events.component.html
│ │ │ │ │ │ ├── events.component.scss
│ │ │ │ │ │ ├── events.component.spec.ts
│ │ │ │ │ │ ├── events.component.ts
│ │ │ │ │ │ ├── events.module.ts
│ │ │ │ │ │ ├── events.service.spec.ts
│ │ │ │ │ │ └── events.service.ts
│ │ │ │ │ ├── meta-events
│ │ │ │ │ │ ├── meta-events.component.html
│ │ │ │ │ │ ├── meta-events.component.scss
│ │ │ │ │ │ ├── meta-events.component.spec.ts
│ │ │ │ │ │ ├── meta-events.component.ts
│ │ │ │ │ │ ├── meta-events.service.spec.ts
│ │ │ │ │ │ └── meta-events.service.ts
│ │ │ │ │ ├── portal-links
│ │ │ │ │ │ ├── portal-links.component.html
│ │ │ │ │ │ ├── portal-links.component.scss
│ │ │ │ │ │ ├── portal-links.component.spec.ts
│ │ │ │ │ │ ├── portal-links.component.ts
│ │ │ │ │ │ ├── portal-links.service.spec.ts
│ │ │ │ │ │ └── portal-links.service.ts
│ │ │ │ │ ├── project.component.html
│ │ │ │ │ ├── project.component.scss
│ │ │ │ │ ├── project.component.spec.ts
│ │ │ │ │ ├── project.component.ts
│ │ │ │ │ ├── project.module.ts
│ │ │ │ │ ├── project.service.ts
│ │ │ │ │ ├── settings
│ │ │ │ │ │ ├── settings.component.html
│ │ │ │ │ │ ├── settings.component.scss
│ │ │ │ │ │ ├── settings.component.spec.ts
│ │ │ │ │ │ ├── settings.component.ts
│ │ │ │ │ │ └── settings.module.ts
│ │ │ │ │ ├── sources
│ │ │ │ │ │ ├── sources.component.html
│ │ │ │ │ │ ├── sources.component.scss
│ │ │ │ │ │ ├── sources.component.spec.ts
│ │ │ │ │ │ ├── sources.component.ts
│ │ │ │ │ │ ├── sources.module.ts
│ │ │ │ │ │ ├── sources.service.spec.ts
│ │ │ │ │ │ └── sources.service.ts
│ │ │ │ │ └── subscriptions
│ │ │ │ │ │ ├── subscriptions.component.html
│ │ │ │ │ │ ├── subscriptions.component.scss
│ │ │ │ │ │ ├── subscriptions.component.spec.ts
│ │ │ │ │ │ ├── subscriptions.component.ts
│ │ │ │ │ │ └── subscriptions.module.ts
│ │ │ │ ├── projects
│ │ │ │ │ ├── projects.component.html
│ │ │ │ │ ├── projects.component.scss
│ │ │ │ │ ├── projects.component.spec.ts
│ │ │ │ │ ├── projects.component.ts
│ │ │ │ │ └── projects.module.ts
│ │ │ │ ├── settings
│ │ │ │ │ ├── configurations
│ │ │ │ │ │ ├── configurations.component.html
│ │ │ │ │ │ ├── configurations.component.scss
│ │ │ │ │ │ ├── configurations.component.spec.ts
│ │ │ │ │ │ └── configurations.component.ts
│ │ │ │ │ ├── organisation-settings
│ │ │ │ │ │ ├── organisation-settings.component.html
│ │ │ │ │ │ ├── organisation-settings.component.scss
│ │ │ │ │ │ ├── organisation-settings.component.spec.ts
│ │ │ │ │ │ └── organisation-settings.component.ts
│ │ │ │ │ ├── settings.component.html
│ │ │ │ │ ├── settings.component.scss
│ │ │ │ │ ├── settings.component.spec.ts
│ │ │ │ │ ├── settings.component.ts
│ │ │ │ │ ├── settings.module.ts
│ │ │ │ │ ├── settings.service.spec.ts
│ │ │ │ │ ├── settings.service.ts
│ │ │ │ │ └── teams
│ │ │ │ │ │ ├── teams.component.html
│ │ │ │ │ │ ├── teams.component.scss
│ │ │ │ │ │ ├── teams.component.spec.ts
│ │ │ │ │ │ ├── teams.component.ts
│ │ │ │ │ │ ├── teams.module.ts
│ │ │ │ │ │ ├── teams.service.spec.ts
│ │ │ │ │ │ └── teams.service.ts
│ │ │ │ └── setup-project
│ │ │ │ │ ├── setup-project.component.html
│ │ │ │ │ ├── setup-project.component.scss
│ │ │ │ │ ├── setup-project.component.spec.ts
│ │ │ │ │ └── setup-project.component.ts
│ │ │ ├── private-routers.ts
│ │ │ ├── private-routing.ee.module.ts
│ │ │ ├── private-routing.module.ts
│ │ │ ├── private.component.html
│ │ │ ├── private.component.scss
│ │ │ ├── private.component.spec.ts
│ │ │ ├── private.component.ts
│ │ │ ├── private.module.ts
│ │ │ └── private.service.ts
│ │ ├── public
│ │ │ ├── accept-invite
│ │ │ │ ├── accept-invite.component.html
│ │ │ │ ├── accept-invite.component.scss
│ │ │ │ ├── accept-invite.component.spec.ts
│ │ │ │ ├── accept-invite.component.ts
│ │ │ │ ├── accept-invite.service.spec.ts
│ │ │ │ └── accept-invite.service.ts
│ │ │ ├── forgot-password
│ │ │ │ ├── forgot-password.component.html
│ │ │ │ ├── forgot-password.component.scss
│ │ │ │ ├── forgot-password.component.spec.ts
│ │ │ │ ├── forgot-password.component.ts
│ │ │ │ ├── forgot-password.service.spec.ts
│ │ │ │ └── forgot-password.service.ts
│ │ │ ├── login
│ │ │ │ ├── login.component.html
│ │ │ │ ├── login.component.scss
│ │ │ │ ├── login.component.spec.ts
│ │ │ │ ├── login.component.ts
│ │ │ │ ├── login.service.spec.ts
│ │ │ │ └── login.service.ts
│ │ │ ├── public.component.html
│ │ │ ├── public.component.scss
│ │ │ ├── public.component.spec.ts
│ │ │ ├── public.component.ts
│ │ │ ├── public.module.ts
│ │ │ ├── reset-password
│ │ │ │ ├── reset-password.component.html
│ │ │ │ ├── reset-password.component.scss
│ │ │ │ ├── reset-password.component.spec.ts
│ │ │ │ ├── reset-password.component.ts
│ │ │ │ ├── reset-password.service.spec.ts
│ │ │ │ └── reset-password.service.ts
│ │ │ ├── saml
│ │ │ │ ├── saml.component.html
│ │ │ │ ├── saml.component.scss
│ │ │ │ ├── saml.component.spec.ts
│ │ │ │ ├── saml.component.ts
│ │ │ │ ├── saml.service.spec.ts
│ │ │ │ └── saml.service.ts
│ │ │ ├── signup
│ │ │ │ ├── signup.component.html
│ │ │ │ ├── signup.component.scss
│ │ │ │ ├── signup.component.spec.ts
│ │ │ │ ├── signup.component.ts
│ │ │ │ ├── signup.service.spec.ts
│ │ │ │ └── signup.service.ts
│ │ │ └── verify-email
│ │ │ │ ├── verify-email.component.html
│ │ │ │ ├── verify-email.component.scss
│ │ │ │ ├── verify-email.component.spec.ts
│ │ │ │ ├── verify-email.component.ts
│ │ │ │ ├── verify-email.service.spec.ts
│ │ │ │ └── verify-email.service.ts
│ │ └── services
│ │ │ ├── general
│ │ │ ├── general.service.spec.ts
│ │ │ └── general.service.ts
│ │ │ ├── http
│ │ │ ├── http.helper.service.ts
│ │ │ ├── http.service.spec.ts
│ │ │ └── http.service.ts
│ │ │ ├── hubspot
│ │ │ ├── hubspot.service.spec.ts
│ │ │ └── hubspot.service.ts
│ │ │ ├── licenses
│ │ │ ├── licenses.service.spec.ts
│ │ │ └── licenses.service.ts
│ │ │ └── rbac
│ │ │ ├── rbac.service.spec.ts
│ │ │ └── rbac.service.ts
│ ├── assets
│ │ ├── .gitkeep
│ │ ├── fonts
│ │ │ ├── Menlo-Regular.woff
│ │ │ └── inter
│ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2
│ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2
│ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2
│ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2
│ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2
│ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2
│ │ │ │ └── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2
│ │ └── img
│ │ │ ├── Checkbox.svg
│ │ │ ├── Loader.png
│ │ │ ├── Pattern.svg
│ │ │ ├── add-circlar-icon.svg
│ │ │ ├── add-icon.svg
│ │ │ ├── amqp.png
│ │ │ ├── angle-arrow-down.svg
│ │ │ ├── angle-arrow-left.svg
│ │ │ ├── angle-arrow-right-primary.svg
│ │ │ ├── angle-arrow-right.svg
│ │ │ ├── angle-arrow-up.svg
│ │ │ ├── angle-down.svg
│ │ │ ├── apps-icon-grey.svg
│ │ │ ├── apps-icon-transparent.svg
│ │ │ ├── apps-icon.svg
│ │ │ ├── arrow-down-icon.svg
│ │ │ ├── arrow-left-primary.svg
│ │ │ ├── arrow-up-right.svg
│ │ │ ├── button-loader.gif
│ │ │ ├── calendar-icon.svg
│ │ │ ├── chart-icon.svg
│ │ │ ├── chart.svg
│ │ │ ├── check-icon-primary.svg
│ │ │ ├── checkbox-icon.svg
│ │ │ ├── checkmark.svg
│ │ │ ├── clock.svg
│ │ │ ├── close icon.svg
│ │ │ ├── close-icon-black.svg
│ │ │ ├── close-icon-grey.svg
│ │ │ ├── close-icon.svg
│ │ │ ├── copy-icon.svg
│ │ │ ├── copy.svg
│ │ │ ├── doc-icon-primary.svg
│ │ │ ├── download.png
│ │ │ ├── empty-state-img.svg
│ │ │ ├── empty-state.svg
│ │ │ ├── endpoint-discord-icon.svg
│ │ │ ├── endpoint-hubspot-icon.svg
│ │ │ ├── endpoint-slack-icon.svg
│ │ │ ├── endpoint-telegram-icon.svg
│ │ │ ├── endpoint-webhook-icon.svg
│ │ │ ├── endpoint-zapier-icon.svg
│ │ │ ├── endpoints-icon.svg
│ │ │ ├── enter-icon.png
│ │ │ ├── error-icon.svg
│ │ │ ├── events-empty-state-image.svg
│ │ │ ├── events-log-empty-state.png
│ │ │ ├── file.png
│ │ │ ├── filter-icon.svg
│ │ │ ├── filter.gif
│ │ │ ├── forgot-password.svg
│ │ │ ├── github.png
│ │ │ ├── go.png
│ │ │ ├── google.png
│ │ │ ├── group-empty-img.svg
│ │ │ ├── ideas.svg
│ │ │ ├── info-icon.svg
│ │ │ ├── inherit-icon.svg
│ │ │ ├── input-error-icon.svg
│ │ │ ├── input-valid-icon.svg
│ │ │ ├── js.png
│ │ │ ├── kafka.png
│ │ │ ├── link-icon.svg
│ │ │ ├── loader.gif
│ │ │ ├── lock.svg
│ │ │ ├── logo.svg
│ │ │ ├── message-icon-transparent.svg
│ │ │ ├── message-icon.svg
│ │ │ ├── modal-close-icon.svg
│ │ │ ├── more-horizontal.svg
│ │ │ ├── more-icon-vertical.svg
│ │ │ ├── nav-bar-more-primary.svg
│ │ │ ├── new-empty-state.png
│ │ │ ├── new-success.gif
│ │ │ ├── no-group.svg
│ │ │ ├── org-empty-img.svg
│ │ │ ├── organisation-icon.svg
│ │ │ ├── organizations-empty-state.svg
│ │ │ ├── page-loader.gif
│ │ │ ├── password-invisible-icon.svg
│ │ │ ├── password-reset-success-img.svg
│ │ │ ├── password-visible-icon.svg
│ │ │ ├── php.png
│ │ │ ├── portal-link-empty-state.png
│ │ │ ├── primary-info-icon.svg
│ │ │ ├── public-layout.png
│ │ │ ├── python.png
│ │ │ ├── refresh-icon-2.svg
│ │ │ ├── refresh-icon-force.svg
│ │ │ ├── refresh-icon-primary.svg
│ │ │ ├── refresh-icon.svg
│ │ │ ├── retry-icon.svg
│ │ │ ├── rotate-icon.svg
│ │ │ ├── ruby.png
│ │ │ ├── search-icon.svg
│ │ │ ├── setting.svg
│ │ │ ├── shopify.png
│ │ │ ├── small-info-icon.svg
│ │ │ ├── sources-empty-state.png
│ │ │ ├── sqs.png
│ │ │ ├── square-check.svg
│ │ │ ├── status-filter-icon.svg
│ │ │ ├── subscriptions-empty-state.png
│ │ │ ├── success-icon.svg
│ │ │ ├── success.gif
│ │ │ ├── success.png
│ │ │ ├── svg
│ │ │ ├── help-circle.svg
│ │ │ ├── lock-open.svg
│ │ │ ├── lock_open.svg
│ │ │ ├── new-check-icon.svg
│ │ │ ├── page-locked.svg
│ │ │ ├── plus-icon.svg
│ │ │ ├── project-info.svg
│ │ │ ├── projects.svg
│ │ │ └── retention-icon.svg
│ │ │ ├── team-empty-img.svg
│ │ │ ├── teams-empty-state.png
│ │ │ ├── time-icon.svg
│ │ │ ├── trash-icon.svg
│ │ │ ├── twitter.png
│ │ │ ├── user-icon.svg
│ │ │ ├── view-events-icon.svg
│ │ │ ├── view-icon.svg
│ │ │ ├── warning-icon.svg
│ │ │ ├── warning.gif
│ │ │ └── white-logo.svg
│ ├── content
│ │ └── sdks
│ │ │ ├── go
│ │ │ ├── Add Endpoint.md
│ │ │ ├── Installation.md
│ │ │ └── Send Event.md
│ │ │ ├── js
│ │ │ ├── Add Endpoint.md
│ │ │ ├── Installation.md
│ │ │ └── Send Event.md
│ │ │ ├── php
│ │ │ ├── Add Endpoint.md
│ │ │ ├── Installation.md
│ │ │ └── Send Event.md
│ │ │ ├── python
│ │ │ ├── Add Endpoint.md
│ │ │ ├── Installation.md
│ │ │ └── Send Event.md
│ │ │ └── ruby
│ │ │ ├── Add Endpoint.md
│ │ │ ├── Installation.md
│ │ │ └── Send Event.md
│ ├── environments
│ │ ├── environment.ee.ts
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── scss
│ │ ├── _font.scss
│ │ ├── _forms.scss
│ │ ├── _material-design-reset.scss
│ │ └── _prism.scss
│ ├── stories
│ │ ├── Introduction.stories.mdx
│ │ ├── assets
│ │ │ ├── code-brackets.svg
│ │ │ ├── colors.svg
│ │ │ ├── comments.svg
│ │ │ ├── direction.svg
│ │ │ ├── flow.svg
│ │ │ ├── plugin.svg
│ │ │ ├── repo.svg
│ │ │ └── stackalt.svg
│ │ ├── badge
│ │ │ └── Badge.stories.ts
│ │ ├── button
│ │ │ └── Button.stories.ts
│ │ ├── card
│ │ │ └── Card.stories.ts
│ │ ├── dropdown
│ │ │ └── Dropdown.stories.ts
│ │ ├── empty-state
│ │ │ └── Empty-state.stories.ts
│ │ ├── input
│ │ │ └── Input.stories.ts
│ │ ├── modal
│ │ │ └── Modal.stories.ts
│ │ ├── notification
│ │ │ └── Notification.stories.ts
│ │ ├── radio
│ │ │ └── Radio.stories.ts
│ │ ├── table-cell
│ │ │ └── Table-cell.stories.ts
│ │ ├── table-row
│ │ │ └── Table-row.stories.ts
│ │ ├── table
│ │ │ └── Table.stories.ts
│ │ ├── tag
│ │ │ └── Tag.stories.ts
│ │ ├── toggle
│ │ │ └── Toggle.stories.ts
│ │ └── tooltip
│ │ │ └── Tooltip.stories.ts
│ ├── styles.scss
│ └── test.ts
│ ├── tailwind.config.js
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ └── tsconfig.spec.json
└── worker
├── consumer.go
├── scheduler.go
└── task
├── delete_archived_task.go
├── expire_secret.go
├── expire_secret_test.go
├── monitor_twitter_sources.go
├── process_batch_retry.go
├── process_batch_retry_test.go
├── process_broadcast_event_creation.go
├── process_broadcast_event_creation_test.go
├── process_dead_letters.go
├── process_dynamic_event_creation.go
├── process_dynamic_event_creation_test.go
├── process_emails.go
├── process_emails_test.go
├── process_event_channel.go
├── process_event_creation.go
├── process_event_creation_test.go
├── process_event_delivery.go
├── process_event_delivery_test.go
├── process_meta_event.go
├── process_meta_event_test.go
├── process_notifications.go
├── process_notifications_test.go
├── process_retry_event_delivery.go
├── process_retry_event_delivery_test.go
├── push_daily_telemetry.go
├── queue_stuck_event_deliveries.go
├── retention_policies.go
├── retention_policies_test.go
├── retry_event_deliveries.go
├── search_tokenizer.go
├── task.go
└── testdata
└── Config
├── basic-convoy-disable-endpoint.json
└── basic-convoy.json
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | node_modules
3 | docker-compose.yml
4 |
5 | # Convoy Specific
6 | typesense_data
7 | postgres_data
8 | redis_data
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 4
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.githooks/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # git hook that runs before a commit
4 | # this script will regenerate the swagger docs(openapi v3 soon) file
5 |
6 | pwd
7 |
8 | changed=$(git status -s | grep api)
9 |
10 | echo "$changed"
11 |
12 | # if changed is empty then exit, else regenerate the docs
13 | if [ -z "$changed" ]
14 | then
15 | exit 0
16 | else
17 | # regenerate docs
18 | swag init --generatedTime --parseDependency --parseInternal -g handlers/main.go -d api/ api/*
19 | swag fmt -d ./api
20 | go run v3gen/main.go
21 | git add docs/ # add all files under the generated doc folder to git
22 | fi
23 |
24 | exit 0
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/release-template.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Release template
3 | about: This template is used to describe all the todo required for releasing a new
4 | binary
5 | title: "[Release]"
6 | labels: ''
7 | assignees: ''
8 |
9 | ---
10 |
11 | ## Release Title
12 | Example: v23.10.0
13 |
14 | ## To-do
15 | [ ] Helm Charts
16 | [ ] Documentation Release Notes
17 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: Sync API Docs 🦉
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | rdme-openapi:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Check out repo 📚
13 | uses: actions/checkout@v3
14 |
15 | - name: Run `openapi` command 🚀
16 | uses: readmeio/rdme@v8
17 | with:
18 | rdme: openapi docs/v3/openapi3.json --key=${{ secrets.README_API_KEY }} --id=620fb30f3b9bf300264daa75
19 |
--------------------------------------------------------------------------------
/.github/workflows/linter.yml:
--------------------------------------------------------------------------------
1 | name: golangci-lint
2 | on:
3 | pull_request:
4 |
5 | jobs:
6 | golangci:
7 | if: ${{ !(contains(github.head_ref, 'ui/')) || !(contains(github.head_ref, 'cms/')) }}
8 | name: lint
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 | - name: golangci-lint
13 | uses: golangci/golangci-lint-action@v6
14 | with:
15 | version: latest
16 | only-new-issues: true
17 | args: --timeout 3m --verbose
18 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | { "scss.lint.unknownAtRules": "ignore" }
2 |
--------------------------------------------------------------------------------
/ENT-CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 23.05.1
2 |
3 | - [Feature] Add support for Role Based Access Control #1551 #1552
4 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | v25.5.1-hotfix.1
2 |
--------------------------------------------------------------------------------
/api/handlers/license.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/frain-dev/convoy/pkg/log"
7 |
8 | "github.com/frain-dev/convoy/util"
9 | "github.com/go-chi/render"
10 | )
11 |
12 | func (h *Handler) GetLicenseFeatures(w http.ResponseWriter, r *http.Request) {
13 | v, err := h.A.Licenser.FeatureListJSON(r.Context())
14 | if err != nil {
15 | log.FromContext(r.Context()).WithError(err).Error("failed to get license features")
16 | _ = render.Render(w, r, util.NewErrorResponse("failed to get license features", http.StatusBadRequest))
17 | return
18 | }
19 |
20 | _ = render.Render(w, r, util.NewServerResponse("Retrieved license features successfully", v, http.StatusOK))
21 | }
22 |
--------------------------------------------------------------------------------
/api/policies/policy.go:
--------------------------------------------------------------------------------
1 | package policies
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/frain-dev/convoy/api/types"
7 | )
8 |
9 | const AuthUserCtx types.ContextKey = "authUser"
10 |
11 | // ErrNotAllowed is returned when request is not permitted.
12 | var ErrNotAllowed = errors.New("unauthorized to process request")
13 |
--------------------------------------------------------------------------------
/api/policies/policy_test.go:
--------------------------------------------------------------------------------
1 | package policies
2 |
3 | import (
4 | "github.com/frain-dev/convoy/auth"
5 | "github.com/stretchr/testify/require"
6 | )
7 |
8 | type basetest struct {
9 | name string
10 | authCtx *auth.AuthenticatedUser
11 | assertion require.ErrorAssertionFunc
12 | expectedError error
13 | }
14 |
--------------------------------------------------------------------------------
/api/testdata/Auth_Config/no-auth-convoy.json:
--------------------------------------------------------------------------------
1 | {
2 | "base_url": "test-url",
3 | "queue": {
4 | "type": "redis",
5 | "redis": {
6 | "dsn": "abc"
7 | }
8 | },
9 | "server": {
10 | "http": {
11 | "port": 80
12 | }
13 | },
14 | "group": {
15 | "strategy": {
16 | "type": "default",
17 | "default": {
18 | "intervalSeconds": 125,
19 | "retryLimit": 15
20 | }
21 | },
22 | "signature": {
23 | "header": "X-Company-Event-WebHook-Signature",
24 | "hash": "SHA256"
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_BatchRetryEventDelivery/should_batch_retry_all_successfully.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"1 successful, 0 failed","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_BatchRetryEventDelivery/should_batch_retry_one_successfully.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"1 successful, 1 failed","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_BatchRetryEventDelivery/should_fail_to_find_app_endpoint.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"0 successful, 2 failed","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_BatchRetryEventDelivery/should_fail_to_load_event_deliveries.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to fetch event deliveries"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_BatchRetryEventDelivery/should_fail_to_update_app_endpoints_status.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"0 successful, 1 failed","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_BatchRetryEventDelivery/should_fail_to_update_status_of_event_delivery.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"0 successful, 1 failed","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_BatchRetryEventDelivery/should_fail_to_write_to_queue.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"0 successful, 1 failed","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CountAffectedEventDeliveries/should_count_affected_event_deliveries_successfully.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"event deliveries count successful","data":{"num":10}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CountAffectedEventDeliveries/should_fail_to_count_affected_event_deliveries.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"an error occurred while fetching event deliveries"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateAPIKey/create_api_key.golden:
--------------------------------------------------------------------------------
1 | {"uid":"","name":"","role":{"type":"ui_admin","groups":["sendcash-pay"]},"key_type":""}
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateAPIKey/create_api_key_without_expires_at_field.golden:
--------------------------------------------------------------------------------
1 | {"uid":"","name":"","role":{"type":"ui_admin","groups":["sendcash-pay"]},"key_type":""}
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateAPIKey/invalid_expiry_date.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"expiry date is invalid"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateAPIKey/invalid_role.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"invalid api key role"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateApp/invalid_request.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"body must not be empty"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateApp/invalid_request_-_no_app_name.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"name:please provide your appName"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateApp/should_fail_for_application_name_not_unique.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"an application with this name exists: ABC_DEF_TEST"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateApp/should_fail_to_check_if_application_name_is_unique.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to check if application name is unique"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateApp/valid_application.golden:
--------------------------------------------------------------------------------
1 | {"uid":"","group_id":"1234567890","name":"ABC_DEF_TEST","support_email":"","slack_webhook_url":"https://google.com","is_disabled":false,"endpoints":[],"events":0}
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateAppEndpoint/should_error_for_invalid_rate_limit_duration.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"an error occurred parsing the rate limit duration: time: missing unit in duration \"1\""}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateAppEndpoint/valid_endpoint.golden:
--------------------------------------------------------------------------------
1 | {"uid":"","target_url":"https://google.com","description":"Test","status":"active","secret":"abc","http_timeout":"","rate_limit":300,"rate_limit_duration":"1h0m0s","events":["*"]}
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateAppPortalAPIKey/app_id_does_not_belong_to_group.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"app does not belong to group"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateAppPortalAPIKey/create_api_key.golden:
--------------------------------------------------------------------------------
1 | {"uid":"","name":"","role":{"type":"ui_admin","groups":["1234567890"],"apps":["123456"]},"key_type":"app_portal"}
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateGroup/invalid_request_-_no_group_hash_field.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"hash:please provide a valid hash"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateGroup/invalid_request_-_no_group_header_field.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"header:please provide a valid signature header"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateGroup/invalid_request_-_no_group_interval_seconds_field.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"intervalSeconds:please provide a valid interval seconds"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateGroup/invalid_request_-_no_group_name.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"name:please provide a valid name"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateGroup/invalid_request_-_no_group_retry_limit_field.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"retryLimit:please provide a valid interval seconds"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateGroup/invalid_request_-_no_group_strategy_type_field.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"type:please provide a valid strategy type"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateGroup/invalid_request_-_unsupported_group_hash_field.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"hash:unsupported hash type"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateGroup/invalid_request_-_unsupported_group_strategy_type.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"type:unsupported strategy type"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateGroup/should_fail_to_create_group.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to create group"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_CreateGroup/valid_group.golden:
--------------------------------------------------------------------------------
1 | {"uid":"","name":"ABC_DEF_TEST_UPDATE","logo_url":"","config":{"strategy":{"type":"default","default":{"intervalSeconds":10,"retryLimit":3},"exponentialBackoff":{"retryLimit":0}},"signature":{"header":"X-Company-Signature","hash":"SHA1"},"disable_endpoint":false,"replay_attacks":false},"statistics":null,"rate_limit":5000,"rate_limit_duration":"1m"}
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_DeleteGroup/failed_group_apps_delete.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to delete group apps"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_DeleteGroup/failed_group_delete.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to delete group"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_DeleteGroup/failed_group_events_delete.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to delete group events"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_DeleteGroup/valid_group_delete.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"Group deleted successfully","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_ForceResendEventDelivery/should_error_for_discarded_event_status.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"2 successful, 1 failed","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_ForceResendEventDelivery/should_error_for_endpoint_not_found.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"0 successful, 3 failed","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_ForceResendEventDelivery/should_error_for_failed_to_fetch_deliveries.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to fetch event deliveries"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_ForceResendEventDelivery/should_error_for_failed_to_update_event_delivery_status.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"0 successful, 2 failed","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_ForceResendEventDelivery/should_error_for_inactive_endpoint.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"0 successful, 2 failed","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_ForceResendEventDelivery/should_error_for_malformed_body.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"Request is invalid"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_ForceResendEventDelivery/should_force_resend_all_successfully.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"3 successful, 0 failed","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetAPIKeyByID/should_fail_to_find_api_key.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to fetch api key"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetAPIKeyByID/should_find_api_key.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"api key fetched successfully","data":{"uid":"12345","name":"","role":{"type":"","groups":null},"key_type":""}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetAPIKeys/should_fail_to_load_api_keys.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to load api keys"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetAPIKeys/should_load_api_keys.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"api keys fetched successfully","data":{"content":[{"uid":"12345","name":"","role":{"type":"","groups":null},"key_type":""}],"pagination":{"total":0,"page":0,"perPage":100,"prev":0,"next":0,"totalPage":0}}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetApp/app_not_found.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"application not found"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetApp/valid_application.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App fetched successfully","data":{"uid":"123456789","group_id":"1234567890","name":"Valid application","support_email":"","is_disabled":false,"endpoints":[],"events":0}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetAppEndpoint/should_get_application_endpoint.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App endpoints fetched successfully","data":[{"uid":"abc","target_url":"http://amazon.com","description":"desc","status":"","secret":"","http_timeout":"","rate_limit":0,"rate_limit_duration":"","events":null},{"uid":"def","target_url":"http://google.com","description":"desc","status":"","secret":"","http_timeout":"","rate_limit":0,"rate_limit_duration":"","events":null}]}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetAppEndpoints/should_get_application_endpoints.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App endpoints fetched successfully","data":[{"uid":"abc","target_url":"http://amazon.com","description":"desc","status":"","secret":"","http_timeout":"","rate_limit":0,"rate_limit_duration":"","events":null},{"uid":"abc","target_url":"http://google.com","description":"desc","status":"","secret":"","http_timeout":"","rate_limit":0,"rate_limit_duration":"","events":null}]}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetApps/should_fail_to_fetch_applications.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"an error occurred while fetching apps. Error: failed to load"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetApps/valid_applications.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"Apps fetched successfully","data":{"content":[{"uid":"123456789","group_id":"1234567890","name":"Valid application - 0","support_email":"","is_disabled":false,"endpoints":[],"events":0}],"pagination":{"total":0,"page":0,"perPage":0,"prev":0,"next":0,"totalPage":0}}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetGroup/group_not_found.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to fetch group by id"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetGroup/should_fail_to_count_group_apps.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to count group statistics"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetGroup/should_fail_to_count_group_messages.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to count group statistics"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetGroup/valid_group.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"Group fetched successfully","data":{"uid":"1234567890","name":"sendcash-pay","logo_url":"","config":null,"statistics":{"messages_sent":1,"total_apps":1},"rate_limit":0,"rate_limit_duration":""}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetGroups/should_error_for_invalid_group_access.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"invalid group access"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetGroups/should_fail_to_fetch_groups.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"an error occurred while fetching Groups"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetGroups/should_fetch_user_groups.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"Groups fetched successfully","data":[{"uid":"1234567890","name":"sendcash-pay","logo_url":"","config":null,"statistics":{"messages_sent":1,"total_apps":1},"rate_limit":0,"rate_limit_duration":""}]}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_GetGroups/valid_groups.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"Groups fetched successfully","data":[{"uid":"1234567890","name":"sendcash-pay","logo_url":"","config":null,"statistics":{"messages_sent":1,"total_apps":1},"rate_limit":0,"rate_limit_duration":""}]}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_RevokeAPIKey/revoke_api_key.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"api key revoked successfully","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_RevokeAPIKey/should_error_for_revoke_api_key.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"failed to revoke api key"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateAPIKey/invalid_role.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"invalid api key role"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateAPIKey/update_api_key.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"api key updated successfully","data":{"uid":"12345","name":"","role":{"type":"admin","groups":["sendcash-pay"]},"key_type":""}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateApp/invalid_request.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"body must not be empty"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateApp/invalid_request_-_no_app_name.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"name:please provide your appName"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateApp/should_error_for_duplicate_app_name.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"an application with this name exists: ABC_DEF_TEST_UPDATE"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateApp/valid_application_update.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App updated successfully","data":{"uid":"12345","group_id":"1234567890","name":"ABC_DEF_TEST_UPDATE","support_email":"","is_disabled":false,"endpoints":[],"events":0}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateApp/valid_request_-_disable_application.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App updated successfully","data":{"uid":"12345","group_id":"1234567890","name":"ABC","support_email":"","is_disabled":true,"endpoints":[],"events":0}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateApp/valid_request_-_enable_disabled_application.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App updated successfully","data":{"uid":"12345","group_id":"1234567890","name":"ABC","support_email":"","is_disabled":false,"endpoints":[],"events":0}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateApp/valid_request_-_update_secret.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App updated successfully","data":{"uid":"12345","group_id":"1234567890","name":"ABC","support_email":"","is_disabled":false,"endpoints":[],"events":0}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateApp/valid_request_-_update_support_email.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App updated successfully","data":{"uid":"12345","group_id":"1234567890","name":"ABC","support_email":"engineering@frain.dev","is_disabled":false,"endpoints":[],"events":0}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateAppEndpoint/invalid_request.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"body must not be empty"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateAppEndpoint/should_error_for_endpoint_not_found.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"endpoint not found"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateAppEndpoint/should_error_for_invalid_rate_limit_duration.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"time: missing unit in duration \"1\""}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateAppEndpoint/valid_application.golden:
--------------------------------------------------------------------------------
1 | {"uid":"","target_url":"https://google.com","description":"Correct endpoint","status":"active","secret":"","http_timeout":"10s","rate_limit":3000,"rate_limit_duration":"1h0m0s","events":["payment.created"]}
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateAppEndpoint_InvalidRequest/invalid_request.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"endpoint not found"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateAppEndpoint_InvalidRequest/valid_application.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"cannot use localhost or 127.0.0.1"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateGroup/invalid_request.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"body must not be empty"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateGroup/invalid_request_-_no_group_name.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"name:please provide a valid name"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateGroup/should_fail_to_update_group.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"an error occurred while updating Group"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestApplicationHandler_UpdateGroup/valid_group_update.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"Group updated successfully","data":{"uid":"1234567890","name":"ABC_DEF_TEST_UPDATE","logo_url":"","config":{"strategy":{"type":"default","default":{"intervalSeconds":10,"retryLimit":3},"exponentialBackoff":{"retryLimit":0}},"signature":{"header":"X-Company-Signature","hash":"SHA1"},"disable_endpoint":false,"replay_attacks":false},"statistics":null,"rate_limit":0,"rate_limit_duration":""}}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestDashboardIntegrationTestSuiteTest/TestGetDashboardSummary/should_error_for_empty_startDate.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"please specify a startDate query"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestDashboardIntegrationTestSuiteTest/TestGetDashboardSummary/should_error_for_invalid_startDate.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"please specify a startDate in the format 2006-01-02T15:04:05"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestDashboardIntegrationTestSuiteTest/TestGetDashboardSummary/should_error_for_invalid_type.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"please specify a type query in (daily, weekly, monthly, yearly)"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestDashboardIntegrationTestSuiteTest/TestGetDashboardSummary/should_error_for_startDate_greater_than_endDate.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"invalid period 'daily': startDate cannot be greater than endDate"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestRequirePermission_Basic/authorization_failed.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"authorization failed"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestRequirePermission_Basic/credentials_not_provided.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"invalid header structure"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestRequirePermission_Basic/invalid_basic_credentials.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"invalid basic credentials"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestRequirePermission_Basic/invalid_credentials.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"invalid credentials"}
2 |
--------------------------------------------------------------------------------
/api/testdata/TestRequirePermission_Basic/valid_credentials.golden:
--------------------------------------------------------------------------------
1 | Hello
--------------------------------------------------------------------------------
/api/testdata/TestRequirePermission_Noop/authorization_failed.golden:
--------------------------------------------------------------------------------
1 | Hello
--------------------------------------------------------------------------------
/api/testdata/TestRequirePermission_Noop/credentials_not_provided.golden:
--------------------------------------------------------------------------------
1 | Hello
--------------------------------------------------------------------------------
/api/testdata/TestRequirePermission_Noop/invalid_credentials.golden:
--------------------------------------------------------------------------------
1 | Hello
--------------------------------------------------------------------------------
/api/testdata/TestRequirePermission_Noop/valid_credentials.golden:
--------------------------------------------------------------------------------
1 | Hello
--------------------------------------------------------------------------------
/api/testdata/Test_applicationHandler_DeleteApp/should_delete_app.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App deleted successfully","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_applicationHandler_DeleteApp/should_fail_to_delete_app.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"an error occurred while deleting app"}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_applicationHandler_DeleteAppEndpoint/should_delete_app_endpoint.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App endpoint deleted successfully","data":null}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_applicationHandler_DeleteAppEndpoint/should_error_for_endpoint_not_found.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"endpoint not found"}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_applicationHandler_DeleteAppEndpoint/should_fail_to_delete_app_endpoint.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"an error occurred while deleting app endpoint"}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_applicationHandler_GetPaginatedApps/valid_groups.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"Apps fetched successfully","data":{"content":[{"uid":"validID","group_id":"1234567890","name":"Valid application - 0","support_email":"","is_disabled":false,"endpoints":[],"events":0}],"pagination":{"total":0,"page":0,"perPage":0,"prev":0,"next":0,"totalPage":0}}}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_fetchAuthConfig/successful_auth_fetch.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"Auth details fetched successfully","data":{"type":"basic","basic":{"username":"test","password":"test"}}}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_resendEventDelivery/invalid__resend_-_pending_endpoint.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"endpoint is being re-activated"}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_resendEventDelivery/invalid_resend_-_event_not_failed.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"cannot resend event that did not fail previously"}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_resendEventDelivery/invalid_resend_-_event_successful.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"event already sent"}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_resendEventDelivery/valid_resend_-_previously_failed_-_active_endpoint.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App event processed for retry successfully","data":{"uid":"2134453454","event_metadata":{"uid":"1122333444456","name":""},"endpoint":{"uid":"","target_url":"http://localhost","status":"active","secret":"","http_timeout":"","rate_limit":0,"rate_limit_duration":"","sent":false},"app_metadata":{"uid":"12345","title":"","group_id":"","support_email":""},"metadata":null,"status":"Scheduled"}}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_resendEventDelivery/valid_resend_-_previously_failed_and_inactive_endpoint.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App event processed for retry successfully","data":{"uid":"2134453454","event_metadata":{"uid":"1122333444456","name":""},"endpoint":{"uid":"","target_url":"http://localhost","status":"inactive","secret":"","http_timeout":"","rate_limit":0,"rate_limit_duration":"","sent":false},"app_metadata":{"uid":"12345","title":"","group_id":"","support_email":""},"metadata":null,"status":"Scheduled"}}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_resendMessage/invalid__resend_-_pending_endpoint.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"endpoint is being re-activated"}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_resendMessage/invalid_resend_-_event_not_failed.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"cannot resend event that did not fail previously"}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_resendMessage/invalid_resend_-_event_successful.golden:
--------------------------------------------------------------------------------
1 | {"status":false,"message":"event already sent"}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_resendMessage/valid_resend_-_previously_failed_-_active_endpoint.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App event processed for retry successfully","data":{"uid":"1122333444456","app_id":"12345","event_type":"","provider_id":"","data":null,"metadata":null,"status":"Scheduled","app_metadata":{"group_id":"","secret":"","support_email":"","endpoints":[{"uid":"","target_url":"http://localhost","status":"active","sent":false}]}}}
2 |
--------------------------------------------------------------------------------
/api/testdata/Test_resendMessage/valid_resend_-_previously_failed_and_inactive_endpoint.golden:
--------------------------------------------------------------------------------
1 | {"status":true,"message":"App event processed for retry successfully","data":{"uid":"1122333444456","app_id":"12345","event_type":"","provider_id":"","data":null,"metadata":null,"status":"Scheduled","app_metadata":{"group_id":"","secret":"","support_email":"","endpoints":[{"uid":"","target_url":"http://localhost","status":"inactive","sent":false}]}}}
2 |
--------------------------------------------------------------------------------
/api/ui/build/go_test_stub.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frain-dev/convoy/26aa8aa67e85f63c8b73fa68465157ad870c47b7/api/ui/build/go_test_stub.txt
--------------------------------------------------------------------------------
/auth/realm.go:
--------------------------------------------------------------------------------
1 | package auth
2 |
3 | import "context"
4 |
5 | type Realm interface {
6 | GetName() string
7 | Authenticate(ctx context.Context, cred *Credential) (*AuthenticatedUser, error)
8 | }
9 |
--------------------------------------------------------------------------------
/cache/cache.go:
--------------------------------------------------------------------------------
1 | package cache
2 |
3 | import (
4 | "context"
5 | "time"
6 |
7 | rcache "github.com/frain-dev/convoy/cache/redis"
8 | "github.com/frain-dev/convoy/config"
9 | )
10 |
11 | type Cache interface {
12 | Set(ctx context.Context, key string, data interface{}, expiration time.Duration) error
13 | Get(ctx context.Context, key string, data interface{}) error
14 | Delete(ctx context.Context, key string) error
15 | }
16 |
17 | func NewCache(cfg config.RedisConfiguration) (Cache, error) {
18 | ca, err := rcache.NewRedisCache(cfg.BuildDsn())
19 | if err != nil {
20 | return nil, err
21 | }
22 |
23 | return ca, nil
24 | }
25 |
--------------------------------------------------------------------------------
/cache/noop/noop.go:
--------------------------------------------------------------------------------
1 | package ncache
2 |
3 | import (
4 | "context"
5 | "time"
6 | )
7 |
8 | type NoopCache struct{}
9 |
10 | func NewNoopCache() *NoopCache {
11 | return &NoopCache{}
12 | }
13 |
14 | func (n *NoopCache) Set(ctx context.Context, key string, data interface{}, ttl time.Duration) error {
15 | return nil
16 | }
17 |
18 | func (n *NoopCache) Get(ctx context.Context, key string, data interface{}) error {
19 | return nil
20 | }
21 |
22 | func (n *NoopCache) Delete(ctx context.Context, key string) error {
23 | return nil
24 | }
25 |
--------------------------------------------------------------------------------
/cmd/utils/utils.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/frain-dev/convoy/internal/pkg/cli"
5 | "github.com/spf13/cobra"
6 | )
7 |
8 | var utilsCmd = &cobra.Command{
9 | Use: "utils",
10 | Short: "runs utility commands",
11 | Annotations: map[string]string{
12 | "CheckMigration": "true",
13 | "ShouldBootstrap": "false",
14 | },
15 | }
16 |
17 | func AddUtilsCommand(app *cli.App) *cobra.Command {
18 | utilsCmd.AddCommand(AddPartitionCommand(app))
19 | utilsCmd.AddCommand(AddUnPartitionCommand(app))
20 |
21 | utilsCmd.AddCommand(AddInitEncryptionCommand(app))
22 | utilsCmd.AddCommand(AddRotateKeyCommand(app))
23 | utilsCmd.AddCommand(AddRevertEncryptionCommand(app))
24 | return utilsCmd
25 | }
26 |
--------------------------------------------------------------------------------
/cmd/version/version.go:
--------------------------------------------------------------------------------
1 | package version
2 |
3 | import (
4 | "github.com/spf13/cobra"
5 | )
6 |
7 | func AddVersionCommand() *cobra.Command {
8 | cmd := &cobra.Command{
9 | Use: "version",
10 | Short: "Print the version",
11 | Annotations: map[string]string{
12 | "CheckMigration": "true",
13 | "ShouldBootstrap": "false",
14 | },
15 | RunE: func(cmd *cobra.Command, args []string) error {
16 | root := cmd.Root()
17 | root.SetArgs([]string{"--version"})
18 | err := root.Execute()
19 | if err != nil {
20 | return err
21 | }
22 |
23 | return nil
24 | },
25 | PersistentPostRun: func(cmd *cobra.Command, args []string) {},
26 | }
27 |
28 | return cmd
29 | }
30 |
--------------------------------------------------------------------------------
/config/testdata/Config/empty-api-key-auth-group-name.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "scheme": "postgres",
4 | "host": "inside-config-file",
5 | "username": "postgres",
6 | "password": "postgres",
7 | "database": "convoy",
8 | "options": "sslmode=disable&connect_timeout=30",
9 | "port": 5432
10 | },
11 | "redis": {
12 | "port": 8379,
13 | "scheme": "redis",
14 | "host": "localhost"
15 | },
16 | "server": {
17 | "http": {
18 | "port": 80
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/config/testdata/Config/empty-api-key.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "scheme": "postgres",
4 | "host": "inside-config-file",
5 | "username": "postgres",
6 | "password": "postgres",
7 | "database": "convoy",
8 | "options": "sslmode=disable&connect_timeout=30",
9 | "port": 5432
10 | },
11 | "redis": {
12 | "port": 8379,
13 | "scheme": "redis",
14 | "host": "localhost"
15 | },
16 | "server": {
17 | "http": {
18 | "port": 80
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/config/testdata/Config/empty-redis-dsn.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "scheme": "postgres",
4 | "host": "inside-config-file",
5 | "username": "postgres",
6 | "password": "postgres",
7 | "database": "convoy",
8 | "options": "sslmode=disable&connect_timeout=30",
9 | "port": 5432
10 | },
11 | "redis": {
12 | "scheme": "",
13 | "host": "",
14 | "username": "",
15 | "password": "",
16 | "database": "",
17 | "port": 0
18 | },
19 | "server": {
20 | "http": {
21 | "port": 80
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/config/testdata/Config/empty-ssl-cert-file.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "scheme": "postgres",
4 | "host": "inside-config-file",
5 | "username": "postgres",
6 | "password": "postgres",
7 | "database": "convoy",
8 | "options": "sslmode=disable&connect_timeout=30",
9 | "port": 5432
10 | },
11 | "redis": {
12 | "port": 8379,
13 | "scheme": "redis",
14 | "host": "localhost"
15 | },
16 | "server": {
17 | "http": {
18 | "ssl": true,
19 | "ssl_key_file": "abc",
20 | "ssl_cert_file": "",
21 | "port": 80
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/config/testdata/Config/empty-ssl-key-file.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "scheme": "postgres",
4 | "host": "inside-config-file",
5 | "username": "postgres",
6 | "password": "postgres",
7 | "database": "convoy",
8 | "options": "sslmode=disable&connect_timeout=30",
9 | "port": 5432
10 | },
11 | "redis": {
12 | "port": 8379,
13 | "scheme": "redis",
14 | "host": "localhost"
15 | },
16 | "server": {
17 | "http": {
18 | "ssl": true,
19 | "ssl_key_file": "",
20 | "ssl_cert_file": "abc",
21 | "port": 80
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/config/testdata/Config/no-port-convoy.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "scheme": "postgres",
4 | "host": "inside-config-file",
5 | "username": "postgres",
6 | "password": "postgres",
7 | "database": "convoy",
8 | "options": "sslmode=disable&connect_timeout=30",
9 | "port": 5432
10 | },
11 | "redis": {
12 | "port": 8379,
13 | "scheme": "redis",
14 | "host": "localhost"
15 | },
16 | "server": {
17 | "http": {}
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/config/testdata/Config/too-large-max-response-size-convoy.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "scheme": "postgres",
4 | "host": "inside-config-file",
5 | "username": "postgres",
6 | "password": "postgres",
7 | "database": "convoy",
8 | "options": "sslmode=disable&connect_timeout=30",
9 | "port": 5432
10 | },
11 | "redis": {
12 | "port": 8379,
13 | "scheme": "redis",
14 | "host": "localhost"
15 | },
16 | "max_response_size": 55,
17 | "server": {
18 | "http": {
19 | "port": 80
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/config/testdata/Config/unknown-strategy-type.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "scheme": "postgres",
4 | "host": "inside-config-file",
5 | "username": "postgres",
6 | "password": "postgres",
7 | "database": "convoy",
8 | "options": "sslmode=disable&connect_timeout=30",
9 | "port": 5432
10 | },
11 | "redis": {
12 | "port": 8379,
13 | "scheme": "redis",
14 | "host": "localhost"
15 | },
16 | "server": {
17 | "http": {
18 | "port": 80
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/config/testdata/Config/valid-convoy-redis-cluster.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "scheme": "postgres",
4 | "host": "inside-config-file",
5 | "username": "postgres",
6 | "password": "postgres",
7 | "database": "convoy",
8 | "options": "sslmode=disable&connect_timeout=30",
9 | "port": 5432
10 | },
11 | "redis": {
12 | "addresses": "localhost:7001,localhost:7002,localhost:7003,localhost:7004,localhost:7005,localhost:7006"
13 | },
14 | "max_response_size": 40,
15 | "server": {
16 | "http": {
17 | "port": 80
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/config/testdata/Config/valid-convoy.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "scheme": "postgres",
4 | "host": "inside-config-file",
5 | "username": "postgres",
6 | "password": "postgres",
7 | "database": "convoy",
8 | "options": "sslmode=disable&connect_timeout=30",
9 | "port": 5432
10 | },
11 | "redis": {
12 | "port": 8379,
13 | "scheme": "redis",
14 | "host": "localhost"
15 | },
16 | "retention_policy": {
17 | "enabled": true,
18 | "policy": "720h"
19 | },
20 | "max_response_size": 40,
21 | "server": {
22 | "http": {
23 | "port": 80
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/config/testdata/Config/zero-max-response-size-convoy.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "scheme": "postgres",
4 | "host": "inside-config-file",
5 | "username": "postgres",
6 | "password": "postgres",
7 | "database": "convoy",
8 | "options": "sslmode=disable&connect_timeout=30",
9 | "port": 5432
10 | },
11 | "redis": {
12 | "port": 8379,
13 | "scheme": "redis",
14 | "host": "localhost"
15 | },
16 | "server": {
17 | "http": {
18 | "port": 80
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/config/testdata/Test_ConfigurationFromEnvironment/convoy.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "scheme": "postgres",
4 | "host": "inside-config-file",
5 | "username": "postgres",
6 | "password": "postgres",
7 | "database": "convoy",
8 | "options": "sslmode=disable&connect_timeout=30",
9 | "port": 5432
10 | },
11 | "redis": {
12 | "port": 8379,
13 | "scheme": "redis",
14 | "host": "localhost"
15 | },
16 | "server": {
17 | "http": {
18 | "port": 8080
19 | }
20 | },
21 | "auth": {
22 | "require_auth": true,
23 | "native": {
24 | "enabled": true
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/configs/caddyfile:
--------------------------------------------------------------------------------
1 | localhost
2 |
--------------------------------------------------------------------------------
/configs/local/conf/userlists.txt:
--------------------------------------------------------------------------------
1 | "convoy" "pg_password"
2 |
--------------------------------------------------------------------------------
/configs/local/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | ./cmd migrate up
4 | ./cmd server --config convoy.json
5 |
--------------------------------------------------------------------------------
/const.go:
--------------------------------------------------------------------------------
1 | package convoy
2 |
3 | import "time"
4 |
5 | const (
6 | HttpPost HttpMethod = "POST"
7 | HttpGet HttpMethod = "GET"
8 | )
9 |
10 | const (
11 | HTTP_RATE_LIMIT = 25
12 | HTTP_RATE_LIMIT_PER_MIN = HTTP_RATE_LIMIT * 60
13 |
14 | INGRESS_RATE_LIMIT = 100
15 | INGRESS_RATE_LIMIT_PER_MIN = HTTP_RATE_LIMIT * 60
16 |
17 | EGRESS_RATE_LIMIT = 100
18 | EGRESS_RATE_LIMIT_PER_MIN = HTTP_RATE_LIMIT * 60
19 |
20 | HTTP_TIMEOUT = 10
21 | HTTP_TIMEOUT_IN_DURATION = time.Duration(HTTP_TIMEOUT) * time.Second
22 | )
23 |
--------------------------------------------------------------------------------
/convoy.go:
--------------------------------------------------------------------------------
1 | package convoy
2 |
--------------------------------------------------------------------------------
/database/database.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "context"
5 | "github.com/frain-dev/convoy/database/hooks"
6 | "github.com/jmoiron/sqlx"
7 | )
8 |
9 | type Database interface {
10 | GetDB() *sqlx.DB
11 | GetReadDB() *sqlx.DB
12 | BeginTx(context.Context) (*sqlx.Tx, error)
13 | GetHook() *hooks.Hook
14 | Rollback(tx *sqlx.Tx, err error)
15 | Close() error
16 | }
17 |
--------------------------------------------------------------------------------
/database/sqlite3/sqlite3.go:
--------------------------------------------------------------------------------
1 | package sqlite3
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/jmoiron/sqlx"
7 | _ "github.com/mattn/go-sqlite3"
8 | )
9 |
10 | const pkgName = "sqlite3"
11 |
12 | type Sqlite struct {
13 | dbx *sqlx.DB
14 | }
15 |
16 | func NewDB() (*Sqlite, error) {
17 | db, err := sqlx.Connect("sqlite3", "convoy.db")
18 | if err != nil {
19 | return nil, fmt.Errorf("[%s]: failed to open database - %v", pkgName, err)
20 | }
21 | return &Sqlite{dbx: db}, nil
22 | }
23 |
24 | func (s *Sqlite) GetDB() *sqlx.DB {
25 | return s.dbx
26 | }
27 |
--------------------------------------------------------------------------------
/datastore/testdata/organisations.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - id: 2dade341-799e-4bb7-bf4a-b04a23b551c3
3 | org_name: Convoy Inc
4 |
5 |
--------------------------------------------------------------------------------
/ee/VERSION:
--------------------------------------------------------------------------------
1 | v25.2.2 Enterprise Edition
2 |
--------------------------------------------------------------------------------
/internal/pkg/limiter/limiter.go:
--------------------------------------------------------------------------------
1 | package limiter
2 |
3 | import (
4 | "context"
5 | "github.com/frain-dev/convoy/config"
6 | rlimiter "github.com/frain-dev/convoy/internal/pkg/limiter/redis"
7 | )
8 |
9 | type RateLimiter interface {
10 | // Allow rate limits outgoing events to endpoints based on a rate in a specified time duration by the endpoint id
11 | Allow(ctx context.Context, key string, rate int) error
12 | AllowWithDuration(ctx context.Context, key string, rate int, duration int) error
13 | }
14 |
15 | func NewLimiter(cfg config.Configuration) (RateLimiter, error) {
16 | r, err := rlimiter.NewRedisLimiter(cfg.Redis.BuildDsn())
17 | if err != nil {
18 | return nil, err
19 | }
20 |
21 | return r, nil
22 | }
23 |
--------------------------------------------------------------------------------
/internal/pkg/memorystore/row.go:
--------------------------------------------------------------------------------
1 | package memorystore
2 |
3 | type Row struct {
4 | key string
5 | value interface{}
6 | }
7 |
8 | func NewRow(key string, value interface{}) *Row {
9 | return &Row{
10 | key: key,
11 | value: value,
12 | }
13 | }
14 |
15 | func (r *Row) Key() string {
16 | return r.key
17 | }
18 |
19 | func (r *Row) Value() interface{} {
20 | return r.value
21 | }
22 |
--------------------------------------------------------------------------------
/internal/pkg/object-store/onprem.go:
--------------------------------------------------------------------------------
1 | package objectstore
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/frain-dev/convoy/pkg/log"
7 | )
8 |
9 | type OnPremClient struct {
10 | opts ObjectStoreOptions
11 | }
12 |
13 | func NewOnPremClient(opts ObjectStoreOptions) (ObjectStore, error) {
14 | client := &OnPremClient{
15 | opts: opts,
16 | }
17 | return client, nil
18 |
19 | }
20 |
21 | func (o *OnPremClient) Save(filename string) error {
22 | if _, err := os.Stat(filename); err != nil {
23 | return err
24 | }
25 | log.Printf("Successfully saved %q \n", filename)
26 | return nil
27 | }
28 |
--------------------------------------------------------------------------------
/internal/pkg/smtp/smtp_test.go:
--------------------------------------------------------------------------------
1 | package smtp
2 |
--------------------------------------------------------------------------------
/internal/pkg/socket/socket.go:
--------------------------------------------------------------------------------
1 | package socket
2 |
3 | type WebSocketConnection interface {
4 | Close() error
5 | SetReadLimit(limit int64)
6 | SetPingHandler(h func(appData string) error)
7 | WriteMessage(messageType int, data []byte) error
8 | ReadMessage() (messageType int, p []byte, err error)
9 | }
10 |
--------------------------------------------------------------------------------
/internal/pkg/tracer/noop.go:
--------------------------------------------------------------------------------
1 | package tracer
2 |
3 | import (
4 | "context"
5 | "time"
6 |
7 | "github.com/frain-dev/convoy/config"
8 | )
9 |
10 | // NoOpBackend is a no-operation tracer backend implementation.
11 | type NoOpBackend struct{}
12 |
13 | func (NoOpBackend) Init(string) error { return nil }
14 | func (NoOpBackend) Type() config.TracerProvider {
15 | return ""
16 | }
17 | func (NoOpBackend) Capture(context.Context, string, map[string]interface{}, time.Time, time.Time) {}
18 | func (NoOpBackend) Shutdown(context.Context) error {
19 | return nil
20 | }
21 |
--------------------------------------------------------------------------------
/pkg/flatten/types.go:
--------------------------------------------------------------------------------
1 | package flatten
2 |
3 | type M = map[string]interface{}
4 |
--------------------------------------------------------------------------------
/pkg/models/webhook.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | // WebhookSchema represents a webhook schema extracted from OpenAPI spec
4 | type WebhookSchema struct {
5 | Name string `json:"name"`
6 | Description string `json:"description,omitempty"`
7 | Schema map[string]interface{} `json:"schema"`
8 | }
9 |
10 | // WebhookCollection represents a collection of webhook schemas
11 | type WebhookCollection struct {
12 | ProjectID string `json:"project_id"`
13 | Webhooks []WebhookSchema `json:"webhooks"`
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/msgpack/msgpack.go:
--------------------------------------------------------------------------------
1 | package msgpack
2 |
3 | import (
4 | "bytes"
5 | "github.com/vmihailenco/msgpack/v5"
6 | )
7 |
8 | func EncodeMsgPack(payload interface{}) ([]byte, error) {
9 | var buf bytes.Buffer
10 | enc := msgpack.NewEncoder(&buf)
11 | enc.SetCustomStructTag("json")
12 |
13 | err := enc.Encode(payload)
14 | if err != nil {
15 | return nil, err
16 | }
17 |
18 | return buf.Bytes(), nil
19 | }
20 |
21 | func DecodeMsgPack(pack []byte, target interface{}) error {
22 | var buf bytes.Buffer
23 | buf.Write(pack)
24 |
25 | enc := msgpack.NewDecoder(&buf)
26 | enc.SetCustomStructTag("json")
27 |
28 | err := enc.Decode(&target)
29 | if err != nil {
30 | return err
31 | }
32 |
33 | return nil
34 | }
35 |
--------------------------------------------------------------------------------
/pkg/url/url_query.go:
--------------------------------------------------------------------------------
1 | package url
2 |
3 | import "net/url"
4 |
5 | func ConcatQueryParams(targetURL, query string) (string, error) {
6 | u, err := url.Parse(targetURL)
7 | if err != nil {
8 | return "", err
9 | }
10 |
11 | parsedValues, err := url.ParseQuery(query)
12 | if err != nil {
13 | return "", err
14 | }
15 |
16 | q := u.Query()
17 |
18 | for k, v := range parsedValues {
19 | for _, s := range v {
20 | q.Add(k, s)
21 | }
22 | }
23 |
24 | u.RawQuery = q.Encode()
25 |
26 | return u.String(), nil
27 | }
28 |
--------------------------------------------------------------------------------
/plugin.go:
--------------------------------------------------------------------------------
1 | package convoy
2 |
3 | import "net/http"
4 |
5 | type Plugin interface {
6 | Apply(http.ResponseWriter, *http.Request) error
7 | Name() string
8 | IsEnabled() bool
9 | }
10 |
--------------------------------------------------------------------------------
/plugins/add_headers_plugin.go:
--------------------------------------------------------------------------------
1 | package convoy
2 |
3 | import "net/http"
4 |
5 | type AddHeadersPlugin struct {
6 | config map[string]string
7 | }
8 |
9 | func (a *AddHeadersPlugin) Apply(w http.ResponseWriter, r *http.Request) error {
10 |
11 | for k, v := range a.config {
12 | w.Header().Add(k, v)
13 | }
14 |
15 | return nil
16 | }
17 |
18 | func (a *AddHeadersPlugin) Name() string { return "Add Headers" }
19 |
20 | func (a *AddHeadersPlugin) IsEnabled() bool { return len(a.config) > 0 }
21 |
--------------------------------------------------------------------------------
/prometheus/prometheus.yml:
--------------------------------------------------------------------------------
1 | global:
2 | scrape_interval: 15s
3 | evaluation_interval: 15s
4 |
5 | scrape_configs:
6 | - job_name: prometheus
7 | static_configs:
8 | - targets: ['prometheus:9090']
9 | - job_name: agent
10 | metrics_path: /metrics
11 | static_configs:
12 | - targets: ["agent:5008"]
13 | - job_name: web
14 | metrics_path: /metrics
15 | static_configs:
16 | - targets: ["web:5005"]
17 | - job_name: worker
18 | metrics_path: /metrics
19 | static_configs:
20 | - targets: ["worker:5006"]
21 | - job_name: ingest
22 | metrics_path: /metrics
23 | static_configs:
24 | - targets: ["ingest:5009"]
25 |
--------------------------------------------------------------------------------
/queue/queue.go:
--------------------------------------------------------------------------------
1 | package queue
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/frain-dev/convoy"
7 | "github.com/frain-dev/convoy/internal/pkg/rdb"
8 | )
9 |
10 | type Queuer interface {
11 | Write(convoy.TaskName, convoy.QueueName, *Job) error
12 | Options() QueueOptions
13 | }
14 |
15 | type Job struct {
16 | ID string `json:"id"`
17 | Payload []byte `json:"payload"`
18 | Delay time.Duration `json:"delay"`
19 | }
20 |
21 | type QueueOptions struct {
22 | Names map[string]int
23 | Type string
24 | RedisClient *rdb.Redis
25 | RedisAddress []string
26 | PrometheusAddress string
27 | }
28 |
--------------------------------------------------------------------------------
/queue/testdata/convoy_redis.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "dsn": "mongodb://inside-config-file"
4 | },
5 | "queue": {
6 | "type": "redis",
7 | "redis": {
8 | "dsn": "redis://localhost:6379"
9 | }
10 | },
11 | "server": {
12 | "http": {
13 | "port": 80
14 | }
15 | },
16 | "group": {
17 | "strategy": {
18 | "type": "default",
19 | "default": {
20 | "intervalSeconds": 125,
21 | "retryLimit": 15
22 | }
23 | },
24 | "signature": {
25 | "hash": "SHA256"
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/release.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.20.2
2 |
3 | # Define a build-time argument
4 | ARG IMAGE_SHA
5 |
6 | # Set an environment variable using the ARG
7 | ENV CORE_GATEWAY_IMAGE_SHA=${IMAGE_SHA}
8 |
9 | # Copy the Convoy binary
10 | COPY convoy /cmd
11 |
12 | # Copy the migrations directory
13 | COPY sql/ /sql/
14 |
15 | # Copy the startup script
16 | COPY configs/local/start.sh /start.sh
17 |
18 | # Set permissions
19 | RUN chmod +x /cmd /start.sh
20 |
21 | # Install necessary dependencies
22 | RUN apk add --no-cache gcompat
23 |
24 | # Set the startup command
25 | CMD ["/start.sh"]
26 |
27 |
--------------------------------------------------------------------------------
/retrystrategies/default.go:
--------------------------------------------------------------------------------
1 | package retrystrategies
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | type DefaultRetryStrategy struct {
8 | intervalSeconds uint64
9 | }
10 |
11 | func (r *DefaultRetryStrategy) NextDuration(attempts uint64) time.Duration {
12 | return time.Duration(r.intervalSeconds) * time.Second
13 | }
14 |
15 | func NewDefault(intervalSeconds uint64) *DefaultRetryStrategy {
16 | return &DefaultRetryStrategy{
17 | intervalSeconds: intervalSeconds,
18 | }
19 | }
20 |
21 | var _ RetryStrategy = (*DefaultRetryStrategy)(nil)
22 |
--------------------------------------------------------------------------------
/retrystrategies/retry.go:
--------------------------------------------------------------------------------
1 | package retrystrategies
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/frain-dev/convoy/datastore"
7 | )
8 |
9 | type RetryStrategy interface {
10 | // NextDuration is how long we should wait before next retry
11 | NextDuration(attempts uint64) time.Duration
12 | }
13 |
14 | func NewRetryStrategyFromMetadata(m datastore.Metadata) RetryStrategy {
15 | if string(m.Strategy) == string(datastore.ExponentialStrategyProvider) {
16 | return NewExponential(m.IntervalSeconds, m.MaxRetrySeconds)
17 | }
18 |
19 | return NewDefault(m.IntervalSeconds)
20 | }
21 |
--------------------------------------------------------------------------------
/scripts/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # export CGO_ENABLED=0
4 | # export GOOS=linux
5 | # export GOARCH=arm64
6 |
7 | buildConvoy() {
8 | echo "Building Convoy ..."
9 |
10 | # Build UI.
11 | UIDIR="api/ui/build"
12 |
13 | # Remove build folder
14 | rm -rf $UIDIR
15 |
16 | # Recreate build folder
17 | mkdir $UIDIR
18 |
19 | # Enter UI directory
20 | cd ./web/ui/dashboard
21 |
22 | # Install dependencies
23 | npm i
24 |
25 | # Run production build
26 | npm run build
27 |
28 | # Copy build artifacts
29 | cd ../../../
30 | mv web/ui/dashboard/dist/* $UIDIR
31 |
32 | # Build Binary
33 | go build -o convoy ./cmd/*.go
34 | }
35 |
36 | buildConvoy
37 |
--------------------------------------------------------------------------------
/scripts/integration-test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export TEST_DB_SCHEME=postgres
4 | export TEST_DB_OPTIONS="sslmode=disable&connect_timeout=30"
5 | export TEST_DB_HOST=localhost
6 | export TEST_DB_USERNAME=postgres
7 | export TEST_DB_PASSWORD=postgres
8 | export TEST_DB_DATABASE=test
9 | export TEST_DB_PORT=5432
10 |
11 | export TEST_REDIS_SCHEME=redis
12 | export TEST_REDIS_HOST=localhost
13 | export TEST_REDIS_PORT=6379
14 |
15 | make integration_tests
16 |
17 | make docker_e2e_tests
18 |
--------------------------------------------------------------------------------
/scripts/release.sh:
--------------------------------------------------------------------------------
1 | # Set version
2 | tag=$1
3 | etag="$tag Enterprise Edition"
4 | : > ./VERSION && echo $tag > VERSION
5 | : > ./ee/VERSION && echo $etag > ./ee/VERSION
6 |
7 | # Commit version number & push
8 | git add VERSION ./ee/VERSION
9 | git commit -m "Bump version to $tag"
10 | git push origin
11 |
12 | # Tag & Push.
13 | git tag $tag
14 | git push origin $tag
15 |
--------------------------------------------------------------------------------
/scripts/replica-set.sh:
--------------------------------------------------------------------------------
1 | docker exec mongo1 mongosh --eval "rs.initiate({
2 | _id: \"myReplicaSet\",
3 | members: [
4 | {_id: 0, host: \"mongo1\"},
5 | {_id: 1, host: \"mongo2\"},
6 | {_id: 2, host: \"mongo3\"}
7 | ]
8 | })"
9 |
--------------------------------------------------------------------------------
/services/errors.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | type ServiceError struct {
4 | ErrMsg string
5 | Err error
6 | }
7 |
8 | func (a *ServiceError) Error() string {
9 | return a.ErrMsg
10 | }
11 |
12 | func (a *ServiceError) Unwrap() error {
13 | return a.Err
14 | }
15 |
--------------------------------------------------------------------------------
/slim.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM gcr.io/distroless/base
2 | # the name of the binary generated by goreleaser
3 | COPY convoy /cmd
4 | ENTRYPOINT [ "/cmd" ]
5 | CMD ["server", "--config", "convoy.json"]
--------------------------------------------------------------------------------
/sql/1679836136.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | CREATE TABLE IF NOT EXISTS convoy.devices_backup AS SELECT * FROM convoy.devices;
3 | ALTER TABLE convoy.devices DROP COLUMN endpoint_id;
4 | DROP TABLE IF EXISTS convoy.devices_backup;
5 |
6 | -- +migrate Down
7 | CREATE TABLE IF NOT EXISTS convoy.devices_backup AS SELECT * FROM convoy.devices;
8 | ALTER TABLE convoy.devices ADD COLUMN endpoint_id CHAR(26) REFERENCES convoy.endpoints (id);
9 | DROP TABLE IF EXISTS convoy.devices_backup;
10 |
11 |
--------------------------------------------------------------------------------
/sql/1684884904.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.meta_events ALTER COLUMN id TYPE VARCHAR;
3 | ALTER TABLE convoy.project_configurations ALTER COLUMN id TYPE VARCHAR;
4 | ALTER TABLE convoy.source_verifiers ALTER COLUMN id TYPE VARCHAR;
5 |
6 | -- +migrate Down
7 | ALTER TABLE convoy.meta_events ALTER COLUMN id TYPE CHAR(26);
8 | ALTER TABLE convoy.project_configurations ALTER COLUMN id TYPE CHAR(26);
9 | ALTER TABLE convoy.source_verifiers ALTER COLUMN id TYPE CHAR(26);
10 |
--------------------------------------------------------------------------------
/sql/1684918027.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | CREATE UNIQUE INDEX IF NOT EXISTS organisation_invites_invitee_email_1 ON convoy.organisation_invites(organisation_id, invitee_email, deleted_at) NULLS NOT DISTINCT;
3 |
4 | DROP INDEX IF EXISTS convoy.organisation_invites_invitee_email;
5 |
6 | ALTER INDEX convoy.organisation_invites_invitee_email_1 RENAME TO organisation_invites_invitee_email;
7 |
8 | -- +migrate Down
9 | CREATE UNIQUE INDEX IF NOT EXISTS organisation_invites_invitee_email_1 ON convoy.organisation_invites(organisation_id, invitee_email);
10 |
11 | DROP INDEX IF EXISTS convoy.organisation_invites_invitee_email;
12 |
13 | ALTER INDEX convoy.organisation_invites_invitee_email_1 RENAME TO organisation_invites_invitee_email;
14 |
15 |
--------------------------------------------------------------------------------
/sql/1684929840.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.sources ADD COLUMN IF NOT EXISTS custom_response_body VARCHAR;
3 | ALTER TABLE convoy.sources ADD COLUMN IF NOT EXISTS custom_response_content_type VARCHAR;
4 | -- +migrate Down
5 | ALTER TABLE convoy.sources DROP COLUMN IF EXISTS custom_response_content_type;
6 | ALTER TABLE convoy.sources DROP COLUMN IF EXISTS custom_response_body;
7 |
8 |
--------------------------------------------------------------------------------
/sql/1685202737.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.portal_links
3 | ADD COLUMN IF NOT EXISTS owner_id VARCHAR,
4 | ADD COLUMN IF NOT EXISTS can_manage_endpoint BOOLEAN,
5 | ALTER COLUMN can_manage_endpoint SET DEFAULT false,
6 | ALTER COLUMN endpoints DROP NOT NULL;
7 |
8 | -- +migrate Up
9 | CREATE INDEX IF NOT EXISTS idx_portal_links_owner_id_key ON convoy.portal_links (owner_id);
10 |
11 | -- +migrate Down
12 | ALTER TABLE convoy.portal_links
13 | DROP COLUMN IF EXISTS owner_id,
14 | DROP COLUMN IF EXISTS can_manage_endpoint;
15 |
16 | -- +migrate Down
17 | DROP INDEX IF EXISTS convoy.idx_portal_links_owner_id_key;
18 |
--------------------------------------------------------------------------------
/sql/1686048402.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.events ADD COLUMN IF NOT EXISTS url_query_params VARCHAR;
3 | ALTER TABLE convoy.event_deliveries ADD COLUMN IF NOT EXISTS url_query_params VARCHAR;
4 |
5 | -- +migrate Down
6 | ALTER TABLE convoy.events DROP COLUMN IF EXISTS url_query_params;
7 | ALTER TABLE convoy.event_deliveries DROP COLUMN IF EXISTS url_query_params;
8 |
--------------------------------------------------------------------------------
/sql/1692024707.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.endpoints
3 | ADD CONSTRAINT endpoints_title_project_id_pk
4 | UNIQUE (title, project_id, deleted_at);
5 |
6 | -- +migrate Down
7 | ALTER TABLE convoy.endpoints
8 | DROP CONSTRAINT IF EXISTS endpoints_title_project_id_pk;
9 |
--------------------------------------------------------------------------------
/sql/1693908172.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.subscriptions ADD COLUMN IF NOT EXISTS function TEXT;
3 |
4 | -- +migrate Down
5 | ALTER TABLE IF EXISTS convoy.subscriptions DROP COLUMN IF EXISTS function;
6 |
7 |
--------------------------------------------------------------------------------
/sql/1698074481.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.configurations ADD COLUMN s3_prefix text;
3 |
4 | -- +migrate Down
5 | ALTER TABLE convoy.configurations DROP COLUMN s3_prefix;
6 |
7 |
--------------------------------------------------------------------------------
/sql/1698683940.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.event_deliveries ADD COLUMN IF NOT EXISTS latency TEXT;
3 |
4 | -- +migrate Down
5 | ALTER TABLE convoy.event_deliveries DROP COLUMN IF EXISTS latency;
6 |
7 |
--------------------------------------------------------------------------------
/sql/1704372039.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.event_deliveries ADD COLUMN IF NOT EXISTS event_type TEXT;
3 | CREATE INDEX IF NOT EXISTS event_deliveries_event_type_1 ON convoy.event_deliveries(event_type);
4 |
5 | -- +migrate Down
6 | ALTER TABLE convoy.event_deliveries DROP COLUMN IF EXISTS event_type;
7 | DROP INDEX IF EXISTS convoy.event_deliveries_event_type_1;
8 |
--------------------------------------------------------------------------------
/sql/1709568783.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | CREATE INDEX IF NOT EXISTS idx_project_id_on_not_deleted ON convoy.events(project_id) WHERE deleted_at IS NULL;
3 |
4 | -- +migrate Down
5 | DROP INDEX IF EXISTS idx_project_id_on_not_deleted;
6 |
7 |
8 |
--------------------------------------------------------------------------------
/sql/1710685343.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE IF EXISTS convoy.sources DROP COLUMN IF EXISTS function;
3 | ALTER TABLE convoy.sources ADD COLUMN IF NOT EXISTS body_function TEXT;
4 | ALTER TABLE convoy.sources ADD COLUMN IF NOT EXISTS header_function TEXT;
5 | ALTER TABLE convoy.project_configurations ADD COLUMN IF NOT EXISTS multiple_endpoint_subscriptions bool NOT NULL DEFAULT FALSE;
6 |
7 | -- +migrate Down
8 | ALTER TABLE IF EXISTS convoy.sources DROP COLUMN IF EXISTS body_function;
9 | ALTER TABLE IF EXISTS convoy.sources DROP COLUMN IF EXISTS header_function;
10 | ALTER TABLE IF EXISTS convoy.project_configurations DROP COLUMN IF EXISTS multiple_endpoint_subscriptions;
11 |
--------------------------------------------------------------------------------
/sql/1710763531.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.project_configurations ADD COLUMN IF NOT EXISTS ssl_enforce_secure_endpoints BOOLEAN DEFAULT TRUE;
3 |
4 | -- +migrate Down
5 | ALTER TABLE convoy.project_configurations DROP COLUMN IF EXISTS ssl_enforce_secure_endpoints;
6 |
--------------------------------------------------------------------------------
/sql/1718819653.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.subscriptions ADD COLUMN filter_config_filter_is_flattened BOOLEAN DEFAULT false;
3 |
4 | -- +migrate Down
5 | ALTER TABLE convoy.subscriptions DROP COLUMN filter_config_filter_is_flattened;
6 |
--------------------------------------------------------------------------------
/sql/1719521272.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.events ADD COLUMN IF NOT EXISTS acknowledged_at TIMESTAMPTZ DEFAULT NULL;
3 | ALTER TABLE convoy.event_deliveries ADD COLUMN IF NOT EXISTS acknowledged_at TIMESTAMPTZ DEFAULT NULL;
4 |
5 | -- +migrate Down
6 | ALTER TABLE convoy.events DROP COLUMN IF EXISTS acknowledged_at;
7 | ALTER TABLE convoy.event_deliveries DROP COLUMN IF EXISTS acknowledged_at;
8 |
--------------------------------------------------------------------------------
/sql/1719521273.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.event_deliveries ADD COLUMN IF NOT EXISTS latency_seconds numeric DEFAULT NULL;
3 |
4 | -- +migrate Down
5 | ALTER TABLE convoy.event_deliveries DROP COLUMN IF EXISTS latency_seconds;
6 |
--------------------------------------------------------------------------------
/sql/1729585620.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | CREATE INDEX IF NOT EXISTS idx_delivery_attempts_event_delivery_id_created_at_desc ON convoy.delivery_attempts(event_delivery_id, created_at DESC);
3 |
4 | -- +migrate Down
5 | DROP INDEX IF EXISTS convoy.idx_delivery_attempts_event_delivery_id_created_at_desc;
6 |
--------------------------------------------------------------------------------
/sql/1729709223.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.users ADD COLUMN IF NOT EXISTS auth_type TEXT NOT NULL DEFAULT 'local';
3 |
4 | -- +migrate Down
5 | ALTER TABLE convoy.users DROP COLUMN IF EXISTS auth_type;
6 |
--------------------------------------------------------------------------------
/sql/1733488138.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | create unique index if not exists idx_event_types_name_project_id
3 | on convoy.event_types (project_id, name);
4 |
5 | -- +migrate Down
6 | drop index if exists convoy.idx_event_types_name_project_id;
7 |
--------------------------------------------------------------------------------
/sql/1733518672.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | ALTER TABLE convoy.endpoints
3 | ADD COLUMN IF NOT EXISTS is_encrypted BOOLEAN DEFAULT FALSE,
4 | ADD COLUMN IF NOT EXISTS secrets_cipher bytea,
5 | ADD COLUMN IF NOT EXISTS authentication_type_api_key_header_value_cipher bytea;
6 |
7 | CREATE INDEX idx_endpoints_is_encrypted ON convoy.endpoints (is_encrypted);
8 |
9 | -- +migrate Down
10 | DROP INDEX IF EXISTS idx_endpoints_is_encrypted;
11 |
12 | ALTER TABLE convoy.endpoints
13 | DROP COLUMN IF EXISTS is_encrypted,
14 | DROP COLUMN IF EXISTS secrets_cipher,
15 | DROP COLUMN IF EXISTS authentication_type_api_key_header_value_cipher;
16 |
--------------------------------------------------------------------------------
/sql/1735652782.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | alter table convoy.subscriptions add column filter_config_filter_raw_headers jsonb not null default '{}';
3 | alter table convoy.subscriptions add column filter_config_filter_raw_body jsonb not null default '{}';
4 |
5 | update convoy.subscriptions
6 | set
7 | filter_config_filter_raw_headers = filter_config_filter_headers,
8 | filter_config_filter_raw_body = filter_config_filter_body
9 | where
10 | id > '';
11 |
12 | -- +migrate Down
13 | alter table convoy.subscriptions drop column filter_config_filter_raw_headers;
14 | alter table convoy.subscriptions drop column filter_config_filter_raw_body;
15 |
16 |
--------------------------------------------------------------------------------
/sql/1736807673.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | update convoy.delivery_attempts set response_data = '' where id > '';
3 | alter table convoy.delivery_attempts
4 | alter column response_data type bytea
5 | using response_data::bytea;
6 |
7 | -- +migrate Down
8 | alter table convoy.delivery_attempts
9 | alter column response_data type text
10 | using encode(response_data, 'escape');
11 |
--------------------------------------------------------------------------------
/sql/1742902597.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | alter table convoy.event_types add column json_schema jsonb default '{}';
3 |
4 | -- +migrate Down
5 | alter table convoy.event_types drop column json_schema;
6 |
--------------------------------------------------------------------------------
/sql/1745514784.sql:
--------------------------------------------------------------------------------
1 | -- +migrate Up
2 | create table if not exists convoy.portal_tokens (
3 | id varchar primary key,
4 | portal_link_id varchar not null,
5 | token_mask_id text default '',
6 | token_hash text default '',
7 | token_salt text default '',
8 | token_expires_at timestamptz default null,
9 | created_at timestamptz default current_timestamp,
10 | updated_at timestamptz default current_timestamp,
11 | deleted_at timestamptz,
12 | constraint fk_portal_links
13 | foreign key (portal_link_id)
14 | references convoy.portal_links(id)
15 | on delete cascade
16 | );
17 |
18 | -- +migrate Down
19 | drop table if exists convoy.portal_tokens;
--------------------------------------------------------------------------------
/test/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 |
5 | mongodb:
6 | image: mongo:latest
7 | environment:
8 | MONGO_INITDB_ROOT_USERNAME: root
9 | MONGO_INITDB_ROOT_PASSWORD: rootpassword
10 | ports:
11 | - "57017:27017"
12 | volumes:
13 | - ./data/mongo:/data/db
14 |
--------------------------------------------------------------------------------
/testcon/manifest/counter.go:
--------------------------------------------------------------------------------
1 | package manifest
2 |
3 | import (
4 | "sync"
5 | "sync/atomic"
6 | )
7 |
8 | var ctrLock = sync.RWMutex{}
9 |
10 | func DecrementAndGet(ctr *atomic.Int64) int64 {
11 | ctrLock.Lock()
12 | defer ctrLock.Unlock()
13 | ctr.Add(-1)
14 | return ctr.Load()
15 | }
16 |
--------------------------------------------------------------------------------
/testcon/manifest/endpoint.go:
--------------------------------------------------------------------------------
1 | package manifest
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | )
7 |
8 | var endpoints = map[string]int{}
9 | var lock = sync.RWMutex{}
10 |
11 | func ReadEndpoint(k string) int {
12 | lock.RLock()
13 | defer lock.RUnlock()
14 | return endpoints[k]
15 | }
16 |
17 | func WriteEndpoint(k string, v int) {
18 | lock.Lock()
19 | defer lock.Unlock()
20 | endpoints[k] = v
21 | }
22 |
23 | func IncEndpoint(k string) {
24 | lock.Lock()
25 | defer lock.Unlock()
26 | count := endpoints[k]
27 | count++
28 | endpoints[k] = count
29 | }
30 |
31 | func PrintEndpoints() {
32 | lock.RLock()
33 | defer lock.RUnlock()
34 | fmt.Printf("Size: %d Endpoints: %+v\n", len(endpoints), endpoints)
35 | }
36 |
--------------------------------------------------------------------------------
/testcon/manifest/event.go:
--------------------------------------------------------------------------------
1 | package manifest
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | )
7 |
8 | var events = map[string]int{}
9 | var evLock = sync.RWMutex{}
10 |
11 | func ReadEvent(k string) int {
12 | evLock.RLock()
13 | defer evLock.RUnlock()
14 | return events[k]
15 | }
16 |
17 | func WriteEvent(k string, v int) {
18 | evLock.Lock()
19 | defer evLock.Unlock()
20 | events[k] = v
21 | }
22 |
23 | func IncEvent(k string) {
24 | evLock.Lock()
25 | defer evLock.Unlock()
26 | count := events[k]
27 | count++
28 | events[k] = count
29 | }
30 |
31 | func PrintEvents() {
32 | evLock.RLock()
33 | defer evLock.RUnlock()
34 | fmt.Printf("Size: %d Events: %+v\n", len(events), events)
35 | }
36 |
--------------------------------------------------------------------------------
/util/db.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | func BoolToText(b bool) string {
4 | if b {
5 | return "true"
6 | } else {
7 | return "false"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/util/header.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "net/http"
5 | "strings"
6 |
7 | "github.com/frain-dev/convoy/datastore"
8 | )
9 |
10 | // ConvertDefaultHeaderToCustomHeader converts http.Header to convoy.HttpHeader
11 | func ConvertDefaultHeaderToCustomHeader(h *http.Header) *datastore.HttpHeader {
12 | res := make(datastore.HttpHeader)
13 | for k, v := range *h {
14 | res[k] = strings.Join(v, " ")
15 | }
16 |
17 | return &res
18 | }
19 |
--------------------------------------------------------------------------------
/util/slice.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | // Difference returns the items present in a, that
4 | // are not found in b. E.g.
5 | // E.g. Difference([1,2,3], [1,2]) = [3]
6 | func Difference(a, b []string) []string {
7 | mb := make(map[string]struct{}, len(b))
8 | for _, x := range b {
9 | mb[x] = struct{}{}
10 | }
11 |
12 | var diff []string
13 | for _, x := range a {
14 | if _, found := mb[x]; !found {
15 | diff = append(diff, x)
16 | }
17 | }
18 |
19 | return diff
20 | }
21 |
--------------------------------------------------------------------------------
/web/ui/dashboard/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 4
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/web/ui/dashboard/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "tabWidth": 4,
4 | "printWidth": 280,
5 | "semi": true,
6 | "endOfLine": "auto",
7 | "arrowParens": "avoid",
8 | "bracketSpacing": true,
9 | "htmlWhitespaceSensitivity": "ignore",
10 | "insertPragma": false,
11 | "jsxBracketSameLine": true,
12 | "jsxSingleQuote": false,
13 | "proseWrap": "preserve",
14 | "quoteProps": "as-needed",
15 | "requirePragma": false,
16 | "trailingComma": "none",
17 | "useTabs": true,
18 | "vueIndentScriptAndStyle": false
19 | }
20 |
--------------------------------------------------------------------------------
/web/ui/dashboard/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "stories": [
3 | "../src/**/*.stories.mdx",
4 | "../src/**/*.stories.@(js|jsx|ts|tsx)"
5 | ],
6 | "addons": [
7 | "@storybook/addon-links",
8 | "@storybook/addon-essentials",
9 | "@storybook/addon-interactions"
10 | ],
11 | "framework": "@storybook/angular",
12 | "core": {
13 | "builder": "@storybook/builder-webpack5"
14 | }
15 | }
--------------------------------------------------------------------------------
/web/ui/dashboard/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import { setCompodocJson } from "@storybook/addon-docs/angular";
2 | import docJson from "../documentation.json";
3 | setCompodocJson(docJson);
4 |
5 | export const parameters = {
6 | actions: { argTypesRegex: "^on[A-Z].*" },
7 | controls: {
8 | matchers: {
9 | color: /(background|color)$/i,
10 | date: /Date$/,
11 | },
12 | },
13 | docs: { inlineStories: true },
14 | }
--------------------------------------------------------------------------------
/web/ui/dashboard/.storybook/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.app.json",
3 | "compilerOptions": {
4 | "types": [
5 | "node"
6 | ],
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "exclude": [
10 | "../src/test.ts",
11 | "../src/**/*.spec.ts",
12 | "../projects/**/*.spec.ts"
13 | ],
14 | "include": [
15 | "../src/**/*",
16 | "../projects/**/*"
17 | ],
18 | "files": [
19 | "./typings.d.ts"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/web/ui/dashboard/.storybook/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.md' {
2 | const content: string;
3 | export default content;
4 | }
5 |
--------------------------------------------------------------------------------
/web/ui/dashboard/documentation.json:
--------------------------------------------------------------------------------
1 | {
2 | "pipes": [],
3 | "interfaces": [],
4 | "injectables": [],
5 | "guards": [],
6 | "interceptors": [],
7 | "classes": [],
8 | "directives": [],
9 | "components": [],
10 | "modules": [],
11 | "miscellaneous": [],
12 | "routes": [],
13 | "coverage": {
14 | "count": 0,
15 | "status": "low",
16 | "files": []
17 | }
18 | }
--------------------------------------------------------------------------------
/web/ui/dashboard/purgecss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | safelist: ['tag--Failure', 'tag--Retry', 'tag--Success']
3 | };
4 |
--------------------------------------------------------------------------------
/web/ui/dashboard/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
{{ description }}
5 | 6 |