has_helpers 5.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +38 -0
- data/.rspec +3 -0
- data/.rubocop.yml +10 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.tekton/Chart.yaml +14 -0
- data/.tekton/templates/pipelinerun.yaml +44 -0
- data/.tekton/values-main.yaml +53 -0
- data/.tekton/values-pr.yaml +40 -0
- data/.tekton/values.yaml +17 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +204 -0
- data/Gemfile +25 -0
- data/README.md +76 -0
- data/Rakefile +46 -0
- data/app/api/has_helpers/rack_api.rb +116 -0
- data/app/api/has_helpers/search_api.rb +173 -0
- data/app/assets/stylesheets/has_helpers/base.scss +5 -0
- data/app/assets/stylesheets/has_helpers/color_palette.scss +33 -0
- data/app/assets/stylesheets/has_helpers/color_palettes/default.scss +11 -0
- data/app/assets/stylesheets/has_helpers/components/dataset.scss +43 -0
- data/app/assets/stylesheets/has_helpers/mixins.scss +149 -0
- data/app/commands/has_helpers/cards/available_cards_command.rb +98 -0
- data/app/commands/has_helpers/cards/card_data_command.rb +1062 -0
- data/app/commands/reports/base_options.rb +79 -0
- data/app/commands/reports/belongs_to_options.rb +178 -0
- data/app/commands/reports/constant_options.rb +66 -0
- data/app/commands/reports/external_graphql_client.rb +116 -0
- data/app/commands/reports/fill_dates.rb +65 -0
- data/app/commands/reports/filter_option_generator.rb +186 -0
- data/app/commands/reports/generator.rb +310 -0
- data/app/commands/reports/has_many_options.rb +105 -0
- data/app/commands/reports/importer.rb +132 -0
- data/app/commands/reports/nested_attribute_options.rb +134 -0
- data/app/constants/color.rb +9 -0
- data/app/constants/file_format.rb +12 -0
- data/app/constants/has_helpers/cards/card_custom_field.rb +12 -0
- data/app/constants/has_helpers/cards/card_data_type.rb +11 -0
- data/app/constants/has_helpers/cards/card_identifier.rb +10 -0
- data/app/constants/has_helpers/cards/card_layout_type.rb +11 -0
- data/app/constants/has_helpers/cards/card_variant_type.rb +12 -0
- data/app/constants/has_helpers/link_type.rb +10 -0
- data/app/constants/has_helpers/notification_alert_status.rb +14 -0
- data/app/constants/has_helpers/notification_alert_type.rb +10 -0
- data/app/constants/has_helpers/operation.rb +11 -0
- data/app/constants/has_helpers/resource_type.rb +25 -0
- data/app/constants/icon.rb +22 -0
- data/app/constants/logical_operator.rb +7 -0
- data/app/constants/option.rb +10 -0
- data/app/constants/time_zone.rb +12 -0
- data/app/constructors/has_helpers/base_constructor.rb +62 -0
- data/app/controllers/concerns/has_helpers/service_trust/authenticated_as_service.rb +51 -0
- data/app/controllers/concerns/has_helpers/service_trust/graphql_authentication.rb +57 -0
- data/app/controllers/has_helpers/controller.rb +63 -0
- data/app/controllers/has_helpers/factory_controller.rb +261 -0
- data/app/controllers/has_helpers/graphql_extensions.rb +83 -0
- data/app/controllers/has_helpers/health_check_controller.rb +46 -0
- data/app/controllers/has_helpers/search_extensions.rb +92 -0
- data/app/controllers/has_helpers/service_trust/jwks_controller.rb +20 -0
- data/app/controllers/has_helpers/xlsx_export.rb +115 -0
- data/app/controllers/search_extensions.rb +5 -0
- data/app/graphql/has_helpers/base_type.rb +13 -0
- data/app/graphql/has_helpers/base_union.rb +9 -0
- data/app/graphql/has_helpers/enums/resource_enum.rb +24 -0
- data/app/graphql/has_helpers/middleware/application_access.rb +26 -0
- data/app/graphql/has_helpers/mutations/base.rb +27 -0
- data/app/graphql/has_helpers/mutations/external_data_sync.rb +72 -0
- data/app/graphql/has_helpers/mutations/mark_individual_alert_read.rb +31 -0
- data/app/graphql/has_helpers/mutations/mark_notifications_read.rb +50 -0
- data/app/graphql/has_helpers/mutations/toggle_favorite.rb +39 -0
- data/app/graphql/has_helpers/resolvers/app_nav.rb +107 -0
- data/app/graphql/has_helpers/resolvers/base.rb +27 -0
- data/app/graphql/has_helpers/resolvers/cards/available_cards.rb +31 -0
- data/app/graphql/has_helpers/resolvers/cards/card_data.rb +74 -0
- data/app/graphql/has_helpers/resolvers/cards/hover_card_data.rb +153 -0
- data/app/graphql/has_helpers/resolvers/favorite.rb +62 -0
- data/app/graphql/has_helpers/resolvers/internal/roles.rb +54 -0
- data/app/graphql/has_helpers/resolvers/is_favorite.rb +24 -0
- data/app/graphql/has_helpers/resolvers/list_notification_alerts.rb +47 -0
- data/app/graphql/has_helpers/resolvers/lookup.rb +36 -0
- data/app/graphql/has_helpers/resolvers/recently_viewed.rb +54 -0
- data/app/graphql/has_helpers/resolvers/resource_tab.rb +22 -0
- data/app/graphql/has_helpers/resolvers/search.rb +18 -0
- data/app/graphql/has_helpers/resolvers/search_query.rb +38 -0
- data/app/graphql/has_helpers/resolvers/user_application_order.rb +35 -0
- data/app/graphql/has_helpers/resources/organization_option_resource.rb +28 -0
- data/app/graphql/has_helpers/resources/organization_resource_resource.rb +18 -0
- data/app/graphql/has_helpers/resources/resource_group_resource.rb +10 -0
- data/app/graphql/has_helpers/resources/resource_resource.rb +21 -0
- data/app/graphql/has_helpers/resources/user_role_resource.rb +9 -0
- data/app/graphql/has_helpers/types/application_order_type.rb +18 -0
- data/app/graphql/has_helpers/types/cards/available_card_type.rb +20 -0
- data/app/graphql/has_helpers/types/cards/card_badge_type.rb +12 -0
- data/app/graphql/has_helpers/types/cards/card_config_type.rb +18 -0
- data/app/graphql/has_helpers/types/cards/card_data_type.rb +13 -0
- data/app/graphql/has_helpers/types/cards/card_entry_field_type.rb +21 -0
- data/app/graphql/has_helpers/types/cards/card_entry_type.rb +12 -0
- data/app/graphql/has_helpers/types/cards/card_field_config_type.rb +17 -0
- data/app/graphql/has_helpers/types/layouts/app_nav/link_section_type.rb +17 -0
- data/app/graphql/has_helpers/types/layouts/app_nav/link_type.rb +20 -0
- data/app/graphql/has_helpers/types/layouts/app_nav/sub_link_type.rb +17 -0
- data/app/graphql/has_helpers/types/layouts/app_nav/type.rb +16 -0
- data/app/graphql/has_helpers/types/lookup.rb +16 -0
- data/app/graphql/has_helpers/types/resource_tab/type.rb +15 -0
- data/app/graphql/has_helpers/types/role_type.rb +13 -0
- data/app/graphql/has_helpers/types/search/detail_badge_type.rb +15 -0
- data/app/graphql/has_helpers/types/search/detail_item_type.rb +14 -0
- data/app/graphql/has_helpers/types/search/detail_type.rb +16 -0
- data/app/graphql/has_helpers/types/search/filter_operation_enum.rb +19 -0
- data/app/graphql/has_helpers/types/search/input_type.rb +16 -0
- data/app/graphql/has_helpers/types/search/result_type.rb +72 -0
- data/app/graphql/has_helpers/types/search/search_filter_input_type.rb +17 -0
- data/app/graphql/has_helpers/types/search/type.rb +17 -0
- data/app/importers/has_helpers/cards/importer.rb +1223 -0
- data/app/lib/has_helpers/application_helper.rb +21 -0
- data/app/lib/has_helpers/dn/processor.rb +55 -0
- data/app/lib/has_helpers/dn/refresh.rb +13 -0
- data/app/lib/has_helpers/dn/utils/backfill_utils.rb +80 -0
- data/app/lib/has_helpers/dn/utils/query_utils.rb +216 -0
- data/app/lib/has_helpers/feature.rb +88 -0
- data/app/lib/has_helpers/hq_exception.rb +18 -0
- data/app/lib/has_helpers/index.rb +890 -0
- data/app/lib/has_helpers/organization_merge.rb +128 -0
- data/app/lib/has_helpers/query/elasticsearch.rb +20 -0
- data/app/lib/has_helpers/query/provider.rb +105 -0
- data/app/lib/has_helpers/query/sql.rb +133 -0
- data/app/lib/has_helpers/query.rb +14 -0
- data/app/lib/has_helpers/search/elasticsearch.rb +41 -0
- data/app/lib/has_helpers/search/elasticsearch_shared.rb +70 -0
- data/app/lib/has_helpers/search/provider.rb +149 -0
- data/app/lib/has_helpers/search/search_utils.rb +74 -0
- data/app/lib/has_helpers/search/sql_search.rb +219 -0
- data/app/lib/has_helpers/search.rb +46 -0
- data/app/models/ability.rb +233 -0
- data/app/models/has_helpers/application.rb +47 -0
- data/app/models/has_helpers/application_record.rb +7 -0
- data/app/models/has_helpers/cards/card_builder/base.rb +74 -0
- data/app/models/has_helpers/cards/card_builder/card_definition_builder.rb +486 -0
- data/app/models/has_helpers/cards/card_builder/definition_context.rb +82 -0
- data/app/models/has_helpers/cards/card_builder/field_builder.rb +142 -0
- data/app/models/has_helpers/cards/card_builder/resource_builder.rb +34 -0
- data/app/models/has_helpers/cards/card_definition.rb +41 -0
- data/app/models/has_helpers/cards/card_definition_resource.rb +19 -0
- data/app/models/has_helpers/cards/card_field.rb +39 -0
- data/app/models/has_helpers/cards/registry.rb +115 -0
- data/app/models/has_helpers/cards/role_card_field.rb +28 -0
- data/app/models/has_helpers/cards/role_resource_card.rb +41 -0
- data/app/models/has_helpers/dn_utils.rb +61 -0
- data/app/models/has_helpers/favorite.rb +20 -0
- data/app/models/has_helpers/master_record.rb +18 -0
- data/app/models/has_helpers/notification_alert.rb +52 -0
- data/app/models/has_helpers/organization.rb +16 -0
- data/app/models/has_helpers/organization_link.rb +92 -0
- data/app/models/has_helpers/organization_methods.rb +62 -0
- data/app/models/has_helpers/organization_option.rb +31 -0
- data/app/models/has_helpers/organization_resource.rb +45 -0
- data/app/models/has_helpers/pending_db_operation.rb +31 -0
- data/app/models/has_helpers/recently_viewed.rb +34 -0
- data/app/models/has_helpers/reindex_queue.rb +43 -0
- data/app/models/has_helpers/resource.rb +38 -0
- data/app/models/has_helpers/resource_group.rb +15 -0
- data/app/models/has_helpers/restriction.rb +15 -0
- data/app/models/has_helpers/restriction_operation.rb +13 -0
- data/app/models/has_helpers/role.rb +20 -0
- data/app/models/has_helpers/service_trust/audit_log.rb +20 -0
- data/app/models/has_helpers/service_trust/key.rb +56 -0
- data/app/models/has_helpers/stat.rb +13 -0
- data/app/models/has_helpers/user.rb +18 -0
- data/app/models/has_helpers/user_methods.rb +184 -0
- data/app/models/has_helpers/user_organization.rb +33 -0
- data/app/models/has_helpers/user_role.rb +27 -0
- data/app/models/has_helpers/user_session.rb +19 -0
- data/app/presenters/has_helpers/barcode_base_presenter.rb +113 -0
- data/app/presenters/has_helpers/base_presenter.rb +100 -0
- data/app/presenters/has_helpers/cacheable_item.rb +28 -0
- data/app/presenters/has_helpers/item_presenter.rb +52 -0
- data/app/presenters/has_helpers/items_presenter.rb +33 -0
- data/app/presenters/has_helpers/resource/action_presenter.rb +8 -0
- data/app/presenters/has_helpers/resource/cell_presenter.rb +52 -0
- data/app/presenters/has_helpers/resource/email_presenter.rb +34 -0
- data/app/presenters/has_helpers/resource/form_presenter.rb +76 -0
- data/app/presenters/has_helpers/resource/info_collection_presenter.rb +33 -0
- data/app/presenters/has_helpers/resource/info_presenter.rb +34 -0
- data/app/presenters/has_helpers/resource/list_presenter.rb +120 -0
- data/app/presenters/has_helpers/resource/note_presenter.rb +14 -0
- data/app/presenters/has_helpers/resource/row_presenter.rb +25 -0
- data/app/presenters/has_helpers/resource/tag_presenter.rb +31 -0
- data/app/presenters/has_helpers/search_presenter.rb +230 -0
- data/app/triggers/has_helpers/erase_denormalized_belongs_to_column_trigger.rb +85 -0
- data/app/triggers/has_helpers/limit_favorites.rb +97 -0
- data/app/utils/has_helpers/redis_configurable.rb +79 -0
- data/app/utils/has_helpers/sync_utils.rb +234 -0
- data/app/views/has_helpers/actions/_action.html.erb +31 -0
- data/app/views/has_helpers/alerts/_alert.html.erb +3 -0
- data/app/views/has_helpers/barcodes.html.erb +36 -0
- data/app/views/has_helpers/containers/_container.html.erb +3 -0
- data/app/views/has_helpers/email_notifier/system.html.erb +8 -0
- data/app/views/has_helpers/email_notifier/system.text.erb +7 -0
- data/app/views/has_helpers/enumerables/_enumerable.html.erb +1 -0
- data/app/views/has_helpers/icons/_icon.html.erb +1 -0
- data/app/views/has_helpers/render_objects/_render_object.html.erb +1 -0
- data/app/views/has_helpers/services/_service.html.erb +1 -0
- data/app/views/has_helpers/text_nodes/_text_node.html.erb +4 -0
- data/app/views/has_helpers/ui_attributes/_html_attrs.html.erb +1 -0
- data/app/workers/elasticsearch_process_queue.rb +11 -0
- data/app/workers/elasticsearch_reindex.rb +11 -0
- data/app/workers/elasticsearch_reindex_scheduler.rb +35 -0
- data/app/workers/good_job/configurable_backfill.rb +64 -0
- data/app/workers/good_job/cron_denormalization_stats_manager.rb +39 -0
- data/app/workers/good_job/denormalization_stats_worker.rb +57 -0
- data/app/workers/good_job/dn_update_worker.rb +30 -0
- data/app/workers/good_job/dn_worker.rb +47 -0
- data/app/workers/good_job/elasticsearch_old_index_cleaner_worker.rb +113 -0
- data/app/workers/good_job/elasticsearch_on_finish_reindex.rb +26 -0
- data/app/workers/good_job/elasticsearch_process_batch.rb +78 -0
- data/app/workers/good_job/elasticsearch_process_change_logs_master_worker.rb +27 -0
- data/app/workers/good_job/elasticsearch_process_change_logs_worker.rb +155 -0
- data/app/workers/good_job/elasticsearch_process_queue.rb +50 -0
- data/app/workers/good_job/elasticsearch_reindex.rb +31 -0
- data/app/workers/has_helpers/cards/orphan_card_sweeper.rb +28 -0
- data/app/workers/has_helpers/db_operations/clean_stats_worker.rb +42 -0
- data/app/workers/has_helpers/db_operations/helper_worker.rb +131 -0
- data/app/workers/has_helpers/db_operations/main_worker.rb +164 -0
- data/app/workers/has_helpers/db_operations/operations.rb +134 -0
- data/app/workers/has_helpers/db_operations/update_operations_stats_resume_worker.rb +35 -0
- data/app/workers/has_helpers/default_worker.rb +17 -0
- data/app/workers/has_helpers/good_job/notification_alert_cleanup_worker.rb +43 -0
- data/app/workers/has_helpers/service_trust/audit_log_cleanup_worker.rb +19 -0
- data/app/workers/sidekiq/elasticsearch_old_index_cleaner_worker.rb +112 -0
- data/app/workers/sidekiq/elasticsearch_process_batch.rb +87 -0
- data/app/workers/sidekiq/elasticsearch_process_queue.rb +40 -0
- data/app/workers/sidekiq/elasticsearch_reindex.rb +53 -0
- data/config/initializers/action_view.rb +7 -0
- data/config/initializers/inflections.rb +24 -0
- data/config/initializers/marginalia.rb +8 -0
- data/config/initializers/mime_types.rb +6 -0
- data/config/initializers/opentelemetry.rb +12 -0
- data/config/initializers/redis_conf.rb +40 -0
- data/config/initializers/searchkick.rb +9 -0
- data/config/initializers/will_paginate.rb +39 -0
- data/config/redis.yml +12 -0
- data/config/routes.rb +27 -0
- data/config/service_trust_routes.rb +5 -0
- data/config.ru +9 -0
- data/db/migrate/20230127194725_create_organization_table.rb +25 -0
- data/db/migrate/20230127194726_validate_organization_foreign_keys.rb +7 -0
- data/db/migrate/20230206141413_create_security_models.rb +42 -0
- data/db/migrate/20230206141513_create_restriction_model.rb +20 -0
- data/db/migrate/20230206142613_add_security_indexes.rb +14 -0
- data/db/migrate/20230206143513_security_model_constants_seed.rb +8 -0
- data/db/migrate/20230206161513_add_organization_to_security_models.rb +13 -0
- data/db/migrate/20230209121019_create_search_group_models.rb +36 -0
- data/db/migrate/20230209122919_add_search_group_indexes.rb +21 -0
- data/db/migrate/20230209181832_add_search_group_resources_type_constraint_trigger.rb +61 -0
- data/db/migrate/20230313220337_create_table_user_sessions.rb +31 -0
- data/db/migrate/20230517141106_update_resource_types.rb +12 -0
- data/db/migrate/20230518194611_add_resources_contraints.rb +17 -0
- data/db/migrate/20230518194642_create_nested_resource_model.rb +21 -0
- data/db/migrate/20230518194645_validate_security_models_constraints.rb +23 -0
- data/db/migrate/20230608125037_add_inactive_field_to_user_sessions.rb +7 -0
- data/db/migrate/20230919191525_add_parent_id_to_resources.rb +8 -0
- data/db/migrate/20230919191535_validate_parent_id_fk_resources.rb +7 -0
- data/db/migrate/20230919210456_drop_nested_resources.rb +7 -0
- data/db/migrate/20231222135620_add_user_role.rb +19 -0
- data/db/migrate/20240108172021_create_favorites_table.rb +20 -0
- data/db/migrate/20240108190209_create_organization_link_table_if_doesnt_exists.rb +31 -0
- data/db/migrate/20240109211445_validate_fks_org_links.rb +9 -0
- data/db/migrate/20240110135440_create_recently_vieweds.rb +20 -0
- data/db/migrate/20240112185312_link_type_options_seed.rb +7 -0
- data/db/migrate/20240116173201_organization_link_resource_tab_constraints.rb +19 -0
- data/db/migrate/20240131160949_add_user_organizations_table.rb +32 -0
- data/db/migrate/20240212132519_add_organization_in_user_sessions.rb +13 -0
- data/db/migrate/20240227135052_add_user_organization_composite_fks.rb +32 -0
- data/db/migrate/20240312193501_update_organization_links.rb +11 -0
- data/db/migrate/20240426185902_add_user_organizations_current_organization_column.rb +12 -0
- data/db/migrate/20240429214601_add_primary_trigger.rb +10 -0
- data/db/migrate/20241112131819_add_json_file_format_seed.rb +7 -0
- data/db/migrate/20250520133417_create_restriction_operations.rb +18 -0
- data/db/migrate/20250520133420_validate_create_restriction_operations.rb +8 -0
- data/db/migrate/20250520140642_add_indexes_to_restrictions.rb +9 -0
- data/db/migrate/20250529130741_create_notification_alerts_table.rb +28 -0
- data/db/migrate/20250530223332_create_data_migrations_table.rb +19 -0
- data/db/migrate/20250605121622_seed_notification_alert_status.rb +7 -0
- data/db/migrate/20250605131439_seed_notification_alert_types.rb +7 -0
- data/db/migrate/20250606173128_update_notification_alert_length_constraint.rb +18 -0
- data/db/migrate/20250606174043_validate_notification_alert_length_constraints.rb +8 -0
- data/db/migrate/20250624183000_add_data_import_alert_type.rb +11 -0
- data/db/migrate/20250722183935_create_organization_options_table.rb +38 -0
- data/db/migrate/20250722192724_validate_organization_options_fks_and_constraints.rb +32 -0
- data/db/migrate/20250722192936_add_indexes_to_organization_options_table.rb +15 -0
- data/db/migrate/20250915132448_add_column_restrictions_hash.rb +7 -0
- data/db/migrate/20250917143211_add_column_permissions_hash.rb +7 -0
- data/db/migrate/20251001145950_add_trust_this_device.rb +7 -0
- data/db/migrate/20251021130835_add_backend_urls_to_applications_table.rb +13 -0
- data/db/migrate/20251021172715_validate_backend_urls_constraints_in_applications.rb +9 -0
- data/db/migrate/20251030200020_create_card_definitions.rb +23 -0
- data/db/migrate/20251030201012_create_card_definition_resources.rb +24 -0
- data/db/migrate/20251030201236_create_card_fields.rb +34 -0
- data/db/migrate/20251103194317_create_role_resource_cards.rb +37 -0
- data/db/migrate/20251103194426_create_role_card_fields.rb +34 -0
- data/db/migrate/20251107173218_validate_columns_from_cards.rb +17 -0
- data/db/migrate/20251111173212_add_column_application_array.rb +7 -0
- data/db/migrate/20251112203326_add_card_types.rb +10 -0
- data/db/migrate/20251115120000_add_virtual_cards_support.rb +28 -0
- data/db/migrate/20251202231256_add_org_scoped_fk_role_resource_cards_to_roles.rb +14 -0
- data/db/migrate/20260105134504_add_accessor_field_resource_type.rb +7 -0
- data/db/migrate/20260115123000_add_default_visibility_to_card_definitions.rb +9 -0
- data/db/migrate/20260120175037_is_new_admin.rb +9 -0
- data/db/migrate/20260122392008_add_stats_table.rb +17 -0
- data/db/migrate/20260414204718_add_index_to_stats_table.rb +10 -0
- data/db/migrate/20260509120000_create_service_trust_keys.rb +25 -0
- data/db/migrate/20260509120001_create_service_trust_audit_logs.rb +26 -0
- data/db/migrate/20260511120000_add_direction_to_service_trust_audit_logs.rb +17 -0
- data/db/migrate/20260511121000_remove_role_fk_from_role_resource_cards.rb +19 -0
- data/db/migrate/20260511121001_add_disabled_at_to_role_resource_cards.rb +10 -0
- data/db/migrate/20260605120100_seed_hover_card_options.rb +8 -0
- data/db/migrate/20260608120000_seed_badge_list_card_data_type.rb +7 -0
- data/db/migrate/20260727000000_add_two_factor_trust_id_to_user_sessions.rb +11 -0
- data/db/seeds.rb +14 -0
- data/docs/Elasticsearch.md +23 -0
- data/docs/Search_API.md +126 -0
- data/docs/cards_role_proxy.md +107 -0
- data/docs/factory_api.md +135 -0
- data/has-helpers-pr-289-pipeline.yaml +16 -0
- data/has-helpers-pr-298-pipeline.yaml +15 -0
- data/has_helpers.gemspec +68 -0
- data/lib/active_record/migration/active_migration_patch.rb +123 -0
- data/lib/generators/has_helpers/install/USAGE +8 -0
- data/lib/generators/has_helpers/install/install_generator.rb +22 -0
- data/lib/generators/has_helpers/install/templates/migrate/has_helpers_initial_setup.rb +26 -0
- data/lib/has_helpers/active_record/base.rb +50 -0
- data/lib/has_helpers/active_record/date_range.rb +228 -0
- data/lib/has_helpers/active_record/nested_resource.rb +58 -0
- data/lib/has_helpers/active_record/validation.rb +63 -0
- data/lib/has_helpers/active_record.rb +520 -0
- data/lib/has_helpers/app/command.rb +107 -0
- data/lib/has_helpers/app.rb +10 -0
- data/lib/has_helpers/assets_precompile_helper.rb +20 -0
- data/lib/has_helpers/attributes.rb +196 -0
- data/lib/has_helpers/aws.rb +67 -0
- data/lib/has_helpers/cards/cache.rb +49 -0
- data/lib/has_helpers/cards/config.rb +42 -0
- data/lib/has_helpers/cards/remote_role.rb +27 -0
- data/lib/has_helpers/cards/remote_roles.rb +75 -0
- data/lib/has_helpers/cards/roles.rb +55 -0
- data/lib/has_helpers/cards.rb +34 -0
- data/lib/has_helpers/concerns/hydrate_concern.rb +76 -0
- data/lib/has_helpers/concerns/importable_accessors.rb +25 -0
- data/lib/has_helpers/concerns/restrictions_parseable.rb +51 -0
- data/lib/has_helpers/config.rb +11 -0
- data/lib/has_helpers/constant.rb +578 -0
- data/lib/has_helpers/constraints.rb +10 -0
- data/lib/has_helpers/core_ext/array.rb +28 -0
- data/lib/has_helpers/core_ext/date/infinity.rb +25 -0
- data/lib/has_helpers/core_ext/date.rb +39 -0
- data/lib/has_helpers/core_ext/enumerable.rb +20 -0
- data/lib/has_helpers/core_ext/false_class.rb +14 -0
- data/lib/has_helpers/core_ext/float.rb +18 -0
- data/lib/has_helpers/core_ext/hash.rb +71 -0
- data/lib/has_helpers/core_ext/integer.rb +18 -0
- data/lib/has_helpers/core_ext/nil_class.rb +30 -0
- data/lib/has_helpers/core_ext/object.rb +60 -0
- data/lib/has_helpers/core_ext/proc.rb +16 -0
- data/lib/has_helpers/core_ext/string.rb +81 -0
- data/lib/has_helpers/core_ext/symbol.rb +17 -0
- data/lib/has_helpers/core_ext/true_class.rb +14 -0
- data/lib/has_helpers/cursor.rb +46 -0
- data/lib/has_helpers/data_migration/base.rb +293 -0
- data/lib/has_helpers/data_migration/large_worker.rb +20 -0
- data/lib/has_helpers/data_migration/master_worker.rb +33 -0
- data/lib/has_helpers/data_migration/migrator.rb +83 -0
- data/lib/has_helpers/data_migration/model.rb +18 -0
- data/lib/has_helpers/data_migration/small_worker.rb +20 -0
- data/lib/has_helpers/data_migration.rb +8 -0
- data/lib/has_helpers/engine.rb +47 -0
- data/lib/has_helpers/extensions.rb +17 -0
- data/lib/has_helpers/factory_api.rb +47 -0
- data/lib/has_helpers/factory_scenario/definition.rb +33 -0
- data/lib/has_helpers/factory_scenario/dsl.rb +80 -0
- data/lib/has_helpers/factory_scenario/factory_bot_override.rb +53 -0
- data/lib/has_helpers/factory_scenario/registry.rb +31 -0
- data/lib/has_helpers/factory_scenario/runner.rb +285 -0
- data/lib/has_helpers/factory_scenario.rb +27 -0
- data/lib/has_helpers/fixed_expiration_time_cache_session_store.rb +20 -0
- data/lib/has_helpers/flattener/association.rb +140 -0
- data/lib/has_helpers/flattener/attribute.rb +42 -0
- data/lib/has_helpers/flattener/base.rb +428 -0
- data/lib/has_helpers/flattener/column/types.rb +71 -0
- data/lib/has_helpers/flattener/column.rb +97 -0
- data/lib/has_helpers/flattener/pivot.rb +49 -0
- data/lib/has_helpers/flattener/reset.rb +33 -0
- data/lib/has_helpers/flattener/resource.rb +114 -0
- data/lib/has_helpers/flattener/resource_generator.rb +21 -0
- data/lib/has_helpers/flattener/utils.rb +14 -0
- data/lib/has_helpers/flattener.rb +53 -0
- data/lib/has_helpers/hash_accessors.rb +62 -0
- data/lib/has_helpers/helpers/application.rb +291 -0
- data/lib/has_helpers/index/config.rb +15 -0
- data/lib/has_helpers/lazy_config.rb +61 -0
- data/lib/has_helpers/resource_importer.rb +260 -0
- data/lib/has_helpers/resources.rb +127 -0
- data/lib/has_helpers/rspec/constants.rb +61 -0
- data/lib/has_helpers/rspec/factories.rb +11 -0
- data/lib/has_helpers/rspec.rb +22 -0
- data/lib/has_helpers/sentry/graphql_trace.rb +37 -0
- data/lib/has_helpers/serializer.rb +41 -0
- data/lib/has_helpers/service_trust/audit.rb +45 -0
- data/lib/has_helpers/service_trust/authorization.rb +34 -0
- data/lib/has_helpers/service_trust/bearer_token.rb +23 -0
- data/lib/has_helpers/service_trust/capability.rb +32 -0
- data/lib/has_helpers/service_trust/client/response.rb +13 -0
- data/lib/has_helpers/service_trust/client/retry.rb +35 -0
- data/lib/has_helpers/service_trust/client.rb +128 -0
- data/lib/has_helpers/service_trust/configuration.rb +76 -0
- data/lib/has_helpers/service_trust/engine.rb +10 -0
- data/lib/has_helpers/service_trust/errors.rb +32 -0
- data/lib/has_helpers/service_trust/graphql/authorizable.rb +72 -0
- data/lib/has_helpers/service_trust/graphql_client.rb +54 -0
- data/lib/has_helpers/service_trust/jwks_client.rb +133 -0
- data/lib/has_helpers/service_trust/jwt_codec.rb +45 -0
- data/lib/has_helpers/service_trust/key_manager.rb +160 -0
- data/lib/has_helpers/service_trust/rate_limiter.rb +27 -0
- data/lib/has_helpers/service_trust/replay_cache.rb +26 -0
- data/lib/has_helpers/service_trust/signer.rb +47 -0
- data/lib/has_helpers/service_trust/test.rb +60 -0
- data/lib/has_helpers/service_trust/verifier.rb +85 -0
- data/lib/has_helpers/service_trust/verify_middleware.rb +55 -0
- data/lib/has_helpers/service_trust.rb +51 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_addresses.rb +42 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_advisors.rb +272 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_emails.rb +37 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_organizations.rb +48 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_people.rb +34 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_phones.rb +41 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_states.rb +26 -0
- data/lib/has_helpers/test_helpers/factory_data/unit_webs.rb +31 -0
- data/lib/has_helpers/test_helpers/shoulda_helpers.rb +249 -0
- data/lib/has_helpers/ui_attributes.rb +39 -0
- data/lib/has_helpers/utils/action.rb +87 -0
- data/lib/has_helpers/utils/active_record_utils.rb +40 -0
- data/lib/has_helpers/utils/alert.rb +42 -0
- data/lib/has_helpers/utils/container.rb +46 -0
- data/lib/has_helpers/utils/dataset.rb +34 -0
- data/lib/has_helpers/utils/email_notifier.rb +15 -0
- data/lib/has_helpers/utils/icon.rb +66 -0
- data/lib/has_helpers/utils/input.rb +45 -0
- data/lib/has_helpers/utils/log_notifier.rb +79 -0
- data/lib/has_helpers/utils/render_object.rb +32 -0
- data/lib/has_helpers/utils/s3_utils.rb +37 -0
- data/lib/has_helpers/utils/service.rb +87 -0
- data/lib/has_helpers/utils/slack_conf.rb +31 -0
- data/lib/has_helpers/utils/slack_notifier.rb +42 -0
- data/lib/has_helpers/utils/text_node.rb +34 -0
- data/lib/has_helpers/utils/user_restrictions.rb +266 -0
- data/lib/has_helpers/utils.rb +19 -0
- data/lib/has_helpers/version.rb +6 -0
- data/lib/has_helpers/web_socket_publisher.rb +20 -0
- data/lib/has_helpers/webhook.rb +17 -0
- data/lib/has_helpers.rb +63 -0
- data/lib/hq/resource.rb +55 -0
- data/lib/hq/structure/inserts.rb +176 -0
- data/lib/hq/structure.rb +115 -0
- data/lib/tasks/has_helpers/service_trust.rake +39 -0
- data/script/rails +10 -0
- data/sorbet/config +5 -0
- data/sorbet/rbi/gems/actionpack.rbi +1361 -0
- data/sorbet/rbi/gems/activemodel.rbi +755 -0
- data/sorbet/rbi/gems/activerecord.rbi +4713 -0
- data/sorbet/rbi/gems/activesupport.rbi +1970 -0
- data/sorbet/rbi/gems/barby.rbi +129 -0
- data/sorbet/rbi/gems/better_errors.rbi +155 -0
- data/sorbet/rbi/gems/binding_of_caller.rbi +22 -0
- data/sorbet/rbi/gems/byebug.rbi +19 -0
- data/sorbet/rbi/gems/chronic.rbi +480 -0
- data/sorbet/rbi/gems/chunky_png.rbi +510 -0
- data/sorbet/rbi/gems/climate_control.rbi +45 -0
- data/sorbet/rbi/gems/coderay.rbi +91 -0
- data/sorbet/rbi/gems/concurrent-ruby.rbi +1587 -0
- data/sorbet/rbi/gems/debug_inspector.rbi +18 -0
- data/sorbet/rbi/gems/erubi.rbi +26 -0
- data/sorbet/rbi/gems/holidays.rbi +362 -0
- data/sorbet/rbi/gems/i18n.rbi +132 -0
- data/sorbet/rbi/gems/method_source.rbi +63 -0
- data/sorbet/rbi/gems/mime-types-data.rbi +16 -0
- data/sorbet/rbi/gems/mime-types.rbi +217 -0
- data/sorbet/rbi/gems/mimemagic.rbi +36 -0
- data/sorbet/rbi/gems/minitest.rbi +282 -0
- data/sorbet/rbi/gems/mustache.rbi +151 -0
- data/sorbet/rbi/gems/paperclip.rbi +644 -0
- data/sorbet/rbi/gems/parallel.rbi +81 -0
- data/sorbet/rbi/gems/parallel_tests.rbi +27 -0
- data/sorbet/rbi/gems/pry-rails.rbi +71 -0
- data/sorbet/rbi/gems/pry.rbi +1964 -0
- data/sorbet/rbi/gems/rack.rbi +228 -0
- data/sorbet/rbi/gems/railties.rbi +364 -0
- data/sorbet/rbi/gems/rspec-core.rbi +1704 -0
- data/sorbet/rbi/gems/rspec-expectations.rbi +389 -0
- data/sorbet/rbi/gems/rspec-support.rbi +206 -0
- data/sorbet/rbi/gems/rspec.rbi +14 -0
- data/sorbet/rbi/gems/spreadsheet_architect.rbi +10 -0
- data/sorbet/rbi/gems/sprockets.rbi +659 -0
- data/sorbet/rbi/gems/terrapin.rbi +115 -0
- data/sorbet/rbi/gems/testhq.rbi +14 -0
- data/sorbet/rbi/gems/thor.rbi +482 -0
- data/sorbet/rbi/gems/thread_safe.rbi +81 -0
- data/sorbet/rbi/gems/tzinfo.rbi +407 -0
- data/sorbet/rbi/gems/zeitwerk.rbi +131 -0
- data/sorbet/rbi/hidden-definitions/errors.txt +35111 -0
- data/sorbet/rbi/hidden-definitions/hidden.rbi +59974 -0
- data/sorbet/rbi/sorbet-typed/lib/actionmailer/all/actionmailer.rbi +13 -0
- data/sorbet/rbi/sorbet-typed/lib/actionpack/all/actionpack.rbi +855 -0
- data/sorbet/rbi/sorbet-typed/lib/actionview/all/actionview.rbi +314 -0
- data/sorbet/rbi/sorbet-typed/lib/activemodel/all/activemodel.rbi +423 -0
- data/sorbet/rbi/sorbet-typed/lib/activerecord/>=5.2/activerecord.rbi +17 -0
- data/sorbet/rbi/sorbet-typed/lib/activerecord/all/activerecord.rbi +1415 -0
- data/sorbet/rbi/sorbet-typed/lib/activerecord/~>6.0.0/activerecord.rbi +475 -0
- data/sorbet/rbi/sorbet-typed/lib/activestorage/>=6.0.0/activestorage.rbi +19 -0
- data/sorbet/rbi/sorbet-typed/lib/activestorage/all/activestorage.rbi +173 -0
- data/sorbet/rbi/sorbet-typed/lib/activesupport/>=6.0.0.rc1/activesupport.rbi +23 -0
- data/sorbet/rbi/sorbet-typed/lib/activesupport/all/activesupport.rbi +625 -0
- data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +8684 -0
- data/sorbet/rbi/sorbet-typed/lib/minitest/all/minitest.rbi +99 -0
- data/sorbet/rbi/sorbet-typed/lib/railties/all/railties.rbi +25 -0
- data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +254 -0
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/gem.rbi +4222 -0
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +111 -0
- data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +543 -0
- data/sorbet/rbi/todo.rbi +244 -0
- data/spec/commands/cards/available_cards_command_spec.rb +90 -0
- data/spec/commands/cards/card_data_command_spec.rb +1048 -0
- data/spec/constructors/has_helpers/base_constructor_spec.rb +49 -0
- data/spec/controllers/has_helpers/controller_spec.rb +71 -0
- data/spec/controllers/has_helpers/xlsx_export_spec.rb +96 -0
- data/spec/factories/advisors.rb +20 -0
- data/spec/factories/anonymous_cowards.rb +5 -0
- data/spec/factories/applications.rb +5 -0
- data/spec/factories/appointments.rb +6 -0
- data/spec/factories/attachment.rb +15 -0
- data/spec/factories/audits.rb +5 -0
- data/spec/factories/basic_schedules.rb +12 -0
- data/spec/factories/cards/card_definition_resources.rb +7 -0
- data/spec/factories/cards/card_definitions.rb +11 -0
- data/spec/factories/cards/card_fields.rb +22 -0
- data/spec/factories/cards/role_card_fields.rb +17 -0
- data/spec/factories/cards/role_resource_cards.rb +12 -0
- data/spec/factories/constant_definition.rb +7 -0
- data/spec/factories/contracts.rb +10 -0
- data/spec/factories/data_migration_models.rb +9 -0
- data/spec/factories/demographics.rb +22 -0
- data/spec/factories/events.rb +21 -0
- data/spec/factories/firms.rb +21 -0
- data/spec/factories/imports.rb +9 -0
- data/spec/factories/meetups.rb +13 -0
- data/spec/factories/organizations.rb +13 -0
- data/spec/factories/policies.rb +7 -0
- data/spec/factories/policy_notes.rb +9 -0
- data/spec/factories/read_only_values.rb +7 -0
- data/spec/factories/resources.rb +7 -0
- data/spec/factories/roles.rb +11 -0
- data/spec/factories/schedules.rb +13 -0
- data/spec/factories/shared/flattener/attributes.rb +10 -0
- data/spec/factories/shared/flattener/columns.rb +6 -0
- data/spec/factories/shared/flattener/resources.rb +5 -0
- data/spec/factories/templates.rb +12 -0
- data/spec/factories/user_organizations.rb +23 -0
- data/spec/factories/user_session.rb +11 -0
- data/spec/factories/users.rb +55 -0
- data/spec/fixtures/sample_config.yml +3 -0
- data/spec/graphql/has_helpers/mutations/external_data_sync_spec.rb +101 -0
- data/spec/graphql/has_helpers/resolvers/cards/available_cards_spec.rb +36 -0
- data/spec/graphql/has_helpers/resolvers/cards/card_data_spec.rb +42 -0
- data/spec/graphql/has_helpers/resolvers/cards/hover_card_data_spec.rb +59 -0
- data/spec/has_helpers/cards/cache_spec.rb +58 -0
- data/spec/has_helpers/cards/config_spec.rb +68 -0
- data/spec/has_helpers/cards/orphan_card_sweeper_spec.rb +97 -0
- data/spec/has_helpers/cards/remote_role_spec.rb +17 -0
- data/spec/has_helpers/cards/remote_roles_spec.rb +97 -0
- data/spec/has_helpers/cards/roles_spec.rb +52 -0
- data/spec/has_helpers/cards/smoke_spec.rb +168 -0
- data/spec/has_helpers/cards/support.rb +52 -0
- data/spec/has_helpers/resolvers/internal/roles_spec.rb +73 -0
- data/spec/has_helpers/service_trust/audit_log_cleanup_worker_spec.rb +33 -0
- data/spec/has_helpers/service_trust/audit_spec.rb +70 -0
- data/spec/has_helpers/service_trust/authenticated_as_service_spec.rb +65 -0
- data/spec/has_helpers/service_trust/authorization_spec.rb +37 -0
- data/spec/has_helpers/service_trust/capability_spec.rb +35 -0
- data/spec/has_helpers/service_trust/client_spec.rb +129 -0
- data/spec/has_helpers/service_trust/configuration_spec.rb +35 -0
- data/spec/has_helpers/service_trust/engine_spec.rb +13 -0
- data/spec/has_helpers/service_trust/errors_spec.rb +23 -0
- data/spec/has_helpers/service_trust/graphql_authentication_spec.rb +59 -0
- data/spec/has_helpers/service_trust/graphql_authorizable_spec.rb +91 -0
- data/spec/has_helpers/service_trust/graphql_client_spec.rb +47 -0
- data/spec/has_helpers/service_trust/jwks_client_spec.rb +85 -0
- data/spec/has_helpers/service_trust/jwks_controller_spec.rb +47 -0
- data/spec/has_helpers/service_trust/jwt_codec_spec.rb +30 -0
- data/spec/has_helpers/service_trust/key_manager_spec.rb +80 -0
- data/spec/has_helpers/service_trust/key_spec.rb +52 -0
- data/spec/has_helpers/service_trust/rake_tasks_spec.rb +58 -0
- data/spec/has_helpers/service_trust/rate_limiter_spec.rb +31 -0
- data/spec/has_helpers/service_trust/replay_cache_spec.rb +29 -0
- data/spec/has_helpers/service_trust/signer_verifier_spec.rb +159 -0
- data/spec/has_helpers/service_trust/smoke_spec.rb +216 -0
- data/spec/has_helpers/service_trust/support.rb +121 -0
- data/spec/has_helpers/service_trust/test_module_spec.rb +39 -0
- data/spec/has_helpers/service_trust/verify_middleware_spec.rb +82 -0
- data/spec/helpers/application_spec.rb +155 -0
- data/spec/importers/cards/importer_spec.rb +861 -0
- data/spec/internal/app/assets/config/manifest.js +0 -0
- data/spec/internal/app/constants/cat_egory.rb +33 -0
- data/spec/internal/app/constants/color.rb +19 -0
- data/spec/internal/app/constants/exclude_group_nil_constant.rb +5 -0
- data/spec/internal/app/constants/foo.rb +6 -0
- data/spec/internal/app/constants/fruit.rb +3 -0
- data/spec/internal/app/constants/generic_group_example.rb +12 -0
- data/spec/internal/app/constants/my_constant.rb +4 -0
- data/spec/internal/app/constants/product.rb +3 -0
- data/spec/internal/app/constants/shirt_size.rb +18 -0
- data/spec/internal/app/constants/simple_option.rb +14 -0
- data/spec/internal/app/constants/status.rb +5 -0
- data/spec/internal/app/constructors/organization_constructor.rb +4 -0
- data/spec/internal/app/controllers/application_controller.rb +27 -0
- data/spec/internal/app/controllers/search_extensions.rb +3 -0
- data/spec/internal/app/indices/advisor_index.rb +35 -0
- data/spec/internal/app/indices/contract_index.rb +52 -0
- data/spec/internal/app/indices/firm_index.rb +16 -0
- data/spec/internal/app/indices/policy_index.rb +20 -0
- data/spec/internal/app/models/advisor.rb +24 -0
- data/spec/internal/app/models/agency.rb +3 -0
- data/spec/internal/app/models/anonymous_coward.rb +5 -0
- data/spec/internal/app/models/another_event.rb +2 -0
- data/spec/internal/app/models/appointment.rb +3 -0
- data/spec/internal/app/models/attachment.rb +10 -0
- data/spec/internal/app/models/basic_schedule.rb +6 -0
- data/spec/internal/app/models/beneficiary.rb +6 -0
- data/spec/internal/app/models/client.rb +5 -0
- data/spec/internal/app/models/contract.rb +9 -0
- data/spec/internal/app/models/demographic.rb +4 -0
- data/spec/internal/app/models/division.rb +3 -0
- data/spec/internal/app/models/event.rb +10 -0
- data/spec/internal/app/models/event_template.rb +1 -0
- data/spec/internal/app/models/firm.rb +18 -0
- data/spec/internal/app/models/has_audits/audit.rb +15 -0
- data/spec/internal/app/models/import.rb +7 -0
- data/spec/internal/app/models/meetup.rb +8 -0
- data/spec/internal/app/models/organization.rb +17 -0
- data/spec/internal/app/models/organization_event.rb +5 -0
- data/spec/internal/app/models/policy.rb +16 -0
- data/spec/internal/app/models/policy_holder.rb +7 -0
- data/spec/internal/app/models/policy_note.rb +8 -0
- data/spec/internal/app/models/project.rb +3 -0
- data/spec/internal/app/models/read_only_value.rb +8 -0
- data/spec/internal/app/models/schedule.rb +12 -0
- data/spec/internal/app/models/template.rb +9 -0
- data/spec/internal/app/models/user.rb +31 -0
- data/spec/internal/app/models/user_organization.rb +17 -0
- data/spec/internal/app/views/has_lists/lists/_table.html.erb +1 -0
- data/spec/internal/config/database-standard.yml +24 -0
- data/spec/internal/config/initializers/resources.rb +19 -0
- data/spec/internal/config/routes.rb +19 -0
- data/spec/internal/config/storage.yml +3 -0
- data/spec/internal/db/good_job_schema.rb +141 -0
- data/spec/internal/db/schema.rb +334 -0
- data/spec/internal/log/.gitignore +1 -0
- data/spec/internal/public/favicon.ico +0 -0
- data/spec/lib/flattener/base_spec.rb +93 -0
- data/spec/lib/flattener/column/types_spec.rb +65 -0
- data/spec/lib/has_helpers/active_record/base_spec.rb +53 -0
- data/spec/lib/has_helpers/active_record/date_range_spec.rb +498 -0
- data/spec/lib/has_helpers/active_record/lookup_model_spec.rb +408 -0
- data/spec/lib/has_helpers/active_record/nested_resource_spec.rb +43 -0
- data/spec/lib/has_helpers/app/command/reports/generator_specs.rb +887 -0
- data/spec/lib/has_helpers/app/command_spec.rb +117 -0
- data/spec/lib/has_helpers/app/support/mocks/graphql_reports_helpers.rb +96 -0
- data/spec/lib/has_helpers/app/support/mocks/reports_api_mock.rb +47 -0
- data/spec/lib/has_helpers/attributes_spec.rb +220 -0
- data/spec/lib/has_helpers/aws_spec.rb +126 -0
- data/spec/lib/has_helpers/concerns/hydrate_concern_spec.rb +473 -0
- data/spec/lib/has_helpers/constant/definition_spec.rb +128 -0
- data/spec/lib/has_helpers/constant_spec.rb +432 -0
- data/spec/lib/has_helpers/core_ext/array_spec.rb +22 -0
- data/spec/lib/has_helpers/core_ext/date/infinity_spec.rb +67 -0
- data/spec/lib/has_helpers/core_ext/date_spec.rb +27 -0
- data/spec/lib/has_helpers/core_ext/false_class_spec.rb +7 -0
- data/spec/lib/has_helpers/core_ext/float_spec.rb +9 -0
- data/spec/lib/has_helpers/core_ext/hash_spec.rb +48 -0
- data/spec/lib/has_helpers/core_ext/integer_spec.rb +9 -0
- data/spec/lib/has_helpers/core_ext/nil_class_spec.rb +17 -0
- data/spec/lib/has_helpers/core_ext/object_spec.rb +54 -0
- data/spec/lib/has_helpers/core_ext/proc_spec.rb +24 -0
- data/spec/lib/has_helpers/core_ext/string_spec.rb +154 -0
- data/spec/lib/has_helpers/core_ext/true_class_spec.rb +7 -0
- data/spec/lib/has_helpers/data_migration/base_spec.rb +470 -0
- data/spec/lib/has_helpers/data_migration/large_worker_spec.rb +34 -0
- data/spec/lib/has_helpers/data_migration/master_worker_spec.rb +51 -0
- data/spec/lib/has_helpers/data_migration/migrator_spec.rb +113 -0
- data/spec/lib/has_helpers/data_migration/model_spec.rb +52 -0
- data/spec/lib/has_helpers/data_migration/small_worker_spec.rb +34 -0
- data/spec/lib/has_helpers/good_job/notification_alert_cleanup_worker_spec.rb +89 -0
- data/spec/lib/has_helpers/hash_accessors_spec.rb +66 -0
- data/spec/lib/has_helpers/index_spec.rb +442 -0
- data/spec/lib/has_helpers/lazy_config_spec.rb +31 -0
- data/spec/lib/has_helpers/organization_merge_spec.rb +88 -0
- data/spec/lib/has_helpers/resource_importer_spec.rb +119 -0
- data/spec/lib/has_helpers/rspec_spec.rb +16 -0
- data/spec/lib/has_helpers/search/provider_spec.rb +26 -0
- data/spec/lib/has_helpers/search/search_utils_spec.rb +134 -0
- data/spec/lib/has_helpers/sentry/graphql_trace_spec.rb +140 -0
- data/spec/lib/has_helpers/ui_attributes_spec.rb +42 -0
- data/spec/lib/has_helpers/utils/action_spec.rb +49 -0
- data/spec/lib/has_helpers/utils/alert_spec.rb +33 -0
- data/spec/lib/has_helpers/utils/dataset_spec.rb +10 -0
- data/spec/lib/has_helpers/utils/icon_spec.rb +23 -0
- data/spec/lib/has_helpers/utils/input_spec.rb +40 -0
- data/spec/lib/has_helpers/utils/log_notifier_spec.rb +25 -0
- data/spec/lib/has_helpers/utils/render_object_spec.rb +17 -0
- data/spec/lib/has_helpers/utils/s3_utils_spec.rb +78 -0
- data/spec/lib/has_helpers/utils/service_spec.rb +79 -0
- data/spec/lib/has_helpers/utils/slack_notifier_spec.rb +70 -0
- data/spec/lib/has_helpers/utils/user_restrictions_spec.rb +343 -0
- data/spec/lib/hq/structure/inserts_spec.rb +152 -0
- data/spec/lib/hq/structure_spec.rb +12 -0
- data/spec/models/cards/base_spec.rb +149 -0
- data/spec/models/cards/card_definition_resource_spec.rb +103 -0
- data/spec/models/cards/card_definition_spec.rb +50 -0
- data/spec/models/cards/card_field_spec.rb +46 -0
- data/spec/models/cards/field_builder_spec.rb +318 -0
- data/spec/models/cards/registry_spec.rb +147 -0
- data/spec/models/cards/role_card_field_spec.rb +111 -0
- data/spec/models/cards/role_resource_card_spec.rb +112 -0
- data/spec/models/has_helpers/master_record_spec.rb +20 -0
- data/spec/models/has_helpers/user_organization_spec.rb +28 -0
- data/spec/models/has_helpers/user_session_spec.rb +16 -0
- data/spec/presenters/base_presenter_spec.rb +166 -0
- data/spec/rails_helper.rb +119 -0
- data/spec/requests/has_helpers/factory_controller_spec.rb +159 -0
- data/spec/requests/has_helpers/health_check_controller_spec.rb +37 -0
- data/spec/scenarios/empty_setup.rb +6 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/has_helpers/database_helpers.rb +12 -0
- data/spec/utils/sync_utils_spec.rb +233 -0
- data/spec/workers/goodjob/elasticsearch_process_batch_spec.rb +94 -0
- data/spec/workers/goodjob/elasticsearch_process_change_logs_master_worker_spec.rb +40 -0
- data/spec/workers/goodjob/elasticsearch_process_change_logs_worker_spec.rb +82 -0
- data/spec/workers/goodjob/elasticsearch_process_queue_spec.rb +89 -0
- data/spec/workers/goodjob/elasticsearch_reindex_spec.rb +49 -0
- metadata +1593 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# An LogNotifier can be used to send notifications to Elk, and/or the
|
|
4
|
+
# console - all depending on the options passed to the initializer (see
|
|
5
|
+
# the comments for #initialize).
|
|
6
|
+
#
|
|
7
|
+
# In addition, there is also a class level method (HasHelpers::LogNotifier.notify) which uses
|
|
8
|
+
# a default notifier that outputs to Elk in production and staging, and
|
|
9
|
+
# outputs to the console in dev and test. We can use this instead of directly
|
|
10
|
+
# calling Elk so we still get error output in dev and test, and also in case
|
|
11
|
+
# we ever move away from Elk.
|
|
12
|
+
#
|
|
13
|
+
# based on ::Frameworkhq::HQNotifier
|
|
14
|
+
|
|
15
|
+
module HasHelpers
|
|
16
|
+
class LogNotifier
|
|
17
|
+
MAX_SNS_SUBJECT_LENGTH = 99
|
|
18
|
+
|
|
19
|
+
###### CLASS METHODS ######
|
|
20
|
+
|
|
21
|
+
# This uses a default notifier to write the exception_or_msg to the console
|
|
22
|
+
def self.notify(exception_or_msg, subject: nil, user: nil)
|
|
23
|
+
@default_notifier ||= HasHelpers::LogNotifier.new
|
|
24
|
+
@default_notifier.notify(exception_or_msg, subject: subject, user: user)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
###### INSTANCE METHODS ######
|
|
28
|
+
|
|
29
|
+
# Instantiates an HasHelpers::HQNotifier that outputs to ELK, and/or the console
|
|
30
|
+
# depending on these options:
|
|
31
|
+
# - notify_console: By default, this notifier will output to the console.
|
|
32
|
+
# Set this to false if you don't want log messages copied to the console.
|
|
33
|
+
|
|
34
|
+
def initialize(notify_console: true)
|
|
35
|
+
@notify_console = notify_console
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# The argument can be an Exception or a String.
|
|
39
|
+
# Paramerters are:
|
|
40
|
+
# - subject: an optional subject for SNS messages
|
|
41
|
+
# - file_path: required if the argument is String | Optional if Exception
|
|
42
|
+
# - line_number: required if the argument is String | Optional if Exception
|
|
43
|
+
# - function_name: required if the argument is String | Optional if Exception
|
|
44
|
+
def notify(exception_or_msg, subject: nil, user: nil, backtrace: caller) # rubocop:disable Lint/UnusedMethodArgument
|
|
45
|
+
unless exception_or_msg.is_a?(Exception) || exception_or_msg.is_a?(String)
|
|
46
|
+
raise "HasHelpers::HQNotifier expected an Exception or String, but got a #{exception_or_msg.class}."
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
message = message_from_exception(exception_or_msg)
|
|
50
|
+
|
|
51
|
+
exception = exception_or_msg.is_a?(Exception) ? exception_or_msg : message_to_exception(exception_or_msg, backtrace: backtrace) # rubocop:disable Lint/UselessAssignment
|
|
52
|
+
|
|
53
|
+
puts message if @notify_console
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def message_to_exception(exception_or_msg = nil, backtrace: caller)
|
|
59
|
+
unless exception_or_msg.is_a? Class
|
|
60
|
+
exception = RuntimeError.new(exception_or_msg.to_s)
|
|
61
|
+
exception.set_backtrace(backtrace)
|
|
62
|
+
return exception
|
|
63
|
+
end
|
|
64
|
+
exception_or_msg
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# If the argument passed in is an Exception, combines the exception message
|
|
68
|
+
# with its backtrace, formatted with newlines. Otherwise, just returns the
|
|
69
|
+
# argument converted to a String.
|
|
70
|
+
def message_from_exception(exception_or_msg)
|
|
71
|
+
if exception_or_msg.is_a?(Exception)
|
|
72
|
+
lines = [exception_or_msg.to_s, *exception_or_msg.backtrace]
|
|
73
|
+
lines.join("\n")
|
|
74
|
+
else
|
|
75
|
+
exception_or_msg.to_s
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# A light-weight container for arguments intended for use with rendering a template.
|
|
5
|
+
#
|
|
6
|
+
# === Examples
|
|
7
|
+
# render_object = RenderObject.new(:partial => "some/template", :object => :derp)
|
|
8
|
+
#
|
|
9
|
+
# # Inside a view
|
|
10
|
+
# <%= render *render_object.args %>
|
|
11
|
+
class RenderObject # TODO: Find a better name. This one is horrible *
|
|
12
|
+
attr_accessor :args
|
|
13
|
+
|
|
14
|
+
def initialize(*args)
|
|
15
|
+
self.args = args
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_action
|
|
19
|
+
::Action.new(render_object: self)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_renderable
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# :nocov:
|
|
27
|
+
def to_partial_path
|
|
28
|
+
"/has_helpers/render_objects/render_object"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# :nocov:
|
|
32
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HasHelpers
|
|
4
|
+
module S3Utils
|
|
5
|
+
# Returns the requested bucket.
|
|
6
|
+
# Raises a RuntimeError if there is an error getting that bucket.
|
|
7
|
+
def get_s3_bucket(bucket_name)
|
|
8
|
+
@s3_client ||= ::HasHelpers::Aws.new_s3_client(credentials_conf: ::HasHelpers::Aws.credentials_conf)
|
|
9
|
+
@s3_resource ||= ::Aws::S3::Resource.new(client: @s3_client)
|
|
10
|
+
|
|
11
|
+
bucket = @s3_resource.bucket(bucket_name)
|
|
12
|
+
|
|
13
|
+
unless bucket.exists?
|
|
14
|
+
raise "Cannot access S3 bucket: #{bucket_name}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
bucket
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def get_s3_file(bucket_name, s3_file_path)
|
|
21
|
+
s3_object = get_s3_bucket(bucket_name).object(s3_file_path)
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
file_name = File.basename(s3_object.key)
|
|
25
|
+
::Tempfile.new(file_name).tap do |temp_file|
|
|
26
|
+
s3_object.get do |chunk|
|
|
27
|
+
temp_file.write(chunk.dup.force_encoding("UTF-8"))
|
|
28
|
+
end
|
|
29
|
+
temp_file.rewind
|
|
30
|
+
end
|
|
31
|
+
rescue => e
|
|
32
|
+
Rails.logger.error("Fetching file(/#{bucket_name}/#{s3_file_path}) in S3 encountered an issue with message: #{e.message}")
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
class Service
|
|
5
|
+
include ::HasHelpers::Attributes::AttributeAssignment
|
|
6
|
+
include ::HasHelpers::UIAttributes
|
|
7
|
+
attr_accessor :external,
|
|
8
|
+
:nested,
|
|
9
|
+
:http_method # HTTP method to use, e.g., one of :get, :post, :put
|
|
10
|
+
attr_writer :icon, :icon_placement
|
|
11
|
+
|
|
12
|
+
# Handle options passed to new. Hash args are assigned to attributes like ActiveRecord does.
|
|
13
|
+
# ==== Examples
|
|
14
|
+
# Service.new("http://derp.com")
|
|
15
|
+
# Service.new("Derp", :url => "http://derp.com")
|
|
16
|
+
# Service.new("Derp", :url => "http://derp.com", :external => true)
|
|
17
|
+
def initialize(name, **options)
|
|
18
|
+
@name = name
|
|
19
|
+
assign_attributes(options)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def url=(value)
|
|
23
|
+
@url_cached = true
|
|
24
|
+
@url = value
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Returns the current url value or falls back to the current object. Note
|
|
28
|
+
# that if url is explicitly set to nil, then no fallback will be used, but
|
|
29
|
+
# rather nil will be returned.
|
|
30
|
+
def url
|
|
31
|
+
@url_cached ? @url : to_s
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_s
|
|
35
|
+
@name
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_service(**options)
|
|
39
|
+
assign_attributes(**options) # Apply any given attributes
|
|
40
|
+
self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_action
|
|
44
|
+
::Action.new(service: self)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_partial_path
|
|
48
|
+
"has_helpers/services/service"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def icon
|
|
52
|
+
@icon && @icon.to_icon
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# :right or :left, default to :left
|
|
56
|
+
def icon_placement
|
|
57
|
+
@icon_placement == :right ? :right : :left
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def to_renderable
|
|
61
|
+
self
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def raw_html_attrs
|
|
65
|
+
super.tap do |raw_attrs|
|
|
66
|
+
raw_attrs.reverse_merge!(external: nil) if external
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def props
|
|
71
|
+
{}.tap do |hash|
|
|
72
|
+
hash[:type] = "Service"
|
|
73
|
+
hash[:icon] = icon
|
|
74
|
+
hash[:url] = url
|
|
75
|
+
hash[:children] = @name
|
|
76
|
+
end.camelize_keys!
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def ==(other)
|
|
80
|
+
case other
|
|
81
|
+
when self.class
|
|
82
|
+
url == other.url && to_s == other.to_s
|
|
83
|
+
else
|
|
84
|
+
super
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "slack"
|
|
4
|
+
|
|
5
|
+
class SlackConf
|
|
6
|
+
class << self
|
|
7
|
+
def client
|
|
8
|
+
@client ||= begin
|
|
9
|
+
Slack.configure do |config|
|
|
10
|
+
config.token = config_options
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
Slack::Web::Client.new
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def config_options
|
|
18
|
+
@config_options ||= (config.token || "")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def config
|
|
24
|
+
@config ||= ::HasHelpers::LazyConfig.new(config_path)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def config_path
|
|
28
|
+
Rails.root.join("config/secrets/slack.yaml")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HasHelpers
|
|
4
|
+
class SlackNotifier
|
|
5
|
+
###### CLASS METHODS ######
|
|
6
|
+
|
|
7
|
+
def self.notify(message, channel: default_channel)
|
|
8
|
+
@default_notifier ||= HasHelpers::SlackNotifier.new
|
|
9
|
+
@default_notifier.notify(message, channel: channel)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
attr_accessor :default_channel_resolver
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.default_channel
|
|
17
|
+
return nil if ::HasUtils::Env.test? || ::HasUtils::Env.development?
|
|
18
|
+
default_channel_resolver&.call
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
###### INSTANCE METHODS ######
|
|
22
|
+
|
|
23
|
+
def notify(message, channel:)
|
|
24
|
+
if channel.nil?
|
|
25
|
+
puts message
|
|
26
|
+
return
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
begin
|
|
30
|
+
slack_client.chat_postMessage(channel: channel, text: message, as_user: true, mrkdwn: true, link_names: true)
|
|
31
|
+
rescue => e
|
|
32
|
+
::Rails.logger.warn("Unable to send slack notification: #{e.message}")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def slack_client
|
|
39
|
+
@slack_client ||= SlackConf.client
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# typed: false
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module HasHelpers
|
|
5
|
+
class TextNode
|
|
6
|
+
include HasHelpers::Attributes
|
|
7
|
+
include HasHelpers::UIAttributes
|
|
8
|
+
|
|
9
|
+
attr_accessor :str
|
|
10
|
+
alias to_s str
|
|
11
|
+
|
|
12
|
+
def initialize(str, *args, **options, &block)
|
|
13
|
+
super(*args, **options, &block)
|
|
14
|
+
self.str = str.to_s
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_renderable
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_partial_path
|
|
22
|
+
"has_helpers/text_nodes/text_node"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def ==(other)
|
|
26
|
+
case other
|
|
27
|
+
when self.class
|
|
28
|
+
to_s == other.to_s
|
|
29
|
+
else
|
|
30
|
+
super
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cancan"
|
|
4
|
+
require "has_helpers/concerns/restrictions_parseable"
|
|
5
|
+
|
|
6
|
+
module HasHelpers
|
|
7
|
+
# Resolves and queries the resource-level restrictions of a user, and exposes
|
|
8
|
+
# them through the CanCanCan interface (`can?`/`cannot?`).
|
|
9
|
+
#
|
|
10
|
+
# Uses a restrictions-based model (negative permissions): the restrictions_hash
|
|
11
|
+
# on the user's primary user_organization defines what the user CANNOT do.
|
|
12
|
+
# An empty restrictions hash means the user is unrestricted (`can :manage, :all`).
|
|
13
|
+
#
|
|
14
|
+
# This class `include`s `CanCan::Ability` and mirrors frameworkhq's `Ability`:
|
|
15
|
+
# a single catch-all `can` block delegates each check to the negative
|
|
16
|
+
# `restricted?` query, and results are memoized per (subject, action) within the
|
|
17
|
+
# instance (see `memoized_permissions`). Build one instance per request and reuse
|
|
18
|
+
# it — the authorize lambda and host `::Ability` subclasses do exactly this.
|
|
19
|
+
#
|
|
20
|
+
# The hash is keyed by application name and may hold restrictions for several
|
|
21
|
+
# host apps at once (e.g. AgenciesHQ, ReadyGOP, Reports, HQAdmin). Each host
|
|
22
|
+
# passes its own keys via `app_keys` (looked up first), or relies on
|
|
23
|
+
# case-insensitive matching against `application` / the PsqlHq `client_app`
|
|
24
|
+
# variable.
|
|
25
|
+
#
|
|
26
|
+
# Payload shape (populated by hqadmin during the login sync; may arrive
|
|
27
|
+
# multi-encoded as JSON text — see RestrictionsParseable):
|
|
28
|
+
#
|
|
29
|
+
# {
|
|
30
|
+
# "AppName" => {
|
|
31
|
+
# "Client" => {
|
|
32
|
+
# "restriction_operations" => ["View", "Create"], # operations the user CANNOT do
|
|
33
|
+
# "resource_type" => "Base",
|
|
34
|
+
# "alias_name" => "Customer", # optional: alternate resource name
|
|
35
|
+
# "nested_resources" => [...] # optional: NOT consumed here
|
|
36
|
+
# }
|
|
37
|
+
# }
|
|
38
|
+
# }
|
|
39
|
+
#
|
|
40
|
+
# Usage:
|
|
41
|
+
#
|
|
42
|
+
# restrictions = HasHelpers::UserRestrictions.new(user, app_keys: ["MyApp"])
|
|
43
|
+
# restrictions.empty? # => true if unrestricted
|
|
44
|
+
# restrictions.can?(:read, "Client") # => false if View/Read restricted
|
|
45
|
+
# restrictions.restricted?(:read, "Client") # => true if View/Read restricted
|
|
46
|
+
# restrictions.restricted_resource_names # => ["Client", ...] (read-restricted)
|
|
47
|
+
class UserRestrictions
|
|
48
|
+
include ::CanCan::Ability
|
|
49
|
+
include ::HasHelpers::Concerns::RestrictionsParseable
|
|
50
|
+
|
|
51
|
+
# Operations that block reading a resource.
|
|
52
|
+
READ_OPERATIONS = %w[View Read].freeze
|
|
53
|
+
|
|
54
|
+
# Maps CanCanCan-style action names to the restriction operation names
|
|
55
|
+
# stored in the DB. Read-like actions match both "View" and "Read".
|
|
56
|
+
OPERATION_MAP = {
|
|
57
|
+
"read" => READ_OPERATIONS,
|
|
58
|
+
"view" => READ_OPERATIONS,
|
|
59
|
+
"create" => ["Create"].freeze,
|
|
60
|
+
"update" => ["Update"].freeze,
|
|
61
|
+
"destroy" => ["Delete"].freeze,
|
|
62
|
+
"delete" => ["Delete"].freeze,
|
|
63
|
+
"export" => ["Export"].freeze,
|
|
64
|
+
"merge" => ["Merge"].freeze
|
|
65
|
+
}.freeze
|
|
66
|
+
|
|
67
|
+
# UI/action aliases (the standard frameworkhq set) mapped to the canonical
|
|
68
|
+
# action that `OPERATION_MAP` understands. CanCanCan passes the ORIGINAL
|
|
69
|
+
# action to a catch-all `can` block (not the one expanded by `alias_action`),
|
|
70
|
+
# so the block normalizes through this map before delegating to `restricted?`.
|
|
71
|
+
# The `alias_action` declarations in `ability_aliases!` mirror this and cover
|
|
72
|
+
# the rest of the CanCanCan API (e.g. `cannot?`). Keeping both in sync is
|
|
73
|
+
# intentional — do not remove either.
|
|
74
|
+
ACTION_ALIASES = {
|
|
75
|
+
cards: :read,
|
|
76
|
+
download: :read,
|
|
77
|
+
favorite: :read,
|
|
78
|
+
copy: :create,
|
|
79
|
+
delete: :destroy,
|
|
80
|
+
edit: :update,
|
|
81
|
+
email: :export,
|
|
82
|
+
re_run: :update
|
|
83
|
+
}.freeze
|
|
84
|
+
|
|
85
|
+
attr_reader :user, :application, :app_keys
|
|
86
|
+
|
|
87
|
+
def initialize(user, application: nil, app_keys: [])
|
|
88
|
+
@user = user
|
|
89
|
+
@application = application
|
|
90
|
+
@app_keys = Array(app_keys)
|
|
91
|
+
|
|
92
|
+
ability_aliases!
|
|
93
|
+
authorize_restrictions!
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Restrictions hash for the resolved application, keyed by resource name.
|
|
97
|
+
def restrictions
|
|
98
|
+
return @restrictions if defined?(@restrictions)
|
|
99
|
+
|
|
100
|
+
@restrictions = restrictions_for_app
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def empty?
|
|
104
|
+
restrictions.blank?
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Returns true if the given action is restricted for the resource.
|
|
108
|
+
# action: CanCanCan-style (:read, :create, ...); resource: String, Class,
|
|
109
|
+
# or an object responding to :klass (e.g. HasReports::Resource).
|
|
110
|
+
def restricted?(action, resource)
|
|
111
|
+
resource_info = find_resource_info(resource)
|
|
112
|
+
return false if resource_info.blank?
|
|
113
|
+
|
|
114
|
+
operations = Array(resource_info["restriction_operations"]).map(&:to_s)
|
|
115
|
+
(normalize_operations(action) & operations).any?
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Names of the resources restricted for any of the given operations.
|
|
119
|
+
def restricted_resource_names(operations: READ_OPERATIONS)
|
|
120
|
+
ops = Array(operations).map(&:to_s)
|
|
121
|
+
restrictions.select do |_name, info|
|
|
122
|
+
info.is_a?(Hash) && (Array(info["restriction_operations"]).map(&:to_s) & ops).any?
|
|
123
|
+
end.keys
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
# Registers the standard OneHQ action aliases (mirrors frameworkhq's
|
|
129
|
+
# `ability_aliases!`) so `can?`/`cannot?` accept UI action names.
|
|
130
|
+
def ability_aliases!
|
|
131
|
+
alias_action :cards, to: :read
|
|
132
|
+
alias_action :download, to: :read
|
|
133
|
+
alias_action :favorite, to: :read
|
|
134
|
+
alias_action :copy, to: :create
|
|
135
|
+
alias_action :delete, to: :destroy
|
|
136
|
+
alias_action :edit, to: :update
|
|
137
|
+
alias_action :email, to: :export
|
|
138
|
+
alias_action :re_run, to: :update
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Defines the CanCanCan rules from the user's restrictions. With no
|
|
142
|
+
# restrictions the user can manage everything; otherwise a single catch-all
|
|
143
|
+
# `can` block delegates to the negative `restricted?` query and memoizes the
|
|
144
|
+
# boolean result per (subject, action) within this instance (mirrors
|
|
145
|
+
# frameworkhq's `Ability`). The instance is request-scoped, so the cache dies
|
|
146
|
+
# with the request — there is no cross-request state.
|
|
147
|
+
def authorize_restrictions!
|
|
148
|
+
if empty?
|
|
149
|
+
can :manage, :all
|
|
150
|
+
return
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
can do |action, subject_class, subject|
|
|
154
|
+
subject_key = subject || subject_class
|
|
155
|
+
memoized = memoized_permissions[subject_key] ||= {}
|
|
156
|
+
memoized.fetch(action) do |cached_action|
|
|
157
|
+
canonical = ACTION_ALIASES.fetch(action.to_sym, action)
|
|
158
|
+
memoized[cached_action] = !restricted?(canonical, subject_key)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def memoized_permissions
|
|
164
|
+
@memoized_permissions ||= {}
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Resolves the application key in the restrictions hash:
|
|
168
|
+
# 1. Explicit app_keys (first present key wins)
|
|
169
|
+
# 2. Case-insensitive match against application_name
|
|
170
|
+
# 3. Case-insensitive match against application_name.titleize
|
|
171
|
+
def restrictions_for_app
|
|
172
|
+
hash = restrictions_hash_for_user
|
|
173
|
+
return {} if hash.blank?
|
|
174
|
+
|
|
175
|
+
key =
|
|
176
|
+
app_keys.find { |k| hash.key?(k) } ||
|
|
177
|
+
hash.keys.find { |k| k.to_s.casecmp(application_name.to_s).zero? } ||
|
|
178
|
+
hash.keys.find { |k| k.to_s.casecmp(application_name.to_s.titleize).zero? }
|
|
179
|
+
|
|
180
|
+
normalize_restrictions(hash[key])
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Reads and parses the raw restrictions_hash from the user's primary
|
|
184
|
+
# organization (falling back to the first one).
|
|
185
|
+
def restrictions_hash_for_user
|
|
186
|
+
user_organization =
|
|
187
|
+
user&.user_organizations&.find_by(is_primary: true) ||
|
|
188
|
+
user&.user_organizations&.first
|
|
189
|
+
raw = user_organization&.restrictions_hash
|
|
190
|
+
parse_raw_restrictions(raw, user_id: user&.id)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Resolves the application name from the application object, string, or
|
|
194
|
+
# falls back to the PsqlHq client_app variable.
|
|
195
|
+
def application_name
|
|
196
|
+
if application.respond_to?(:name)
|
|
197
|
+
application.name
|
|
198
|
+
elsif application.present?
|
|
199
|
+
application.to_s
|
|
200
|
+
else
|
|
201
|
+
PsqlHq::Variables.get(:client_app).to_s.strip.presence
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Converts a CanCanCan action (e.g., :read, :destroy) to the restriction
|
|
206
|
+
# operation names stored in the DB (e.g., ["View", "Read"], ["Delete"]).
|
|
207
|
+
def normalize_operations(action)
|
|
208
|
+
OPERATION_MAP.fetch(action.to_s, [action.to_s.capitalize])
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Finds the restriction info hash for a given resource.
|
|
212
|
+
# Supports multiple matching strategies:
|
|
213
|
+
# 1. Exact name match (e.g., "Client")
|
|
214
|
+
# 2. Demodulized match (e.g., "Crm::Client" matches "Client" key)
|
|
215
|
+
# 3. Reverse demodulized match (e.g., "Client" matches "Crm::Client" key)
|
|
216
|
+
# 4. Alias name match (e.g., resource has alias_name "CustomName")
|
|
217
|
+
def find_resource_info(resource)
|
|
218
|
+
name = resource_name(resource)
|
|
219
|
+
return nil if name.blank?
|
|
220
|
+
|
|
221
|
+
info = restrictions[name] || restrictions[name.demodulize]
|
|
222
|
+
return info if info
|
|
223
|
+
|
|
224
|
+
demodulized_name = name.demodulize
|
|
225
|
+
restrictions.each do |key, resource_info|
|
|
226
|
+
return resource_info if key.demodulize == demodulized_name
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# `alias_name` is an alternate NAME a resource may be exposed under — it is
|
|
230
|
+
# unrelated to the payload's `nested_resources` key (which is not consumed
|
|
231
|
+
# here). Nested/cross-resource columns in reports are covered by the
|
|
232
|
+
# demodulized name matching above, not by this branch.
|
|
233
|
+
restrictions.each_value do |resource_info|
|
|
234
|
+
next unless resource_info.is_a?(Hash)
|
|
235
|
+
|
|
236
|
+
alias_name = resource_info["alias_name"]
|
|
237
|
+
next if alias_name.blank?
|
|
238
|
+
return resource_info if [alias_name, alias_name.demodulize].include?(name)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
nil
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Extracts a string resource name from various input types:
|
|
245
|
+
# - String: returned as-is (e.g., "Client")
|
|
246
|
+
# - Class: returns class name (e.g., Client → "Client")
|
|
247
|
+
# - Objects responding to :klass (e.g. HasReports::Resource): reads klass safely
|
|
248
|
+
# - Other objects: falls back to class name
|
|
249
|
+
def resource_name(resource)
|
|
250
|
+
case resource
|
|
251
|
+
when String
|
|
252
|
+
resource
|
|
253
|
+
when Class
|
|
254
|
+
resource.name
|
|
255
|
+
else
|
|
256
|
+
if resource.respond_to?(:klass)
|
|
257
|
+
klass_value = resource.respond_to?(:read_attribute) ? resource.read_attribute(:klass) : resource.klass
|
|
258
|
+
return nil if klass_value.blank?
|
|
259
|
+
klass_value.is_a?(Class) ? klass_value.name : klass_value.to_s
|
|
260
|
+
elsif resource.respond_to?(:class)
|
|
261
|
+
resource.class.name
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "has_helpers/utils/alert"
|
|
5
|
+
require "has_helpers/utils/input"
|
|
6
|
+
require "has_helpers/utils/action"
|
|
7
|
+
require "has_helpers/utils/icon"
|
|
8
|
+
require "has_helpers/utils/render_object"
|
|
9
|
+
require "has_helpers/utils/service"
|
|
10
|
+
require "has_helpers/utils/container"
|
|
11
|
+
require "has_helpers/utils/dataset"
|
|
12
|
+
require "has_helpers/utils/log_notifier"
|
|
13
|
+
require "has_helpers/utils/email_notifier"
|
|
14
|
+
require "has_helpers/utils/slack_conf"
|
|
15
|
+
require "has_helpers/utils/slack_notifier"
|
|
16
|
+
require "has_helpers/utils/text_node"
|
|
17
|
+
require "has_helpers/utils/active_record_utils"
|
|
18
|
+
require "has_helpers/utils/s3_utils"
|
|
19
|
+
require "has_helpers/utils/user_restrictions"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HasHelpers
|
|
4
|
+
module WebSocketPublisher
|
|
5
|
+
def self.publish(channel, _payload)
|
|
6
|
+
Rails.logger.warn <<~LOG.strip
|
|
7
|
+
[HasHelpers::WebSocketPublisher] Attempted to publish to Redis (#{channel}) but no publisher is configured.
|
|
8
|
+
Please define `HasHelpers::WebSocketPublisher` in an initializer (e.g., config/initializers/web_socket_publisher.rb)
|
|
9
|
+
Example:
|
|
10
|
+
module HasHelpers
|
|
11
|
+
module WebSocketPublisher
|
|
12
|
+
def self.publish(channel, payload)
|
|
13
|
+
REDIS.publish(channel, payload.to_json)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
LOG
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# typed: false
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module HasHelpers::Webhook
|
|
5
|
+
# Sends a webhook request in a standard format.
|
|
6
|
+
def self.send(action:, data:, url:, type:)
|
|
7
|
+
conn = Faraday.new(url: url)
|
|
8
|
+
conn.post do |req|
|
|
9
|
+
req.headers["Content-Type"] = "application/json"
|
|
10
|
+
req.body = {
|
|
11
|
+
action: action,
|
|
12
|
+
type: type,
|
|
13
|
+
data: data
|
|
14
|
+
}.to_json
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|