├── .ackrc ├── .cfignore ├── .github ├── dependabot.yml └── workflows │ └── go.yml ├── .gitignore ├── .tool-versions ├── LICENSE ├── NOTICE ├── README.md ├── V1_API.md ├── application ├── application.go ├── environment.go ├── environment_test.go ├── init_test.go ├── migrator.go ├── migrator_test.go └── mother.go ├── bin ├── acceptance ├── env │ └── test ├── integration ├── run ├── test └── unit ├── catalog └── catalog-info.yaml ├── cf ├── cloud_controller.go ├── get_auditors_by_organization_guid.go ├── get_auditors_by_organization_guid_test.go ├── get_billing_managers_by_organization_guid.go ├── get_billing_managers_by_organization_guid_test.go ├── get_managers_by_organization_guid.go ├── get_managers_by_organization_guid_test.go ├── get_users_by_organization_guid.go ├── get_users_by_organization_guid_test.go ├── get_users_by_space_guid.go ├── get_users_by_space_guid_test.go ├── init_test.go ├── load_organization.go ├── load_organization_test.go ├── load_space.go ├── load_space_test.go ├── org_finder.go ├── org_finder_test.go ├── space_finder.go └── space_finder_test.go ├── db ├── config.go ├── connection.go ├── connection_test.go ├── database.go ├── database_test.go ├── init_test.go ├── migrations │ ├── 10_add_template_id_to_clients.sql │ ├── 11_add_template_id_to_kinds.sql │ ├── 12_add_metadata_to_templates.sql │ ├── 13_add_overridden_to_templates.sql │ ├── 14_add_updated_at_to_kinds.sql │ ├── 15_messages.sql │ ├── 16_sets_template_id_defaults.sql │ ├── 17_sets_metadata_defaults.sql │ ├── 18_add_senders.sql │ ├── 19_add_notification_types.sql │ ├── 1_templates.sql │ ├── 20_rename_notification_types_campaign_types.sql │ ├── 21_create_v2_templates.sql │ ├── 22_add_campaigns.sql │ ├── 23_add_campaign_status_columns.sql │ ├── 24_add_campaign_id_to_messages.sql │ ├── 25_add_unsubscribers.sql │ ├── 26_add_unique_key_to_unsubscribers.sql │ ├── 27_remove_name_client_key_from_templates.sql │ ├── 28_remove_v2.sql │ ├── 2_clients.sql │ ├── 3_kinds.sql │ ├── 4_receipts.sql │ ├── 5_unsubscribes.sql │ ├── 6_global_unsubscribes.sql │ ├── 7_remove_overridden_from_templates.sql │ ├── 8_add_updated_at_to_templates.sql │ └── 9_add_id_and_subject_to_templates.sql ├── transaction.go └── transaction_test.go ├── docker └── docker-compose.yml ├── docs ├── diff.go ├── diff_test.go ├── docs.go ├── docs_test.go ├── init_test.go ├── roundtrip.go ├── roundtrip_recorder.go ├── structure.go └── template.tmpl ├── go.mod ├── go.sum ├── gobble ├── config.go ├── database.go ├── database_test.go ├── heartbeater.go ├── heartbeater_test.go ├── init_test.go ├── job.go ├── job_test.go ├── metrics.go ├── migrations │ ├── 1_create_jobs.sql │ ├── 2_add_retry_count_to_jobs.sql │ ├── 3_change_payload_type_to_longtext.sql │ └── 4_drop_goose_table.sql ├── queue.go ├── queue_test.go ├── ticker.go ├── ticker_test.go ├── worker.go └── worker_test.go ├── mail ├── client.go ├── client_test.go ├── exports_test.go ├── init_test.go ├── message.go └── message_test.go ├── main.go ├── manifest.yml ├── postal ├── boot.go ├── common │ ├── delivery_failure_handler.go │ ├── delivery_failure_handler_test.go │ ├── errors.go │ ├── init_test.go │ ├── message_context.go │ ├── message_context_test.go │ ├── packager.go │ ├── packager_test.go │ ├── status.go │ ├── user_loader.go │ └── user_loader_test.go ├── delivery_worker.go ├── delivery_worker_test.go ├── init_test.go ├── message_gc.go ├── message_gc_test.go ├── v1 │ ├── delivery_job_processor.go │ ├── delivery_job_processor_test.go │ ├── init_test.go │ ├── logger.go │ ├── message_status_updater.go │ ├── message_status_updater_test.go │ ├── template_loader.go │ └── template_loader_test.go ├── worker_generator.go └── worker_generator_test.go ├── templates └── default.json ├── testing ├── fixtures │ ├── docs.md │ ├── private.pem │ └── public.pem ├── helpers │ ├── contain_middleware.go │ ├── tokens.go │ └── truncate_tables.go ├── mocks │ ├── all_users.go │ ├── authenticator.go │ ├── clients_repository.go │ ├── cloak.go │ ├── clock.go │ ├── cloud_controller.go │ ├── connection.go │ ├── database.go │ ├── database_migrator.go │ ├── delivery_failure_handler.go │ ├── enqueuer.go │ ├── error_writer.go │ ├── finds_user_ids.go │ ├── global_unsubscribes_repo.go │ ├── gobble_initializer.go │ ├── gobble_job.go │ ├── html_extractor.go │ ├── id_generator.go │ ├── kinds_repo.go │ ├── mail_client.go │ ├── message_finder.go │ ├── message_status_updater.go │ ├── messages_repo.go │ ├── metrics_emitter.go │ ├── notification_updater.go │ ├── notifications_finder.go │ ├── notify.go │ ├── org_finder.go │ ├── organization_loader.go │ ├── packager.go │ ├── persistence_provider.go │ ├── preference_updater.go │ ├── preferences_finder.go │ ├── preferences_repo.go │ ├── queue.go │ ├── rainmaker_organization_finder.go │ ├── rainmaker_spaces_service.go │ ├── receipts_repo.go │ ├── registrar.go │ ├── space_finder.go │ ├── space_loader.go │ ├── strategy.go │ ├── template_assigner.go │ ├── template_associations_lister.go │ ├── template_creator.go │ ├── template_deleter.go │ ├── template_finder.go │ ├── template_lister.go │ ├── template_updater.go │ ├── templates_loader.go │ ├── templates_repo.go │ ├── token_validator.go │ ├── transaction.go │ ├── uaa_client.go │ ├── unsubscribes_repo.go │ ├── user_finder.go │ ├── user_loader.go │ ├── v1_delivery_job_processor.go │ ├── validator.go │ ├── warrant_client_service.go │ ├── warrant_user_service.go │ └── zoned_token_loader.go └── servers │ ├── cc.go │ ├── notifications.go │ ├── smtp.go │ └── uaa.go ├── uaa ├── init_test.go ├── token_loader.go ├── token_loader_test.go ├── token_validator.go ├── token_validator_test.go ├── uaa.go ├── user_finder.go └── user_finder_test.go ├── util ├── clock.go ├── clock_test.go ├── id_generator.go ├── id_generator_test.go └── init_test.go ├── v1 ├── acceptance │ ├── deprecated_registration_endpoint_test.go │ ├── get_all_notifications_test.go │ ├── get_api_version_test.go │ ├── get_debug_metrics_test.go │ ├── get_message_status_test.go │ ├── init_test.go │ ├── manage_any_users_preferences_test.go │ ├── manage_users_own_preferences_test.go │ ├── send_notification_to_all_users_test.go │ ├── send_notification_to_email_test.go │ ├── send_notification_to_email_with_default_template_test.go │ ├── send_notification_to_organization_role_test.go │ ├── send_notification_to_organization_test.go │ ├── send_notification_to_scope_test.go │ ├── send_notification_to_space_test.go │ ├── send_notification_to_user_test.go │ ├── support │ │ ├── client.go │ │ ├── default_template_service.go │ │ ├── documents.go │ │ ├── messages_service.go │ │ ├── notifications_service.go │ │ ├── notify_service.go │ │ ├── preferences.go │ │ ├── preferences_service.go │ │ ├── templates_service.go │ │ └── user_preferences_service.go │ ├── template_CRUD_test.go │ ├── template_assignment_test.go │ ├── template_default_test.go │ ├── template_metadata_test.go │ └── update_notification_test.go ├── collections │ ├── init_test.go │ ├── templates.go │ └── templates_test.go ├── models │ ├── client.go │ ├── client_test.go │ ├── clients_repo.go │ ├── clients_repo_test.go │ ├── database.go │ ├── database_migrator.go │ ├── database_migrator_test.go │ ├── errors.go │ ├── global_unsubscribe.go │ ├── global_unsubscribes_repo.go │ ├── global_unsubscribes_repo_test.go │ ├── init_test.go │ ├── kind.go │ ├── kind_test.go │ ├── kinds_repo.go │ ├── kinds_repo_test.go │ ├── message.go │ ├── messages_repo.go │ ├── messages_repo_test.go │ ├── preferences.go │ ├── preferences_repo.go │ ├── preferences_repo_test.go │ ├── receipt.go │ ├── receipts_repo.go │ ├── receipts_repo_test.go │ ├── template.go │ ├── templates_repo.go │ ├── templates_repo_test.go │ ├── unsubscribe.go │ ├── unsubscribe_test.go │ ├── unsubscribes_repo.go │ └── unsubscribes_repo_test.go ├── services │ ├── all_users.go │ ├── all_users_test.go │ ├── database.go │ ├── dispatch.go │ ├── email_strategy.go │ ├── email_strategy_test.go │ ├── enqueuer.go │ ├── enqueuer_test.go │ ├── errors.go │ ├── everyone_strategy.go │ ├── everyone_strategy_test.go │ ├── finds_user_ids.go │ ├── finds_user_ids_test.go │ ├── init_test.go │ ├── message_finder.go │ ├── message_finder_test.go │ ├── notifications_finder.go │ ├── notifications_finder_test.go │ ├── notifications_updater.go │ ├── notifications_updater_test.go │ ├── organization_loader.go │ ├── organization_loader_test.go │ ├── organization_strategy.go │ ├── organization_strategy_test.go │ ├── preference_updater.go │ ├── preference_updater_test.go │ ├── preferences_builder.go │ ├── preferences_builder_test.go │ ├── preferences_finder.go │ ├── preferences_finder_test.go │ ├── registrar.go │ ├── registrar_test.go │ ├── repos.go │ ├── response.go │ ├── space_loader.go │ ├── space_loader_test.go │ ├── space_strategy.go │ ├── space_strategy_test.go │ ├── template_finder.go │ ├── template_finder_test.go │ ├── template_lister.go │ ├── template_lister_test.go │ ├── template_updater.go │ ├── template_updater_test.go │ ├── uaa_scope_strategy.go │ ├── uaa_scope_strategy_test.go │ ├── user.go │ ├── user_strategy.go │ └── user_strategy_test.go └── web │ ├── clients │ ├── assign_template_handler.go │ ├── assign_template_handler_test.go │ ├── database.go │ ├── init_test.go │ ├── routes.go │ └── routes_test.go │ ├── info │ ├── get_handler.go │ ├── get_handler_test.go │ ├── init_test.go │ ├── routes.go │ └── routes_test.go │ ├── messages │ ├── database.go │ ├── get_handler.go │ ├── get_handler_test.go │ ├── init_test.go │ ├── routes.go │ └── routes_test.go │ ├── middleware │ ├── authenticator.go │ ├── authenticator_test.go │ ├── cors.go │ ├── cors_test.go │ ├── database_allocator.go │ ├── database_allocator_test.go │ ├── init_test.go │ ├── request_counter.go │ ├── request_counter_test.go │ ├── request_logging.go │ └── request_logging_test.go │ ├── notifications │ ├── assign_template_handler.go │ ├── assign_template_handler_test.go │ ├── client_registration_params.go │ ├── client_registration_params_test.go │ ├── database.go │ ├── init_test.go │ ├── list_handler.go │ ├── list_handler_test.go │ ├── put_handler.go │ ├── put_handler_test.go │ ├── registration_handler.go │ ├── registration_handler_test.go │ ├── registration_params.go │ ├── registration_params_test.go │ ├── routes.go │ ├── routes_test.go │ ├── update_handler.go │ ├── update_handler_test.go │ ├── update_params.go │ └── update_params_test.go │ ├── notify │ ├── database.go │ ├── email_handler.go │ ├── email_handler_test.go │ ├── everyone_handler.go │ ├── everyone_handler_test.go │ ├── init_test.go │ ├── notify.go │ ├── notify_params.go │ ├── notify_params_test.go │ ├── notify_params_validator.go │ ├── notify_params_validator_test.go │ ├── notify_test.go │ ├── organization_handler.go │ ├── organization_handler_test.go │ ├── routes.go │ ├── routes_test.go │ ├── space_handler.go │ ├── space_handler_test.go │ ├── uaa_scope_handler.go │ ├── uaa_scope_handler_test.go │ ├── user_handler.go │ └── user_handler_test.go │ ├── preferences │ ├── database.go │ ├── get_preferences_handler.go │ ├── get_preferences_handler_test.go │ ├── get_user_preferences_handler.go │ ├── get_user_preferences_handler_test.go │ ├── init_test.go │ ├── options_handler.go │ ├── options_handler_test.go │ ├── routes.go │ ├── routes_test.go │ ├── update_preferences_handler.go │ ├── update_preferences_handler_test.go │ ├── update_user_preferences_handler.go │ └── update_user_preferences_handler_test.go │ ├── router.go │ ├── templates │ ├── create_handler.go │ ├── create_handler_test.go │ ├── database.go │ ├── delete_handler.go │ ├── delete_handler_test.go │ ├── get_default_handler.go │ ├── get_default_handler_test.go │ ├── get_handler.go │ ├── get_handler_test.go │ ├── init_test.go │ ├── list_associations_handler.go │ ├── list_associations_handler_test.go │ ├── list_handler.go │ ├── list_handler_test.go │ ├── routes.go │ ├── routes_test.go │ ├── template_params.go │ ├── template_params_test.go │ ├── update_default_handler.go │ ├── update_default_handler_test.go │ ├── update_handler.go │ └── update_handler_test.go │ └── webutil │ ├── error_writer.go │ ├── error_writer_test.go │ ├── errors.go │ └── init_test.go ├── valiant ├── errors.go ├── extra_keys.go ├── init_test.go ├── validate_required.go ├── validator.go └── validator_test.go ├── vendor ├── filippo.io │ └── edwards25519 │ │ ├── LICENSE │ │ ├── README.md │ │ ├── doc.go │ │ ├── edwards25519.go │ │ ├── extra.go │ │ ├── field │ │ ├── fe.go │ │ ├── fe_amd64.go │ │ ├── fe_amd64.s │ │ ├── fe_amd64_noasm.go │ │ ├── fe_arm64.go │ │ ├── fe_arm64.s │ │ ├── fe_arm64_noasm.go │ │ ├── fe_extra.go │ │ └── fe_generic.go │ │ ├── scalar.go │ │ ├── scalar_fiat.go │ │ ├── scalarmult.go │ │ └── tables.go ├── github.com │ ├── DATA-DOG │ │ └── go-sqlmock │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── argument.go │ │ │ ├── column.go │ │ │ ├── driver.go │ │ │ ├── expectations.go │ │ │ ├── expectations_before_go18.go │ │ │ ├── expectations_go18.go │ │ │ ├── options.go │ │ │ ├── query.go │ │ │ ├── result.go │ │ │ ├── rows.go │ │ │ ├── rows_go18.go │ │ │ ├── sqlmock.go │ │ │ ├── sqlmock_before_go18.go │ │ │ ├── sqlmock_go18.go │ │ │ ├── sqlmock_go18_19.go │ │ │ ├── sqlmock_go19.go │ │ │ ├── statement.go │ │ │ ├── statement_before_go18.go │ │ │ └── statement_go18.go │ ├── PuerkitoBio │ │ └── goquery │ │ │ ├── .gitattributes │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── array.go │ │ │ ├── doc.go │ │ │ ├── expand.go │ │ │ ├── filter.go │ │ │ ├── iteration.go │ │ │ ├── manipulation.go │ │ │ ├── property.go │ │ │ ├── query.go │ │ │ ├── traversal.go │ │ │ ├── type.go │ │ │ └── utilities.go │ ├── andybalholm │ │ └── cascadia │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── parser.go │ │ │ ├── pseudo_classes.go │ │ │ ├── selector.go │ │ │ ├── serialize.go │ │ │ └── specificity.go │ ├── chrj │ │ └── smtpd │ │ │ ├── .hgignore │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── address.go │ │ │ ├── protocol.go │ │ │ └── smtpd.go │ ├── dgrijalva │ │ └── jwt-go │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── MIGRATION_GUIDE.md │ │ │ ├── README.md │ │ │ ├── VERSION_HISTORY.md │ │ │ ├── claims.go │ │ │ ├── doc.go │ │ │ ├── ecdsa.go │ │ │ ├── ecdsa_utils.go │ │ │ ├── errors.go │ │ │ ├── hmac.go │ │ │ ├── map_claims.go │ │ │ ├── none.go │ │ │ ├── parser.go │ │ │ ├── rsa.go │ │ │ ├── rsa_pss.go │ │ │ ├── rsa_utils.go │ │ │ ├── signing_method.go │ │ │ └── token.go │ ├── go-gorp │ │ └── gorp │ │ │ └── v3 │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── column.go │ │ │ ├── db.go │ │ │ ├── dialect.go │ │ │ ├── dialect_mysql.go │ │ │ ├── dialect_oracle.go │ │ │ ├── dialect_postgres.go │ │ │ ├── dialect_snowflake.go │ │ │ ├── dialect_sqlite.go │ │ │ ├── dialect_sqlserver.go │ │ │ ├── doc.go │ │ │ ├── errors.go │ │ │ ├── gorp.go │ │ │ ├── hooks.go │ │ │ ├── index.go │ │ │ ├── lockerror.go │ │ │ ├── logging.go │ │ │ ├── nulltypes.go │ │ │ ├── select.go │ │ │ ├── table.go │ │ │ ├── table_bindings.go │ │ │ ├── test_all.sh │ │ │ └── transaction.go │ ├── go-logr │ │ └── logr │ │ │ ├── .golangci.yaml │ │ │ ├── CHANGELOG.md │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── SECURITY.md │ │ │ ├── context.go │ │ │ ├── context_noslog.go │ │ │ ├── context_slog.go │ │ │ ├── discard.go │ │ │ ├── funcr │ │ │ ├── funcr.go │ │ │ └── slogsink.go │ │ │ ├── logr.go │ │ │ ├── sloghandler.go │ │ │ ├── slogr.go │ │ │ └── slogsink.go │ ├── go-sql-driver │ │ └── mysql │ │ │ ├── .gitignore │ │ │ ├── AUTHORS │ │ │ ├── CHANGELOG.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── atomic_bool.go │ │ │ ├── atomic_bool_go118.go │ │ │ ├── auth.go │ │ │ ├── buffer.go │ │ │ ├── collations.go │ │ │ ├── conncheck.go │ │ │ ├── conncheck_dummy.go │ │ │ ├── connection.go │ │ │ ├── connector.go │ │ │ ├── const.go │ │ │ ├── driver.go │ │ │ ├── dsn.go │ │ │ ├── errors.go │ │ │ ├── fields.go │ │ │ ├── infile.go │ │ │ ├── nulltime.go │ │ │ ├── packets.go │ │ │ ├── result.go │ │ │ ├── rows.go │ │ │ ├── statement.go │ │ │ ├── transaction.go │ │ │ └── utils.go │ ├── go-task │ │ └── slim-sprig │ │ │ └── v3 │ │ │ ├── .editorconfig │ │ │ ├── .gitattributes │ │ │ ├── .gitignore │ │ │ ├── CHANGELOG.md │ │ │ ├── LICENSE.txt │ │ │ ├── README.md │ │ │ ├── Taskfile.yml │ │ │ ├── crypto.go │ │ │ ├── date.go │ │ │ ├── defaults.go │ │ │ ├── dict.go │ │ │ ├── doc.go │ │ │ ├── functions.go │ │ │ ├── list.go │ │ │ ├── network.go │ │ │ ├── numeric.go │ │ │ ├── reflect.go │ │ │ ├── regex.go │ │ │ ├── strings.go │ │ │ └── url.go │ ├── golang-jwt │ │ └── jwt │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── MIGRATION_GUIDE.md │ │ │ ├── README.md │ │ │ ├── VERSION_HISTORY.md │ │ │ ├── claims.go │ │ │ ├── doc.go │ │ │ ├── ecdsa.go │ │ │ ├── ecdsa_utils.go │ │ │ ├── ed25519.go │ │ │ ├── ed25519_utils.go │ │ │ ├── errors.go │ │ │ ├── hmac.go │ │ │ ├── map_claims.go │ │ │ ├── none.go │ │ │ ├── parser.go │ │ │ ├── rsa.go │ │ │ ├── rsa_pss.go │ │ │ ├── rsa_utils.go │ │ │ ├── signing_method.go │ │ │ ├── token.go │ │ │ └── v5 │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── MIGRATION_GUIDE.md │ │ │ ├── README.md │ │ │ ├── SECURITY.md │ │ │ ├── VERSION_HISTORY.md │ │ │ ├── claims.go │ │ │ ├── doc.go │ │ │ ├── ecdsa.go │ │ │ ├── ecdsa_utils.go │ │ │ ├── ed25519.go │ │ │ ├── ed25519_utils.go │ │ │ ├── errors.go │ │ │ ├── errors_go1_20.go │ │ │ ├── errors_go_other.go │ │ │ ├── hmac.go │ │ │ ├── map_claims.go │ │ │ ├── none.go │ │ │ ├── parser.go │ │ │ ├── parser_option.go │ │ │ ├── registered_claims.go │ │ │ ├── rsa.go │ │ │ ├── rsa_pss.go │ │ │ ├── rsa_utils.go │ │ │ ├── signing_method.go │ │ │ ├── staticcheck.conf │ │ │ ├── token.go │ │ │ ├── token_option.go │ │ │ ├── types.go │ │ │ └── validator.go │ ├── google │ │ ├── go-cmp │ │ │ ├── LICENSE │ │ │ └── cmp │ │ │ │ ├── compare.go │ │ │ │ ├── export.go │ │ │ │ ├── internal │ │ │ │ ├── diff │ │ │ │ │ ├── debug_disable.go │ │ │ │ │ ├── debug_enable.go │ │ │ │ │ └── diff.go │ │ │ │ ├── flags │ │ │ │ │ └── flags.go │ │ │ │ ├── function │ │ │ │ │ └── func.go │ │ │ │ └── value │ │ │ │ │ ├── name.go │ │ │ │ │ ├── pointer.go │ │ │ │ │ └── sort.go │ │ │ │ ├── options.go │ │ │ │ ├── path.go │ │ │ │ ├── report.go │ │ │ │ ├── report_compare.go │ │ │ │ ├── report_references.go │ │ │ │ ├── report_reflect.go │ │ │ │ ├── report_slices.go │ │ │ │ ├── report_text.go │ │ │ │ └── report_value.go │ │ └── pprof │ │ │ ├── AUTHORS │ │ │ ├── CONTRIBUTORS │ │ │ ├── LICENSE │ │ │ └── profile │ │ │ ├── encode.go │ │ │ ├── filter.go │ │ │ ├── index.go │ │ │ ├── legacy_java_profile.go │ │ │ ├── legacy_profile.go │ │ │ ├── merge.go │ │ │ ├── profile.go │ │ │ ├── proto.go │ │ │ └── prune.go │ ├── gorilla │ │ └── mux │ │ │ ├── .editorconfig │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── doc.go │ │ │ ├── middleware.go │ │ │ ├── mux.go │ │ │ ├── regexp.go │ │ │ ├── route.go │ │ │ └── test_helpers.go │ ├── onsi │ │ ├── ginkgo │ │ │ └── v2 │ │ │ │ ├── .gitignore │ │ │ │ ├── CHANGELOG.md │ │ │ │ ├── CONTRIBUTING.md │ │ │ │ ├── LICENSE │ │ │ │ ├── Makefile │ │ │ │ ├── README.md │ │ │ │ ├── RELEASING.md │ │ │ │ ├── config │ │ │ │ └── deprecated.go │ │ │ │ ├── core_dsl.go │ │ │ │ ├── decorator_dsl.go │ │ │ │ ├── deprecated_dsl.go │ │ │ │ ├── formatter │ │ │ │ ├── colorable_others.go │ │ │ │ ├── colorable_windows.go │ │ │ │ └── formatter.go │ │ │ │ ├── ginkgo │ │ │ │ ├── build │ │ │ │ │ └── build_command.go │ │ │ │ ├── command │ │ │ │ │ ├── abort.go │ │ │ │ │ ├── command.go │ │ │ │ │ └── program.go │ │ │ │ ├── generators │ │ │ │ │ ├── boostrap_templates.go │ │ │ │ │ ├── bootstrap_command.go │ │ │ │ │ ├── generate_command.go │ │ │ │ │ ├── generate_templates.go │ │ │ │ │ └── generators_common.go │ │ │ │ ├── internal │ │ │ │ │ ├── compile.go │ │ │ │ │ ├── gocovmerge.go │ │ │ │ │ ├── profiles_and_reports.go │ │ │ │ │ ├── run.go │ │ │ │ │ ├── test_suite.go │ │ │ │ │ ├── utils.go │ │ │ │ │ └── verify_version.go │ │ │ │ ├── labels │ │ │ │ │ └── labels_command.go │ │ │ │ ├── main.go │ │ │ │ ├── outline │ │ │ │ │ ├── ginkgo.go │ │ │ │ │ ├── import.go │ │ │ │ │ ├── outline.go │ │ │ │ │ └── outline_command.go │ │ │ │ ├── run │ │ │ │ │ └── run_command.go │ │ │ │ ├── unfocus │ │ │ │ │ └── unfocus_command.go │ │ │ │ └── watch │ │ │ │ │ ├── delta.go │ │ │ │ │ ├── delta_tracker.go │ │ │ │ │ ├── dependencies.go │ │ │ │ │ ├── package_hash.go │ │ │ │ │ ├── package_hashes.go │ │ │ │ │ ├── suite.go │ │ │ │ │ └── watch_command.go │ │ │ │ ├── ginkgo_cli_dependencies.go │ │ │ │ ├── ginkgo_t_dsl.go │ │ │ │ ├── internal │ │ │ │ ├── counter.go │ │ │ │ ├── failer.go │ │ │ │ ├── focus.go │ │ │ │ ├── global │ │ │ │ │ └── init.go │ │ │ │ ├── group.go │ │ │ │ ├── interrupt_handler │ │ │ │ │ ├── interrupt_handler.go │ │ │ │ │ ├── sigquit_swallower_unix.go │ │ │ │ │ └── sigquit_swallower_windows.go │ │ │ │ ├── node.go │ │ │ │ ├── ordering.go │ │ │ │ ├── output_interceptor.go │ │ │ │ ├── output_interceptor_unix.go │ │ │ │ ├── output_interceptor_wasm.go │ │ │ │ ├── output_interceptor_win.go │ │ │ │ ├── parallel_support │ │ │ │ │ ├── client_server.go │ │ │ │ │ ├── http_client.go │ │ │ │ │ ├── http_server.go │ │ │ │ │ ├── rpc_client.go │ │ │ │ │ ├── rpc_server.go │ │ │ │ │ └── server_handler.go │ │ │ │ ├── progress_report.go │ │ │ │ ├── progress_report_bsd.go │ │ │ │ ├── progress_report_unix.go │ │ │ │ ├── progress_report_wasm.go │ │ │ │ ├── progress_report_win.go │ │ │ │ ├── progress_reporter_manager.go │ │ │ │ ├── report_entry.go │ │ │ │ ├── spec.go │ │ │ │ ├── spec_context.go │ │ │ │ ├── suite.go │ │ │ │ ├── testingtproxy │ │ │ │ │ └── testing_t_proxy.go │ │ │ │ ├── tree.go │ │ │ │ └── writer.go │ │ │ │ ├── reporters │ │ │ │ ├── default_reporter.go │ │ │ │ ├── deprecated_reporter.go │ │ │ │ ├── json_report.go │ │ │ │ ├── junit_report.go │ │ │ │ ├── reporter.go │ │ │ │ └── teamcity_report.go │ │ │ │ ├── reporting_dsl.go │ │ │ │ ├── table_dsl.go │ │ │ │ └── types │ │ │ │ ├── code_location.go │ │ │ │ ├── config.go │ │ │ │ ├── deprecated_types.go │ │ │ │ ├── deprecation_support.go │ │ │ │ ├── enum_support.go │ │ │ │ ├── errors.go │ │ │ │ ├── file_filter.go │ │ │ │ ├── flags.go │ │ │ │ ├── label_filter.go │ │ │ │ ├── report_entry.go │ │ │ │ ├── types.go │ │ │ │ └── version.go │ │ └── gomega │ │ │ ├── .gitignore │ │ │ ├── CHANGELOG.md │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── RELEASING.md │ │ │ ├── format │ │ │ └── format.go │ │ │ ├── gomega_dsl.go │ │ │ ├── internal │ │ │ ├── assertion.go │ │ │ ├── async_assertion.go │ │ │ ├── duration_bundle.go │ │ │ ├── gomega.go │ │ │ ├── gutil │ │ │ │ ├── post_ioutil.go │ │ │ │ └── using_ioutil.go │ │ │ ├── polling_signal_error.go │ │ │ └── vetoptdesc.go │ │ │ ├── matchers.go │ │ │ ├── matchers │ │ │ ├── and.go │ │ │ ├── assignable_to_type_of_matcher.go │ │ │ ├── attributes_slice.go │ │ │ ├── be_a_directory.go │ │ │ ├── be_a_regular_file.go │ │ │ ├── be_an_existing_file.go │ │ │ ├── be_closed_matcher.go │ │ │ ├── be_comparable_to_matcher.go │ │ │ ├── be_element_of_matcher.go │ │ │ ├── be_empty_matcher.go │ │ │ ├── be_equivalent_to_matcher.go │ │ │ ├── be_false_matcher.go │ │ │ ├── be_identical_to.go │ │ │ ├── be_key_of_matcher.go │ │ │ ├── be_nil_matcher.go │ │ │ ├── be_numerically_matcher.go │ │ │ ├── be_sent_matcher.go │ │ │ ├── be_temporally_matcher.go │ │ │ ├── be_true_matcher.go │ │ │ ├── be_zero_matcher.go │ │ │ ├── consist_of.go │ │ │ ├── contain_element_matcher.go │ │ │ ├── contain_elements_matcher.go │ │ │ ├── contain_substring_matcher.go │ │ │ ├── equal_matcher.go │ │ │ ├── have_cap_matcher.go │ │ │ ├── have_each_matcher.go │ │ │ ├── have_exact_elements.go │ │ │ ├── have_existing_field_matcher.go │ │ │ ├── have_field.go │ │ │ ├── have_http_body_matcher.go │ │ │ ├── have_http_header_with_value_matcher.go │ │ │ ├── have_http_status_matcher.go │ │ │ ├── have_key_matcher.go │ │ │ ├── have_key_with_value_matcher.go │ │ │ ├── have_len_matcher.go │ │ │ ├── have_occurred_matcher.go │ │ │ ├── have_prefix_matcher.go │ │ │ ├── have_suffix_matcher.go │ │ │ ├── have_value.go │ │ │ ├── match_error_matcher.go │ │ │ ├── match_json_matcher.go │ │ │ ├── match_regexp_matcher.go │ │ │ ├── match_xml_matcher.go │ │ │ ├── match_yaml_matcher.go │ │ │ ├── not.go │ │ │ ├── or.go │ │ │ ├── panic_matcher.go │ │ │ ├── receive_matcher.go │ │ │ ├── satisfy_matcher.go │ │ │ ├── semi_structured_data_support.go │ │ │ ├── succeed_matcher.go │ │ │ ├── support │ │ │ │ └── goraph │ │ │ │ │ ├── bipartitegraph │ │ │ │ │ ├── bipartitegraph.go │ │ │ │ │ └── bipartitegraphmatching.go │ │ │ │ │ ├── edge │ │ │ │ │ └── edge.go │ │ │ │ │ ├── node │ │ │ │ │ └── node.go │ │ │ │ │ └── util │ │ │ │ │ └── util.go │ │ │ ├── type_support.go │ │ │ └── with_transform.go │ │ │ └── types │ │ │ └── types.go │ ├── pivotal-cf-experimental │ │ ├── rainmaker │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── application.go │ │ │ ├── application_service.go │ │ │ ├── applications_list.go │ │ │ ├── buildpack.go │ │ │ ├── buildpacks_service.go │ │ │ ├── client.go │ │ │ ├── errors.go │ │ │ ├── internal │ │ │ │ ├── documents │ │ │ │ │ ├── application_response.go │ │ │ │ │ ├── buildpack_response.go │ │ │ │ │ ├── create_application_request.go │ │ │ │ │ ├── create_buildpack_request.go │ │ │ │ │ ├── create_organization_request.go │ │ │ │ │ ├── create_service_instance_request.go │ │ │ │ │ ├── create_space_request.go │ │ │ │ │ ├── create_user_request.go │ │ │ │ │ ├── organization_response.go │ │ │ │ │ ├── organizations_list_response.go │ │ │ │ │ ├── page_response.go │ │ │ │ │ ├── service_instance_response.go │ │ │ │ │ ├── space_response.go │ │ │ │ │ ├── spaces_list_response.go │ │ │ │ │ ├── update_buildpack_request.go │ │ │ │ │ ├── update_organization_request.go │ │ │ │ │ ├── user_response.go │ │ │ │ │ └── users_list_response.go │ │ │ │ └── network │ │ │ │ │ ├── authorization.go │ │ │ │ │ ├── client.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── errors.go │ │ │ │ │ ├── print.go │ │ │ │ │ ├── request_body.go │ │ │ │ │ └── transport.go │ │ │ ├── organization.go │ │ │ ├── organizations_list.go │ │ │ ├── organizations_service.go │ │ │ ├── page.go │ │ │ ├── pointers.go │ │ │ ├── request_plan.go │ │ │ ├── service_instance.go │ │ │ ├── service_instances_service.go │ │ │ ├── space.go │ │ │ ├── spaces_list.go │ │ │ ├── spaces_service.go │ │ │ ├── user.go │ │ │ ├── users_list.go │ │ │ └── users_service.go │ │ └── warrant │ │ │ ├── .gitignore │ │ │ ├── .gitmodules │ │ │ ├── Gopkg.lock │ │ │ ├── Gopkg.toml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── client.go │ │ │ ├── clients_service.go │ │ │ ├── doc.go │ │ │ ├── errors.go │ │ │ ├── group.go │ │ │ ├── groups_service.go │ │ │ ├── internal │ │ │ ├── documents │ │ │ │ ├── clients.go │ │ │ │ ├── groups.go │ │ │ │ ├── members.go │ │ │ │ ├── meta.go │ │ │ │ ├── password.go │ │ │ │ ├── tokens.go │ │ │ │ └── users.go │ │ │ └── network │ │ │ │ ├── authorization.go │ │ │ │ ├── client.go │ │ │ │ ├── doc.go │ │ │ │ ├── errors.go │ │ │ │ ├── print.go │ │ │ │ ├── request_body.go │ │ │ │ └── transport.go │ │ │ ├── member.go │ │ │ ├── sort.go │ │ │ ├── token.go │ │ │ ├── tokens_service.go │ │ │ ├── user.go │ │ │ ├── users_service.go │ │ │ └── warrant.go │ ├── pivotal-cf │ │ └── uaa-sso-golang │ │ │ └── uaa │ │ │ ├── all_users.go │ │ │ ├── client.go │ │ │ ├── exchange.go │ │ │ ├── get_client_token.go │ │ │ ├── get_token_key.go │ │ │ ├── refresh.go │ │ │ ├── token.go │ │ │ ├── uaa.go │ │ │ ├── user_by_id.go │ │ │ ├── users_by_ids.go │ │ │ ├── users_emails_by_ids.go │ │ │ └── users_guids_by_scope.go │ ├── pivotal-golang │ │ ├── conceal │ │ │ ├── README.md │ │ │ └── conceal.go │ │ └── lager │ │ │ ├── LICENSE │ │ │ ├── NOTICE │ │ │ ├── README.md │ │ │ ├── json_redacter.go │ │ │ ├── logger.go │ │ │ ├── models.go │ │ │ ├── package.go │ │ │ ├── reconfigurable_sink.go │ │ │ ├── redacting_sink.go │ │ │ └── writer_sink.go │ ├── rcrowley │ │ └── go-metrics │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── counter.go │ │ │ ├── debug.go │ │ │ ├── ewma.go │ │ │ ├── exp │ │ │ └── exp.go │ │ │ ├── gauge.go │ │ │ ├── gauge_float64.go │ │ │ ├── graphite.go │ │ │ ├── healthcheck.go │ │ │ ├── histogram.go │ │ │ ├── json.go │ │ │ ├── log.go │ │ │ ├── memory.md │ │ │ ├── meter.go │ │ │ ├── metrics.go │ │ │ ├── opentsdb.go │ │ │ ├── registry.go │ │ │ ├── runtime.go │ │ │ ├── runtime_cgo.go │ │ │ ├── runtime_gccpufraction.go │ │ │ ├── runtime_no_cgo.go │ │ │ ├── runtime_no_gccpufraction.go │ │ │ ├── sample.go │ │ │ ├── syslog.go │ │ │ ├── timer.go │ │ │ ├── validate.sh │ │ │ └── writer.go │ ├── rubenv │ │ └── sql-migrate │ │ │ ├── .dockerignore │ │ │ ├── .gitignore │ │ │ ├── .golangci.yaml │ │ │ ├── Dockerfile │ │ │ ├── LICENSE │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── doc.go │ │ │ ├── migrate.go │ │ │ └── sqlparse │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── sqlparse.go │ └── ryanmoran │ │ ├── stack │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── context.go │ │ ├── logging.go │ │ ├── middleware.go │ │ ├── recover.go │ │ └── stack.go │ │ └── viron │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── errors.go │ │ ├── parse.go │ │ └── print.go ├── golang.org │ └── x │ │ ├── net │ │ ├── LICENSE │ │ ├── PATENTS │ │ ├── context │ │ │ ├── context.go │ │ │ ├── go17.go │ │ │ ├── go19.go │ │ │ ├── pre_go17.go │ │ │ └── pre_go19.go │ │ └── html │ │ │ ├── atom │ │ │ ├── atom.go │ │ │ └── table.go │ │ │ ├── charset │ │ │ └── charset.go │ │ │ ├── const.go │ │ │ ├── doc.go │ │ │ ├── doctype.go │ │ │ ├── entity.go │ │ │ ├── escape.go │ │ │ ├── foreign.go │ │ │ ├── node.go │ │ │ ├── parse.go │ │ │ ├── render.go │ │ │ └── token.go │ │ ├── sys │ │ ├── LICENSE │ │ ├── PATENTS │ │ └── unix │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── affinity_linux.go │ │ │ ├── aliases.go │ │ │ ├── asm_aix_ppc64.s │ │ │ ├── asm_bsd_386.s │ │ │ ├── asm_bsd_amd64.s │ │ │ ├── asm_bsd_arm.s │ │ │ ├── asm_bsd_arm64.s │ │ │ ├── asm_bsd_ppc64.s │ │ │ ├── asm_bsd_riscv64.s │ │ │ ├── asm_linux_386.s │ │ │ ├── asm_linux_amd64.s │ │ │ ├── asm_linux_arm.s │ │ │ ├── asm_linux_arm64.s │ │ │ ├── asm_linux_loong64.s │ │ │ ├── asm_linux_mips64x.s │ │ │ ├── asm_linux_mipsx.s │ │ │ ├── asm_linux_ppc64x.s │ │ │ ├── asm_linux_riscv64.s │ │ │ ├── asm_linux_s390x.s │ │ │ ├── asm_openbsd_mips64.s │ │ │ ├── asm_solaris_amd64.s │ │ │ ├── asm_zos_s390x.s │ │ │ ├── bluetooth_linux.go │ │ │ ├── bpxsvc_zos.go │ │ │ ├── bpxsvc_zos.s │ │ │ ├── cap_freebsd.go │ │ │ ├── constants.go │ │ │ ├── dev_aix_ppc.go │ │ │ ├── dev_aix_ppc64.go │ │ │ ├── dev_darwin.go │ │ │ ├── dev_dragonfly.go │ │ │ ├── dev_freebsd.go │ │ │ ├── dev_linux.go │ │ │ ├── dev_netbsd.go │ │ │ ├── dev_openbsd.go │ │ │ ├── dev_zos.go │ │ │ ├── dirent.go │ │ │ ├── endian_big.go │ │ │ ├── endian_little.go │ │ │ ├── env_unix.go │ │ │ ├── fcntl.go │ │ │ ├── fcntl_darwin.go │ │ │ ├── fcntl_linux_32bit.go │ │ │ ├── fdset.go │ │ │ ├── gccgo.go │ │ │ ├── gccgo_c.c │ │ │ ├── gccgo_linux_amd64.go │ │ │ ├── ifreq_linux.go │ │ │ ├── ioctl_linux.go │ │ │ ├── ioctl_signed.go │ │ │ ├── ioctl_unsigned.go │ │ │ ├── ioctl_zos.go │ │ │ ├── mkall.sh │ │ │ ├── mkerrors.sh │ │ │ ├── mmap_nomremap.go │ │ │ ├── mremap.go │ │ │ ├── pagesize_unix.go │ │ │ ├── pledge_openbsd.go │ │ │ ├── ptrace_darwin.go │ │ │ ├── ptrace_ios.go │ │ │ ├── race.go │ │ │ ├── race0.go │ │ │ ├── readdirent_getdents.go │ │ │ ├── readdirent_getdirentries.go │ │ │ ├── sockcmsg_dragonfly.go │ │ │ ├── sockcmsg_linux.go │ │ │ ├── sockcmsg_unix.go │ │ │ ├── sockcmsg_unix_other.go │ │ │ ├── sockcmsg_zos.go │ │ │ ├── symaddr_zos_s390x.s │ │ │ ├── syscall.go │ │ │ ├── syscall_aix.go │ │ │ ├── syscall_aix_ppc.go │ │ │ ├── syscall_aix_ppc64.go │ │ │ ├── syscall_bsd.go │ │ │ ├── syscall_darwin.go │ │ │ ├── syscall_darwin_amd64.go │ │ │ ├── syscall_darwin_arm64.go │ │ │ ├── syscall_darwin_libSystem.go │ │ │ ├── syscall_dragonfly.go │ │ │ ├── syscall_dragonfly_amd64.go │ │ │ ├── syscall_freebsd.go │ │ │ ├── syscall_freebsd_386.go │ │ │ ├── syscall_freebsd_amd64.go │ │ │ ├── syscall_freebsd_arm.go │ │ │ ├── syscall_freebsd_arm64.go │ │ │ ├── syscall_freebsd_riscv64.go │ │ │ ├── syscall_hurd.go │ │ │ ├── syscall_hurd_386.go │ │ │ ├── syscall_illumos.go │ │ │ ├── syscall_linux.go │ │ │ ├── syscall_linux_386.go │ │ │ ├── syscall_linux_alarm.go │ │ │ ├── syscall_linux_amd64.go │ │ │ ├── syscall_linux_amd64_gc.go │ │ │ ├── syscall_linux_arm.go │ │ │ ├── syscall_linux_arm64.go │ │ │ ├── syscall_linux_gc.go │ │ │ ├── syscall_linux_gc_386.go │ │ │ ├── syscall_linux_gc_arm.go │ │ │ ├── syscall_linux_gccgo_386.go │ │ │ ├── syscall_linux_gccgo_arm.go │ │ │ ├── syscall_linux_loong64.go │ │ │ ├── syscall_linux_mips64x.go │ │ │ ├── syscall_linux_mipsx.go │ │ │ ├── syscall_linux_ppc.go │ │ │ ├── syscall_linux_ppc64x.go │ │ │ ├── syscall_linux_riscv64.go │ │ │ ├── syscall_linux_s390x.go │ │ │ ├── syscall_linux_sparc64.go │ │ │ ├── syscall_netbsd.go │ │ │ ├── syscall_netbsd_386.go │ │ │ ├── syscall_netbsd_amd64.go │ │ │ ├── syscall_netbsd_arm.go │ │ │ ├── syscall_netbsd_arm64.go │ │ │ ├── syscall_openbsd.go │ │ │ ├── syscall_openbsd_386.go │ │ │ ├── syscall_openbsd_amd64.go │ │ │ ├── syscall_openbsd_arm.go │ │ │ ├── syscall_openbsd_arm64.go │ │ │ ├── syscall_openbsd_libc.go │ │ │ ├── syscall_openbsd_mips64.go │ │ │ ├── syscall_openbsd_ppc64.go │ │ │ ├── syscall_openbsd_riscv64.go │ │ │ ├── syscall_solaris.go │ │ │ ├── syscall_solaris_amd64.go │ │ │ ├── syscall_unix.go │ │ │ ├── syscall_unix_gc.go │ │ │ ├── syscall_unix_gc_ppc64x.go │ │ │ ├── syscall_zos_s390x.go │ │ │ ├── sysvshm_linux.go │ │ │ ├── sysvshm_unix.go │ │ │ ├── sysvshm_unix_other.go │ │ │ ├── timestruct.go │ │ │ ├── unveil_openbsd.go │ │ │ ├── xattr_bsd.go │ │ │ ├── zerrors_aix_ppc.go │ │ │ ├── zerrors_aix_ppc64.go │ │ │ ├── zerrors_darwin_amd64.go │ │ │ ├── zerrors_darwin_arm64.go │ │ │ ├── zerrors_dragonfly_amd64.go │ │ │ ├── zerrors_freebsd_386.go │ │ │ ├── zerrors_freebsd_amd64.go │ │ │ ├── zerrors_freebsd_arm.go │ │ │ ├── zerrors_freebsd_arm64.go │ │ │ ├── zerrors_freebsd_riscv64.go │ │ │ ├── zerrors_linux.go │ │ │ ├── zerrors_linux_386.go │ │ │ ├── zerrors_linux_amd64.go │ │ │ ├── zerrors_linux_arm.go │ │ │ ├── zerrors_linux_arm64.go │ │ │ ├── zerrors_linux_loong64.go │ │ │ ├── zerrors_linux_mips.go │ │ │ ├── zerrors_linux_mips64.go │ │ │ ├── zerrors_linux_mips64le.go │ │ │ ├── zerrors_linux_mipsle.go │ │ │ ├── zerrors_linux_ppc.go │ │ │ ├── zerrors_linux_ppc64.go │ │ │ ├── zerrors_linux_ppc64le.go │ │ │ ├── zerrors_linux_riscv64.go │ │ │ ├── zerrors_linux_s390x.go │ │ │ ├── zerrors_linux_sparc64.go │ │ │ ├── zerrors_netbsd_386.go │ │ │ ├── zerrors_netbsd_amd64.go │ │ │ ├── zerrors_netbsd_arm.go │ │ │ ├── zerrors_netbsd_arm64.go │ │ │ ├── zerrors_openbsd_386.go │ │ │ ├── zerrors_openbsd_amd64.go │ │ │ ├── zerrors_openbsd_arm.go │ │ │ ├── zerrors_openbsd_arm64.go │ │ │ ├── zerrors_openbsd_mips64.go │ │ │ ├── zerrors_openbsd_ppc64.go │ │ │ ├── zerrors_openbsd_riscv64.go │ │ │ ├── zerrors_solaris_amd64.go │ │ │ ├── zerrors_zos_s390x.go │ │ │ ├── zptrace_armnn_linux.go │ │ │ ├── zptrace_linux_arm64.go │ │ │ ├── zptrace_mipsnn_linux.go │ │ │ ├── zptrace_mipsnnle_linux.go │ │ │ ├── zptrace_x86_linux.go │ │ │ ├── zsymaddr_zos_s390x.s │ │ │ ├── zsyscall_aix_ppc.go │ │ │ ├── zsyscall_aix_ppc64.go │ │ │ ├── zsyscall_aix_ppc64_gc.go │ │ │ ├── zsyscall_aix_ppc64_gccgo.go │ │ │ ├── zsyscall_darwin_amd64.go │ │ │ ├── zsyscall_darwin_amd64.s │ │ │ ├── zsyscall_darwin_arm64.go │ │ │ ├── zsyscall_darwin_arm64.s │ │ │ ├── zsyscall_dragonfly_amd64.go │ │ │ ├── zsyscall_freebsd_386.go │ │ │ ├── zsyscall_freebsd_amd64.go │ │ │ ├── zsyscall_freebsd_arm.go │ │ │ ├── zsyscall_freebsd_arm64.go │ │ │ ├── zsyscall_freebsd_riscv64.go │ │ │ ├── zsyscall_illumos_amd64.go │ │ │ ├── zsyscall_linux.go │ │ │ ├── zsyscall_linux_386.go │ │ │ ├── zsyscall_linux_amd64.go │ │ │ ├── zsyscall_linux_arm.go │ │ │ ├── zsyscall_linux_arm64.go │ │ │ ├── zsyscall_linux_loong64.go │ │ │ ├── zsyscall_linux_mips.go │ │ │ ├── zsyscall_linux_mips64.go │ │ │ ├── zsyscall_linux_mips64le.go │ │ │ ├── zsyscall_linux_mipsle.go │ │ │ ├── zsyscall_linux_ppc.go │ │ │ ├── zsyscall_linux_ppc64.go │ │ │ ├── zsyscall_linux_ppc64le.go │ │ │ ├── zsyscall_linux_riscv64.go │ │ │ ├── zsyscall_linux_s390x.go │ │ │ ├── zsyscall_linux_sparc64.go │ │ │ ├── zsyscall_netbsd_386.go │ │ │ ├── zsyscall_netbsd_amd64.go │ │ │ ├── zsyscall_netbsd_arm.go │ │ │ ├── zsyscall_netbsd_arm64.go │ │ │ ├── zsyscall_openbsd_386.go │ │ │ ├── zsyscall_openbsd_386.s │ │ │ ├── zsyscall_openbsd_amd64.go │ │ │ ├── zsyscall_openbsd_amd64.s │ │ │ ├── zsyscall_openbsd_arm.go │ │ │ ├── zsyscall_openbsd_arm.s │ │ │ ├── zsyscall_openbsd_arm64.go │ │ │ ├── zsyscall_openbsd_arm64.s │ │ │ ├── zsyscall_openbsd_mips64.go │ │ │ ├── zsyscall_openbsd_mips64.s │ │ │ ├── zsyscall_openbsd_ppc64.go │ │ │ ├── zsyscall_openbsd_ppc64.s │ │ │ ├── zsyscall_openbsd_riscv64.go │ │ │ ├── zsyscall_openbsd_riscv64.s │ │ │ ├── zsyscall_solaris_amd64.go │ │ │ ├── zsyscall_zos_s390x.go │ │ │ ├── zsysctl_openbsd_386.go │ │ │ ├── zsysctl_openbsd_amd64.go │ │ │ ├── zsysctl_openbsd_arm.go │ │ │ ├── zsysctl_openbsd_arm64.go │ │ │ ├── zsysctl_openbsd_mips64.go │ │ │ ├── zsysctl_openbsd_ppc64.go │ │ │ ├── zsysctl_openbsd_riscv64.go │ │ │ ├── zsysnum_darwin_amd64.go │ │ │ ├── zsysnum_darwin_arm64.go │ │ │ ├── zsysnum_dragonfly_amd64.go │ │ │ ├── zsysnum_freebsd_386.go │ │ │ ├── zsysnum_freebsd_amd64.go │ │ │ ├── zsysnum_freebsd_arm.go │ │ │ ├── zsysnum_freebsd_arm64.go │ │ │ ├── zsysnum_freebsd_riscv64.go │ │ │ ├── zsysnum_linux_386.go │ │ │ ├── zsysnum_linux_amd64.go │ │ │ ├── zsysnum_linux_arm.go │ │ │ ├── zsysnum_linux_arm64.go │ │ │ ├── zsysnum_linux_loong64.go │ │ │ ├── zsysnum_linux_mips.go │ │ │ ├── zsysnum_linux_mips64.go │ │ │ ├── zsysnum_linux_mips64le.go │ │ │ ├── zsysnum_linux_mipsle.go │ │ │ ├── zsysnum_linux_ppc.go │ │ │ ├── zsysnum_linux_ppc64.go │ │ │ ├── zsysnum_linux_ppc64le.go │ │ │ ├── zsysnum_linux_riscv64.go │ │ │ ├── zsysnum_linux_s390x.go │ │ │ ├── zsysnum_linux_sparc64.go │ │ │ ├── zsysnum_netbsd_386.go │ │ │ ├── zsysnum_netbsd_amd64.go │ │ │ ├── zsysnum_netbsd_arm.go │ │ │ ├── zsysnum_netbsd_arm64.go │ │ │ ├── zsysnum_openbsd_386.go │ │ │ ├── zsysnum_openbsd_amd64.go │ │ │ ├── zsysnum_openbsd_arm.go │ │ │ ├── zsysnum_openbsd_arm64.go │ │ │ ├── zsysnum_openbsd_mips64.go │ │ │ ├── zsysnum_openbsd_ppc64.go │ │ │ ├── zsysnum_openbsd_riscv64.go │ │ │ ├── zsysnum_zos_s390x.go │ │ │ ├── ztypes_aix_ppc.go │ │ │ ├── ztypes_aix_ppc64.go │ │ │ ├── ztypes_darwin_amd64.go │ │ │ ├── ztypes_darwin_arm64.go │ │ │ ├── ztypes_dragonfly_amd64.go │ │ │ ├── ztypes_freebsd_386.go │ │ │ ├── ztypes_freebsd_amd64.go │ │ │ ├── ztypes_freebsd_arm.go │ │ │ ├── ztypes_freebsd_arm64.go │ │ │ ├── ztypes_freebsd_riscv64.go │ │ │ ├── ztypes_linux.go │ │ │ ├── ztypes_linux_386.go │ │ │ ├── ztypes_linux_amd64.go │ │ │ ├── ztypes_linux_arm.go │ │ │ ├── ztypes_linux_arm64.go │ │ │ ├── ztypes_linux_loong64.go │ │ │ ├── ztypes_linux_mips.go │ │ │ ├── ztypes_linux_mips64.go │ │ │ ├── ztypes_linux_mips64le.go │ │ │ ├── ztypes_linux_mipsle.go │ │ │ ├── ztypes_linux_ppc.go │ │ │ ├── ztypes_linux_ppc64.go │ │ │ ├── ztypes_linux_ppc64le.go │ │ │ ├── ztypes_linux_riscv64.go │ │ │ ├── ztypes_linux_s390x.go │ │ │ ├── ztypes_linux_sparc64.go │ │ │ ├── ztypes_netbsd_386.go │ │ │ ├── ztypes_netbsd_amd64.go │ │ │ ├── ztypes_netbsd_arm.go │ │ │ ├── ztypes_netbsd_arm64.go │ │ │ ├── ztypes_openbsd_386.go │ │ │ ├── ztypes_openbsd_amd64.go │ │ │ ├── ztypes_openbsd_arm.go │ │ │ ├── ztypes_openbsd_arm64.go │ │ │ ├── ztypes_openbsd_mips64.go │ │ │ ├── ztypes_openbsd_ppc64.go │ │ │ ├── ztypes_openbsd_riscv64.go │ │ │ ├── ztypes_solaris_amd64.go │ │ │ └── ztypes_zos_s390x.go │ │ ├── text │ │ ├── LICENSE │ │ ├── PATENTS │ │ ├── encoding │ │ │ ├── charmap │ │ │ │ ├── charmap.go │ │ │ │ └── tables.go │ │ │ ├── encoding.go │ │ │ ├── htmlindex │ │ │ │ ├── htmlindex.go │ │ │ │ ├── map.go │ │ │ │ └── tables.go │ │ │ ├── internal │ │ │ │ ├── identifier │ │ │ │ │ ├── identifier.go │ │ │ │ │ └── mib.go │ │ │ │ └── internal.go │ │ │ ├── japanese │ │ │ │ ├── all.go │ │ │ │ ├── eucjp.go │ │ │ │ ├── iso2022jp.go │ │ │ │ ├── shiftjis.go │ │ │ │ └── tables.go │ │ │ ├── korean │ │ │ │ ├── euckr.go │ │ │ │ └── tables.go │ │ │ ├── simplifiedchinese │ │ │ │ ├── all.go │ │ │ │ ├── gbk.go │ │ │ │ ├── hzgb2312.go │ │ │ │ └── tables.go │ │ │ ├── traditionalchinese │ │ │ │ ├── big5.go │ │ │ │ └── tables.go │ │ │ └── unicode │ │ │ │ ├── override.go │ │ │ │ └── unicode.go │ │ ├── internal │ │ │ ├── language │ │ │ │ ├── common.go │ │ │ │ ├── compact.go │ │ │ │ ├── compact │ │ │ │ │ ├── compact.go │ │ │ │ │ ├── language.go │ │ │ │ │ ├── parents.go │ │ │ │ │ ├── tables.go │ │ │ │ │ └── tags.go │ │ │ │ ├── compose.go │ │ │ │ ├── coverage.go │ │ │ │ ├── language.go │ │ │ │ ├── lookup.go │ │ │ │ ├── match.go │ │ │ │ ├── parse.go │ │ │ │ ├── tables.go │ │ │ │ └── tags.go │ │ │ ├── tag │ │ │ │ └── tag.go │ │ │ └── utf8internal │ │ │ │ └── utf8internal.go │ │ ├── language │ │ │ ├── coverage.go │ │ │ ├── doc.go │ │ │ ├── language.go │ │ │ ├── match.go │ │ │ ├── parse.go │ │ │ ├── tables.go │ │ │ └── tags.go │ │ ├── runes │ │ │ ├── cond.go │ │ │ └── runes.go │ │ └── transform │ │ │ └── transform.go │ │ └── tools │ │ ├── LICENSE │ │ ├── PATENTS │ │ ├── cover │ │ └── profile.go │ │ └── go │ │ └── ast │ │ └── inspector │ │ ├── inspector.go │ │ └── typeof.go ├── gopkg.in │ ├── alexcesaro │ │ └── quotedprintable.v1 │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── header.go │ │ │ ├── internal │ │ │ └── quotedprintable.go │ │ │ └── quotedprintable.go │ ├── gomail.v1 │ │ ├── LICENSE │ │ ├── README.md │ │ ├── export.go │ │ ├── gomail.go │ │ ├── mailer.go │ │ └── send.go │ ├── gorp.v1 │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── dialect.go │ │ ├── errors.go │ │ ├── gorp.go │ │ └── test_all.sh │ └── yaml.v3 │ │ ├── LICENSE │ │ ├── NOTICE │ │ ├── README.md │ │ ├── apic.go │ │ ├── decode.go │ │ ├── emitterc.go │ │ ├── encode.go │ │ ├── parserc.go │ │ ├── readerc.go │ │ ├── resolve.go │ │ ├── scannerc.go │ │ ├── sorter.go │ │ ├── writerc.go │ │ ├── yaml.go │ │ ├── yamlh.go │ │ └── yamlprivateh.go └── modules.txt ├── version ├── walkthrough.md └── web ├── init_test.go ├── muxer.go ├── router.go ├── server.go ├── version_router.go └── version_router_test.go /.ackrc: -------------------------------------------------------------------------------- 1 | --ignore-dir=Godeps 2 | -------------------------------------------------------------------------------- /.cfignore: -------------------------------------------------------------------------------- 1 | /.ackrc 2 | /.git 3 | /.gitignore 4 | /LICENSE 5 | /README.md 6 | /V1_API.md 7 | /testing 8 | /v1/acceptance 9 | /walkthrough.md 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | /bin/a1deploy 3 | /bin/bosh-lite_deploy 4 | /bin/env/development 5 | /templates/overrides 6 | /notifications.iml 7 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | ginkgo 2.9.4 2 | golang 1.20.6 3 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | notifications 2 | 3 | Copyright (c) 2015-Present CloudFoundry.org Foundation, Inc. All Rights Reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /application/init_test.go: -------------------------------------------------------------------------------- 1 | package application_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestApplicationSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "application") 13 | } 14 | -------------------------------------------------------------------------------- /bin/acceptance: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | set +e 3 | 4 | DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" 5 | ENVIRONMENT="${ENVIRONMENT:-test}" 6 | 7 | GREEN="\033[0;32m" 8 | RED="\033[0;31m" 9 | NONE="\033[0m" 10 | 11 | # shellcheck source=./env/test 12 | source "$DIR/env/$ENVIRONMENT" 13 | EXIT_CODE=0 14 | 15 | function run() { 16 | set +e 17 | ginkgo --randomize-all=true --randomize-suites=true -succinct=true "$@" 18 | EXIT_CODE=$? 19 | set -e 20 | } 21 | 22 | if [[ $EXIT_CODE = 0 ]]; then 23 | run --poll-progress-after=10s ./v1/acceptance 24 | fi 25 | 26 | if [[ $EXIT_CODE = 0 ]]; then 27 | STATE="${GREEN}ACCEPTANCE SUITE PASS${NONE}" 28 | else 29 | STATE="${RED}ACCEPTANCE SUITE FAIL${NONE}" 30 | fi 31 | 32 | echo 33 | echo -e $STATE 34 | 35 | exit $EXIT_CODE 36 | -------------------------------------------------------------------------------- /bin/integration: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | set +e 3 | 4 | DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" 5 | ENVIRONMENT="${ENVIRONMENT:-test}" 6 | 7 | GREEN="\033[0;32m" 8 | RED="\033[0;31m" 9 | NONE="\033[0m" 10 | 11 | # shellcheck source=./env/test 12 | source "$DIR/env/$ENVIRONMENT" 13 | 14 | function run() { 15 | set +e 16 | ginkgo --randomize-all=true --randomize-suites=true -succinct=true "$@" 17 | EXIT_CODE=$? 18 | if [[ $EXIT_CODE != 0 ]]; then 19 | fail 20 | fi 21 | set -e 22 | } 23 | 24 | function fail() { 25 | echo 26 | echo -e "${RED}INTEGRATION SUITE FAIL${NONE}" 27 | exit $EXIT_CODE 28 | } 29 | 30 | if [[ -z "$1" ]]; then 31 | run ./v1/models ./gobble ./db 32 | else 33 | run "$@" 34 | fi 35 | 36 | echo -e "${GREEN}INTEGRATION SUITE PASS${NONE}" 37 | exit $EXIT_CODE 38 | -------------------------------------------------------------------------------- /bin/run: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | set -e 3 | 4 | DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" 5 | ENVIRONMENT="${ENVIRONMENT:-development}" 6 | 7 | # shellcheck source=./env/test 8 | source "$DIR/env/$ENVIRONMENT" 9 | 10 | go run main.go "$@" 11 | -------------------------------------------------------------------------------- /bin/unit: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | set +e 3 | 4 | DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" 5 | ENVIRONMENT="${ENVIRONMENT:-test}" 6 | export ROOT_PATH="$DIR/.." 7 | 8 | GREEN="\033[0;32m" 9 | RED="\033[0;31m" 10 | NONE="\033[0m" 11 | 12 | # shellcheck source=./env/test 13 | source "$DIR/env/$ENVIRONMENT" 14 | 15 | function run() { 16 | ginkgo --randomize-all=true --randomize-suites=true -succinct=true "$@" 17 | EXIT_CODE=$? 18 | if [[ $EXIT_CODE != 0 ]]; then 19 | fail 20 | fi 21 | } 22 | 23 | function fail() { 24 | echo 25 | echo -e "${RED}UNIT SUITE FAIL${NONE}" 26 | exit $EXIT_CODE 27 | } 28 | 29 | if [[ -z "$1" ]]; then 30 | run -r --skip-package=acceptance,models,gobble,db,vendor -p ./ 31 | else 32 | run "$@" 33 | fi 34 | 35 | echo -e "${GREEN}UNIT SUITE PASS${NONE}" 36 | exit $EXIT_CODE 37 | -------------------------------------------------------------------------------- /catalog/catalog-info.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: backstage.io/v1alpha1 3 | kind: Component 4 | metadata: 5 | name: notifications 6 | # This is an extra long description 7 | description: A backend service for sending (usually email) notifications to CF users, including templating and subscription management 8 | links: [] 9 | tags: 10 | - cloud-foundry 11 | - go 12 | spec: 13 | type: library 14 | lifecycle: production 15 | owner: wg-app-runtime-interfaces-notifications-approvers 16 | system: notifications 17 | providesApis: 18 | - notifications 19 | --- 20 | apiVersion: backstage.io/v1alpha1 21 | kind: System 22 | metadata: 23 | name: notifications 24 | description: A service for sending email notifications to CF users 25 | tags: 26 | - cloud-foundry 27 | spec: 28 | owner: wg-app-runtime-interfaces-notifications-approvers 29 | domain: app-runtime 30 | -------------------------------------------------------------------------------- /cf/get_auditors_by_organization_guid.go: -------------------------------------------------------------------------------- 1 | package cf 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/rcrowley/go-metrics" 7 | ) 8 | 9 | func (cc CloudController) GetAuditorsByOrgGuid(guid, token string) ([]CloudControllerUser, error) { 10 | ccUsers := make([]CloudControllerUser, 0) 11 | 12 | then := time.Now() 13 | list, err := cc.client.Organizations.ListAuditors(guid, token) 14 | 15 | if err != nil { 16 | return ccUsers, NewFailure(0, err.Error()) 17 | } 18 | 19 | metrics.GetOrRegisterTimer("notifications.external-requests.cc.auditors-by-org-guid", nil).Update(time.Since(then)) 20 | 21 | for _, user := range list.Users { 22 | ccUsers = append(ccUsers, CloudControllerUser{ 23 | GUID: user.GUID, 24 | }) 25 | } 26 | return ccUsers, nil 27 | } 28 | -------------------------------------------------------------------------------- /cf/get_billing_managers_by_organization_guid.go: -------------------------------------------------------------------------------- 1 | package cf 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/rcrowley/go-metrics" 7 | ) 8 | 9 | func (cc CloudController) GetBillingManagersByOrgGuid(guid, token string) ([]CloudControllerUser, error) { 10 | var ccUsers []CloudControllerUser 11 | then := time.Now() 12 | 13 | list, err := cc.client.Organizations.ListBillingManagers(guid, token) 14 | if err != nil { 15 | return ccUsers, NewFailure(0, err.Error()) 16 | } 17 | 18 | metrics.GetOrRegisterTimer("notifications.external-requests.cc.billing-managers-by-org-guid", nil).Update(time.Since(then)) 19 | 20 | for _, user := range list.Users { 21 | ccUsers = append(ccUsers, CloudControllerUser{ 22 | GUID: user.GUID, 23 | }) 24 | } 25 | 26 | return ccUsers, nil 27 | } 28 | -------------------------------------------------------------------------------- /cf/get_managers_by_organization_guid.go: -------------------------------------------------------------------------------- 1 | package cf 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/rcrowley/go-metrics" 7 | ) 8 | 9 | func (cc CloudController) GetManagersByOrgGuid(guid, token string) ([]CloudControllerUser, error) { 10 | var ccUsers []CloudControllerUser 11 | then := time.Now() 12 | 13 | list, err := cc.client.Organizations.ListManagers(guid, token) 14 | if err != nil { 15 | return ccUsers, NewFailure(0, err.Error()) 16 | } 17 | 18 | metrics.GetOrRegisterTimer("notifications.external-requests.cc.managers-by-org-guid", nil).Update(time.Since(then)) 19 | 20 | for _, user := range list.Users { 21 | ccUsers = append(ccUsers, CloudControllerUser{ 22 | GUID: user.GUID, 23 | }) 24 | } 25 | 26 | return ccUsers, nil 27 | } 28 | -------------------------------------------------------------------------------- /cf/get_users_by_organization_guid.go: -------------------------------------------------------------------------------- 1 | package cf 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/rcrowley/go-metrics" 7 | ) 8 | 9 | func (cc CloudController) GetUsersByOrgGuid(guid, token string) ([]CloudControllerUser, error) { 10 | var ccUsers []CloudControllerUser 11 | then := time.Now() 12 | 13 | list, err := cc.client.Organizations.ListUsers(guid, token) 14 | if err != nil { 15 | return ccUsers, NewFailure(0, err.Error()) 16 | } 17 | 18 | metrics.GetOrRegisterTimer("notifications.external-requests.cc.users-by-org-guid", nil).Update(time.Since(then)) 19 | 20 | for _, user := range list.Users { 21 | ccUsers = append(ccUsers, CloudControllerUser{ 22 | GUID: user.GUID, 23 | }) 24 | } 25 | 26 | return ccUsers, nil 27 | } 28 | -------------------------------------------------------------------------------- /cf/get_users_by_space_guid.go: -------------------------------------------------------------------------------- 1 | package cf 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/rcrowley/go-metrics" 7 | ) 8 | 9 | func (cc CloudController) GetUsersBySpaceGuid(guid, token string) ([]CloudControllerUser, error) { 10 | then := time.Now() 11 | 12 | list, err := cc.client.Spaces.ListUsers(guid, token) 13 | if err != nil { 14 | return []CloudControllerUser{}, NewFailure(0, err.Error()) 15 | } 16 | 17 | metrics.GetOrRegisterTimer("notifications.external-requests.cc.users-by-space-guid", nil).Update(time.Since(then)) 18 | 19 | ccUsers := []CloudControllerUser{} 20 | for _, user := range list.Users { 21 | ccUsers = append(ccUsers, CloudControllerUser{ 22 | GUID: user.GUID, 23 | }) 24 | } 25 | 26 | return ccUsers, nil 27 | } 28 | -------------------------------------------------------------------------------- /cf/init_test.go: -------------------------------------------------------------------------------- 1 | package cf_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestCFSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "cf") 13 | } 14 | -------------------------------------------------------------------------------- /cf/load_organization.go: -------------------------------------------------------------------------------- 1 | package cf 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/pivotal-cf-experimental/rainmaker" 8 | metrics "github.com/rcrowley/go-metrics" 9 | ) 10 | 11 | func (cc CloudController) LoadOrganization(guid, token string) (CloudControllerOrganization, error) { 12 | then := time.Now() 13 | 14 | org, err := cc.client.Organizations.Get(guid, token) 15 | if err != nil { 16 | _, ok := err.(rainmaker.NotFoundError) 17 | if ok { 18 | return CloudControllerOrganization{}, NotFoundError{fmt.Sprintf("Organization %q could not be found", guid)} 19 | } else { 20 | return CloudControllerOrganization{}, NewFailure(0, err.Error()) 21 | } 22 | } 23 | 24 | metrics.GetOrRegisterTimer("notifications.external-requests.cc.organization", nil).Update(time.Since(then)) 25 | 26 | return CloudControllerOrganization{ 27 | GUID: org.GUID, 28 | Name: org.Name, 29 | }, nil 30 | } 31 | -------------------------------------------------------------------------------- /cf/load_space.go: -------------------------------------------------------------------------------- 1 | package cf 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/pivotal-cf-experimental/rainmaker" 8 | "github.com/rcrowley/go-metrics" 9 | ) 10 | 11 | func (cc CloudController) LoadSpace(spaceGuid, token string) (CloudControllerSpace, error) { 12 | then := time.Now() 13 | 14 | space, err := cc.client.Spaces.Get(spaceGuid, token) 15 | if err != nil { 16 | _, ok := err.(rainmaker.NotFoundError) 17 | if ok { 18 | return CloudControllerSpace{}, NotFoundError{fmt.Sprintf("Space %q could not be found", spaceGuid)} 19 | } else { 20 | return CloudControllerSpace{}, NewFailure(0, err.Error()) 21 | } 22 | } 23 | 24 | metrics.GetOrRegisterTimer("notifications.external-requests.cc.space", nil).Update(time.Since(then)) 25 | 26 | return CloudControllerSpace{ 27 | GUID: space.GUID, 28 | Name: space.Name, 29 | OrganizationGUID: space.OrganizationGUID, 30 | }, nil 31 | } 32 | -------------------------------------------------------------------------------- /db/config.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | type Config struct { 4 | DefaultTemplatePath string 5 | } 6 | -------------------------------------------------------------------------------- /db/connection.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "database/sql" 5 | 6 | "gopkg.in/gorp.v1" 7 | ) 8 | 9 | type ConnectionInterface interface { 10 | Transaction() TransactionInterface 11 | GetDbMap() *gorp.DbMap 12 | Delete(...interface{}) (int64, error) 13 | Insert(...interface{}) error 14 | Select(interface{}, string, ...interface{}) ([]interface{}, error) 15 | SelectOne(interface{}, string, ...interface{}) error 16 | Update(...interface{}) (int64, error) 17 | Exec(string, ...interface{}) (sql.Result, error) 18 | Get(i interface{}, keys ...interface{}) (interface{}, error) 19 | } 20 | 21 | type Connection struct { 22 | *gorp.DbMap 23 | } 24 | 25 | func (conn *Connection) Transaction() TransactionInterface { 26 | return NewTransaction(conn) 27 | } 28 | 29 | func (conn *Connection) GetDbMap() *gorp.DbMap { 30 | return conn.DbMap 31 | } 32 | -------------------------------------------------------------------------------- /db/connection_test.go: -------------------------------------------------------------------------------- 1 | package db_test 2 | 3 | import ( 4 | "github.com/cloudfoundry-incubator/notifications/db" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("Connection", func() { 11 | var conn *db.Connection 12 | 13 | BeforeEach(func() { 14 | conn = &db.Connection{} 15 | }) 16 | 17 | Describe("Transaction", func() { 18 | It("returns an uninitialized transaction", func() { 19 | transaction := conn.Transaction() 20 | Expect(transaction).To(BeAssignableToTypeOf(&db.Transaction{})) 21 | }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /db/database_test.go: -------------------------------------------------------------------------------- 1 | package db_test 2 | 3 | import ( 4 | "github.com/cloudfoundry-incubator/notifications/db" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("Database", func() { 11 | var database *db.DB 12 | 13 | BeforeEach(func() { 14 | database = db.NewDatabase(sqlDB, db.Config{}) 15 | }) 16 | 17 | Describe("Connection", func() { 18 | It("returns a Connection", func() { 19 | connection := database.Connection() 20 | Expect(connection).To(BeAssignableToTypeOf(&db.Connection{})) 21 | }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /db/init_test.go: -------------------------------------------------------------------------------- 1 | package db_test 2 | 3 | import ( 4 | "database/sql" 5 | "testing" 6 | 7 | "github.com/cloudfoundry-incubator/notifications/application" 8 | 9 | . "github.com/onsi/ginkgo/v2" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | func TestDBSuite(t *testing.T) { 14 | RegisterFailHandler(Fail) 15 | RunSpecs(t, "db") 16 | } 17 | 18 | var sqlDB *sql.DB 19 | 20 | var _ = BeforeEach(func() { 21 | env, err := application.NewEnvironment() 22 | Expect(err).NotTo(HaveOccurred()) 23 | 24 | sqlDB, err = sql.Open("mysql", env.DatabaseURL) 25 | Expect(err).NotTo(HaveOccurred()) 26 | }) 27 | -------------------------------------------------------------------------------- /db/migrations/10_add_template_id_to_clients.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | ALTER TABLE `clients` ADD `template_id` varchar(255) DEFAULT ""; 4 | 5 | -- +migrate Down 6 | -- SQL section 'Down' is executed when this migration is rolled back 7 | ALTER TABLE `clients` DROP COLUMN `template_id`; 8 | -------------------------------------------------------------------------------- /db/migrations/11_add_template_id_to_kinds.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | ALTER TABLE `kinds` ADD `template_id` varchar(255) DEFAULT ""; 4 | 5 | -- +migrate Down 6 | -- SQL section 'Down' is executed when this migration is rolled back 7 | ALTER TABLE `kinds` DROP COLUMN `template_id`; 8 | -------------------------------------------------------------------------------- /db/migrations/12_add_metadata_to_templates.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | ALTER TABLE `templates` ADD `metadata` longtext; 4 | 5 | -- +migrate Down 6 | -- SQL section 'Down' is executed when this migration is rolled back 7 | ALTER TABLE `templates` DROP COLUMN `metadata`; 8 | -------------------------------------------------------------------------------- /db/migrations/13_add_overridden_to_templates.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | ALTER TABLE `templates` ADD `overridden` bool DEFAULT false; 4 | 5 | -- +migrate Down 6 | -- SQL section 'Down' is executed when this migration is rolled back 7 | ALTER TABLE `templates` DROP COLUMN `overridden`; 8 | -------------------------------------------------------------------------------- /db/migrations/14_add_updated_at_to_kinds.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | ALTER TABLE `kinds` ADD `updated_at` datetime DEFAULT '2000-01-01 00:00:00'; 4 | 5 | -- +migrate Down 6 | -- SQL section 'Down' is executed when this migration is rolled back 7 | ALTER TABLE `kinds` DROP COLUMN `updated_at`; 8 | -------------------------------------------------------------------------------- /db/migrations/15_messages.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | CREATE TABLE IF NOT EXISTS `messages` ( 4 | `id` varchar(255) NOT NULL, 5 | `status` varchar(255) NOT NULL, 6 | `updated_at` datetime NOT NULL, 7 | PRIMARY KEY (`id`) 8 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 9 | 10 | -- +migrate Down 11 | -- SQL section 'Down' is executed when this migration is rolled back 12 | DROP TABLE messages; 13 | -------------------------------------------------------------------------------- /db/migrations/16_sets_template_id_defaults.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | UPDATE `clients` SET `template_id` = "default" WHERE `template_id` = "" OR `template_id` IS NULL; 4 | ALTER TABLE `clients` MODIFY COLUMN `template_id` varchar(255) NOT NULL DEFAULT "default"; 5 | 6 | UPDATE `kinds` SET `template_id` = "default" WHERE `template_id` = "" OR `template_id` IS NULL; 7 | ALTER TABLE `kinds` MODIFY COLUMN `template_id` varchar(255) NOT NULL DEFAULT "default"; 8 | 9 | -- +migrate Down 10 | -- SQL section 'Down' is executed when this migration is rolled back 11 | ALTER TABLE `clients` MODIFY COLUMN `template_id` varchar(255) DEFAULT ""; 12 | ALTER TABLE `kinds` MODIFY COLUMN `template_id` varchar(255) DEFAULT ""; 13 | -------------------------------------------------------------------------------- /db/migrations/17_sets_metadata_defaults.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | UPDATE `templates` SET `metadata` = "{}" WHERE `metadata` = "" OR `metadata` IS NULL; 4 | 5 | -- +migrate Down 6 | -- SQL section 'Down' is executed when this migration is rolled back 7 | -------------------------------------------------------------------------------- /db/migrations/18_add_senders.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | CREATE TABLE IF NOT EXISTS `senders` ( 4 | `id` varchar(36) NOT NULL, 5 | `name` varchar(255) DEFAULT NULL, 6 | `client_id` varchar(255) DEFAULT NULL, 7 | PRIMARY KEY (`id`), 8 | UNIQUE KEY `name_client_id` (`name`, `client_id`) 9 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 10 | 11 | -- +migrate Down 12 | -- SQL section 'Down' is executed when this migration is rolled back 13 | DROP TABLE senders; 14 | -------------------------------------------------------------------------------- /db/migrations/19_add_notification_types.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | CREATE TABLE IF NOT EXISTS `notification_types` ( 4 | `id` varchar(36) NOT NULL, 5 | `name` varchar(255) DEFAULT NULL, 6 | `description` varchar(255) DEFAULT NULL, 7 | `critical` bool DEFAULT FALSE, 8 | `template_id` varchar(255) DEFAULT NULL, 9 | `sender_id` varchar(255) DEFAULT NULL, 10 | PRIMARY KEY (`id`), 11 | UNIQUE KEY `name_sender_id` (`name`, `sender_id`) 12 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 13 | 14 | -- +migrate Down 15 | -- SQL section 'Down' is executed when this migration is rolled back 16 | DROP TABLE notification_types; 17 | -------------------------------------------------------------------------------- /db/migrations/1_templates.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | CREATE TABLE IF NOT EXISTS `templates` ( 4 | `primary` int(11) NOT NULL AUTO_INCREMENT, 5 | `name` varchar(255) DEFAULT NULL, 6 | `text` longtext DEFAULT NULL, 7 | `html` longtext DEFAULT NULL, 8 | `overridden` tinyint(1) DEFAULT NULL, 9 | `created_at` datetime DEFAULT NULL, 10 | PRIMARY KEY (`primary`), 11 | UNIQUE KEY `name` (`name`) 12 | ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8; 13 | 14 | -- +migrate Down 15 | -- SQL section 'Down' is executed when this migration is rolled back 16 | DROP TABLE templates; 17 | -------------------------------------------------------------------------------- /db/migrations/20_rename_notification_types_campaign_types.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | RENAME TABLE notification_types to campaign_types; 4 | 5 | -- +migrate Down 6 | -- SQL section 'Down' is executed when this migration is rolled back 7 | RENAME TABLE campaign_types to notification_types; -------------------------------------------------------------------------------- /db/migrations/21_create_v2_templates.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | CREATE TABLE IF NOT EXISTS `v2_templates` ( 4 | `id` varchar(36) NOT NULL, 5 | `name` varchar(255) DEFAULT NULL, 6 | `html` longtext DEFAULT NULL, 7 | `text` longtext DEFAULT NULL, 8 | `subject` varchar(255) DEFAULT NULL, 9 | `metadata` longtext DEFAULT NULL, 10 | `client_id` varchar(255) DEFAULT NULL, 11 | PRIMARY KEY (`id`), 12 | UNIQUE KEY `name_client_id` (`name`, `client_id`) 13 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 14 | 15 | -- +migrate Down 16 | -- SQL section 'Down' is executed when this migration is rolled back 17 | DROP TABLE v2_templates; 18 | -------------------------------------------------------------------------------- /db/migrations/22_add_campaigns.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | CREATE TABLE IF NOT EXISTS `campaigns` ( 4 | `id` varchar(36) NOT NULL, 5 | `campaign_type_id` varchar(36) DEFAULT NULL, 6 | `template_id` varchar(36) DEFAULT NULL, 7 | `sender_id` varchar(36) DEFAULT NULL, 8 | `send_to` longtext DEFAULT NULL, 9 | `text` longtext DEFAULT NULL, 10 | `html` longtext DEFAULT NULL, 11 | `subject` varchar(255) DEFAULT NULL, 12 | `reply_to` varchar(255) DEFAULT NULL, 13 | PRIMARY KEY (`id`) 14 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 15 | 16 | -- +migrate Down 17 | -- SQL section 'Down' is executed when this migration is rolled back 18 | DROP TABLE campaigns; 19 | -------------------------------------------------------------------------------- /db/migrations/24_add_campaign_id_to_messages.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | ALTER TABLE `messages` ADD `campaign_id` varchar(255); 4 | 5 | -- +migrate Down 6 | -- SQL section 'Down' is executed when this migration is rolled back 7 | ALTER TABLE `messages` DROP COLUMN `campaign_id`; 8 | -------------------------------------------------------------------------------- /db/migrations/25_add_unsubscribers.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | CREATE TABLE IF NOT EXISTS `unsubscribers` ( 4 | `id` varchar(36) NOT NULL, 5 | `campaign_type_id` varchar(36) DEFAULT NULL, 6 | `user_guid` varchar(255) DEFAULT NULL, 7 | PRIMARY KEY (`id`) 8 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 9 | 10 | -- +migrate Down 11 | -- SQL section 'Down' is executed when this migration is rolled back 12 | DROP TABLE unsubscribers; 13 | -------------------------------------------------------------------------------- /db/migrations/26_add_unique_key_to_unsubscribers.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | ALTER TABLE `unsubscribers` ADD UNIQUE KEY `user_guid_campaign_type_id` (`user_guid`, `campaign_type_id`); 4 | 5 | -- +migrate Down 6 | -- SQL section 'Down' is executed when this migration is rolled back 7 | ALTER TABLE `unsubscribers` DROP KEY `user_guid_campaign_type_id`; 8 | -------------------------------------------------------------------------------- /db/migrations/27_remove_name_client_key_from_templates.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | ALTER TABLE `v2_templates` DROP KEY `name_client_id`; 4 | 5 | -- +migrate Down 6 | -- SQL section 'Down' is executed when this migration is rolled back 7 | ALTER TABLE `v2_templates` ADD UNIQUE KEY `name_client_id` (`name`, `client_id`); 8 | -------------------------------------------------------------------------------- /db/migrations/2_clients.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | CREATE TABLE IF NOT EXISTS `clients` ( 4 | `primary` int(11) NOT NULL AUTO_INCREMENT, 5 | `id` varchar(255) DEFAULT NULL, 6 | `description` varchar(255) DEFAULT NULL, 7 | `created_at` datetime DEFAULT NULL, 8 | PRIMARY KEY (`primary`), 9 | UNIQUE KEY `id` (`id`) 10 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 11 | 12 | -- +migrate Down 13 | -- SQL section 'Down' is executed when this migration is rolled back 14 | DROP TABLE `clients`; 15 | -------------------------------------------------------------------------------- /db/migrations/3_kinds.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | CREATE TABLE IF NOT EXISTS `kinds` ( 4 | `primary` int(11) NOT NULL AUTO_INCREMENT, 5 | `id` varchar(255) DEFAULT NULL, 6 | `description` varchar(255) DEFAULT NULL, 7 | `critical` tinyint(1) DEFAULT NULL, 8 | `client_id` varchar(255) DEFAULT NULL, 9 | `created_at` datetime DEFAULT NULL, 10 | PRIMARY KEY (`primary`), 11 | UNIQUE KEY `id` (`id`,`client_id`), 12 | UNIQUE KEY `id_2` (`id`,`client_id`) 13 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 14 | 15 | -- +migrate Down 16 | -- SQL section 'Down' is executed when this migration is rolled back 17 | DROP TABLE `kinds`; 18 | -------------------------------------------------------------------------------- /db/migrations/4_receipts.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | CREATE TABLE IF NOT EXISTS `receipts` ( 4 | `primary` int(11) NOT NULL AUTO_INCREMENT, 5 | `user_guid` varchar(255) DEFAULT NULL, 6 | `client_id` varchar(255) DEFAULT NULL, 7 | `kind_id` varchar(255) DEFAULT NULL, 8 | `count` int(11) DEFAULT NULL, 9 | `created_at` datetime DEFAULT NULL, 10 | PRIMARY KEY (`primary`), 11 | UNIQUE KEY `user_guid` (`user_guid`,`client_id`,`kind_id`), 12 | UNIQUE KEY `user_guid_2` (`user_guid`,`client_id`,`kind_id`) 13 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 14 | 15 | -- +migrate Down 16 | -- SQL section 'Down' is executed when this migration is rolled back 17 | DROP TABLE `receipts`; 18 | -------------------------------------------------------------------------------- /db/migrations/5_unsubscribes.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | CREATE TABLE IF NOT EXISTS `unsubscribes` ( 4 | `primary` int(11) NOT NULL AUTO_INCREMENT, 5 | `user_id` varchar(255) DEFAULT NULL, 6 | `client_id` varchar(255) DEFAULT NULL, 7 | `kind_id` varchar(255) DEFAULT NULL, 8 | `created_at` datetime DEFAULT NULL, 9 | PRIMARY KEY (`primary`), 10 | UNIQUE KEY `user_id` (`user_id`,`client_id`,`kind_id`), 11 | UNIQUE KEY `user_id_2` (`user_id`,`client_id`,`kind_id`) 12 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 13 | 14 | -- +migrate Down 15 | -- SQL section 'Down' is executed when this migration is rolled back 16 | DROP TABLE `unsubscribes`; 17 | -------------------------------------------------------------------------------- /db/migrations/6_global_unsubscribes.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | CREATE TABLE IF NOT EXISTS `global_unsubscribes` ( 4 | `primary` int(11) NOT NULL AUTO_INCREMENT, 5 | `user_id` varchar(255) DEFAULT NULL, 6 | `created_at` datetime DEFAULT NULL, 7 | PRIMARY KEY (`primary`), 8 | UNIQUE KEY `user_id` (`user_id`) 9 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 10 | 11 | -- +migrate Down 12 | -- SQL section 'Down' is executed when this migration is rolled back 13 | DROP TABLE `global_unsubscribes`; 14 | -------------------------------------------------------------------------------- /db/migrations/7_remove_overridden_from_templates.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | ALTER TABLE `templates` DROP COLUMN `overridden`; 4 | 5 | -- +migrate Down 6 | -- SQL section 'Down' is executed when this migration is rolled back 7 | ALTER TABLE `templates` ADD `overridden` tinyint(1) DEFAULT NULL; 8 | -------------------------------------------------------------------------------- /db/migrations/8_add_updated_at_to_templates.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | ALTER TABLE `templates` ADD `updated_at` datetime DEFAULT NULL; 4 | 5 | -- +migrate Down 6 | -- SQL section 'Down' is executed when this migration is rolled back 7 | ALTER TABLE `templates` DROP COLUMN `updated_at`; 8 | -------------------------------------------------------------------------------- /db/migrations/9_add_id_and_subject_to_templates.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | -- SQL in section 'Up' is executed when this migration is applied 3 | ALTER TABLE `templates` ADD `id` varchar(255) DEFAULT NULL; 4 | ALTER TABLE `templates` ADD UNIQUE KEY `id` (`id`); 5 | ALTER TABLE `templates` DROP INDEX `name`; 6 | ALTER TABLE `templates` ADD `subject` varchar(255) DEFAULT NULL; 7 | 8 | -- +migrate Down 9 | -- SQL section 'Down' is executed when this migration is rolled back 10 | ALTER TABLE `templates` DROP COLUMN `id`; 11 | ALTER TABLE `templates` DROP INDEX `id`; 12 | ALTER TABLE `templates` ADD UNIQUE KEY `name` (`name`); 13 | ALTER TABLE `templates` DROP COLUMN `subject`; 14 | 15 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: '2' 3 | services: 4 | mariadb: 5 | container_name: notifications 6 | image: mariadb 7 | ports: 8 | - 3306:3306 9 | environment: 10 | - MYSQL_ROOT_PASSWORD=password 11 | - MYSQL_DATABASE=notifications_test 12 | - MYSQL_USER=user 13 | - MYSQL_PASSWORD=password 14 | -------------------------------------------------------------------------------- /docs/diff.go: -------------------------------------------------------------------------------- 1 | package docs 2 | 3 | import "regexp" 4 | 5 | const ( 6 | date = "Date: [A-Z]{1}[a-z]{2}, [0-3]{1}[0-9]{1} [A-Z]{1}[a-z]{2} 20[0-9]{2} [0-2]{1}[0-9]{1}:[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1} [A-Z]{3}" 7 | auth = "Authorization: Bearer .*" 8 | guid = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" 9 | timestamp = "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z" 10 | ) 11 | 12 | func Diff(left, right string) bool { 13 | regexes := []*regexp.Regexp{ 14 | regexp.MustCompile(date), 15 | regexp.MustCompile(auth), 16 | regexp.MustCompile(guid), 17 | regexp.MustCompile(timestamp), 18 | } 19 | 20 | for _, regex := range regexes { 21 | left = regex.ReplaceAllString(left, "") 22 | right = regex.ReplaceAllString(right, "") 23 | } 24 | 25 | return left != right 26 | } 27 | -------------------------------------------------------------------------------- /docs/init_test.go: -------------------------------------------------------------------------------- 1 | package docs_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestDocs(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "docs") 13 | } 14 | -------------------------------------------------------------------------------- /docs/roundtrip_recorder.go: -------------------------------------------------------------------------------- 1 | package docs 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | type RoundTripRecorder struct { 9 | RoundTrips map[string]RoundTrip 10 | } 11 | 12 | func NewRoundTripRecorder() *RoundTripRecorder { 13 | return &RoundTripRecorder{ 14 | RoundTrips: make(map[string]RoundTrip), 15 | } 16 | } 17 | 18 | func (r *RoundTripRecorder) Record(key string, request *http.Request, response *http.Response) error { 19 | if _, present := r.RoundTrips[key]; present { 20 | return fmt.Errorf("new roundtrip %q conflicts with existing roundtrip", key) 21 | } 22 | 23 | r.RoundTrips[key] = RoundTrip{ 24 | Request: request, 25 | Response: response, 26 | } 27 | 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /gobble/config.go: -------------------------------------------------------------------------------- 1 | package gobble 2 | 3 | import "time" 4 | 5 | type Config struct { 6 | WaitMaxDuration time.Duration 7 | MaxQueueLength int 8 | } 9 | -------------------------------------------------------------------------------- /gobble/heartbeater.go: -------------------------------------------------------------------------------- 1 | package gobble 2 | 3 | type Heartbeater struct { 4 | queue QueueInterface 5 | ticker TickerInterface 6 | haltChan chan struct{} 7 | } 8 | 9 | func NewHeartbeater(queue QueueInterface, ticker TickerInterface) Heartbeater { 10 | return Heartbeater{ 11 | queue: queue, 12 | ticker: ticker, 13 | haltChan: make(chan struct{}), 14 | } 15 | } 16 | 17 | func (beater Heartbeater) Beat(job *Job) { 18 | beater.ticker.Start() 19 | for { 20 | select { 21 | case job.ActiveAt = <-beater.ticker.Tick(): 22 | beater.queue.Requeue(job) 23 | case <-beater.haltChan: 24 | beater.ticker.Stop() 25 | return 26 | } 27 | } 28 | } 29 | 30 | func (beater Heartbeater) Halt() { 31 | beater.haltChan <- struct{}{} 32 | } 33 | -------------------------------------------------------------------------------- /gobble/metrics.go: -------------------------------------------------------------------------------- 1 | package gobble 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/rcrowley/go-metrics" 7 | ) 8 | 9 | type QueueGauge struct { 10 | queue queue 11 | timer <-chan time.Time 12 | } 13 | 14 | type queue interface { 15 | Len() (int, error) 16 | } 17 | 18 | func NewQueueGauge(queue queue, timer <-chan time.Time) QueueGauge { 19 | return QueueGauge{ 20 | queue: queue, 21 | timer: timer, 22 | } 23 | } 24 | 25 | func (g QueueGauge) Run() { 26 | for range g.timer { 27 | ql, _ := g.queue.Len() 28 | 29 | metrics.GetOrRegisterGauge("notifications.queue.length", nil).Update(int64(ql)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /gobble/migrations/1_create_jobs.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | CREATE TABLE IF NOT EXISTS `jobs` ( 3 | `id` int(11) NOT NULL AUTO_INCREMENT, 4 | `worker_id` varchar(255) DEFAULT NULL, 5 | `payload` text DEFAULT NULL, 6 | `version` bigint(20) DEFAULT NULL, 7 | PRIMARY KEY (`id`) 8 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; 9 | 10 | -- +migrate Down 11 | DROP TABLE jobs; 12 | -------------------------------------------------------------------------------- /gobble/migrations/3_change_payload_type_to_longtext.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | ALTER TABLE `jobs` MODIFY payload longtext; 3 | 4 | -- +migrate Down 5 | ALTER TABLE `jobs` MODIFY payload text; 6 | -------------------------------------------------------------------------------- /gobble/migrations/4_drop_goose_table.sql: -------------------------------------------------------------------------------- 1 | -- +migrate Up 2 | DROP TABLE IF EXISTS `goose_db_version`; 3 | 4 | -- +migrate Down 5 | CREATE TABLE IF NOT EXISTS `goose_db_version`; 6 | -------------------------------------------------------------------------------- /gobble/ticker.go: -------------------------------------------------------------------------------- 1 | package gobble 2 | 3 | import "time" 4 | 5 | type TickerInterface interface { 6 | Tick() <-chan time.Time 7 | Start() 8 | Stop() 9 | } 10 | 11 | type Ticker struct { 12 | constructor func(time.Duration) *time.Ticker 13 | duration time.Duration 14 | ticker *time.Ticker 15 | } 16 | 17 | func NewTicker(tickerConstructor func(time.Duration) *time.Ticker, duration time.Duration) *Ticker { 18 | return &Ticker{ 19 | constructor: tickerConstructor, 20 | duration: duration, 21 | } 22 | } 23 | 24 | func (t Ticker) Tick() <-chan time.Time { 25 | if t.ticker == nil { 26 | return nil 27 | } 28 | 29 | return t.ticker.C 30 | } 31 | 32 | func (t *Ticker) Start() { 33 | t.ticker = t.constructor(t.duration) 34 | } 35 | 36 | func (t Ticker) Stop() { 37 | t.ticker.Stop() 38 | } 39 | -------------------------------------------------------------------------------- /mail/exports_test.go: -------------------------------------------------------------------------------- 1 | package mail 2 | 3 | import "time" 4 | 5 | func (c *Client) ConnectTimeout() time.Duration { 6 | return c.config.ConnectTimeout 7 | } 8 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/cloudfoundry-incubator/notifications/application" 7 | ) 8 | 9 | func main() { 10 | env, err := application.NewEnvironment() 11 | if err != nil { 12 | log.Fatalf("CRASHING: %s\n", err) 13 | } 14 | 15 | dbp := application.NewDBProvider(env) 16 | app := application.New(env, dbp) 17 | defer app.Crash() 18 | 19 | app.Run() 20 | } 21 | -------------------------------------------------------------------------------- /manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: notifications 4 | command: bin/notifications 5 | memory: 64M 6 | buildpack: go_buildpack 7 | env: 8 | GOVERSION: go1.11 9 | GOPACKAGENAME: github.com/cloudfoundry-incubator/notifications 10 | -------------------------------------------------------------------------------- /postal/common/status.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | const ( 4 | StatusFailed = "failed" 5 | StatusRetry = "retry" 6 | StatusDelivered = "delivered" 7 | StatusQueued = "queued" 8 | StatusUndeliverable = "undeliverable" 9 | ) 10 | -------------------------------------------------------------------------------- /postal/init_test.go: -------------------------------------------------------------------------------- 1 | package postal_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestPostalSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "postal") 13 | } 14 | -------------------------------------------------------------------------------- /postal/v1/logger.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/pivotal-golang/lager" 7 | ) 8 | 9 | type gorpCompatibleLogger struct { 10 | logger lager.Logger 11 | } 12 | 13 | func (g gorpCompatibleLogger) Printf(format string, v ...interface{}) { 14 | g.logger.Info("db", lager.Data{ 15 | "statement": fmt.Sprintf(format, v...), 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /postal/worker_generator.go: -------------------------------------------------------------------------------- 1 | package postal 2 | 3 | type WorkerGenerator struct { 4 | InstanceIndex int 5 | Count int 6 | } 7 | 8 | type Worker interface { 9 | Work() 10 | } 11 | 12 | func (w WorkerGenerator) Work(workerFunc func(id int) Worker) { 13 | firstID := w.InstanceIndex*w.Count + 1 14 | for i := 0; i < w.Count; i++ { 15 | workerFunc(firstID + i).Work() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /templates/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Default Template", 3 | "subject": "CF Notification: {{.Subject}}", 4 | "html": "
{{.Endorsement}}
{{.HTML}}", 5 | "text": "{{.Endorsement}}\n{{.Text}}", 6 | "metadata": {} 7 | } 8 | -------------------------------------------------------------------------------- /testing/fixtures/public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1eUDU+9MaLnurRstr0z3 3 | iMeiUeYawSVZwSWv3LHRirLiJrsxuSFSZUoqe2q9XQdW9PZZXe/sJHUx5fHSbTam 4 | XytWJF4e9hPTV4PHa1FI7DhXTikMU49zK3++KPuquHNINyO/dFo4oQRd/LwUHNI1 5 | /D44wpGnLX/Fj/7mpJbBt76FM7ATTV2o386NVzVsRZfh7dg3Nlqz7gaccOv4NQoO 6 | e6bAeb9Ev1rGVAE1J8R6RFg7a0QGtUTVaHOeeQbJYWd/vHPqsA6+gFSp6RGklrCr 7 | UMhmxTFz8nDFY+8+Gw4Rpsj55Dg9bRJe1ceAvHb3bbRDXtKdAnZntdad1WX7wiqc 8 | 1QIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /testing/helpers/contain_middleware.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ryanmoran/stack" 7 | 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | func ExpectToContainMiddlewareStack(actualMiddleware []stack.Middleware, expectedMiddleware ...stack.Middleware) { 13 | if len(actualMiddleware) != len(expectedMiddleware) { 14 | Fail(fmt.Sprintf("Expected to see a middleware with %d elements, got %d", len(expectedMiddleware), len(actualMiddleware))) 15 | } 16 | 17 | for i, ware := range expectedMiddleware { 18 | Expect(actualMiddleware[i]).To(BeAssignableToTypeOf(ware)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /testing/helpers/truncate_tables.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "github.com/cloudfoundry-incubator/notifications/application" 5 | "github.com/cloudfoundry-incubator/notifications/db" 6 | "github.com/cloudfoundry-incubator/notifications/v1/models" 7 | ) 8 | 9 | func TruncateTables(database *db.DB) { 10 | env, err := application.NewEnvironment() 11 | if err != nil { 12 | panic(err) 13 | } 14 | 15 | dbMigrator := models.DatabaseMigrator{} 16 | dbMigrator.Migrate(database.RawConnection(), env.ModelMigrationsPath) 17 | models.Setup(database) 18 | 19 | connection := database.Connection().(*db.Connection) 20 | err = connection.TruncateTables() 21 | if err != nil { 22 | panic(err) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /testing/mocks/all_users.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | type AllUsers struct { 4 | AllUserGUIDsCall struct { 5 | Receives struct { 6 | Token string 7 | } 8 | Returns struct { 9 | GUIDs []string 10 | Error error 11 | } 12 | } 13 | } 14 | 15 | func NewAllUsers() *AllUsers { 16 | return &AllUsers{} 17 | } 18 | 19 | func (au *AllUsers) AllUserGUIDs(token string) ([]string, error) { 20 | au.AllUserGUIDsCall.Receives.Token = token 21 | return au.AllUserGUIDsCall.Returns.GUIDs, au.AllUserGUIDsCall.Returns.Error 22 | } 23 | -------------------------------------------------------------------------------- /testing/mocks/authenticator.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/ryanmoran/stack" 7 | ) 8 | 9 | type Authenticator struct { 10 | ServeHTTPCall struct { 11 | Receives struct { 12 | Writer http.ResponseWriter 13 | Request *http.Request 14 | Context stack.Context 15 | } 16 | Returns struct { 17 | Continue bool 18 | } 19 | } 20 | } 21 | 22 | func (a *Authenticator) ServeHTTP(writer http.ResponseWriter, request *http.Request, context stack.Context) bool { 23 | a.ServeHTTPCall.Receives.Writer = writer 24 | a.ServeHTTPCall.Receives.Request = request 25 | a.ServeHTTPCall.Receives.Context = context 26 | return a.ServeHTTPCall.Returns.Continue 27 | } 28 | -------------------------------------------------------------------------------- /testing/mocks/cloak.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | type Cloak struct { 4 | VeilCall struct { 5 | Receives struct { 6 | PlainText []byte 7 | } 8 | Returns struct { 9 | CipherText []byte 10 | Error error 11 | } 12 | } 13 | 14 | UnveilCall struct { 15 | Receives struct { 16 | CipherText []byte 17 | } 18 | Returns struct { 19 | PlainText []byte 20 | Error error 21 | } 22 | } 23 | } 24 | 25 | func NewCloak() *Cloak { 26 | return &Cloak{} 27 | } 28 | 29 | func (c *Cloak) Veil(plaintext []byte) ([]byte, error) { 30 | c.VeilCall.Receives.PlainText = plaintext 31 | 32 | return c.VeilCall.Returns.CipherText, c.VeilCall.Returns.Error 33 | } 34 | 35 | func (c *Cloak) Unveil(ciphertext []byte) ([]byte, error) { 36 | c.UnveilCall.Receives.CipherText = ciphertext 37 | 38 | return c.UnveilCall.Returns.PlainText, c.UnveilCall.Returns.Error 39 | } 40 | -------------------------------------------------------------------------------- /testing/mocks/clock.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "time" 4 | 5 | type Clock struct { 6 | NowCall struct { 7 | Returns struct { 8 | Time time.Time 9 | } 10 | } 11 | } 12 | 13 | func NewClock() *Clock { 14 | return &Clock{} 15 | } 16 | 17 | func (c *Clock) Now() time.Time { 18 | return c.NowCall.Returns.Time 19 | } 20 | -------------------------------------------------------------------------------- /testing/mocks/delivery_failure_handler.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/cloudfoundry-incubator/notifications/postal/common" 5 | "github.com/pivotal-golang/lager" 6 | ) 7 | 8 | type DeliveryFailureHandler struct { 9 | HandleCall struct { 10 | WasCalled bool 11 | Receives struct { 12 | Job common.Retryable 13 | Logger lager.Logger 14 | } 15 | } 16 | } 17 | 18 | func NewDeliveryFailureHandler() *DeliveryFailureHandler { 19 | return &DeliveryFailureHandler{} 20 | } 21 | 22 | func (h *DeliveryFailureHandler) Handle(job common.Retryable, logger lager.Logger) { 23 | h.HandleCall.WasCalled = true 24 | h.HandleCall.Receives.Job = job 25 | h.HandleCall.Receives.Logger = logger 26 | } 27 | -------------------------------------------------------------------------------- /testing/mocks/error_writer.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "net/http" 4 | 5 | type ErrorWriter struct { 6 | WriteCall struct { 7 | Receives struct { 8 | Writer http.ResponseWriter 9 | Error error 10 | } 11 | } 12 | } 13 | 14 | func NewErrorWriter() *ErrorWriter { 15 | return &ErrorWriter{} 16 | } 17 | 18 | func (ew *ErrorWriter) Write(writer http.ResponseWriter, err error) { 19 | ew.WriteCall.Receives.Writer = writer 20 | ew.WriteCall.Receives.Error = err 21 | } 22 | -------------------------------------------------------------------------------- /testing/mocks/gobble_initializer.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "gopkg.in/gorp.v1" 4 | 5 | type GobbleInitializer struct { 6 | InitializeDBMapCall struct { 7 | Receives struct { 8 | DbMap *gorp.DbMap 9 | } 10 | } 11 | } 12 | 13 | func NewGobbleInitializer() *GobbleInitializer { 14 | return &GobbleInitializer{} 15 | } 16 | 17 | func (m *GobbleInitializer) InitializeDBMap(dbmap *gorp.DbMap) { 18 | m.InitializeDBMapCall.Receives.DbMap = dbmap 19 | } 20 | -------------------------------------------------------------------------------- /testing/mocks/gobble_job.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "time" 4 | 5 | type GobbleJob struct { 6 | RetryCall struct { 7 | WasCalled bool 8 | Receives struct { 9 | Duration time.Duration 10 | } 11 | } 12 | 13 | StateCall struct { 14 | Returns struct { 15 | Count int 16 | Time time.Time 17 | } 18 | } 19 | } 20 | 21 | func NewGobbleJob() *GobbleJob { 22 | return &GobbleJob{} 23 | } 24 | 25 | func (j *GobbleJob) Retry(duration time.Duration) { 26 | j.RetryCall.WasCalled = true 27 | j.RetryCall.Receives.Duration = duration 28 | } 29 | 30 | func (j *GobbleJob) State() (int, time.Time) { 31 | return j.StateCall.Returns.Count, j.StateCall.Returns.Time 32 | } 33 | -------------------------------------------------------------------------------- /testing/mocks/html_extractor.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | type HTMLExtractor struct { 4 | ExtractCall struct { 5 | Returns struct { 6 | Error error 7 | } 8 | } 9 | } 10 | 11 | func NewHTMLExtractor() *HTMLExtractor { 12 | return &HTMLExtractor{} 13 | } 14 | 15 | func (e HTMLExtractor) Extract(html string) (doctype, head, bodyContent, bodyAttributes string, err error) { 16 | return "", "", "", "", e.ExtractCall.Returns.Error 17 | } 18 | -------------------------------------------------------------------------------- /testing/mocks/id_generator.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "errors" 4 | 5 | type IDGenerator struct { 6 | GenerateCall struct { 7 | CallCount int 8 | Returns struct { 9 | IDs []string 10 | Error error 11 | } 12 | } 13 | } 14 | 15 | func NewIDGenerator() *IDGenerator { 16 | return &IDGenerator{} 17 | } 18 | 19 | func (g *IDGenerator) Generate() (string, error) { 20 | if g.GenerateCall.CallCount >= len(g.GenerateCall.Returns.IDs) { 21 | return "", errors.New("no IDs to return") 22 | } 23 | 24 | guid := g.GenerateCall.Returns.IDs[g.GenerateCall.CallCount] 25 | g.GenerateCall.CallCount++ 26 | 27 | return guid, g.GenerateCall.Returns.Error 28 | } 29 | -------------------------------------------------------------------------------- /testing/mocks/message_finder.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/services" 4 | 5 | type MessageFinder struct { 6 | FindCall struct { 7 | Receives struct { 8 | Database services.DatabaseInterface 9 | MessageID string 10 | } 11 | Returns struct { 12 | Message services.Message 13 | Error error 14 | } 15 | } 16 | } 17 | 18 | func NewMessageFinder() *MessageFinder { 19 | return &MessageFinder{} 20 | } 21 | 22 | func (f *MessageFinder) Find(database services.DatabaseInterface, messageID string) (services.Message, error) { 23 | f.FindCall.Receives.Database = database 24 | f.FindCall.Receives.MessageID = messageID 25 | 26 | return f.FindCall.Returns.Message, f.FindCall.Returns.Error 27 | } 28 | -------------------------------------------------------------------------------- /testing/mocks/metrics_emitter.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | type MetricsEmitter struct { 4 | IncrementCall struct { 5 | Receives struct { 6 | Counter string 7 | } 8 | } 9 | } 10 | 11 | func NewMetricsEmitter() *MetricsEmitter { 12 | return &MetricsEmitter{} 13 | } 14 | 15 | func (e *MetricsEmitter) Increment(counter string) { 16 | e.IncrementCall.Receives.Counter = counter 17 | } 18 | -------------------------------------------------------------------------------- /testing/mocks/notification_updater.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/cloudfoundry-incubator/notifications/v1/models" 5 | "github.com/cloudfoundry-incubator/notifications/v1/services" 6 | ) 7 | 8 | type NotificationUpdater struct { 9 | UpdateCall struct { 10 | Receives struct { 11 | Database services.DatabaseInterface 12 | Notification models.Kind 13 | } 14 | Returns struct { 15 | Error error 16 | } 17 | } 18 | } 19 | 20 | func (f *NotificationUpdater) Update(database services.DatabaseInterface, notification models.Kind) error { 21 | f.UpdateCall.Receives.Database = database 22 | f.UpdateCall.Receives.Notification = notification 23 | 24 | return f.UpdateCall.Returns.Error 25 | } 26 | -------------------------------------------------------------------------------- /testing/mocks/org_finder.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | type OrgFinder struct { 4 | ExistsCall struct { 5 | Receives struct { 6 | GUID string 7 | } 8 | Returns struct { 9 | Exists bool 10 | Error error 11 | } 12 | } 13 | } 14 | 15 | func NewOrgFinder() *OrgFinder { 16 | return &OrgFinder{} 17 | } 18 | 19 | func (f *OrgFinder) Exists(guid string) (bool, error) { 20 | f.ExistsCall.Receives.GUID = guid 21 | 22 | return f.ExistsCall.Returns.Exists, f.ExistsCall.Returns.Error 23 | } 24 | -------------------------------------------------------------------------------- /testing/mocks/preferences_finder.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/services" 4 | 5 | type PreferencesFinder struct { 6 | FindCall struct { 7 | Receives struct { 8 | Database services.DatabaseInterface 9 | UserGUID string 10 | } 11 | Returns struct { 12 | PreferencesBuilder services.PreferencesBuilder 13 | Error error 14 | } 15 | } 16 | } 17 | 18 | func NewPreferencesFinder() *PreferencesFinder { 19 | return &PreferencesFinder{} 20 | } 21 | 22 | func (pb *PreferencesFinder) Find(database services.DatabaseInterface, userGUID string) (services.PreferencesBuilder, error) { 23 | pb.FindCall.Receives.Database = database 24 | pb.FindCall.Receives.UserGUID = userGUID 25 | 26 | return pb.FindCall.Returns.PreferencesBuilder, pb.FindCall.Returns.Error 27 | } 28 | -------------------------------------------------------------------------------- /testing/mocks/preferences_repo.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/models" 4 | 5 | type PreferencesRepo struct { 6 | FindNonCriticalPreferencesCall struct { 7 | Receives struct { 8 | Connection models.ConnectionInterface 9 | UserGUID string 10 | } 11 | Returns struct { 12 | Preferences []models.Preference 13 | Error error 14 | } 15 | } 16 | } 17 | 18 | func NewPreferencesRepo() *PreferencesRepo { 19 | return &PreferencesRepo{} 20 | } 21 | 22 | func (pr *PreferencesRepo) FindNonCriticalPreferences(conn models.ConnectionInterface, userGUID string) ([]models.Preference, error) { 23 | pr.FindNonCriticalPreferencesCall.Receives.Connection = conn 24 | pr.FindNonCriticalPreferencesCall.Receives.UserGUID = userGUID 25 | 26 | return pr.FindNonCriticalPreferencesCall.Returns.Preferences, pr.FindNonCriticalPreferencesCall.Returns.Error 27 | } 28 | -------------------------------------------------------------------------------- /testing/mocks/rainmaker_organization_finder.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/pivotal-cf-experimental/rainmaker" 4 | 5 | type RainmakerOrganizationsService struct { 6 | GetCall struct { 7 | Receives struct { 8 | GUID string 9 | Token string 10 | } 11 | Returns struct { 12 | Organization rainmaker.Organization 13 | Error error 14 | } 15 | } 16 | } 17 | 18 | func NewRainmakerOrganizationsService() *RainmakerOrganizationsService { 19 | return &RainmakerOrganizationsService{} 20 | } 21 | 22 | func (s *RainmakerOrganizationsService) Get(guid, token string) (rainmaker.Organization, error) { 23 | s.GetCall.Receives.GUID = guid 24 | s.GetCall.Receives.Token = token 25 | 26 | return s.GetCall.Returns.Organization, s.GetCall.Returns.Error 27 | } 28 | -------------------------------------------------------------------------------- /testing/mocks/rainmaker_spaces_service.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/pivotal-cf-experimental/rainmaker" 4 | 5 | type RainmakerSpacesService struct { 6 | GetCall struct { 7 | Receives struct { 8 | GUID string 9 | Token string 10 | } 11 | Returns struct { 12 | Space rainmaker.Space 13 | Error error 14 | } 15 | } 16 | } 17 | 18 | func NewRainmakerSpacesService() *RainmakerSpacesService { 19 | return &RainmakerSpacesService{} 20 | } 21 | 22 | func (s *RainmakerSpacesService) Get(guid, token string) (rainmaker.Space, error) { 23 | s.GetCall.Receives.GUID = guid 24 | s.GetCall.Receives.Token = token 25 | 26 | return s.GetCall.Returns.Space, s.GetCall.Returns.Error 27 | } 28 | -------------------------------------------------------------------------------- /testing/mocks/receipts_repo.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/models" 4 | 5 | type ReceiptsRepo struct { 6 | CreateReceiptsCall struct { 7 | Receives struct { 8 | Connection models.ConnectionInterface 9 | UserGUIDs []string 10 | ClientID string 11 | KindID string 12 | } 13 | Returns struct { 14 | Error error 15 | } 16 | } 17 | } 18 | 19 | func NewReceiptsRepo() *ReceiptsRepo { 20 | return &ReceiptsRepo{} 21 | } 22 | 23 | func (rr *ReceiptsRepo) CreateReceipts(conn models.ConnectionInterface, userGUIDs []string, clientID, kindID string) error { 24 | rr.CreateReceiptsCall.Receives.Connection = conn 25 | rr.CreateReceiptsCall.Receives.UserGUIDs = userGUIDs 26 | rr.CreateReceiptsCall.Receives.ClientID = clientID 27 | rr.CreateReceiptsCall.Receives.KindID = kindID 28 | 29 | return rr.CreateReceiptsCall.Returns.Error 30 | } 31 | -------------------------------------------------------------------------------- /testing/mocks/space_finder.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | type SpaceFinder struct { 4 | ExistsCall struct { 5 | Receives struct { 6 | GUID string 7 | } 8 | 9 | Returns struct { 10 | Exists bool 11 | Error error 12 | } 13 | } 14 | } 15 | 16 | func NewSpaceFinder() *SpaceFinder { 17 | return &SpaceFinder{} 18 | } 19 | 20 | func (u *SpaceFinder) Exists(guid string) (bool, error) { 21 | u.ExistsCall.Receives.GUID = guid 22 | 23 | return u.ExistsCall.Returns.Exists, u.ExistsCall.Returns.Error 24 | } 25 | -------------------------------------------------------------------------------- /testing/mocks/template_associations_lister.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/collections" 4 | 5 | type TemplateAssociationLister struct { 6 | ListCall struct { 7 | Receives struct { 8 | Connection collections.ConnectionInterface 9 | TemplateID string 10 | } 11 | Returns struct { 12 | Associations []collections.TemplateAssociation 13 | Error error 14 | } 15 | } 16 | } 17 | 18 | func NewTemplateAssociationLister() *TemplateAssociationLister { 19 | return &TemplateAssociationLister{} 20 | } 21 | 22 | func (l *TemplateAssociationLister) ListAssociations(connection collections.ConnectionInterface, templateID string) ([]collections.TemplateAssociation, error) { 23 | l.ListCall.Receives.Connection = connection 24 | l.ListCall.Receives.TemplateID = templateID 25 | 26 | return l.ListCall.Returns.Associations, l.ListCall.Returns.Error 27 | } 28 | -------------------------------------------------------------------------------- /testing/mocks/template_creator.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/collections" 4 | 5 | type TemplateCreator struct { 6 | CreateCall struct { 7 | Receives struct { 8 | Connection collections.ConnectionInterface 9 | Template collections.Template 10 | } 11 | Returns struct { 12 | Template collections.Template 13 | Error error 14 | } 15 | } 16 | } 17 | 18 | func NewTemplateCreator() *TemplateCreator { 19 | return &TemplateCreator{} 20 | } 21 | 22 | func (tc *TemplateCreator) Create(connection collections.ConnectionInterface, template collections.Template) (collections.Template, error) { 23 | tc.CreateCall.Receives.Connection = connection 24 | tc.CreateCall.Receives.Template = template 25 | 26 | return tc.CreateCall.Returns.Template, tc.CreateCall.Returns.Error 27 | } 28 | -------------------------------------------------------------------------------- /testing/mocks/template_deleter.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/collections" 4 | 5 | type TemplateDeleter struct { 6 | DeleteCall struct { 7 | Receives struct { 8 | Connection collections.ConnectionInterface 9 | TemplateID string 10 | } 11 | Returns struct { 12 | Error error 13 | } 14 | } 15 | } 16 | 17 | func NewTemplateDeleter() *TemplateDeleter { 18 | return &TemplateDeleter{} 19 | } 20 | 21 | func (td *TemplateDeleter) Delete(connection collections.ConnectionInterface, templateID string) error { 22 | td.DeleteCall.Receives.Connection = connection 23 | td.DeleteCall.Receives.TemplateID = templateID 24 | 25 | return td.DeleteCall.Returns.Error 26 | } 27 | -------------------------------------------------------------------------------- /testing/mocks/template_finder.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/cloudfoundry-incubator/notifications/v1/models" 5 | "github.com/cloudfoundry-incubator/notifications/v1/services" 6 | ) 7 | 8 | type TemplateFinder struct { 9 | FindByIDCall struct { 10 | Receives struct { 11 | Database services.DatabaseInterface 12 | TemplateID string 13 | } 14 | Returns struct { 15 | Template models.Template 16 | Error error 17 | } 18 | } 19 | } 20 | 21 | func NewTemplateFinder() *TemplateFinder { 22 | return &TemplateFinder{} 23 | } 24 | 25 | func (tf *TemplateFinder) FindByID(database services.DatabaseInterface, templateID string) (models.Template, error) { 26 | tf.FindByIDCall.Receives.Database = database 27 | tf.FindByIDCall.Receives.TemplateID = templateID 28 | 29 | return tf.FindByIDCall.Returns.Template, tf.FindByIDCall.Returns.Error 30 | } 31 | -------------------------------------------------------------------------------- /testing/mocks/template_lister.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/services" 4 | 5 | type TemplateLister struct { 6 | ListCall struct { 7 | Receives struct { 8 | Database services.DatabaseInterface 9 | } 10 | Returns struct { 11 | TemplateSummaries map[string]services.TemplateSummary 12 | Error error 13 | } 14 | } 15 | } 16 | 17 | func NewTemplateLister() *TemplateLister { 18 | return &TemplateLister{} 19 | } 20 | 21 | func (tl *TemplateLister) List(database services.DatabaseInterface) (map[string]services.TemplateSummary, error) { 22 | tl.ListCall.Receives.Database = database 23 | 24 | return tl.ListCall.Returns.TemplateSummaries, tl.ListCall.Returns.Error 25 | } 26 | -------------------------------------------------------------------------------- /testing/mocks/template_updater.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/cloudfoundry-incubator/notifications/v1/models" 5 | "github.com/cloudfoundry-incubator/notifications/v1/services" 6 | ) 7 | 8 | type TemplateUpdater struct { 9 | UpdateCall struct { 10 | Receives struct { 11 | Database services.DatabaseInterface 12 | TemplateID string 13 | Template models.Template 14 | } 15 | Returns struct { 16 | Error error 17 | } 18 | } 19 | } 20 | 21 | func NewTemplateUpdater() *TemplateUpdater { 22 | return &TemplateUpdater{} 23 | } 24 | 25 | func (tu *TemplateUpdater) Update(database services.DatabaseInterface, templateID string, template models.Template) error { 26 | tu.UpdateCall.Receives.Database = database 27 | tu.UpdateCall.Receives.TemplateID = templateID 28 | tu.UpdateCall.Receives.Template = template 29 | 30 | return tu.UpdateCall.Returns.Error 31 | } 32 | -------------------------------------------------------------------------------- /testing/mocks/templates_loader.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/postal/common" 4 | 5 | type TemplatesLoader struct { 6 | LoadTemplatesCall struct { 7 | Receives struct { 8 | ClientID string 9 | KindID string 10 | TemplateID string 11 | } 12 | Returns struct { 13 | Templates common.Templates 14 | Error error 15 | } 16 | } 17 | } 18 | 19 | func NewTemplatesLoader() *TemplatesLoader { 20 | return &TemplatesLoader{} 21 | } 22 | 23 | func (tl *TemplatesLoader) LoadTemplates(clientID, kindID, templateID string) (common.Templates, error) { 24 | tl.LoadTemplatesCall.Receives.ClientID = clientID 25 | tl.LoadTemplatesCall.Receives.KindID = kindID 26 | tl.LoadTemplatesCall.Receives.TemplateID = templateID 27 | 28 | return tl.LoadTemplatesCall.Returns.Templates, tl.LoadTemplatesCall.Returns.Error 29 | } 30 | -------------------------------------------------------------------------------- /testing/mocks/transaction.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | type Transaction struct { 4 | BeginCall struct { 5 | WasCalled bool 6 | Returns struct { 7 | Error error 8 | } 9 | } 10 | 11 | CommitCall struct { 12 | WasCalled bool 13 | Returns struct { 14 | Error error 15 | } 16 | } 17 | 18 | RollbackCall struct { 19 | WasCalled bool 20 | Returns struct { 21 | Error error 22 | } 23 | } 24 | 25 | *Connection 26 | } 27 | 28 | func NewTransaction() *Transaction { 29 | return &Transaction{} 30 | } 31 | 32 | func (t *Transaction) Begin() error { 33 | t.BeginCall.WasCalled = true 34 | return t.BeginCall.Returns.Error 35 | } 36 | 37 | func (t *Transaction) Commit() error { 38 | t.CommitCall.WasCalled = true 39 | return t.CommitCall.Returns.Error 40 | } 41 | 42 | func (t *Transaction) Rollback() error { 43 | t.RollbackCall.WasCalled = true 44 | return t.RollbackCall.Returns.Error 45 | } 46 | -------------------------------------------------------------------------------- /testing/mocks/user_finder.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | type UserFinder struct { 4 | ExistsCall struct { 5 | Receives struct { 6 | GUID string 7 | } 8 | 9 | Returns struct { 10 | Exists bool 11 | Error error 12 | } 13 | } 14 | } 15 | 16 | func NewUserFinder() *UserFinder { 17 | return &UserFinder{} 18 | } 19 | 20 | func (u *UserFinder) Exists(guid string) (bool, error) { 21 | u.ExistsCall.Receives.GUID = guid 22 | 23 | return u.ExistsCall.Returns.Exists, u.ExistsCall.Returns.Error 24 | } 25 | -------------------------------------------------------------------------------- /testing/mocks/user_loader.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/uaa" 4 | 5 | type UserLoader struct { 6 | LoadCall struct { 7 | Receives struct { 8 | UserGUIDs []string 9 | Token string 10 | } 11 | Returns struct { 12 | Users map[string]uaa.User 13 | Error error 14 | } 15 | } 16 | } 17 | 18 | func NewUserLoader() *UserLoader { 19 | return &UserLoader{} 20 | } 21 | 22 | func (ul *UserLoader) Load(userGUIDs []string, token string) (map[string]uaa.User, error) { 23 | ul.LoadCall.Receives.UserGUIDs = userGUIDs 24 | ul.LoadCall.Receives.Token = token 25 | 26 | return ul.LoadCall.Returns.Users, ul.LoadCall.Returns.Error 27 | } 28 | -------------------------------------------------------------------------------- /testing/mocks/v1_delivery_job_processor.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/cloudfoundry-incubator/notifications/gobble" 5 | "github.com/pivotal-golang/lager" 6 | ) 7 | 8 | type V1DeliveryJobProcessor struct { 9 | ProcessCall struct { 10 | CallCount int 11 | Receives struct { 12 | Job *gobble.Job 13 | Logger lager.Logger 14 | } 15 | Returns struct { 16 | Error error 17 | } 18 | } 19 | } 20 | 21 | func NewV1DeliveryJobProcessor() *V1DeliveryJobProcessor { 22 | return &V1DeliveryJobProcessor{} 23 | } 24 | 25 | func (p *V1DeliveryJobProcessor) Process(job *gobble.Job, logger lager.Logger) error { 26 | p.ProcessCall.Receives.Job = job 27 | p.ProcessCall.Receives.Logger = logger 28 | p.ProcessCall.CallCount++ 29 | 30 | return p.ProcessCall.Returns.Error 31 | } 32 | -------------------------------------------------------------------------------- /testing/mocks/validator.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import ( 4 | "github.com/cloudfoundry-incubator/notifications/v1/web/notify" 5 | ) 6 | 7 | type Validator struct { 8 | ValidateCall struct { 9 | Receives struct { 10 | Params *notify.NotifyParams 11 | } 12 | Returns struct { 13 | Valid bool 14 | } 15 | ErrorsToApply []string 16 | } 17 | } 18 | 19 | func NewValidator() *Validator { 20 | return &Validator{} 21 | } 22 | 23 | func (v *Validator) Validate(params *notify.NotifyParams) bool { 24 | v.ValidateCall.Receives.Params = params 25 | params.Errors = append(params.Errors, v.ValidateCall.ErrorsToApply...) 26 | 27 | return v.ValidateCall.Returns.Valid 28 | } 29 | -------------------------------------------------------------------------------- /testing/mocks/warrant_client_service.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | type WarrantClientService struct { 4 | GetTokenCall struct { 5 | Receives struct { 6 | ID string 7 | Secret string 8 | } 9 | 10 | Returns struct { 11 | Token string 12 | Error error 13 | } 14 | } 15 | } 16 | 17 | func NewWarrantClientService() *WarrantClientService { 18 | return &WarrantClientService{} 19 | } 20 | 21 | func (s *WarrantClientService) GetToken(id, secret string) (string, error) { 22 | s.GetTokenCall.Receives.ID = id 23 | s.GetTokenCall.Receives.Secret = secret 24 | 25 | return s.GetTokenCall.Returns.Token, s.GetTokenCall.Returns.Error 26 | } 27 | -------------------------------------------------------------------------------- /testing/mocks/warrant_user_service.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | import "github.com/pivotal-cf-experimental/warrant" 4 | 5 | type WarrantUserService struct { 6 | GetCall struct { 7 | Receives struct { 8 | GUID string 9 | Token string 10 | } 11 | 12 | Returns struct { 13 | User warrant.User 14 | Error error 15 | } 16 | } 17 | } 18 | 19 | func NewWarrantUserService() *WarrantUserService { 20 | return &WarrantUserService{} 21 | } 22 | 23 | func (s *WarrantUserService) Get(guid, token string) (warrant.User, error) { 24 | s.GetCall.Receives.GUID = guid 25 | s.GetCall.Receives.Token = token 26 | return s.GetCall.Returns.User, s.GetCall.Returns.Error 27 | } 28 | -------------------------------------------------------------------------------- /testing/mocks/zoned_token_loader.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | 3 | type TokenLoader struct { 4 | LoadCall struct { 5 | Receives struct { 6 | UAAHost string 7 | } 8 | Returns struct { 9 | Token string 10 | Error error 11 | } 12 | } 13 | } 14 | 15 | func NewTokenLoader() *TokenLoader { 16 | return &TokenLoader{} 17 | } 18 | 19 | func (t *TokenLoader) Load(uaaHost string) (string, error) { 20 | t.LoadCall.Receives.UAAHost = uaaHost 21 | 22 | return t.LoadCall.Returns.Token, t.LoadCall.Returns.Error 23 | } 24 | -------------------------------------------------------------------------------- /uaa/init_test.go: -------------------------------------------------------------------------------- 1 | package uaa_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestUAASuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "uaa") 13 | } 14 | -------------------------------------------------------------------------------- /uaa/token_loader.go: -------------------------------------------------------------------------------- 1 | package uaa 2 | 3 | import ( 4 | "time" 5 | 6 | metrics "github.com/rcrowley/go-metrics" 7 | ) 8 | 9 | type uaaClient interface { 10 | GetClientToken(string) (string, error) 11 | } 12 | 13 | type TokenLoader struct { 14 | uaa uaaClient 15 | } 16 | 17 | func NewTokenLoader(uaa uaaClient) *TokenLoader { 18 | return &TokenLoader{ 19 | uaa: uaa, 20 | } 21 | } 22 | 23 | func (t *TokenLoader) Load(uaaHost string) (string, error) { 24 | then := time.Now() 25 | 26 | token, err := t.uaa.GetClientToken(uaaHost) 27 | 28 | metrics.GetOrRegisterTimer("notifications.external-requests.uaa.client-token", nil).Update(time.Since(then)) 29 | return token, err 30 | } 31 | -------------------------------------------------------------------------------- /uaa/token_loader_test.go: -------------------------------------------------------------------------------- 1 | package uaa_test 2 | 3 | import ( 4 | "github.com/cloudfoundry-incubator/notifications/testing/mocks" 5 | "github.com/cloudfoundry-incubator/notifications/uaa" 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("TokenLoader", func() { 11 | Describe("#Load", func() { 12 | It("Gets a zoned client token based on hostname", func() { 13 | uaaClient := mocks.NewZonedUAAClient() 14 | uaaClient.GetClientTokenCall.Returns.Token = "my-fake-token" 15 | 16 | tokenLoader := uaa.NewTokenLoader(uaaClient) 17 | 18 | token, err := tokenLoader.Load("my-uaa-zone") 19 | Expect(token).To(Equal("my-fake-token")) 20 | Expect(err).To(BeNil()) 21 | 22 | Expect(uaaClient.GetClientTokenCall.Receives.Host).To(Equal("my-uaa-zone")) 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /util/clock.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "time" 4 | 5 | type Clock struct{} 6 | 7 | func NewClock() Clock { 8 | return Clock{} 9 | } 10 | 11 | func (c Clock) Now() time.Time { 12 | return time.Now() 13 | } 14 | -------------------------------------------------------------------------------- /util/clock_test.go: -------------------------------------------------------------------------------- 1 | package util_test 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/cloudfoundry-incubator/notifications/util" 7 | 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | var _ = Describe("Clock", func() { 13 | Describe("Now", func() { 14 | It("should return the current time", func() { 15 | clock := util.NewClock() 16 | 17 | currentTime := clock.Now() 18 | Expect(currentTime).To(BeTemporally("~", time.Now())) 19 | }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /util/id_generator.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | type IDGenerator struct { 9 | reader io.Reader 10 | } 11 | 12 | func NewIDGenerator(reader io.Reader) IDGenerator { 13 | return IDGenerator{ 14 | reader: reader, 15 | } 16 | } 17 | 18 | func (g IDGenerator) Generate() (string, error) { 19 | var buf [16]byte 20 | 21 | _, err := g.reader.Read(buf[:]) 22 | if err != nil { 23 | return "", err 24 | } 25 | 26 | return fmt.Sprintf("%x-%x-%x-%x-%x", buf[0:4], buf[4:6], buf[6:8], buf[8:10], buf[10:]), nil 27 | } 28 | -------------------------------------------------------------------------------- /util/init_test.go: -------------------------------------------------------------------------------- 1 | package util_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestUtilSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "util") 13 | } 14 | -------------------------------------------------------------------------------- /v1/acceptance/get_api_version_test.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/cloudfoundry-incubator/notifications/v1/acceptance/support" 7 | 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | var _ = Describe("v1 API", func() { 13 | var ( 14 | client *support.Client 15 | ) 16 | 17 | BeforeEach(func() { 18 | client = support.NewClient(Servers.Notifications.URL()) 19 | }) 20 | 21 | It("serves the correct API version number", func() { 22 | status, version, err := client.API.Version() 23 | Expect(err).NotTo(HaveOccurred()) 24 | Expect(status).To(Equal(http.StatusOK)) 25 | Expect(version).To(Equal(1)) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /v1/acceptance/get_debug_metrics_test.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | . "github.com/onsi/ginkgo/v2" 8 | . "github.com/onsi/gomega" 9 | ) 10 | 11 | var _ = Describe("metrics endpoint", func() { 12 | It("returns 200 and exposes metrics", func() { 13 | resp, err := http.Get(Servers.Notifications.URL() + "/debug/metrics") 14 | 15 | Expect(err).NotTo(HaveOccurred()) 16 | Expect(resp.StatusCode).To(Equal(http.StatusOK)) 17 | 18 | var body map[string]interface{} 19 | err = json.NewDecoder(resp.Body).Decode(&body) 20 | Expect(err).ToNot(HaveOccurred()) 21 | defer resp.Body.Close() 22 | Expect(body["notifications.web.GET./info"]).To(BeNumerically( ">=", 1)) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /v1/acceptance/support/messages_service.go: -------------------------------------------------------------------------------- 1 | package support 2 | 3 | import "encoding/json" 4 | 5 | type MessagesService struct { 6 | client *Client 7 | } 8 | 9 | func (s MessagesService) Get(token, messageGUID string) (int, Message, error) { 10 | var message Message 11 | 12 | status, body, err := s.client.makeRequest("GET", s.client.MessagePath(messageGUID), nil, token) 13 | if err != nil { 14 | return status, message, err 15 | } 16 | 17 | err = json.Unmarshal(body, &message) 18 | return status, message, err 19 | } 20 | -------------------------------------------------------------------------------- /v1/collections/init_test.go: -------------------------------------------------------------------------------- 1 | package collections_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebHandlersServicesSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "v1/collections") 13 | } 14 | -------------------------------------------------------------------------------- /v1/models/client.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "gopkg.in/gorp.v1" 7 | ) 8 | 9 | type Client struct { 10 | Primary int `db:"primary"` 11 | ID string `db:"id"` 12 | Description string `db:"description"` 13 | CreatedAt time.Time `db:"created_at"` 14 | TemplateID string `db:"template_id"` 15 | } 16 | 17 | func (c Client) TemplateToUse() string { 18 | if c.TemplateID != "" { 19 | return c.TemplateID 20 | } 21 | 22 | return DefaultTemplateID 23 | } 24 | 25 | func (c *Client) PreInsert(s gorp.SqlExecutor) error { 26 | c.CreatedAt = time.Now().Truncate(1 * time.Second).UTC() 27 | 28 | if c.TemplateID == "" { 29 | c.TemplateID = DefaultTemplateID 30 | } 31 | 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /v1/models/client_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "github.com/cloudfoundry-incubator/notifications/v1/models" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("Client", func() { 11 | var client models.Client 12 | 13 | Describe("TemplateToUse", func() { 14 | Context("when the template is set", func() { 15 | BeforeEach(func() { 16 | client.TemplateID = "template-id" 17 | }) 18 | 19 | It("returns the template value", func() { 20 | Expect(client.TemplateToUse()).To(Equal("template-id")) 21 | }) 22 | }) 23 | 24 | Context("when the template is not set", func() { 25 | BeforeEach(func() { 26 | client.TemplateID = "" 27 | }) 28 | 29 | It("returns the default template value", func() { 30 | Expect(client.TemplateToUse()).To(Equal(models.DefaultTemplateID)) 31 | }) 32 | }) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /v1/models/errors.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type NotFoundError struct { 4 | Err error 5 | } 6 | 7 | func (e NotFoundError) Error() string { 8 | return e.Err.Error() 9 | } 10 | 11 | type DuplicateError struct { 12 | Err error 13 | } 14 | 15 | func (e DuplicateError) Error() string { 16 | return e.Err.Error() 17 | } 18 | 19 | type TransactionCommitError struct { 20 | Err error 21 | } 22 | 23 | func (e TransactionCommitError) Error() string { 24 | return e.Err.Error() 25 | } 26 | 27 | type TemplateUpdateError struct { 28 | Err error 29 | } 30 | 31 | func (e TemplateUpdateError) Error() string { 32 | return e.Err.Error() 33 | } 34 | -------------------------------------------------------------------------------- /v1/models/global_unsubscribe.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type GlobalUnsubscribe struct { 8 | Primary int `db:"primary"` 9 | UserID string `db:"user_id"` 10 | CreatedAt time.Time `db:"created_at"` 11 | } 12 | -------------------------------------------------------------------------------- /v1/models/kind.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "gopkg.in/gorp.v1" 7 | ) 8 | 9 | type Kind struct { 10 | Primary int `db:"primary"` 11 | ID string `db:"id"` 12 | Description string `db:"description"` 13 | Critical bool `db:"critical"` 14 | ClientID string `db:"client_id"` 15 | CreatedAt time.Time `db:"created_at"` 16 | UpdatedAt time.Time `db:"updated_at"` 17 | TemplateID string `db:"template_id"` 18 | } 19 | 20 | func (k Kind) TemplateToUse() string { 21 | if k.TemplateID != "" { 22 | return k.TemplateID 23 | } 24 | 25 | return DefaultTemplateID 26 | } 27 | 28 | func (k *Kind) PreInsert(s gorp.SqlExecutor) error { 29 | now := time.Now().Truncate(1 * time.Second).UTC() 30 | k.CreatedAt = now 31 | k.UpdatedAt = now 32 | 33 | if k.TemplateID == "" { 34 | k.TemplateID = DefaultTemplateID 35 | } 36 | 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /v1/models/kind_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "github.com/cloudfoundry-incubator/notifications/v1/models" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("Kind", func() { 11 | var kind models.Kind 12 | 13 | Describe("TemplateToUse", func() { 14 | Context("when the template is set", func() { 15 | BeforeEach(func() { 16 | kind.TemplateID = "template-id" 17 | }) 18 | 19 | It("returns the template value", func() { 20 | Expect(kind.TemplateToUse()).To(Equal("template-id")) 21 | }) 22 | }) 23 | 24 | Context("when the template is not set", func() { 25 | BeforeEach(func() { 26 | kind.TemplateID = "" 27 | }) 28 | 29 | It("returns the default template value", func() { 30 | Expect(kind.TemplateToUse()).To(Equal(models.DefaultTemplateID)) 31 | }) 32 | }) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /v1/models/message.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "gopkg.in/gorp.v1" 7 | ) 8 | 9 | type Message struct { 10 | ID string `db:"id"` 11 | Status string `db:"status"` 12 | UpdatedAt time.Time `db:"updated_at"` 13 | } 14 | 15 | func (m *Message) PreInsert(s gorp.SqlExecutor) error { 16 | m.UpdatedAt = time.Now().Truncate(1 * time.Second).UTC() 17 | 18 | return nil 19 | } 20 | 21 | func (m *Message) PreUpdate(s gorp.SqlExecutor) error { 22 | m.UpdatedAt = time.Now().Truncate(1 * time.Second).UTC() 23 | 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /v1/models/preferences.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Preference struct { 4 | ClientID string `db:"client_id"` 5 | KindID string `db:"kind_id"` 6 | KindDescription string `db:"kind_description"` 7 | SourceDescription string `db:"source_description"` 8 | Email bool 9 | } 10 | -------------------------------------------------------------------------------- /v1/models/receipt.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "gopkg.in/gorp.v1" 7 | ) 8 | 9 | type Receipt struct { 10 | Primary int `db:"primary"` 11 | UserGUID string `db:"user_guid"` 12 | ClientID string `db:"client_id"` 13 | KindID string `db:"kind_id"` 14 | Count int `db:"count"` 15 | CreatedAt time.Time `db:"created_at"` 16 | } 17 | 18 | func (r *Receipt) PreInsert(s gorp.SqlExecutor) error { 19 | r.CreatedAt = time.Now().Truncate(1 * time.Second).UTC() 20 | 21 | if r.Count == 0 { 22 | r.Count = 1 23 | } 24 | 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /v1/models/unsubscribe.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "gopkg.in/gorp.v1" 7 | ) 8 | 9 | type Unsubscribe struct { 10 | Primary int `db:"primary"` 11 | UserID string `db:"user_id"` 12 | ClientID string `db:"client_id"` 13 | KindID string `db:"kind_id"` 14 | CreatedAt time.Time `db:"created_at"` 15 | } 16 | 17 | func (u *Unsubscribe) PreInsert(s gorp.SqlExecutor) error { 18 | u.CreatedAt = time.Now().Truncate(1 * time.Second).UTC() 19 | 20 | return nil 21 | } 22 | 23 | type Unsubscribes []Unsubscribe 24 | 25 | func (unsubscribes Unsubscribes) Contains(clientID, kindID string) bool { 26 | for _, unsubscribe := range unsubscribes { 27 | if unsubscribe.ClientID == clientID && unsubscribe.KindID == kindID { 28 | return true 29 | } 30 | } 31 | return false 32 | } 33 | -------------------------------------------------------------------------------- /v1/services/all_users.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/uaa" 4 | 5 | type AllUsers struct { 6 | uaa uaaAllUsers 7 | } 8 | 9 | type uaaAllUsers interface { 10 | AllUsers(token string) ([]uaa.User, error) 11 | } 12 | 13 | func NewAllUsers(uaa uaaAllUsers) AllUsers { 14 | return AllUsers{ 15 | uaa: uaa, 16 | } 17 | } 18 | 19 | func (allUsers AllUsers) AllUserGUIDs(token string) ([]string, error) { 20 | var guids []string 21 | 22 | usersMap, err := allUsers.uaa.AllUsers(token) 23 | if err != nil { 24 | return guids, err 25 | } 26 | 27 | for _, user := range usersMap { 28 | guids = append(guids, user.ID) 29 | } 30 | 31 | return guids, nil 32 | } 33 | -------------------------------------------------------------------------------- /v1/services/database.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/models" 4 | 5 | type DatabaseInterface interface { 6 | models.DatabaseInterface 7 | } 8 | 9 | type ConnectionInterface interface { 10 | models.ConnectionInterface 11 | } 12 | -------------------------------------------------------------------------------- /v1/services/init_test.go: -------------------------------------------------------------------------------- 1 | package services_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebHandlersServicesSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "v1/services") 13 | } 14 | -------------------------------------------------------------------------------- /v1/services/message_finder.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/models" 4 | 5 | type Message struct { 6 | Status string 7 | } 8 | 9 | type messagesRepoFinder interface { 10 | FindByID(models.ConnectionInterface, string) (models.Message, error) 11 | } 12 | 13 | type MessageFinder struct { 14 | repo messagesRepoFinder 15 | } 16 | 17 | func NewMessageFinder(repo messagesRepoFinder) MessageFinder { 18 | return MessageFinder{ 19 | repo: repo, 20 | } 21 | } 22 | 23 | func (finder MessageFinder) Find(database DatabaseInterface, messageID string) (Message, error) { 24 | message, err := finder.repo.FindByID(database.Connection(), messageID) 25 | if err != nil { 26 | return Message{}, err 27 | } 28 | 29 | return Message{Status: message.Status}, nil 30 | } 31 | -------------------------------------------------------------------------------- /v1/services/notifications_updater.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/models" 4 | 5 | type NotificationsUpdater struct { 6 | kindsRepo KindsRepo 7 | } 8 | 9 | func NewNotificationsUpdater(kindsRepo KindsRepo) NotificationsUpdater { 10 | return NotificationsUpdater{ 11 | kindsRepo: kindsRepo, 12 | } 13 | } 14 | 15 | func (updater NotificationsUpdater) Update(database DatabaseInterface, notification models.Kind) error { 16 | _, err := updater.kindsRepo.Update(database.Connection(), notification) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /v1/services/organization_loader.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/cf" 4 | 5 | type OrganizationLoader struct { 6 | cc cloudController 7 | } 8 | 9 | func NewOrganizationLoader(cc cloudController) OrganizationLoader { 10 | return OrganizationLoader{ 11 | cc: cc, 12 | } 13 | } 14 | 15 | func (loader OrganizationLoader) Load(orgGUID string, token string) (cf.CloudControllerOrganization, error) { 16 | organization, err := loader.cc.LoadOrganization(orgGUID, token) 17 | if err != nil { 18 | return cf.CloudControllerOrganization{}, CCErrorFor(err) 19 | } 20 | 21 | return organization, nil 22 | } 23 | -------------------------------------------------------------------------------- /v1/services/response.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | type Response struct { 4 | Status string `json:"status"` 5 | Recipient string `json:"recipient"` 6 | NotificationID string `json:"notification_id"` 7 | VCAPRequestID string `json:"vcap_request_id"` 8 | } 9 | -------------------------------------------------------------------------------- /v1/services/space_loader.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/cf" 4 | 5 | type SpaceLoader struct { 6 | cc cloudController 7 | } 8 | 9 | func NewSpaceLoader(cc cloudController) SpaceLoader { 10 | return SpaceLoader{ 11 | cc: cc, 12 | } 13 | } 14 | 15 | func (loader SpaceLoader) Load(spaceGUID string, token string) (cf.CloudControllerSpace, error) { 16 | space, err := loader.cc.LoadSpace(spaceGUID, token) 17 | if err != nil { 18 | return cf.CloudControllerSpace{}, CCErrorFor(err) 19 | } 20 | 21 | return space, nil 22 | } 23 | -------------------------------------------------------------------------------- /v1/services/template_finder.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/models" 4 | 5 | type TemplateFinder struct { 6 | templatesRepo TemplatesRepo 7 | } 8 | 9 | func NewTemplateFinder(templatesRepo TemplatesRepo) TemplateFinder { 10 | return TemplateFinder{ 11 | templatesRepo: templatesRepo, 12 | } 13 | } 14 | 15 | func (finder TemplateFinder) FindByID(database DatabaseInterface, templateID string) (models.Template, error) { 16 | template, err := finder.templatesRepo.FindByID(database.Connection(), templateID) 17 | if err != nil { 18 | return models.Template{}, err 19 | } 20 | 21 | return template, err 22 | } 23 | -------------------------------------------------------------------------------- /v1/services/template_updater.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/models" 4 | 5 | type TemplateUpdater struct { 6 | templatesRepo TemplatesRepo 7 | } 8 | 9 | func NewTemplateUpdater(templatesRepo TemplatesRepo) TemplateUpdater { 10 | return TemplateUpdater{ 11 | templatesRepo: templatesRepo, 12 | } 13 | } 14 | 15 | func (updater TemplateUpdater) Update(database DatabaseInterface, templateID string, template models.Template) error { 16 | _, err := updater.templatesRepo.Update(database.Connection(), templateID, template) 17 | if err != nil { 18 | return err 19 | } 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /v1/services/user.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | type User struct { 4 | GUID string 5 | Email string 6 | } 7 | -------------------------------------------------------------------------------- /v1/web/clients/database.go: -------------------------------------------------------------------------------- 1 | package clients 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/services" 4 | 5 | type DatabaseInterface interface { 6 | services.DatabaseInterface 7 | } 8 | -------------------------------------------------------------------------------- /v1/web/clients/init_test.go: -------------------------------------------------------------------------------- 1 | package clients_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebV1ClientsSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "v1/web/clients") 13 | } 14 | -------------------------------------------------------------------------------- /v1/web/clients/routes.go: -------------------------------------------------------------------------------- 1 | package clients 2 | 3 | import "github.com/ryanmoran/stack" 4 | 5 | type muxer interface { 6 | Handle(method, path string, handler stack.Handler, middleware ...stack.Middleware) 7 | } 8 | 9 | type Routes struct { 10 | RequestCounter stack.Middleware 11 | RequestLogging stack.Middleware 12 | NotificationsManageAuthenticator stack.Middleware 13 | DatabaseAllocator stack.Middleware 14 | 15 | ErrorWriter errorWriter 16 | TemplateAssigner assignsTemplates 17 | } 18 | 19 | func (r Routes) Register(m muxer) { 20 | m.Handle("PUT", "/clients/{client_id}/template", NewAssignTemplateHandler(r.TemplateAssigner, r.ErrorWriter), r.RequestLogging, r.RequestCounter, r.NotificationsManageAuthenticator, r.DatabaseAllocator) 21 | } 22 | -------------------------------------------------------------------------------- /v1/web/info/get_handler.go: -------------------------------------------------------------------------------- 1 | package info 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/ryanmoran/stack" 8 | ) 9 | 10 | type GetHandler struct { 11 | } 12 | 13 | func NewGetHandler() GetHandler { 14 | return GetHandler{} 15 | } 16 | 17 | func (handler GetHandler) ServeHTTP(w http.ResponseWriter, req *http.Request, context stack.Context) { 18 | w.WriteHeader(http.StatusOK) 19 | output, err := json.Marshal(map[string]interface{}{ 20 | "version": 1, 21 | }) 22 | if err != nil { 23 | w.WriteHeader(http.StatusInternalServerError) 24 | return 25 | } 26 | 27 | w.Write(output) 28 | } 29 | -------------------------------------------------------------------------------- /v1/web/info/get_handler_test.go: -------------------------------------------------------------------------------- 1 | package info_test 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | 7 | "github.com/cloudfoundry-incubator/notifications/v1/web/info" 8 | 9 | . "github.com/onsi/ginkgo/v2" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | var _ = Describe("GetHandler", func() { 14 | Describe("ServeHTTP", func() { 15 | var handler info.GetHandler 16 | 17 | BeforeEach(func() { 18 | handler = info.NewGetHandler() 19 | }) 20 | 21 | It("returns a 200 response code and an empty JSON body", func() { 22 | writer := httptest.NewRecorder() 23 | request, err := http.NewRequest("GET", "/info", nil) 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | handler.ServeHTTP(writer, request, nil) 29 | 30 | Expect(writer.Code).To(Equal(http.StatusOK)) 31 | Expect(writer.Body.String()).To(MatchJSON(`{ 32 | "version": 1 33 | }`)) 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /v1/web/info/init_test.go: -------------------------------------------------------------------------------- 1 | package info_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebV1InfoSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "v1/web/info") 13 | } 14 | -------------------------------------------------------------------------------- /v1/web/info/routes.go: -------------------------------------------------------------------------------- 1 | package info 2 | 3 | import "github.com/ryanmoran/stack" 4 | 5 | type muxer interface { 6 | Handle(method, path string, handler stack.Handler, middleware ...stack.Middleware) 7 | } 8 | 9 | type Routes struct { 10 | RequestLogging stack.Middleware 11 | RequestCounter stack.Middleware 12 | } 13 | 14 | func (r Routes) Register(m muxer) { 15 | m.Handle("GET", "/info", NewGetHandler(), r.RequestLogging, r.RequestCounter) 16 | } 17 | -------------------------------------------------------------------------------- /v1/web/messages/database.go: -------------------------------------------------------------------------------- 1 | package messages 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/services" 4 | 5 | type DatabaseInterface interface { 6 | services.DatabaseInterface 7 | } 8 | -------------------------------------------------------------------------------- /v1/web/messages/init_test.go: -------------------------------------------------------------------------------- 1 | package messages_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebV1MessagesSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "v1/web/messages") 13 | } 14 | -------------------------------------------------------------------------------- /v1/web/messages/routes.go: -------------------------------------------------------------------------------- 1 | package messages 2 | 3 | import "github.com/ryanmoran/stack" 4 | 5 | type muxer interface { 6 | Handle(method, path string, handler stack.Handler, middleware ...stack.Middleware) 7 | } 8 | 9 | type Routes struct { 10 | RequestCounter stack.Middleware 11 | RequestLogging stack.Middleware 12 | NotificationsWriteOrEmailsWriteAuthenticator stack.Middleware 13 | DatabaseAllocator stack.Middleware 14 | 15 | MessageFinder messageFinder 16 | ErrorWriter errorWriter 17 | } 18 | 19 | func (r Routes) Register(m muxer) { 20 | m.Handle("GET", "/messages/{message_id}", NewGetHandler(r.MessageFinder, r.ErrorWriter), r.RequestLogging, r.RequestCounter, r.NotificationsWriteOrEmailsWriteAuthenticator, r.DatabaseAllocator) 21 | } 22 | -------------------------------------------------------------------------------- /v1/web/middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/ryanmoran/stack" 7 | ) 8 | 9 | type CORS struct { 10 | origin string 11 | } 12 | 13 | func NewCORS(origin string) CORS { 14 | return CORS{ 15 | origin: origin, 16 | } 17 | } 18 | 19 | func (ware CORS) ServeHTTP(w http.ResponseWriter, req *http.Request, context stack.Context) bool { 20 | w.Header().Set("Access-Control-Allow-Origin", ware.origin) 21 | w.Header().Set("Access-Control-Allow-Methods", "GET, PATCH") 22 | w.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type") 23 | 24 | return true 25 | } 26 | -------------------------------------------------------------------------------- /v1/web/middleware/init_test.go: -------------------------------------------------------------------------------- 1 | package middleware_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebMiddlewareSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "v1/web/middleware") 13 | } 14 | -------------------------------------------------------------------------------- /v1/web/notifications/database.go: -------------------------------------------------------------------------------- 1 | package notifications 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/services" 4 | 5 | type DatabaseInterface interface { 6 | services.DatabaseInterface 7 | } 8 | -------------------------------------------------------------------------------- /v1/web/notifications/init_test.go: -------------------------------------------------------------------------------- 1 | package notifications_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebV1NotificationsSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "v1/web/notifications") 13 | } 14 | -------------------------------------------------------------------------------- /v1/web/notify/database.go: -------------------------------------------------------------------------------- 1 | package notify 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/services" 4 | 5 | type DatabaseInterface interface { 6 | services.DatabaseInterface 7 | } 8 | 9 | type ConnectionInterface interface { 10 | services.ConnectionInterface 11 | } 12 | -------------------------------------------------------------------------------- /v1/web/notify/init_test.go: -------------------------------------------------------------------------------- 1 | package notify_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebV1NotifySuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "v1/web/notify") 13 | } 14 | -------------------------------------------------------------------------------- /v1/web/preferences/database.go: -------------------------------------------------------------------------------- 1 | package preferences 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/services" 4 | 5 | type DatabaseInterface interface { 6 | services.DatabaseInterface 7 | } 8 | -------------------------------------------------------------------------------- /v1/web/preferences/init_test.go: -------------------------------------------------------------------------------- 1 | package preferences_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebV1PreferencesSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "v1/web/preferences") 13 | } 14 | -------------------------------------------------------------------------------- /v1/web/preferences/options_handler.go: -------------------------------------------------------------------------------- 1 | package preferences 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/ryanmoran/stack" 7 | ) 8 | 9 | type OptionsHandler struct{} 10 | 11 | func NewOptionsHandler() OptionsHandler { 12 | return OptionsHandler{} 13 | } 14 | 15 | func (h OptionsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request, context stack.Context) { 16 | w.WriteHeader(http.StatusNoContent) 17 | } 18 | -------------------------------------------------------------------------------- /v1/web/templates/database.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | import "github.com/cloudfoundry-incubator/notifications/v1/services" 4 | 5 | type DatabaseInterface interface { 6 | services.DatabaseInterface 7 | } 8 | -------------------------------------------------------------------------------- /v1/web/templates/init_test.go: -------------------------------------------------------------------------------- 1 | package templates_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebV1TemplatesSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "v1/web/templates") 13 | } 14 | -------------------------------------------------------------------------------- /v1/web/webutil/init_test.go: -------------------------------------------------------------------------------- 1 | package webutil_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestWebutilSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "v1/web/webutil") 13 | } 14 | -------------------------------------------------------------------------------- /valiant/errors.go: -------------------------------------------------------------------------------- 1 | package valiant 2 | 3 | type ExtraFieldError struct { 4 | ErrorMessage string 5 | } 6 | 7 | func (err ExtraFieldError) Error() string { 8 | return err.ErrorMessage 9 | } 10 | 11 | type RequiredFieldError struct { 12 | ErrorMessage string 13 | } 14 | 15 | func (err RequiredFieldError) Error() string { 16 | return err.ErrorMessage 17 | } 18 | -------------------------------------------------------------------------------- /valiant/init_test.go: -------------------------------------------------------------------------------- 1 | package valiant_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestValiantSuite(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "valiant") 13 | } 14 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/field/fe_amd64.go: -------------------------------------------------------------------------------- 1 | // Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. 2 | 3 | //go:build amd64 && gc && !purego 4 | // +build amd64,gc,!purego 5 | 6 | package field 7 | 8 | // feMul sets out = a * b. It works like feMulGeneric. 9 | // 10 | //go:noescape 11 | func feMul(out *Element, a *Element, b *Element) 12 | 13 | // feSquare sets out = a * a. It works like feSquareGeneric. 14 | // 15 | //go:noescape 16 | func feSquare(out *Element, a *Element) 17 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !amd64 || !gc || purego 6 | // +build !amd64 !gc purego 7 | 8 | package field 9 | 10 | func feMul(v, x, y *Element) { feMulGeneric(v, x, y) } 11 | 12 | func feSquare(v, x *Element) { feSquareGeneric(v, x) } 13 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/field/fe_arm64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build arm64 && gc && !purego 6 | // +build arm64,gc,!purego 7 | 8 | package field 9 | 10 | //go:noescape 11 | func carryPropagate(v *Element) 12 | 13 | func (v *Element) carryPropagate() *Element { 14 | carryPropagate(v) 15 | return v 16 | } 17 | -------------------------------------------------------------------------------- /vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !arm64 || !gc || purego 6 | // +build !arm64 !gc purego 7 | 8 | package field 9 | 10 | func (v *Element) carryPropagate() *Element { 11 | return v.carryPropagateGeneric() 12 | } 13 | -------------------------------------------------------------------------------- /vendor/github.com/DATA-DOG/go-sqlmock/.gitignore: -------------------------------------------------------------------------------- 1 | /examples/blog/blog 2 | /examples/orders/orders 3 | /examples/basic/basic 4 | .idea/ 5 | -------------------------------------------------------------------------------- /vendor/github.com/DATA-DOG/go-sqlmock/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go_import_path: github.com/DATA-DOG/go-sqlmock 4 | 5 | go: 6 | - 1.2.x 7 | - 1.3.x 8 | - 1.4 # has no cover tool for latest releases 9 | - 1.5.x 10 | - 1.6.x 11 | - 1.7.x 12 | - 1.8.x 13 | - 1.9.x 14 | - 1.10.x 15 | - 1.11.x 16 | - 1.12.x 17 | - 1.13.x 18 | - 1.14.x 19 | - 1.15.x 20 | - 1.16.x 21 | - 1.17.x 22 | 23 | script: 24 | - go vet 25 | - test -z "$(go fmt ./...)" # fail if not formatted properly 26 | - go test -race -coverprofile=coverage.txt -covermode=atomic 27 | 28 | after_success: 29 | - bash <(curl -s https://codecov.io/bash) 30 | -------------------------------------------------------------------------------- /vendor/github.com/DATA-DOG/go-sqlmock/argument.go: -------------------------------------------------------------------------------- 1 | package sqlmock 2 | 3 | import "database/sql/driver" 4 | 5 | // Argument interface allows to match 6 | // any argument in specific way when used with 7 | // ExpectedQuery and ExpectedExec expectations. 8 | type Argument interface { 9 | Match(driver.Value) bool 10 | } 11 | 12 | // AnyArg will return an Argument which can 13 | // match any kind of arguments. 14 | // 15 | // Useful for time.Time or similar kinds of arguments. 16 | func AnyArg() Argument { 17 | return anyArgument{} 18 | } 19 | 20 | type anyArgument struct{} 21 | 22 | func (a anyArgument) Match(_ driver.Value) bool { 23 | return true 24 | } 25 | -------------------------------------------------------------------------------- /vendor/github.com/DATA-DOG/go-sqlmock/sqlmock_go18_19.go: -------------------------------------------------------------------------------- 1 | // +build go1.8,!go1.9 2 | 3 | package sqlmock 4 | 5 | import "database/sql/driver" 6 | 7 | // CheckNamedValue meets https://golang.org/pkg/database/sql/driver/#NamedValueChecker 8 | func (c *sqlmock) CheckNamedValue(nv *driver.NamedValue) (err error) { 9 | nv.Value, err = c.converter.ConvertValue(nv.Value) 10 | return err 11 | } 12 | -------------------------------------------------------------------------------- /vendor/github.com/DATA-DOG/go-sqlmock/sqlmock_go19.go: -------------------------------------------------------------------------------- 1 | // +build go1.9 2 | 3 | package sqlmock 4 | 5 | import ( 6 | "database/sql" 7 | "database/sql/driver" 8 | ) 9 | 10 | // CheckNamedValue meets https://golang.org/pkg/database/sql/driver/#NamedValueChecker 11 | func (c *sqlmock) CheckNamedValue(nv *driver.NamedValue) (err error) { 12 | switch nv.Value.(type) { 13 | case sql.Out: 14 | return nil 15 | default: 16 | nv.Value, err = c.converter.ConvertValue(nv.Value) 17 | return err 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vendor/github.com/DATA-DOG/go-sqlmock/statement.go: -------------------------------------------------------------------------------- 1 | package sqlmock 2 | 3 | type statement struct { 4 | conn *sqlmock 5 | ex *ExpectedPrepare 6 | query string 7 | } 8 | 9 | func (stmt *statement) Close() error { 10 | stmt.ex.wasClosed = true 11 | return stmt.ex.closeErr 12 | } 13 | 14 | func (stmt *statement) NumInput() int { 15 | return -1 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/DATA-DOG/go-sqlmock/statement_before_go18.go: -------------------------------------------------------------------------------- 1 | // +build !go1.8 2 | 3 | package sqlmock 4 | 5 | import ( 6 | "database/sql/driver" 7 | ) 8 | 9 | // Deprecated: Drivers should implement ExecerContext instead. 10 | func (stmt *statement) Exec(args []driver.Value) (driver.Result, error) { 11 | return stmt.conn.Exec(stmt.query, args) 12 | } 13 | 14 | // Deprecated: Drivers should implement StmtQueryContext instead (or additionally). 15 | func (stmt *statement) Query(args []driver.Value) (driver.Rows, error) { 16 | return stmt.conn.Query(stmt.query, args) 17 | } 18 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/.gitattributes: -------------------------------------------------------------------------------- 1 | testdata/* linguist-vendored 2 | -------------------------------------------------------------------------------- /vendor/github.com/PuerkitoBio/goquery/.gitignore: -------------------------------------------------------------------------------- 1 | # editor temporary files 2 | *.sublime-* 3 | .DS_Store 4 | *.swp 5 | #*.*# 6 | tags 7 | 8 | # direnv config 9 | .env* 10 | 11 | # test binaries 12 | *.test 13 | 14 | # coverage and profilte outputs 15 | *.out 16 | 17 | -------------------------------------------------------------------------------- /vendor/github.com/andybalholm/cascadia/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.3 5 | - 1.4 6 | 7 | install: 8 | - go get github.com/andybalholm/cascadia 9 | 10 | script: 11 | - go test -v 12 | 13 | notifications: 14 | email: false 15 | -------------------------------------------------------------------------------- /vendor/github.com/andybalholm/cascadia/specificity.go: -------------------------------------------------------------------------------- 1 | package cascadia 2 | 3 | // Specificity is the CSS specificity as defined in 4 | // https://www.w3.org/TR/selectors/#specificity-rules 5 | // with the convention Specificity = [A,B,C]. 6 | type Specificity [3]int 7 | 8 | // returns `true` if s < other (strictly), false otherwise 9 | func (s Specificity) Less(other Specificity) bool { 10 | for i := range s { 11 | if s[i] < other[i] { 12 | return true 13 | } 14 | if s[i] > other[i] { 15 | return false 16 | } 17 | } 18 | return false 19 | } 20 | 21 | func (s Specificity) Add(other Specificity) Specificity { 22 | for i, sp := range other { 23 | s[i] += sp 24 | } 25 | return s 26 | } 27 | -------------------------------------------------------------------------------- /vendor/github.com/chrj/smtpd/.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | 3 | *.orig 4 | -------------------------------------------------------------------------------- /vendor/github.com/chrj/smtpd/README.md: -------------------------------------------------------------------------------- 1 | [](https://godoc.org/bitbucket.org/chrj/smtpd) 2 | -------------------------------------------------------------------------------- /vendor/github.com/chrj/smtpd/address.go: -------------------------------------------------------------------------------- 1 | package smtpd 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func parseAddress(src string) (string, error) { 9 | 10 | if src[0] != '<' || src[len(src)-1] != '>' { 11 | return "", fmt.Errorf("Ill-formatted e-mail address: %s", src) 12 | } 13 | 14 | if strings.Count(src, "@") > 1 { 15 | return "", fmt.Errorf("Ill-formatted e-mail address: %s", src) 16 | } 17 | 18 | return src[1 : len(src)-1], nil 19 | } 20 | -------------------------------------------------------------------------------- /vendor/github.com/dgrijalva/jwt-go/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin 3 | .idea/ 4 | 5 | -------------------------------------------------------------------------------- /vendor/github.com/dgrijalva/jwt-go/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | script: 4 | - go vet ./... 5 | - go test -v ./... 6 | 7 | go: 8 | - 1.3 9 | - 1.4 10 | - 1.5 11 | - 1.6 12 | - 1.7 13 | - tip 14 | -------------------------------------------------------------------------------- /vendor/github.com/dgrijalva/jwt-go/doc.go: -------------------------------------------------------------------------------- 1 | // Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html 2 | // 3 | // See README.md for more info. 4 | package jwt 5 | -------------------------------------------------------------------------------- /vendor/github.com/go-gorp/gorp/v3/.gitignore: -------------------------------------------------------------------------------- 1 | _test 2 | *.test 3 | _testmain.go 4 | _obj 5 | *~ 6 | *.6 7 | 6.out 8 | gorptest.bin 9 | tmp 10 | .idea 11 | coverage.out 12 | -------------------------------------------------------------------------------- /vendor/github.com/go-gorp/gorp/v3/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 James Cooper. All rights reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package gorp provides a simple way to marshal Go structs to and from 6 | // SQL databases. It uses the database/sql package, and should work with any 7 | // compliant database/sql driver. 8 | // 9 | // Source code and project home: 10 | // https://github.com/go-gorp/gorp 11 | package gorp 12 | -------------------------------------------------------------------------------- /vendor/github.com/go-gorp/gorp/v3/test_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | # on macs, you may need to: 4 | # export GOBUILDFLAG=-ldflags -linkmode=external 5 | 6 | echo "Running unit tests" 7 | go test -race 8 | 9 | echo "Testing against postgres" 10 | export GORP_TEST_DSN="host=postgres user=gorptest password=gorptest dbname=gorptest sslmode=disable" 11 | export GORP_TEST_DIALECT=postgres 12 | go test -tags integration $GOBUILDFLAG $@ . 13 | 14 | echo "Testing against sqlite" 15 | export GORP_TEST_DSN=/tmp/gorptest.bin 16 | export GORP_TEST_DIALECT=sqlite 17 | go test -tags integration $GOBUILDFLAG $@ . 18 | rm -f /tmp/gorptest.bin 19 | 20 | echo "Testing against mysql" 21 | export GORP_TEST_DSN="gorptest:gorptest@tcp(mysql)/gorptest" 22 | export GORP_TEST_DIALECT=mysql 23 | go test -tags integration $GOBUILDFLAG $@ . 24 | -------------------------------------------------------------------------------- /vendor/github.com/go-logr/logr/.golangci.yaml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 1m 3 | tests: true 4 | 5 | linters: 6 | disable-all: true 7 | enable: 8 | - asciicheck 9 | - errcheck 10 | - forcetypeassert 11 | - gocritic 12 | - gofmt 13 | - goimports 14 | - gosimple 15 | - govet 16 | - ineffassign 17 | - misspell 18 | - revive 19 | - staticcheck 20 | - typecheck 21 | - unused 22 | 23 | issues: 24 | exclude-use-default: false 25 | max-issues-per-linter: 0 26 | max-same-issues: 10 27 | -------------------------------------------------------------------------------- /vendor/github.com/go-logr/logr/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## v1.0.0-rc1 4 | 5 | This is the first logged release. Major changes (including breaking changes) 6 | have occurred since earlier tags. 7 | -------------------------------------------------------------------------------- /vendor/github.com/go-logr/logr/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Logr is open to pull-requests, provided they fit within the intended scope of 4 | the project. Specifically, this library aims to be VERY small and minimalist, 5 | with no external dependencies. 6 | 7 | ## Compatibility 8 | 9 | This project intends to follow [semantic versioning](http://semver.org) and 10 | is very strict about compatibility. Any proposed changes MUST follow those 11 | rules. 12 | 13 | ## Performance 14 | 15 | As a logging library, logr must be as light-weight as possible. Any proposed 16 | code change must include results of running the [benchmark](./benchmark) 17 | before and after the change. 18 | -------------------------------------------------------------------------------- /vendor/github.com/go-logr/logr/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | If you have discovered a security vulnerability in this project, please report it 4 | privately. **Do not disclose it as a public issue.** This gives us time to work with you 5 | to fix the issue before public exposure, reducing the chance that the exploit will be 6 | used before a patch is released. 7 | 8 | You may submit the report in the following ways: 9 | 10 | - send an email to go-logr-security@googlegroups.com 11 | - send us a [private vulnerability report](https://github.com/go-logr/logr/security/advisories/new) 12 | 13 | Please provide the following information in your report: 14 | 15 | - A description of the vulnerability and its impact 16 | - How to reproduce the issue 17 | 18 | We ask that you give us 90 days to work on a fix before public exposure. 19 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .DS_Store? 3 | ._* 4 | .Spotlight-V100 5 | .Trashes 6 | Icon? 7 | ehthumbs.db 8 | Thumbs.db 9 | .idea 10 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/atomic_bool.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package. 2 | // 3 | // Copyright 2022 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | //go:build go1.19 9 | // +build go1.19 10 | 11 | package mysql 12 | 13 | import "sync/atomic" 14 | 15 | /****************************************************************************** 16 | * Sync utils * 17 | ******************************************************************************/ 18 | 19 | type atomicBool = atomic.Bool 20 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/conncheck_dummy.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2019 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | //go:build !linux && !darwin && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !illumos 10 | // +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!illumos 11 | 12 | package mysql 13 | 14 | import "net" 15 | 16 | func connCheck(conn net.Conn) error { 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/go-sql-driver/mysql/transaction.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | type mysqlTx struct { 12 | mc *mysqlConn 13 | } 14 | 15 | func (tx *mysqlTx) Commit() (err error) { 16 | if tx.mc == nil || tx.mc.closed.Load() { 17 | return ErrInvalidConn 18 | } 19 | err = tx.mc.exec("COMMIT") 20 | tx.mc = nil 21 | return 22 | } 23 | 24 | func (tx *mysqlTx) Rollback() (err error) { 25 | if tx.mc == nil || tx.mc.closed.Load() { 26 | return ErrInvalidConn 27 | } 28 | err = tx.mc.exec("ROLLBACK") 29 | tx.mc = nil 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/go-task/slim-sprig/v3/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | insert_final_newline = true 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | indent_style = tab 10 | indent_size = 8 11 | 12 | [*.{md,yml,yaml,json}] 13 | indent_style = space 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /vendor/github.com/go-task/slim-sprig/v3/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /vendor/github.com/go-task/slim-sprig/v3/.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | /.glide 3 | -------------------------------------------------------------------------------- /vendor/github.com/go-task/slim-sprig/v3/Taskfile.yml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | tasks: 6 | default: 7 | cmds: 8 | - task: test 9 | 10 | test: 11 | cmds: 12 | - go test -v . 13 | -------------------------------------------------------------------------------- /vendor/github.com/go-task/slim-sprig/v3/crypto.go: -------------------------------------------------------------------------------- 1 | package sprig 2 | 3 | import ( 4 | "crypto/sha1" 5 | "crypto/sha256" 6 | "encoding/hex" 7 | "fmt" 8 | "hash/adler32" 9 | ) 10 | 11 | func sha256sum(input string) string { 12 | hash := sha256.Sum256([]byte(input)) 13 | return hex.EncodeToString(hash[:]) 14 | } 15 | 16 | func sha1sum(input string) string { 17 | hash := sha1.Sum([]byte(input)) 18 | return hex.EncodeToString(hash[:]) 19 | } 20 | 21 | func adler32sum(input string) string { 22 | hash := adler32.Checksum([]byte(input)) 23 | return fmt.Sprintf("%d", hash) 24 | } 25 | -------------------------------------------------------------------------------- /vendor/github.com/go-task/slim-sprig/v3/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package sprig provides template functions for Go. 3 | 4 | This package contains a number of utility functions for working with data 5 | inside of Go `html/template` and `text/template` files. 6 | 7 | To add these functions, use the `template.Funcs()` method: 8 | 9 | t := templates.New("foo").Funcs(sprig.FuncMap()) 10 | 11 | Note that you should add the function map before you parse any template files. 12 | 13 | In several cases, Sprig reverses the order of arguments from the way they 14 | appear in the standard library. This is to make it easier to pipe 15 | arguments into functions. 16 | 17 | See http://masterminds.github.io/sprig/ for more detailed documentation on each of the available functions. 18 | */ 19 | package sprig 20 | -------------------------------------------------------------------------------- /vendor/github.com/go-task/slim-sprig/v3/network.go: -------------------------------------------------------------------------------- 1 | package sprig 2 | 3 | import ( 4 | "math/rand" 5 | "net" 6 | ) 7 | 8 | func getHostByName(name string) string { 9 | addrs, _ := net.LookupHost(name) 10 | //TODO: add error handing when release v3 comes out 11 | return addrs[rand.Intn(len(addrs))] 12 | } 13 | -------------------------------------------------------------------------------- /vendor/github.com/go-task/slim-sprig/v3/reflect.go: -------------------------------------------------------------------------------- 1 | package sprig 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // typeIs returns true if the src is the type named in target. 9 | func typeIs(target string, src interface{}) bool { 10 | return target == typeOf(src) 11 | } 12 | 13 | func typeIsLike(target string, src interface{}) bool { 14 | t := typeOf(src) 15 | return target == t || "*"+target == t 16 | } 17 | 18 | func typeOf(src interface{}) string { 19 | return fmt.Sprintf("%T", src) 20 | } 21 | 22 | func kindIs(target string, src interface{}) bool { 23 | return target == kindOf(src) 24 | } 25 | 26 | func kindOf(src interface{}) string { 27 | return reflect.ValueOf(src).Kind().String() 28 | } 29 | -------------------------------------------------------------------------------- /vendor/github.com/golang-jwt/jwt/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin 3 | .idea/ 4 | 5 | -------------------------------------------------------------------------------- /vendor/github.com/golang-jwt/jwt/doc.go: -------------------------------------------------------------------------------- 1 | // Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html 2 | // 3 | // See README.md for more info. 4 | package jwt 5 | -------------------------------------------------------------------------------- /vendor/github.com/golang-jwt/jwt/v5/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin 3 | .idea/ 4 | 5 | -------------------------------------------------------------------------------- /vendor/github.com/golang-jwt/jwt/v5/claims.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | // Claims represent any form of a JWT Claims Set according to 4 | // https://datatracker.ietf.org/doc/html/rfc7519#section-4. In order to have a 5 | // common basis for validation, it is required that an implementation is able to 6 | // supply at least the claim names provided in 7 | // https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 namely `exp`, 8 | // `iat`, `nbf`, `iss`, `sub` and `aud`. 9 | type Claims interface { 10 | GetExpirationTime() (*NumericDate, error) 11 | GetIssuedAt() (*NumericDate, error) 12 | GetNotBefore() (*NumericDate, error) 13 | GetIssuer() (string, error) 14 | GetSubject() (string, error) 15 | GetAudience() (ClaimStrings, error) 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/golang-jwt/jwt/v5/doc.go: -------------------------------------------------------------------------------- 1 | // Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html 2 | // 3 | // See README.md for more info. 4 | package jwt 5 | -------------------------------------------------------------------------------- /vendor/github.com/golang-jwt/jwt/v5/staticcheck.conf: -------------------------------------------------------------------------------- 1 | checks = ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1023"] 2 | -------------------------------------------------------------------------------- /vendor/github.com/golang-jwt/jwt/v5/token_option.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | // TokenOption is a reserved type, which provides some forward compatibility, 4 | // if we ever want to introduce token creation-related options. 5 | type TokenOption func(*Token) 6 | -------------------------------------------------------------------------------- /vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017, The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !cmp_debug 6 | // +build !cmp_debug 7 | 8 | package diff 9 | 10 | var debug debugger 11 | 12 | type debugger struct{} 13 | 14 | func (debugger) Begin(_, _ int, f EqualFunc, _, _ *EditScript) EqualFunc { 15 | return f 16 | } 17 | func (debugger) Update() {} 18 | func (debugger) Finish() {} 19 | -------------------------------------------------------------------------------- /vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019, The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package flags 6 | 7 | // Deterministic controls whether the output of Diff should be deterministic. 8 | // This is only used for testing. 9 | var Deterministic bool 10 | -------------------------------------------------------------------------------- /vendor/github.com/google/pprof/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of pprof authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS files. 3 | # See the latter for an explanation. 4 | # Names should be added to this file as: 5 | # Name or Organization