├── log └── .keep ├── app ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── concerns │ │ ├── .keep │ │ └── has_primitive_type.rb │ ├── member.rb │ ├── api_gateway_integration.rb │ ├── application_record.rb │ ├── api_error_instance.rb │ ├── metadatum_instance.rb │ ├── resource_instance.rb │ ├── scheme.rb │ ├── metadata_response.rb │ ├── validation_error.rb │ ├── query_parameter.rb │ ├── api_error.rb │ ├── header.rb │ ├── metadatum.rb │ ├── mock_profile.rb │ └── report.rb ├── controllers │ ├── concerns │ │ ├── .keep │ │ ├── authenticated.rb │ │ └── project_related.rb │ ├── api_error_instances_controller.rb │ ├── metadatum_instances_controller.rb │ ├── instances_controller.rb │ ├── resource_instances_controller.rb │ ├── headers_controller.rb │ ├── validations_controller.rb │ ├── audits_controller.rb │ ├── users │ │ └── omniauth_callbacks_controller.rb │ ├── schemes_controller.rb │ └── users_controller.rb ├── helpers │ ├── application_helper.rb │ ├── android_code_gen_helper.rb │ ├── attributes_helper.rb │ ├── sidebar_helper.rb │ ├── validations_helper.rb │ └── responses_helper.rb ├── views │ ├── audits │ │ ├── _missing_partial.html.haml │ │ ├── _missing_partial.mrkdwn.erb │ │ ├── route │ │ │ ├── _destroy.mrkdwn.erb │ │ │ ├── _destroy.html.erb │ │ │ ├── _create.mrkdwn.erb │ │ │ ├── _create.html.erb │ │ │ ├── _update.mrkdwn.erb │ │ │ └── _update.html.erb │ │ ├── resource │ │ │ ├── _destroy.mrkdwn.erb │ │ │ ├── _destroy.html.erb │ │ │ ├── _create.mrkdwn.erb │ │ │ ├── _create.html.erb │ │ │ ├── _update.mrkdwn.erb │ │ │ └── _update.html.erb │ │ ├── metadatum │ │ │ ├── _destroy.mrkdwn.erb │ │ │ ├── _destroy.html.erb │ │ │ ├── _create.mrkdwn.erb │ │ │ ├── _create.html.erb │ │ │ ├── _update.mrkdwn.erb │ │ │ └── _update.html.erb │ │ ├── resources_representation │ │ │ ├── _destroy.mrkdwn.erb │ │ │ ├── _destroy.html.erb │ │ │ ├── _create.mrkdwn.erb │ │ │ ├── _create.html.erb │ │ │ ├── _update.mrkdwn.erb │ │ │ └── _update.html.erb │ │ ├── _changes.mrkdwn.erb │ │ ├── response │ │ │ ├── _create.mrkdwn.erb │ │ │ ├── _destroy.mrkdwn.erb │ │ │ ├── _update.mrkdwn.erb │ │ │ ├── _destroy.html.erb │ │ │ ├── _create.html.erb │ │ │ └── _update.html.erb │ │ ├── _changes.html.haml │ │ ├── metadata_response │ │ │ ├── _create.mrkdwn.erb │ │ │ ├── _destroy.mrkdwn.erb │ │ │ ├── _destroy.html.erb │ │ │ ├── _update.mrkdwn.erb │ │ │ ├── _create.html.erb │ │ │ └── _update.html.erb │ │ ├── attribute │ │ │ ├── _destroy.mrkdwn.erb │ │ │ ├── _create.mrkdwn.erb │ │ │ ├── _destroy.html.erb │ │ │ ├── _create.html.erb │ │ │ ├── _update.mrkdwn.erb │ │ │ └── _update.html.erb │ │ └── attributes_resource_representation │ │ │ ├── _create.mrkdwn.erb │ │ │ ├── _destroy.mrkdwn.erb │ │ │ ├── _create.html.erb │ │ │ ├── _destroy.html.erb │ │ │ ├── _update.mrkdwn.erb │ │ │ └── _update.html.erb │ ├── projects │ │ ├── edit.html.haml │ │ ├── new.html.haml │ │ ├── _proxy_configuration_fields.html.haml │ │ ├── _api_gateway_integration_fields.html.haml │ │ └── index.html.haml │ ├── api_errors │ │ ├── new.html.haml │ │ ├── edit.html.haml │ │ ├── _form.html.haml │ │ ├── _new_form.html.haml │ │ └── index.html.haml │ ├── metadata │ │ ├── new.html.haml │ │ ├── edit.html.haml │ │ ├── show.html.haml │ │ ├── index.html.haml │ │ ├── _form.html.haml │ │ └── _metadatum.html.haml │ ├── resources │ │ ├── new.html.haml │ │ ├── edit_resource.html.haml │ │ ├── _edit_resource_form.html.haml │ │ ├── _resource_instances.html.haml │ │ ├── edit_attributes.html.haml │ │ ├── index.html.haml │ │ └── _create_form.html.haml │ ├── resource_instances │ │ ├── edit.html.haml │ │ ├── _resource_instance.html.haml │ │ ├── new.html.haml │ │ └── _form.html.haml │ ├── api_error_instances │ │ ├── _api_error_instance.html.haml │ │ ├── edit.html.haml │ │ ├── new.html.haml │ │ └── _form.html.haml │ ├── metadatum_instances │ │ ├── edit.html.haml │ │ ├── new.html.haml │ │ ├── _form.html.haml │ │ └── _metadatum_instance.html.haml │ ├── routes │ │ ├── new.html.haml │ │ ├── edit.html.haml │ │ ├── _request_header_fields.html.haml │ │ ├── _form.html.haml │ │ └── _request_query_parameter_fields.html.haml │ ├── schemes │ │ ├── new.html.haml │ │ ├── edit.html.haml │ │ ├── _form.html.haml │ │ └── index.html.haml │ ├── mock_profiles │ │ ├── edit.html.haml │ │ └── new.html.haml │ ├── security_schemes │ │ ├── new.html.haml │ │ ├── edit.html.haml │ │ ├── index.html.haml │ │ └── _security_scheme.html.haml │ ├── shared │ │ ├── _alert.html.haml │ │ ├── _last_edit.html.haml │ │ ├── _schema_errors.html.haml │ │ ├── _errors.html.haml │ │ └── _head.html.haml │ ├── layouts │ │ ├── no_navbar_layout.html.haml │ │ └── application.html.haml │ ├── responses │ │ ├── new.html.haml │ │ ├── edit.html.haml │ │ ├── _header_fields.html.haml │ │ └── _metadata_responses_fields.html.haml │ ├── devise │ │ └── mailer │ │ │ └── confirmation_instructions.html.haml │ ├── code │ │ ├── graphql.ruby.erb │ │ ├── rest.kotlin.erb │ │ ├── serializer.ruby.erb │ │ └── rest.swift.erb │ ├── resource_representations │ │ ├── new.html.haml │ │ └── edit.html.haml │ ├── headers │ │ └── _header.html.haml │ ├── users │ │ ├── show.html.haml │ │ └── index.html.haml │ ├── instances │ │ └── _instance.html.haml │ ├── query_parameters │ │ └── _query_parameter.html.haml │ └── validations │ │ └── new.html.haml ├── assets │ ├── images │ │ ├── sad.gif │ │ ├── arrow_back.svg │ │ ├── copy.svg │ │ ├── edit.svg │ │ ├── sort.svg │ │ └── reset.svg │ ├── stylesheets │ │ ├── styles │ │ │ ├── mock_instances.scss │ │ │ ├── responses.scss │ │ │ ├── projects.scss │ │ │ └── audits.scss │ │ ├── variables.scss │ │ └── main.scss │ └── javascripts │ │ ├── setup.js │ │ ├── validations │ │ └── new.js │ │ ├── security_schemes │ │ └── form.js │ │ ├── generate_instance.js │ │ └── resources │ │ └── new_representation.js ├── policies │ ├── api_error_instance_policy.rb │ ├── metadatum_instance_policy.rb │ ├── resource_instance_policy.rb │ ├── api_error_policy.rb │ ├── user_context.rb │ ├── resource_policy.rb │ ├── metadatum_policy.rb │ ├── scheme_policy.rb │ ├── security_scheme_policy.rb │ ├── response_policy.rb │ ├── user_policy.rb │ ├── project_related_policy.rb │ └── mock_profile_policy.rb ├── serializers │ ├── id_serializer.rb │ ├── header_serializer.rb │ ├── user_serializer.rb │ ├── validation_serializer.rb │ ├── resource_representation_serializer.rb │ ├── route_serializer.rb │ ├── resource_serializer.rb │ ├── resource_index_serializer.rb │ ├── extended_resource_representation_serializer.rb │ ├── attributes_resource_representation_serializer.rb │ └── attribute_serializer.rb ├── decorators │ ├── security_scheme_decorator.rb │ ├── api_error_instance_decorator.rb │ ├── metadatum_instance_decorator.rb │ ├── user_decorator.rb │ ├── attributes_resource_representation_decorator.rb │ ├── response_decorator.rb │ ├── proxy │ │ └── route_decorator.rb │ ├── json_schema │ │ ├── metadata_response_decorator.rb │ │ ├── attribute_decorator.rb │ │ ├── metadatum_decorator.rb │ │ └── response_decorator.rb │ ├── resource_decorator.rb │ ├── news │ │ ├── header_audit_decorator.rb │ │ ├── route_audit_decorator.rb │ │ ├── metadatum_audit_decorator.rb │ │ ├── resource_audit_decorator.rb │ │ ├── response_audit_decorator.rb │ │ └── attribute_audit_decorator.rb │ ├── code │ │ ├── resource_decorator.rb │ │ ├── resource_representation_decorator.rb │ │ ├── attribute_decorator.rb │ │ ├── attributes_resource_representation_decorator.rb │ │ └── data_class.rb │ ├── attribute_decorator.rb │ ├── authorization.rb │ ├── resource_instance_decorator.rb │ ├── http │ │ └── response_decorator.rb │ └── report_decorator.rb ├── view_models │ ├── news │ │ └── change.rb │ └── body_error │ │ ├── body_error_view_model.rb │ │ ├── additional_error_view_model.rb │ │ ├── enum_error_view_model.rb │ │ ├── required_error_view_model.rb │ │ └── type_error_view_model.rb ├── validators │ ├── hash_validator.rb │ ├── json_validator.rb │ ├── regexp_validator.rb │ ├── json_object_validator.rb │ └── json_schema_validator.rb ├── jobs │ └── report_validator_job.rb ├── repositories │ └── audits_repository.rb ├── javascript │ └── packs │ │ ├── common │ │ └── markdown.js │ │ ├── resources │ │ ├── ps-unused-resources.vue │ │ └── index.js │ │ └── application.js └── services │ ├── json_schema_builder.rb │ ├── generate_json_instance_service.rb │ ├── abstract_zip_builder.rb │ ├── mocks_zip_builder.rb │ ├── post_slack_news.rb │ └── set_slack_webhook.rb ├── lib ├── assets │ └── .keep ├── tasks │ ├── .keep │ ├── audits.rake │ └── proxy.rake ├── node │ └── generate_mock.js └── bootstrap_form │ └── regex.rb ├── public ├── favicon.ico ├── robots.txt ├── 403.html └── 404.html ├── test ├── helpers │ ├── .keep │ └── validations_helper_test.rb ├── mailers │ └── .keep ├── factories │ ├── members.rb │ ├── responses.rb │ ├── validations.rb │ ├── metadata_responses.rb │ ├── api_error_instances.rb │ ├── metadatum_instances.rb │ ├── mock_pickers.rb │ ├── proxy_configurations.rb │ ├── validation_error.rb │ ├── api_gateway_integrations.rb │ ├── schemes.rb │ ├── headers.rb │ ├── mock_profiles.rb │ ├── api_errors.rb │ ├── resource_instances.rb │ ├── metadata.rb │ ├── query_parameters.rb │ ├── attributes_resource_representations.rb │ ├── security_schemes.rb │ ├── attributes.rb │ ├── reports.rb │ └── users.rb ├── authentication │ └── controller_with_authentication_test.rb ├── models │ ├── audit_test.rb │ ├── factories_test.rb │ ├── validation_error_test.rb │ ├── header_test.rb │ ├── api_error_test.rb │ ├── metadata_response_test.rb │ ├── scheme_test.rb │ ├── query_parameter_test.rb │ └── metadatum_test.rb ├── lib │ └── tasks │ │ └── proxy_test.rb ├── decorators │ └── json_schema │ │ └── resource_representation_decorator_test.rb └── transactions │ └── create_rest_routes_transaction_test.rb ├── .nvmrc ├── .browserslistrc ├── banner.jpg ├── config ├── initializers │ ├── haml.rb │ ├── bootstrap_form.rb │ ├── session_store.rb │ ├── neat_json.rb │ ├── active_model_serializer.rb │ ├── application_controller_renderer.rb │ ├── filter_parameter_logging.rb │ ├── cookies_serializer.rb │ ├── backtrace_silencers.rb │ ├── audited.rb │ ├── wrap_parameters.rb │ ├── mime_types.rb │ └── inflections.rb ├── locales │ ├── metadata │ │ ├── en.yml │ │ └── fr.yml │ ├── audits │ │ ├── en.yml │ │ └── fr.yml │ ├── responses │ │ ├── en.yml │ │ └── fr.yml │ ├── security_schemes │ │ ├── en.yml │ │ └── fr.yml │ ├── users │ │ ├── en.yml │ │ └── fr.yml │ ├── reports │ │ ├── en.yml │ │ └── fr.yml │ ├── attributes │ │ ├── en.yml │ │ └── fr.yml │ ├── api_errors │ │ ├── en.yml │ │ └── fr.yml │ ├── resource_representations │ │ ├── en.yml │ │ └── fr.yml │ ├── fr.yml │ └── validations │ │ └── fr.yml ├── webpack │ ├── test.js │ ├── production.js │ ├── development.js │ ├── loaders │ │ ├── pug.js │ │ ├── vue.js │ │ └── vue-svg-loader.js │ └── environment.js ├── spring.rb ├── boot.rb ├── environment.rb ├── cable.yml └── database.yml ├── proxy ├── config │ ├── config.exs │ ├── dev.exs │ ├── prod.exs │ └── test.exs ├── elixir_buildpack.config ├── test │ └── test_helper.exs ├── .formatter.exs ├── lib │ ├── pericles_proxy │ │ ├── repositories │ │ │ ├── repo.ex │ │ │ └── proxy_configuration_repo.ex │ │ └── models │ │ │ ├── project.ex │ │ │ ├── delayed_job.ex │ │ │ └── proxy_configuration.ex │ └── pericles_proxy.ex ├── app.json └── .gitignore ├── bin ├── rake ├── bundle ├── rails ├── delayed_job ├── webpack ├── webpack-dev-server └── spring ├── docker-entrypoint.sh ├── Procfile ├── config.ru ├── db ├── migrate │ ├── 20170405185230_destroy_json_schemas.rb │ ├── 20171208095001_add_value_to_headers.rb │ ├── 20170524122014_add_enum_to_attributes.rb │ ├── 20170901134536_add_avatar_url_to_users.rb │ ├── 20171213105131_remove_name_from_routes.rb │ ├── 20170530095333_add_pattern_to_attributes.rb │ ├── 20170721161004_add_server_url_to_project.rb │ ├── 20170407182241_remove_body_schema_from_routes.rb │ ├── 20170410163242_add_route_id_to_responses.rb │ ├── 20170927092704_rename_faker_to_attribute_faker.rb │ ├── 20170407163730_add_request_body_schema_to_routes.rb │ ├── 20171030102800_rename_server_url_to_proxy_url.rb │ ├── 20171207110941_remove_description_from_headers.rb │ ├── 20180810134422_add_validated_to_reports.rb │ ├── 20171206161957_remove_description_from_responses.rb │ ├── 20171009090238_add_request_description_to_routes.rb │ ├── 20190626123920_add_slack_updated_at_to_projects.rb │ ├── 20170410163241_remove_response_schema_from_routes.rb │ ├── 20170901114536_add_name_to_users.rb │ ├── 20171114144052_remove_example_from_resource_attributes.rb │ ├── 20180109151423_add_is_public_to_projects.rb │ ├── 20190226135641_add_nullable_to_metadata.rb │ ├── 20170720153921_add_nullable_to_attribute.rb │ ├── 20170830082826_add_is_collection_to_routes.rb │ ├── 20171114100627_add_active_mock_profile_to_projects.rb │ ├── 20171206155832_remove_request_description_from_routes.rb │ ├── 20180727095817_add_instances_number_to_mock_pickers.rb │ ├── 20190529154416_add_priority_to_mock_pickers.rb │ ├── 20190121145846_add_key_to_metadata_responses.rb │ ├── 20170529094105_add_is_required_to_attributes.rb │ ├── 20170922083249_rename_body_schema_to_body_schema_backup.rb │ ├── 20191128145342_add_required_to_metadata_responses.rb │ ├── 20180529073528_add_unique_constraint_to_attribute_name.rb │ ├── 20171212072623_add_ancestry_to_mock_profiles.rb │ ├── 20170614152319_add_resource_representation_id_to_responses.rb │ ├── 20170918155835_add_faker_to_attributes_resource_representation.rb │ ├── 20180219094508_add_ignore_ssl_to_proxy_configuration.rb │ ├── 20190613080952_add_slack_to_projects.rb │ ├── 20191006172737_route_deprecated.rb │ ├── 20191101142812_add_operation_id.rb │ ├── 20171031084915_add_min_max_items_to_attributes.rb │ ├── 20180403141405_remove_schemas_backup.rb │ ├── 20161213093837_create_projects.rb │ ├── 20180116160324_add_recoverable_to_users.rb │ ├── 20170623124732_add_is_collection_to_resource_representations.rb │ ├── 20171013123912_rename_request_body_schema_to_request_body_schema_backup.rb │ ├── 20170322151410_create_validations.rb │ ├── 20170828152358_add_custom_nullable_to_attributes_resource_representations.rb │ ├── 20171130154438_add_is_null_to_attributes_resource_representation.rb │ ├── 20170407165817_create_responses.rb │ ├── 20171227103848_create_members.rb │ ├── 20171228165935_rename_key_name_to_custom_key_name.rb │ ├── 20170310105503_create_resources.rb │ ├── 20170329124037_create_json_errors.rb │ ├── 20170407172714_create_headers.rb │ ├── 20161214162402_create_json_schemas.rb │ ├── 20171229144723_change_validation_errors_description_to_text.rb │ ├── 20180117161236_create_metadata.rb │ ├── 20170602145951_create_resource_representations.rb │ ├── 20170720143737_add_missing_constraints_in_attribute.rb │ ├── 20170720101800_add_request_ressource_representation_to_route.rb │ ├── 20180208103459_create_metadata_responses.rb │ ├── 20171108092720_create_mock_instances.rb │ ├── 20180208151538_create_metadatum_instances.rb │ ├── 20171013130330_add_root_key_and_is_collection_to_route.rb │ ├── 20170313102138_create_routes.rb │ ├── 20170407175225_create_query_parameters.rb │ ├── 20171030155238_merge_attribute_min_max.rb │ ├── 20171115105126_add_patterns_to_mock_pickers.rb │ ├── 20190928112108_add_api_gateway_integration.rb │ ├── 20190926081509_change_parameters_to_jsonb_in_security_schemes.rb │ ├── 20190304112548_add_project_id_to_audits.rb │ ├── 20190715164030_add_internal_to_users.rb │ ├── 20170919124934_add_root_key_to_responses.rb │ ├── 20171030094823_remove_custom_fields_from_resource_representation.rb │ ├── 20180223163412_drop_attribute_fakers.rb │ ├── 20171115165949_rename_mock_instance_to_resource_instance.rb │ ├── 20171226152120_add_key_name_to_attributes_resource_representation.rb │ ├── 20170919080631_create_fakers.rb │ ├── 20170310132514_create_attributes.rb │ ├── 20190912182400_create_security_schemes.rb │ ├── 20170602161118_create_attributes_resource_representations.rb │ ├── 20200417091346_add_confirmable_to_devise.rb │ ├── 20170830101250_devise_create_users.rb │ ├── 20171229133209_remove_json_errors.rb │ ├── 20171116161642_create_api_errors_and_errors_instances.rb │ ├── 20170921083345_create_schemes.rb │ └── 20171004090741_create_report.rb └── seeds.rb ├── .github └── dependabot.yml ├── Rakefile ├── postcss.config.js └── app.json /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v10.13.0 2 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faberNovel/pericles/HEAD/banner.jpg -------------------------------------------------------------------------------- /config/initializers/haml.rb: -------------------------------------------------------------------------------- 1 | Haml::Template.options[:remove_whitespace] = true -------------------------------------------------------------------------------- /app/views/audits/_missing_partial.html.haml: -------------------------------------------------------------------------------- 1 | %div= "#{audit.id} has no partial" 2 | -------------------------------------------------------------------------------- /app/views/audits/_missing_partial.mrkdwn.erb: -------------------------------------------------------------------------------- 1 | <%= audit.id %> has no partial 2 | -------------------------------------------------------------------------------- /proxy/config/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | import_config "#{Mix.env}.exs" 4 | -------------------------------------------------------------------------------- /app/views/audits/route/_destroy.mrkdwn.erb: -------------------------------------------------------------------------------- 1 | Route *<%= audit.name %>* has been deleted -------------------------------------------------------------------------------- /app/views/audits/resource/_destroy.mrkdwn.erb: -------------------------------------------------------------------------------- 1 | Resource *<%= audit.name %>* has been deleted -------------------------------------------------------------------------------- /app/views/audits/metadatum/_destroy.mrkdwn.erb: -------------------------------------------------------------------------------- 1 | Metadatum *<%= audit.name %>* has been deleted 2 | -------------------------------------------------------------------------------- /proxy/elixir_buildpack.config: -------------------------------------------------------------------------------- 1 | # Elixir version 2 | erlang_version=21.0 3 | elixir_version=1.7.2 4 | -------------------------------------------------------------------------------- /app/assets/images/sad.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faberNovel/pericles/HEAD/app/assets/images/sad.gif -------------------------------------------------------------------------------- /app/policies/api_error_instance_policy.rb: -------------------------------------------------------------------------------- 1 | class ApiErrorInstancePolicy < ProjectRelatedPolicy 2 | end 3 | -------------------------------------------------------------------------------- /app/policies/metadatum_instance_policy.rb: -------------------------------------------------------------------------------- 1 | class MetadatumInstancePolicy < ProjectRelatedPolicy 2 | end 3 | -------------------------------------------------------------------------------- /app/policies/resource_instance_policy.rb: -------------------------------------------------------------------------------- 1 | class ResourceInstancePolicy < ProjectRelatedPolicy 2 | end 3 | -------------------------------------------------------------------------------- /app/serializers/id_serializer.rb: -------------------------------------------------------------------------------- 1 | class IdSerializer < ActiveModel::Serializer 2 | attributes :id 3 | end 4 | -------------------------------------------------------------------------------- /app/views/audits/route/_destroy.html.erb: -------------------------------------------------------------------------------- 1 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Pericles",
3 | "description": "Pericles is a tool that makes API specification easier.",
4 | "repository": "https://github.com/applidium/pericles",
5 | "keywords": ["API", "specification", "RESTful", "JSON", "JSON schema", "resource", "route"],
6 | "addons": ["heroku-postgresql", "sendgrid"],
7 | "buildpacks": [
8 | {
9 | "url": "heroku/nodejs"
10 | },
11 | {
12 | "url": "heroku/ruby"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/app/policies/project_related_policy.rb:
--------------------------------------------------------------------------------
1 | class ProjectRelatedPolicy < ApplicationPolicy
2 | [:create?, :update?, :destroy?].each do |action|
3 | define_method action do
4 | project && Pundit.policy(user, project).update?
5 | end
6 | end
7 |
8 | def show?
9 | project && Pundit.policy(user, project).show?
10 | end
11 |
12 | private
13 |
14 | def project
15 | super || (record.respond_to?(:project) ? record.project : nil)
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/view_models/body_error/body_error_view_model.rb:
--------------------------------------------------------------------------------
1 | module BodyError
2 | class BodyErrorViewModel
3 | attr_reader :path, :description
4 |
5 | def initialize(path, description = nil)
6 | @path = path
7 | @description = description
8 | end
9 |
10 | def readable_path
11 | path&.gsub(/\/\d+/, '')
12 | end
13 |
14 | def type
15 | :unknown
16 | end
17 |
18 | def valid?
19 | path.present?
20 | end
21 | end
22 | end
--------------------------------------------------------------------------------
/app/views/code/rest.kotlin.erb:
--------------------------------------------------------------------------------
1 | package <%= android_company_domain_name =%>.<%= project.title.parameterize(separator: '.') =%>.android.data.net.retrofit.model
2 |
3 | @SuppressWarnings("ConstructorParameterNaming")
4 | data class <%= resource.rest_name =%>(
5 | <% resource.resource_attributes.each do |attribute| %>
6 | val <%= attribute.snake_variable_name =%>: <%= attribute.kotlin_type =%><%= (attribute == resource.resource_attributes.last) ? '' : ',' =%>
7 |
8 | <% end %>
9 | )
--------------------------------------------------------------------------------
/app/views/resources/_resource_instances.html.haml:
--------------------------------------------------------------------------------
1 | - if user.can_create? ResourceInstance, project: project
2 | .top
3 | %div
4 | .pull-right
5 | = link_to 'New Mock', new_resource_resource_instance_path(resource), class: "btn btn-primary"
6 |
7 | .margin-top-element
8 | - if resource.resource_instances.any?
9 | - resource.resource_instances.each do |instance|
10 | = render instance
11 | - else
12 | %p
13 | %i= t('.no_mocks_placeholder')
--------------------------------------------------------------------------------
/app/views/resources/edit_attributes.html.haml:
--------------------------------------------------------------------------------
1 | = content_for :title, "Edit Resource #{resource.name}"
2 |
3 | %ol.breadcrumb
4 | %li
5 | = link_to 'Projects', projects_path
6 | %li
7 | = link_to "#{project.title}", project_path(project)
8 | %li
9 | = link_to 'Resources', project_resources_path(project)
10 | %li
11 | = link_to "#{resource.name}", project_resource_path(project, resource)
12 | %li
13 | Attributes
14 |
15 | = render 'attributes_form'
--------------------------------------------------------------------------------
/db/migrate/20190304112548_add_project_id_to_audits.rb:
--------------------------------------------------------------------------------
1 | class AddProjectIdToAudits < ActiveRecord::Migration[5.1]
2 | def up
3 | add_reference :audits, :project, foreign_key: true, index: true, null: true
4 |
5 | Audited::Audit.reset_column_information
6 | Audited::Audit.find_each do |audit|
7 | audit.update(project_id: audit.auditable&.project&.id)
8 | end
9 | end
10 |
11 | def down
12 | remove_reference :audits, :project
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/assets/images/sort.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/view_models/body_error/additional_error_view_model.rb:
--------------------------------------------------------------------------------
1 | module BodyError
2 | class AdditionalErrorViewModel < BodyErrorViewModel
3 | attr_reader :additional_list
4 |
5 | def initialize(path, additional_list)
6 | super(path)
7 | @additional_list = additional_list
8 | end
9 |
10 | def description
11 | "#{path} - additional properties #{@additional_list}"
12 | end
13 |
14 | def type
15 | :additional
16 | end
17 | end
18 | end
--------------------------------------------------------------------------------
/db/migrate/20190715164030_add_internal_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddInternalToUsers < ActiveRecord::Migration[5.1]
2 | class User < ActiveRecord::Base
3 | end
4 |
5 | def up
6 | add_column :users, :internal, :boolean, default: false
7 |
8 | if ENV['INTERNAL_EMAIL_DOMAIN']
9 | User.where('email LIKE ?', "%#{ENV['INTERNAL_EMAIL_DOMAIN']}").update_all(internal: true)
10 | end
11 | end
12 |
13 | def down
14 | remove_column :users, :internal
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/lib/bootstrap_form/regex.rb:
--------------------------------------------------------------------------------
1 | module BootstrapForm
2 | module RegexFormBuilder
3 | extend ActiveSupport::Concern
4 |
5 | included do
6 | bootstrap_method_alias :regex_field
7 | end
8 |
9 | def regex_field_with_bootstrap(name, options = {})
10 | form_group_builder(name, options) do
11 | prepend_and_append_input(options) do
12 | regex_field_without_bootstrap(name, options)
13 | end
14 | end
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/test/factories/attributes.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 |
3 | sequence :attribute_name do |n|
4 | "Attribute #{n}"
5 | end
6 |
7 | factory :attribute do
8 | name { generate(:attribute_name) }
9 | association :parent_resource, factory: :resource
10 | primitive_type :integer
11 |
12 | trait :with_resource do
13 | primitive_type nil
14 | resource
15 | end
16 |
17 | factory :attribute_with_resource, traits: [:with_resource]
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/views/projects/_api_gateway_integration_fields.html.haml:
--------------------------------------------------------------------------------
1 | %h3 API Gateway Integration
2 |
3 | %p If you do not use API Gateway Integration, keep following fields blank.
4 |
5 | .fields.nested-fields
6 | = f.text_field :title, placeholder: 'Title replacement or configurable title (eg: "${PROJECT_TITLE}")'
7 | = f.text_field :uri_prefix, placeholder: 'Prefix to add to each uri (ex: "http://example.com/context/" or "${PROJECT_URI}")'
8 | = f.number_field :timeout_in_millis, placeholder: 29000
--------------------------------------------------------------------------------
/app/assets/images/reset.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/app/services/json_schema_builder.rb:
--------------------------------------------------------------------------------
1 | class JSONSchemaBuilder
2 | def initialize(object, options = {})
3 | @object = object
4 | @is_collection = options[:is_collection]
5 | @root_key = options[:root_key]
6 | @metadata_responses = options[:metadata_responses] || []
7 | end
8 |
9 | def execute
10 | return if @object.nil?
11 |
12 | JSONSchemaWrapper.new(
13 | @object.json_schema, @root_key, @is_collection, @metadata_responses
14 | ).execute
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/bin/webpack:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "bundler/setup"
11 |
12 | require "webpacker"
13 | require "webpacker/webpack_runner"
14 |
15 | APP_ROOT = File.expand_path("..", __dir__)
16 | Dir.chdir(APP_ROOT) do
17 | Webpacker::WebpackRunner.run(ARGV)
18 | end
19 |
--------------------------------------------------------------------------------
/app/views/users/show.html.haml:
--------------------------------------------------------------------------------
1 | = content_for :title, "User #{current_user.name}"
2 |
3 | -# br tag required otherwise navbar is over page content (cf media tag and margin-top) (Charles-Elie SIMON, 01/09/2017)
4 | %br
5 | .media
6 | .media-left
7 | %img.media-object.img-rounded{ src: "#{current_user.avatar_url}", height: "42", width: "42" }
8 | .media-body
9 | %h1.media-heading= current_user.name
10 |
11 | .row
12 | .col-xs-12
13 | %dl
14 | %dt Email
15 | %dd= current_user.email
--------------------------------------------------------------------------------
/test/factories/reports.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 |
3 | sequence :url_sequence do |n|
4 | "/users/#{n}"
5 | end
6 |
7 | factory :report do
8 | url { generate(:url_sequence) }
9 | response_status_code 200
10 | response_body '{"user": {"id": 1}}'
11 | response_headers {{
12 | 'Content-Length' => 19
13 | }}
14 | request_body '{}'
15 | request_headers {{
16 | 'Accept-Encoding' => 'gzip'
17 | }}
18 | route
19 | project
20 | response
21 | end
22 | end
--------------------------------------------------------------------------------
/bin/webpack-dev-server:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "bundler/setup"
11 |
12 | require "webpacker"
13 | require "webpacker/dev_server_runner"
14 |
15 | APP_ROOT = File.expand_path("..", __dir__)
16 | Dir.chdir(APP_ROOT) do
17 | Webpacker::DevServerRunner.run(ARGV)
18 | end
19 |
--------------------------------------------------------------------------------
/db/migrate/20170919124934_add_root_key_to_responses.rb:
--------------------------------------------------------------------------------
1 | class Response < ApplicationRecord
2 | end
3 |
4 | class AddRootKeyToResponses < ActiveRecord::Migration[5.0]
5 | def change
6 | add_column :responses, :root_key, :string
7 |
8 | Response.find_each do |r|
9 | begin
10 | r.root_key = ActiveSupport::JSON.decode(r.body_schema).fetch('required', nil)&.first
11 | rescue JSON::ParserError
12 | r.root_key = ''
13 | end
14 | r.save
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/views/resources/index.html.haml:
--------------------------------------------------------------------------------
1 | = content_for :title, "Resources"
2 |
3 | - content_for :page_scripts do
4 | = javascript_pack_tag("resources/index.js")
5 |
6 | %ol.breadcrumb
7 | %li
8 | = link_to 'Projects', projects_path
9 | %li
10 | = link_to "#{project.title}", project_path(project)
11 | %li
12 | Resources
13 |
14 | #resources
15 | %ps-resources{':resources': 'displayedResources', ':tree-mode': 'treeMode', ':query': 'query'}
16 |
17 | %ps-unused-resources{':resources': 'unusedResources'}
--------------------------------------------------------------------------------
/app/decorators/authorization.rb:
--------------------------------------------------------------------------------
1 | module Authorization
2 | extend ActiveSupport::Concern
3 |
4 | def can_create?(record_class, options = {})
5 | Pundit.policy(UserContext.new(object, options[:project]), record_class).create?
6 | end
7 |
8 | def can_edit?(record)
9 | Pundit.policy(object, record).edit?
10 | end
11 |
12 | def can_update?(record)
13 | Pundit.policy(object, record).update?
14 | end
15 |
16 | def can_delete?(record)
17 | Pundit.policy(object, record).destroy?
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/decorators/resource_instance_decorator.rb:
--------------------------------------------------------------------------------
1 | class ResourceInstanceDecorator < Draper::Decorator
2 | delegate_all
3 |
4 | def body_errors_by_representations
5 | object.errors[:instance].each_with_index.reduce({}) do |hash, (errors, i)|
6 | representation = object.parent.resource_representations[i]
7 | body_errors = errors.split("\n").map { |e| BodyError::BodyErrorViewModel.new(e) }
8 |
9 | hash.merge({ representation => BodyError::ViewModels.new(*body_errors) })
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/views/routes/_request_query_parameter_fields.html.haml:
--------------------------------------------------------------------------------
1 | %tr.fields.nested-fields
2 | %td
3 | = f.text_field :name, hide_label: true, placeholder: t('.name_placeholder')
4 | %td
5 | = f.select :primitive_type, QueryParameter.primitive_types.keys.to_a, include_blank: true, hide_label: true
6 | %td
7 | = f.check_box :is_optional, hide_label: true
8 | %td
9 | = f.text_area :description, hide_label: true, placeholder: t('.description_placeholder')
10 | %td
11 | = link_to_remove_association "Remove", f
--------------------------------------------------------------------------------
/db/migrate/20171030094823_remove_custom_fields_from_resource_representation.rb:
--------------------------------------------------------------------------------
1 | class RemoveCustomFieldsFromResourceRepresentation < ActiveRecord::Migration[5.0]
2 | def change
3 | remove_column :attributes_resource_representations, :custom_enum, :string
4 | remove_column :attributes_resource_representations, :custom_pattern, :string
5 | remove_column :attributes_resource_representations, :custom_nullable, :boolean
6 | remove_column :attributes_resource_representations, :custom_faker_id, :boolean
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20180223163412_drop_attribute_fakers.rb:
--------------------------------------------------------------------------------
1 | class DropAttributeFakers < ActiveRecord::Migration[5.1]
2 | def up
3 | remove_reference :attributes, :faker, foreign_key: {to_table: :attribute_fakers}, index: true
4 |
5 | drop_table :attribute_fakers
6 | end
7 |
8 | def down
9 | create_table :attribute_fakers do |t|
10 | t.string :name
11 |
12 | t.timestamps
13 | end
14 |
15 | add_reference :attributes, :faker, foreign_key: {to_table: :attribute_fakers}, index: true
16 | end
17 | end
--------------------------------------------------------------------------------
/test/models/header_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class HeaderTest < ActiveSupport::TestCase
4 | test "shouldn't exist without a name" do
5 | assert_not build(:header, name: nil).valid?
6 | end
7 |
8 | test "shouldn't exist without a http_message" do
9 | assert_not build(:header, http_message: nil).valid?
10 | end
11 |
12 | test 'Header should be valid with all attributes set correctly' do
13 | assert build(:header, name: 'Content-Type', value: 'application/json').valid?
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/assets/javascripts/generate_instance.js:
--------------------------------------------------------------------------------
1 | function generateJsonInstance(jsonSchema, callback) {
2 | var schema = JSON.parse(jsonSchema);
3 | var data = {
4 | schema: schema
5 | };
6 | $.ajax({
7 | type: "POST",
8 | url: "/instances",
9 | data: JSON.stringify(data),
10 | contentType: "application/json",
11 | dataType: "json"
12 | })
13 | .done(function(data) {
14 | callback(data);
15 | })
16 | .fail(function(data) {
17 | console.log(data);
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/app/serializers/attribute_serializer.rb:
--------------------------------------------------------------------------------
1 | class AttributeSerializer < ActiveModel::Serializer
2 | attributes :id, :name, :description, :is_array, :primitive_type,
3 | :resource_id, :enum, :minimum, :maximum, :nullable, :scheme,
4 | :min_items, :max_items, :readable_type
5 |
6 | has_many :available_resource_representations
7 |
8 | def readable_type
9 | object.decorate.readable_type
10 | end
11 |
12 | def available_resource_representations
13 | object.resource&.resource_representations
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20171115165949_rename_mock_instance_to_resource_instance.rb:
--------------------------------------------------------------------------------
1 | class RenameMockInstanceToResourceInstance < ActiveRecord::Migration[5.0]
2 | def change
3 | rename_table :mock_instances, :resource_instances
4 | rename_table :mock_instances_pickers, :mock_pickers_resource_instances
5 | rename_column :mock_pickers_resource_instances, :mock_instance_id, :resource_instance_id
6 |
7 | remove_column :responses, :mock_instance_id, :integer
8 | remove_column :mock_pickers, :mock_instance_id
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/test/helpers/validations_helper_test.rb:
--------------------------------------------------------------------------------
1 | class ValidationsHelperTest < ActionView::TestCase
2 | test 'format_json_text should return a pretty json if text is valid json' do
3 | json = '{}'
4 | parsed_json = JSON.parse(json)
5 | expected = JSON.stable_pretty_generate(parsed_json)
6 | assert_equal expected, format_json_text(json)
7 | end
8 |
9 | test 'format_json_text should return the input text if not valid json' do
10 | text = '{ abcdefg }'
11 | assert_equal text, format_json_text(text)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require "rubygems"
8 | require "bundler"
9 |
10 | match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)
11 | if match
12 | Gem.paths = { "GEM_PATH" => [Bundler.bundle_path.to_s, *Gem.path].uniq }
13 | gem "spring", match[1]
14 | require "spring/binstub"
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/view_models/body_error/enum_error_view_model.rb:
--------------------------------------------------------------------------------
1 | module BodyError
2 | class EnumErrorViewModel < BodyErrorViewModel
3 | attr_reader :current_value, :target_values
4 |
5 | def initialize(path, current_value, target_values)
6 | super(path)
7 | @current_value = current_value
8 | @target_values = target_values
9 | end
10 |
11 | def description
12 | "#{path} value is #{current_value} and should be #{target_values}"
13 | end
14 |
15 | def type
16 | :enum
17 | end
18 | end
19 | end
20 |
21 |
--------------------------------------------------------------------------------
/config/webpack/environment.js:
--------------------------------------------------------------------------------
1 | const { environment } = require('@rails/webpacker')
2 | const { VueLoaderPlugin } = require('vue-loader')
3 |
4 | const vue = require('./loaders/vue')
5 | const vueSvgLoader = require('./loaders/vue-svg-loader')
6 | const pug = require('./loaders/pug')
7 |
8 | environment.plugins.append('VueLoaderPlugin', new VueLoaderPlugin())
9 | environment.loaders.append('vue', vue)
10 | environment.loaders.append('vue-svg-loader', vueSvgLoader)
11 | environment.loaders.append('pug', pug)
12 |
13 | module.exports = environment
14 |
--------------------------------------------------------------------------------
/app/views/shared/_head.html.haml:
--------------------------------------------------------------------------------
1 | %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
2 | %title= "Pericles - " + yield(:title)
3 | = stylesheet_link_tag 'application', media: 'all'
4 | %link{href: "https://fonts.googleapis.com/css?family=Open+Sans:400,600", rel: "stylesheet"}
5 | %link{rel: "stylesheet", href: "//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css"}
6 | %script{src: "//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"}
7 | = javascript_include_tag 'application'
8 | = csrf_meta_tags
--------------------------------------------------------------------------------
/db/migrate/20171226152120_add_key_name_to_attributes_resource_representation.rb:
--------------------------------------------------------------------------------
1 | class AddKeyNameToAttributesResourceRepresentation < ActiveRecord::Migration[5.0]
2 | def change
3 | add_column :attributes_resource_representations, :key_name, :string
4 |
5 | AttributesResourceRepresentation.find_each do |attributes_resource_representation|
6 | attributes_resource_representation.key_name = attributes_resource_representation.resource_attribute.default_key_name
7 | attributes_resource_representation.save
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/test/factories/users.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 |
3 | sequence :email do |n|
4 | "person#{n}#{User::INTERNAL_EMAIL_DOMAIN}"
5 | end
6 |
7 | sequence :external_email do |n|
8 | "person#{n}@external.com"
9 | end
10 |
11 | factory :user do
12 | email { generate(:email) }
13 | password Devise.friendly_token[0,20]
14 | first_name 'John'
15 | last_name 'Smith'
16 |
17 | trait :external do
18 | email { generate(:external_email) }
19 | end
20 |
21 | after(:create) { |user| user.confirm }
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/controllers/validations_controller.rb:
--------------------------------------------------------------------------------
1 | class ValidationsController < ApplicationController
2 | def index
3 | @validations = Validation.order(created_at: 'desc').page(params[:page]).per(10)
4 | end
5 |
6 | def new
7 | @default_json_instance = '{}'
8 | end
9 |
10 | def create
11 | validation = Validation.create(validation_params)
12 | render json: validation, status: :created
13 | end
14 |
15 | private
16 |
17 | def validation_params
18 | params.require(:validation).permit(:json_schema, :json_instance)
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/db/migrate/20170919080631_create_fakers.rb:
--------------------------------------------------------------------------------
1 | class CreateFakers < ActiveRecord::Migration[5.0]
2 | def change
3 | create_table :fakers do |t|
4 | t.string :name
5 |
6 | t.timestamps
7 | end
8 |
9 | add_reference :attributes, :faker, foreign_key: true
10 | add_column :attributes_resource_representations, :custom_faker_id, :integer, index: true
11 | add_foreign_key :attributes_resource_representations, :fakers, column: :custom_faker_id
12 | remove_column :attributes_resource_representations, :faker, :string
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/controllers/audits_controller.rb:
--------------------------------------------------------------------------------
1 | class AuditsController < ApplicationController
2 | include Authenticated
3 | include ProjectRelated
4 |
5 | def index
6 | @audits = AuditsRepository.new
7 | .news_of_project(project)
8 | .page(params[:page]).per(200)
9 | end
10 |
11 | def slack_post
12 | news_posted_count = PostSlackNews.new(project).execute
13 | redirect_to project_audits_path(project), notice: t('.news_posted_on', news_posted_count: news_posted_count, slack_channel: project.slack_channel)
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/decorators/code/attribute_decorator.rb:
--------------------------------------------------------------------------------
1 | class Code::AttributeDecorator < Draper::Decorator
2 | include Code::Field
3 | delegate_all
4 | decorates_association :resource, with: Code::ResourceDecorator
5 |
6 | def code_nullable
7 | @code_nullable ||= object.nullable || parent_resource.resource_representations.any? { |rep| rep.attributes_resource_representations.none? { |a| a.attribute_id == id } }
8 | end
9 |
10 | def graphql_nullable
11 | object.nullable
12 | end
13 |
14 | def key_name
15 | object.default_key_name
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/models/metadatum.rb:
--------------------------------------------------------------------------------
1 | class Metadatum < ApplicationRecord
2 | include HasPrimitiveType
3 |
4 | belongs_to :project, required: true
5 |
6 | has_many :metadata_responses, dependent: :destroy
7 | has_many :responses, through: :metadata_responses
8 | has_many :metadatum_instances
9 |
10 | validates :name, presence: true, uniqueness: { scope: :project, case_sensitive: true }
11 | validates :primitive_type, presence: true
12 |
13 | audited
14 |
15 | def json_schema
16 | JSONSchema::MetadatumDecorator.new(self).json_schema
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/config/initializers/audited.rb:
--------------------------------------------------------------------------------
1 | module Audited
2 | class Audit < ::ActiveRecord::Base
3 | belongs_to :project, optional: true
4 |
5 | scope :of_project, ->(project) { where(project: project) }
6 |
7 | before_create :set_project_id
8 |
9 | def decorator_class
10 | "News::#{auditable_type}AuditDecorator".constantize
11 | rescue NameError
12 | News::AuditDecorator
13 | end
14 |
15 | private
16 |
17 | def set_project_id
18 | self.project_id ||= auditable&.project&.id
19 | true
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/db/migrate/20170310132514_create_attributes.rb:
--------------------------------------------------------------------------------
1 | class CreateAttributes < ActiveRecord::Migration[5.0]
2 | def change
3 | create_table :attributes do |t|
4 | t.string :name
5 | t.text :description
6 | t.json :example
7 | t.integer :parent_resource_id
8 | t.boolean :is_array, null: false, default: false
9 | t.integer :primitive_type
10 | t.references :resource, foreign_key: true
11 |
12 | t.timestamps
13 | end
14 |
15 | add_foreign_key :attributes, :resources, column: :parent_resource_id
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/javascript/packs/resources/ps-unused-resources.vue:
--------------------------------------------------------------------------------
1 |
2 | div(v-if='resources && resources.length > 0')
3 | h3 Unused Resources
4 |
5 | .list.margin-top-element
6 | ps-resource(
7 | v-for='resource in resources'
8 | :resource='resource'
9 | depth='0'
10 | )
11 |
12 |
13 |
21 |
22 |
24 |
--------------------------------------------------------------------------------
/app/decorators/http/response_decorator.rb:
--------------------------------------------------------------------------------
1 | class HTTP::ResponseDecorator < Draper::Decorator
2 | delegate_all
3 |
4 | def body
5 | content = object.body.to_s
6 | return content if content.empty?
7 |
8 | case object.headers[:content_encoding]
9 | # NOTE: Clément Villain 28/12/17
10 | # 'deflate' seems to be a nightmare because of different implementations
11 | # and seems to not be used by webserver
12 | when 'gzip'
13 | Zlib::GzipReader.new(StringIO.new(content)).read
14 | else
15 | object.body
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/views/code/serializer.ruby.erb:
--------------------------------------------------------------------------------
1 | class <%= resource.pascal_name %>Serializer < ActiveModel::Serializer
2 | attributes(
3 | <% resource.resource_attributes.select(&:primitive_type).each do |attribute| %>
4 | :<%= attribute.snake_variable_name %>,
5 | <% end %>
6 | )
7 |
8 | <% resource.resource_attributes.reject(&:primitive_type).sort_by { |a| a.is_array ? 0 : 1 }.each do |attribute| %>
9 | <%= attribute.is_array ? 'has_many' : 'belongs_to' %> :<%= attribute.snake_variable_name %>, serializer: <%= attribute.resource.pascal_name %>Serializer
10 | <% end %>
11 | end
12 |
--------------------------------------------------------------------------------
/app/views/metadatum_instances/_metadatum_instance.html.haml:
--------------------------------------------------------------------------------
1 | .flexspace-and-center
2 | .flex
3 | = metadatum_instance.name
4 | .flex{class: ('invalid-instance' unless metadatum_instance.valid?)}
5 | = metadatum_instance.body
6 | %flexcontainer
7 | = link_to 'Delete', polymorphic_path(metadatum_instance), method: :delete, data: { confirm: 'Are you sure?' }, class: "btn btn-default h-margin" if user.can_delete? metadatum_instance
8 | = link_to 'Edit', edit_polymorphic_path(metadatum_instance), class: "btn btn-primary" if user.can_edit? metadatum_instance
9 |
10 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/app/javascript/packs/application.js:
--------------------------------------------------------------------------------
1 | /* eslint no-console:0 */
2 | // This file is automatically compiled by Webpack, along with any other files
3 | // present in this directory. You're encouraged to place your actual application logic in
4 | // a relevant structure within app/javascript and only use these pack files to reference
5 | // that code so it'll be compiled.
6 | //
7 | // To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
8 | // layout file, like app/views/layouts/application.html.erb
9 |
10 | console.log('Hello World from Webpacker')
11 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
7 | # Mayor.create(name: 'Emanuel', city: cities.first)
8 |
9 | Scheme.create(name: 'date-time')
10 | Scheme.create(name: 'email')
11 | Scheme.create(name: 'hostname')
12 | Scheme.create(name: 'ipv4')
13 | Scheme.create(name: 'ipv6')
14 | Scheme.create(name: 'uri')
15 |
--------------------------------------------------------------------------------
/app/decorators/json_schema/attribute_decorator.rb:
--------------------------------------------------------------------------------
1 | module JSONSchema
2 | class AttributeDecorator < Draper::Decorator
3 | delegate_all
4 |
5 | def primitive_type
6 | if datetime? || date?
7 | 'string'
8 | elsif object.any?
9 | %w[string number integer boolean null array object]
10 | else
11 | object.primitive_type
12 | end
13 | end
14 |
15 | def scheme
16 | return Scheme.new(name: 'datetime') if datetime?
17 | return Scheme.new(name: 'date') if date?
18 | object.scheme
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/services/generate_json_instance_service.rb:
--------------------------------------------------------------------------------
1 | class GenerateJsonInstanceService
2 | def initialize(schema)
3 | @schema = schema.is_a?(String) ? JSON.parse(schema) : schema
4 | end
5 |
6 | def execute
7 | return '' if @schema.nil?
8 |
9 | js_file = File.join(Rails.root, 'lib', 'node', 'generate_mock.js')
10 | thread = Thread.new do
11 | JSON.parse(Terrapin::CommandLine.new('node', ':js :schema').run(js: js_file, schema: @schema.to_json))
12 | end
13 | thread.join(5)&.value || { error: 'A loop prevents this json to be generated' }
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/views/security_schemes/index.html.haml:
--------------------------------------------------------------------------------
1 | = content_for :title, "Security Schemes"
2 | - content_for :page_scripts do
3 | = javascript_pack_tag("common/markdown.js")
4 |
5 | .flexspace-and-center
6 | %h1 Security Schemes
7 | = link_to 'New Security Scheme', new_project_security_scheme_path(project), class: "btn btn-primary btn-lg" if user.can_create? Route, project: project
8 |
9 | .margin-top-element
10 | - if @project.security_schemes.empty?
11 | %p= t('.no_security_scheme')
12 | - @project.security_schemes.each do |security_scheme|
13 | = render security_scheme
14 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | development:
8 | adapter: postgresql
9 | database: Pericles_GW_dev
10 | pool: 5
11 | timeout: 5000
12 |
13 | # Warning: The database defined as "test" will be erased and
14 | # re-generated from your development database when you run "rake".
15 | # Do not set this db to the same as development or production.
16 | test:
17 | adapter: postgresql
18 | database: Pericles_GW_test
19 | pool: 5
20 | timeout: 5000
21 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 | Mime::Type.register "text/markdown", :md
6 | Mime::Type.register "application/schema+json", :json_schema
7 | Mime::Type.register "text/plain", :kotlin
8 | Mime::Type.register "text/plain", :swift
9 | Mime::Type.register "text/plain", :ruby
10 | Mime::Type.register "text/plain", :typescript
11 | Mime::Type.register "text/plain", :graphql
12 | Mime::Type.register "application/x-yaml", :swagger
13 |
--------------------------------------------------------------------------------
/app/helpers/sidebar_helper.rb:
--------------------------------------------------------------------------------
1 | module SidebarHelper
2 | def active_class(controller_class)
3 | controller_class == controller.class ? 'active' : ''
4 | end
5 |
6 | def push_class(pusher)
7 | should_add_push_class = (
8 | (pusher == 'Schemes' && (controller.class == ProjectsController || controller.class.included_modules.include?(ProjectRelated))) ||
9 | (pusher == 'JSON Validation' && (controller.class == SchemesController)) ||
10 | (pusher == 'Logout' && (controller.class == ValidationsController))
11 | )
12 | should_add_push_class ? 'push' : ''
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/helpers/validations_helper.rb:
--------------------------------------------------------------------------------
1 | module ValidationsHelper
2 | def validation_string_for_status(status)
3 | t("activerecord.attributes.validation.status.#{status}")
4 | end
5 |
6 | def format_json_text(text)
7 | parsed_json = JSON.parse(text)
8 | JSON.stable_pretty_generate(parsed_json)
9 | rescue JSON::ParserError
10 | text
11 | end
12 |
13 | def self.parse_json_text(text)
14 | if text.is_a? String
15 | begin
16 | JSON.parse(text)
17 | rescue JSON::ParserError
18 | text
19 | end
20 | else
21 | text
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/services/abstract_zip_builder.rb:
--------------------------------------------------------------------------------
1 | require 'zip'
2 |
3 | class AbstractZipBuilder
4 | def collection
5 | raise 'implement me!'
6 | end
7 |
8 | def filename(_object)
9 | raise 'implement me!'
10 | end
11 |
12 | def file_content(_object)
13 | raise 'implement me!'
14 | end
15 |
16 | def zip_data
17 | stringio = Zip::OutputStream.write_buffer do |zio|
18 | collection.each do |object|
19 | zio.put_next_entry(filename(object))
20 | zio.write file_content(object)
21 | end
22 | end
23 | stringio.rewind
24 | stringio.sysread
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/javascript/packs/resources/index.js:
--------------------------------------------------------------------------------
1 | import 'core-js/fn/promise/finally';
2 |
3 | import Vue from 'vue/dist/vue.esm'
4 |
5 | import Store from './store.js';
6 | import ResourcesComponent from './ps-resources.vue';
7 | import UnsedResourcesComponent from './ps-unused-resources.vue';
8 |
9 |
10 | document.addEventListener('DOMContentLoaded', () => {
11 | const app = new Vue({
12 | el: '#resources',
13 | data: Store.state,
14 | components: {
15 | 'ps-resources': ResourcesComponent,
16 | 'ps-unused-resources': UnsedResourcesComponent
17 | }
18 | });
19 |
20 | Store.init();
21 | });
--------------------------------------------------------------------------------
/app/validators/json_object_validator.rb:
--------------------------------------------------------------------------------
1 |
2 | class JsonObjectValidator < ActiveModel::EachValidator
3 | def validate_each(record, attribute, value)
4 | if value.blank?
5 | record.errors.add(attribute, :json_object_cannot_be_blank)
6 | return
7 | end
8 |
9 | begin
10 | parsed_json = JSON.parse(value)
11 | rescue JSON::ParserError
12 | record.errors.add(attribute, :could_not_parse_json)
13 | return
14 | end
15 |
16 | unless parsed_json.is_a? Hash
17 | record.errors.add(attribute, :json_must_be_an_object)
18 | return
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/validators/json_schema_validator.rb:
--------------------------------------------------------------------------------
1 | class JsonSchemaValidator < ActiveModel::EachValidator
2 | def validate_each(record, attribute, value)
3 | return unless value
4 | begin
5 | JSON::Validator.validate!(META_SCHEMA, value, json: true)
6 | rescue JSON::Schema::JsonParseError, JSON::Schema::ValidationError => e
7 | record.errors.add(attribute, :schema_must_be_valid_json) if e.class == JSON::Schema::JsonParseError
8 | record.errors.add(attribute, :schema_must_respect_json_schema_spec, { error: e.message }) if e.class == JSON::Schema::ValidationError
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/test/lib/tasks/proxy_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class CleanOldReportsTest < ActiveSupport::TestCase
4 | def setup
5 | @new_report = create(:report)
6 | travel_to Date.current - 1.month
7 | @old_report = create(:report)
8 | travel_back
9 | Pericles_GWGw::Application.load_tasks
10 | Rake::Task['proxy:clean_reports'].execute
11 | end
12 |
13 | test 'new report is not deleted' do
14 | assert_equal @new_report, Report.find_by(id: @new_report.id)
15 | end
16 |
17 | test 'old report is deleted' do
18 | assert_nil Report.find_by(id: @old_report.id)
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/db/migrate/20190912182400_create_security_schemes.rb:
--------------------------------------------------------------------------------
1 | class CreateSecuritySchemes < ActiveRecord::Migration[5.1]
2 | def up
3 | create_table :security_schemes do |t|
4 | t.string :key
5 | t.string :security_scheme_type
6 | t.string :name
7 | t.string :security_scheme_in
8 | t.text :parameters
9 | t.references :project, foreign_key: true
10 | end
11 |
12 | add_reference :routes, :security_scheme, references: :security_schemes
13 | end
14 |
15 | def down
16 | remove_reference :routes, :security_scheme
17 |
18 | drop_table :security_schemes
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/proxy/lib/pericles_proxy.ex:
--------------------------------------------------------------------------------
1 | defmodule PericlesProxy do
2 | @moduledoc """
3 | Documentation for PericlesProxy.
4 | """
5 |
6 | use Application
7 | require Logger
8 |
9 | def start(_type, _args) do
10 | children = [
11 | {
12 | Plug.Adapters.Cowboy2,
13 | scheme: :http,
14 | plug: PericlesProxy.Router,
15 | options: [port: String.to_integer(System.get_env("PORT") || "8080")]
16 | },
17 | PericlesProxy.Repo
18 | ]
19 |
20 | Logger.info("Started application")
21 |
22 | Supervisor.start_link(children, strategy: :one_for_one)
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/views/projects/index.html.haml:
--------------------------------------------------------------------------------
1 | = content_for :title, "Projects"
2 |
3 | - if user.can_create? Project
4 | .row.margin-top-element
5 | .col-xs-12.flexcontainer.flexcontainer-justify-end
6 | = link_to 'New Project', new_project_path, class: "btn btn-primary btn-lg"
7 | .row.margin-top-element
8 | .col-xs-12
9 | .list-group
10 | - @projects.each do |project|
11 | %a{class: "list-group-item", href: project_path(project)}
12 | = project.title
13 | - if current_user.nil?
14 | %p= t('.visitor_see_only_public', href: link_to(t('actions.sign_in'), new_user_session_path)).html_safe
15 |
--------------------------------------------------------------------------------
/app/views/instances/_instance.html.haml:
--------------------------------------------------------------------------------
1 | .row
2 | .col-xs-6
3 | %div
4 | %strong= instance.name
5 | %pre
6 | %code.mock_instance_text_area{disabled: 'true', class: ('invalid-instance' unless instance.valid?)}=format_json(instance.body)
7 | .col-xs-6
8 | .flexcontainer.flexcontainer-justify-end
9 | .btn-group{ role: "group" }
10 | = link_to 'Edit', edit_polymorphic_path(instance), class: "btn btn-primary" if user.can_edit? instance
11 | = link_to 'Delete', polymorphic_path(instance), method: :delete, data: { confirm: 'Are you sure?' }, class: "btn btn-primary" if user.can_delete? instance
12 |
--------------------------------------------------------------------------------
/app/decorators/report_decorator.rb:
--------------------------------------------------------------------------------
1 | class ReportDecorator < Draper::Decorator
2 | delegate_all
3 |
4 | def body_errors
5 | factory = BodyError::ViewModelFactory.new
6 | body_error_view_models = object.body_errors.map do |e|
7 | e.description.split("\n")
8 | end.flatten.map do |description|
9 | factory.build(description)
10 | end
11 | BodyError::ViewModels.new(*body_error_view_models)
12 | end
13 |
14 | def tr_class
15 | return 'in_validation' unless object.validated?
16 | return 'missing' if object.route.blank? || object.response.blank?
17 | object.correct? ? '' : 'invalid'
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/policies/mock_profile_policy.rb:
--------------------------------------------------------------------------------
1 | class MockProfilePolicy < ProjectRelatedPolicy
2 | def edit?
3 | show?
4 | end
5 |
6 | def permitted_attributes
7 | [
8 | :name,
9 | :parent_id,
10 | mock_pickers_attributes: [
11 | :id,
12 | :body_pattern,
13 | :url_pattern,
14 | :resource_instance_ids,
15 | :api_error_instance_ids,
16 | :response_id,
17 | :instances_number,
18 | :priority,
19 | :_destroy,
20 | resource_instance_ids: [],
21 | api_error_instance_ids: [],
22 | metadatum_instance_ids: []
23 | ]
24 | ]
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/views/api_errors/_new_form.html.haml:
--------------------------------------------------------------------------------
1 | = bootstrap_form_for([project, api_error]) do |f|
2 | .flexcontainer-justify-end= f.submit class: 'btn btn-primary'
3 |
4 | = f.text_field :name
5 |
6 | = f.text_area :json_schema, class: 'json'
7 |
8 | .alert.alert-warning= t('.json_instance_explanation')
9 |
10 | .form-group{class: @json_instance_error.blank? ? '' : 'has-error'}
11 | %label.control-label From JSON (BETA)
12 | = text_area_tag(:json_instance, @json_instance, class: 'form-control json', placeholder: format_json({code: 1, message: 'Error'}.to_json))
13 | - unless @json_instance_error.blank?
14 | %span.help-block= @json_instance_error
15 |
--------------------------------------------------------------------------------
/test/models/api_error_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class AttributeTest < ActiveSupport::TestCase
4 | test 'must have a valid json schema' do
5 | assert_not build(:api_error, json_schema: '').valid?
6 | assert_not build(:api_error, json_schema: '{{').valid?
7 | assert_not build(:api_error, json_schema: '{"id": 1}').valid?
8 |
9 | assert build(:api_error, json_schema: '{}').valid?
10 | end
11 |
12 | test 'destroy should delete instances' do
13 | api_error_instance = create(:api_error_instance)
14 |
15 | assert_difference 'ApiErrorInstance.count', -1 do
16 | api_error_instance.api_error.destroy
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/views/metadata/_metadatum.html.haml:
--------------------------------------------------------------------------------
1 | .flexspace-and-center
2 | %dl.flex
3 | %dt Name
4 | %dd
5 | %strong= metadatum.name
6 | %dl.flex
7 | %dt Type
8 | %dd
9 | %strong= metadatum.primitive_type
10 | .btn-group{ role: "group" }
11 | = link_to 'Show mocks', project_metadatum_path(project, metadatum), class: 'btn btn-primary' if !local_assigns.key? :show || local_assigns[:show]
12 | = link_to 'Edit', edit_metadatum_path(metadatum), class: 'btn btn-primary' if user.can_edit? metadatum
13 | = link_to 'Delete', metadatum_path(metadatum), method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-primary' if user.can_delete? metadatum
--------------------------------------------------------------------------------
/app/decorators/json_schema/metadatum_decorator.rb:
--------------------------------------------------------------------------------
1 | module JSONSchema
2 | class MetadatumDecorator < Draper::Decorator
3 | delegate_all
4 |
5 | def json_schema
6 | non_nullable_property = (datetime? || date?) ? { type: :string, format: primitive_type } : { type: primitive_type }
7 | if nullable
8 | if should_use_nullable
9 | non_nullable_property.merge(nullable: true)
10 | else
11 | { anyOf: [non_nullable_property, { type: 'null' }] }
12 | end
13 | else
14 | non_nullable_property
15 | end
16 | end
17 |
18 | def should_use_nullable
19 | context[:use_nullable]
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/config/locales/fr.yml:
--------------------------------------------------------------------------------
1 | fr:
2 | required: Requis
3 | optional: Optionnel
4 | actions:
5 | edit: Éditer
6 | download: Télécharger
7 | delete: Supprimer
8 | sign_in: S'identifier
9 | mailer:
10 | confirmation_grettings: "Bienvenue %{email} !"
11 | confirmation_instructions: 'Vous pouvez confirmer votre compte en cliquant sur le lien ci-dessous:'
12 | confirmation_action: Confirmer mon compte
13 | errors:
14 | messages:
15 | taken: "%{value} n'est pas disponible"
16 | could_not_parse_json: Impossible de parser le JSON
17 | json_object_cannot_be_blank: Un JSON ne peut pas être vide
18 | json_must_be_an_object: Ce JSON doit être un objet
--------------------------------------------------------------------------------
/db/migrate/20170602161118_create_attributes_resource_representations.rb:
--------------------------------------------------------------------------------
1 | class CreateAttributesResourceRepresentations < ActiveRecord::Migration[5.0]
2 | def change
3 | create_table :attributes_resource_representations do |t|
4 | t.boolean :is_required, null: false, default: false
5 | t.string :custom_enum
6 | t.string :custom_pattern
7 | t.references :resource_representation, foreign_key: true, index: false
8 | t.references :attribute, foreign_key: true
9 |
10 | t.timestamps
11 | end
12 |
13 | add_index :attributes_resource_representations, :resource_representation_id, name: 'index_arr_on_resource_representation_id'
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/proxy/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Pericles Proxy",
3 | "description": "Proxy for Pericles, a tool that makes API specification easier.",
4 | "repository": "https://github.com/applidium/pericles/proxy",
5 | "keywords": ["API", "specification", "RESTful", "JSON", "JSON schema", "resource", "route"],
6 | "env": {
7 | "PROJECT_PATH": {
8 | "description": "Required by subdir-heroku-buildpack",
9 | "value": "/proxy"
10 | }
11 | },
12 | "buildpacks": [
13 | {
14 | "url": "https://github.com/timanovsky/subdir-heroku-buildpack"
15 | },
16 | {
17 | "url": "https://github.com/HashNuke/heroku-buildpack-elixir.git"
18 | }
19 | ]
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/test/models/metadata_response_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class MetadataResponseTest < ActiveSupport::TestCase
4 | test "shouldn't exist without a metadatum" do
5 | assert_not build(:metadata_response, metadatum: nil).valid?
6 | end
7 |
8 | test "shouldn't exist without a response" do
9 | assert_not build(:metadata_response, response: nil).valid?
10 | end
11 |
12 | test 'couple metadatum/response should be unique' do
13 | metadata_response = create(:metadata_response)
14 |
15 | assert_not build(:metadata_response,
16 | metadatum: metadata_response.metadatum,
17 | response: metadata_response.response
18 | ).valid?
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/proxy/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where 3rd-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | pericles_proxy-*.tar
24 |
25 |
--------------------------------------------------------------------------------
/test/models/scheme_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class SchemeTest < ActiveSupport::TestCase
4 | test "shouldn't exist without a name" do
5 | assert_not build(:scheme, name: nil).valid?
6 | end
7 |
8 | test 'set scheme to null if destroyed' do
9 | s = create(:scheme)
10 | a = create(:attribute, primitive_type: :string, scheme: s)
11 | assert s.destroy
12 | assert_not a.reload.scheme
13 | end
14 |
15 | test 'is pattern if regexp is set' do
16 | s = create(:scheme, regexp: 'cool')
17 | assert s.pattern?
18 | end
19 |
20 | test 'is format if regexp is not set' do
21 | s = create(:scheme, regexp: nil)
22 | assert s.format?
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/db/migrate/20200417091346_add_confirmable_to_devise.rb:
--------------------------------------------------------------------------------
1 | class AddConfirmableToDevise < ActiveRecord::Migration[5.1]
2 | def up
3 | add_column :users, :confirmation_token, :string
4 | add_column :users, :confirmed_at, :datetime
5 | add_column :users, :confirmation_sent_at, :datetime
6 | add_column :users, :unconfirmed_email, :string # Only if using reconfirmable
7 | add_index :users, :confirmation_token, unique: true
8 | User.update_all confirmed_at: DateTime.now
9 | end
10 |
11 | def down
12 | remove_columns :users, :confirmation_token, :confirmed_at, :confirmation_sent_at
13 | remove_columns :users, :unconfirmed_email # Only if using reconfirmable
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/controllers/users/omniauth_callbacks_controller.rb:
--------------------------------------------------------------------------------
1 | class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
2 | def google_oauth2
3 | # You need to implement the method below in your model (e.g. app/models/user.rb)
4 | @user = User.from_omniauth(request.env['omniauth.auth'])
5 |
6 | if @user&.persisted?
7 | flash[:notice] = I18n.t 'devise.omniauth_callbacks.success', kind: 'Google'
8 | sign_in_and_redirect @user, event: :authentication
9 | else
10 | flash[:alert] = I18n.t 'devise.omniauth_callbacks.failure', kind: 'Google', reason: I18n.t('users.sign_in.check_domain')
11 | redirect_to new_user_session_path
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20170830101250_devise_create_users.rb:
--------------------------------------------------------------------------------
1 | class DeviseCreateUsers < ActiveRecord::Migration[5.0]
2 | def change
3 | create_table :users do |t|
4 | ## Database authenticatable
5 | t.string :email, null: false, default: ""
6 | t.string :encrypted_password, null: false, default: ""
7 |
8 | ## Trackable
9 | t.integer :sign_in_count, default: 0, null: false
10 | t.datetime :current_sign_in_at
11 | t.datetime :last_sign_in_at
12 | t.inet :current_sign_in_ip
13 | t.inet :last_sign_in_ip
14 |
15 | t.timestamps null: false
16 | end
17 |
18 | add_index :users, :email, unique: true
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/decorators/news/route_audit_decorator.rb:
--------------------------------------------------------------------------------
1 | module News
2 | class RouteAuditDecorator < AuditDecorator
3 | def partial_name
4 | if audit.action == 'create'
5 | 'audits/route/create'
6 | elsif audit.action == 'update'
7 | 'audits/route/update'
8 | elsif audit.action == 'destroy'
9 | 'audits/route/destroy'
10 | end
11 | end
12 |
13 | def name
14 | audit.audited_changes['url']
15 | end
16 |
17 | def url
18 | route = audit.auditable or return
19 | h.project_resource_url(route.project, route)
20 | end
21 |
22 | def link_name
23 | route = audit.auditable or return
24 | route.url
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/assets/javascripts/resources/new_representation.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | $('.btn.selected').on('click', function () {
3 | var id = this.id;
4 | var resourceId = document.location.pathname.split('/')[2];
5 | var url = '/resources/' + resourceId + '/resource_representations/' + id + '/random';
6 |
7 | $('#error.error').text('');
8 | $.ajax({
9 | type: "GET",
10 | url: url,
11 | contentType: "application/json",
12 | success: function (data) {
13 | $('#resource_instance_body').val(JSON.stringify(data, null, 2));
14 | },
15 | error: function () {
16 | $('#error.error').text('An error occurred');
17 | }
18 | });
19 | });
20 | });
--------------------------------------------------------------------------------
/app/assets/stylesheets/styles/projects.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the Projects controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
5 | /*!
6 | * Start Bootstrap - Simple Sidebar (http://startbootstrap.com/)
7 | * Copyright 2013-2016 Start Bootstrap
8 | * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE)
9 | */
10 | /* Toggle Styles */
11 |
12 | #project_description {
13 | height: 530px;
14 | }
15 |
16 | .highlight {
17 | background-color: #008afd;
18 | color: white;
19 | }
20 |
21 | #project-show {
22 | .buttons > a {
23 | margin-right: 4px;
24 | }
25 | }
--------------------------------------------------------------------------------
/app/views/resources/_create_form.html.haml:
--------------------------------------------------------------------------------
1 | - content_for :page_scripts do
2 | = javascript_include_tag("resources/form.js")
3 |
4 | = bootstrap_form_for([project, @resource_creation_contract]) do |f|
5 | = render 'shared/errors', object: @resource_creation_contract
6 |
7 | .flexcontainer-justify-end= f.submit class: 'btn btn-primary'
8 |
9 | = f.text_field :name, placeholder: 'MyResource'
10 |
11 | = f.text_area :description
12 |
13 | .alert.alert-warning= t('.json_instance_explanation')
14 |
15 | = f.text_area :json_instance, label: 'From JSON (BETA)', placeholder: format_json({username: 'John Do', email: 'jd@fabernovel.com', first_name: 'John', last_name: 'Do', manager: nil}.to_json), class: 'json'
16 |
--------------------------------------------------------------------------------
/app/controllers/concerns/project_related.rb:
--------------------------------------------------------------------------------
1 | module ProjectRelated
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | decorates_method :project
6 | end
7 |
8 | def project
9 | return @project if defined? @project
10 | @project = begin
11 | project = find_project
12 |
13 | policy = ProjectPolicy.new(current_user, project)
14 | raise Pundit::NotAuthorizedError, query: :show?, record: project, policy: policy unless policy.show?
15 |
16 | project
17 | end
18 | end
19 |
20 | def find_project
21 | Project.find(params[:project_id]) if params.key? :project_id
22 | end
23 |
24 | def pundit_user
25 | UserContext.new(current_user, project)
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/views/code/rest.swift.erb:
--------------------------------------------------------------------------------
1 | //
2 | // <%= resource.rest_name =%>.swift
3 | //
4 | // Generated by Pericles on <%= Date.today.strftime("%d/%m/%Y") =%>
5 |
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | struct <%= resource.rest_name =%> : Decodable {
12 | <% resource.resource_attributes.each do |attribute| %>
13 | let <%= attribute.camel_variable_name =%>: <%= attribute.swift_type =%>
14 |
15 | <% end %>
16 |
17 | enum CodingKeys : String, CodingKey {
18 | <% resource.resource_attributes.each do |attribute| %>
19 | case <%= attribute.camel_variable_name =%><% if attribute.camel_variable_name != attribute.key_name %> = "<%= attribute.key_name =%>"<% end %>
20 | <% end %>
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/decorators/code/attributes_resource_representation_decorator.rb:
--------------------------------------------------------------------------------
1 | class Code::AttributesResourceRepresentationDecorator < Draper::Decorator
2 | include Code::Field
3 | delegate_all
4 | delegate :name, :is_array, :primitive_type, :date?, :datetime?, to: :resource_attribute
5 |
6 | def resource
7 | Code::ResourceRepresentationDecorator.new(resource_representation)
8 | end
9 |
10 | def code_nullable
11 | !object.is_required || object.resource_attribute.nullable
12 | end
13 |
14 | def graphql_nullable
15 | object.resource_attribute.nullable
16 | end
17 |
18 | def boolean?
19 | object.resource_attribute.boolean?
20 | end
21 |
22 | def enum
23 | object.resource_attribute.enum
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/app/controllers/schemes_controller.rb:
--------------------------------------------------------------------------------
1 | class SchemesController < ApplicationController
2 | lazy_controller_of :scheme, helper_method: true
3 |
4 | def index
5 | @schemes = policy_scope(Scheme).all
6 | end
7 |
8 | def new; end
9 |
10 | def edit; end
11 |
12 | def update
13 | if scheme.update(permitted_attributes(scheme))
14 | redirect_to schemes_path
15 | else
16 | render 'edit', status: :unprocessable_entity
17 | end
18 | end
19 |
20 | def create
21 | if scheme.save
22 | redirect_to schemes_path
23 | else
24 | render 'new', status: :unprocessable_entity
25 | end
26 | end
27 |
28 | def destroy
29 | scheme.destroy
30 | redirect_to schemes_path
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/app/views/query_parameters/_query_parameter.html.haml:
--------------------------------------------------------------------------------
1 | .panel.panel-default
2 | .panel-body
3 | .row
4 | .col-xs-4
5 | %dl
6 | %dt Name
7 | %dd
8 | %strong= query_parameter.name
9 | .col-xs-2
10 | %dl
11 | %dt Type
12 | %dd
13 | %strong= query_parameter.primitive_type
14 | .col-xs-2
15 | %dl
16 | %dt Required ?
17 | %dd= query_parameter.is_optional ? t('optional') : t('required')
18 | - unless query_parameter.description.blank?
19 | .col-xs-4
20 | %dl
21 | %dt Description
22 | %dd{class: 'markdown'}
23 | %vue-markdown{source: query_parameter.description}
24 |
--------------------------------------------------------------------------------
/config/locales/validations/fr.yml:
--------------------------------------------------------------------------------
1 | fr:
2 | activerecord:
3 | attributes:
4 | validation:
5 | status:
6 | one: statut
7 | other: statuts
8 | success: "L'instance de JSON entrée est validée par le JSON schema entré (JSON Schema Draft 4)."
9 | schema_parse_error: "Le JSON schema entré n'est pas un JSON text valide (RFC 7159)."
10 | instance_parse_error: "L'instance de JSON entrée n'est pas un JSON text valide (RFC 7159)."
11 | schema_validation_error: "Le JSON schema entré n'est pas conforme au JSON Schema Draft 4. Erreurs:"
12 | instance_validation_error: "L'instance de JSON entrée n'est pas validée par le JSON schema entré (JSON Schema Draft 4). Erreurs:"
13 |
--------------------------------------------------------------------------------
/app/decorators/news/metadatum_audit_decorator.rb:
--------------------------------------------------------------------------------
1 | module News
2 | class MetadatumAuditDecorator < AuditDecorator
3 | def partial_name
4 | if audit.action == 'create'
5 | 'audits/metadatum/create'
6 | elsif audit.action == 'update'
7 | 'audits/metadatum/update'
8 | elsif audit.action == 'destroy'
9 | 'audits/metadatum/destroy'
10 | end
11 | end
12 |
13 | def name
14 | audit.audited_changes['name']
15 | end
16 |
17 | def url
18 | metadatum = audit.auditable or return
19 | h.project_metadata_url(metadatum.project)
20 | end
21 |
22 | def link_name
23 | metadatum = audit.auditable or return
24 | metadatum.name
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/decorators/news/resource_audit_decorator.rb:
--------------------------------------------------------------------------------
1 | module News
2 | class ResourceAuditDecorator < AuditDecorator
3 | def partial_name
4 | if audit.action == 'create'
5 | 'audits/resource/create'
6 | elsif audit.action == 'update'
7 | 'audits/resource/update'
8 | elsif audit.action == 'destroy'
9 | 'audits/resource/destroy'
10 | end
11 | end
12 |
13 | def name
14 | audit.audited_changes['name']
15 | end
16 |
17 | def url
18 | resource = audit.auditable or return
19 | h.project_resource_url(resource.project, resource)
20 | end
21 |
22 | def link_name
23 | resource = audit.auditable or return
24 | resource.name
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/services/mocks_zip_builder.rb:
--------------------------------------------------------------------------------
1 | require 'zip'
2 |
3 | class MocksZipBuilder < AbstractZipBuilder
4 | def initialize(mock_profile)
5 | @mock_profile = mock_profile
6 | end
7 |
8 | def collection
9 | @mock_profile.mock_pickers.preload(:resource_instances)
10 | end
11 |
12 | def filename(mock_picker)
13 | if mock_picker.url_pattern.blank?
14 | filename = mock_picker.response.route.url
15 | else
16 | filename = mock_picker.url_pattern
17 | end
18 | filename_without_trailing_slashes = filename.gsub(/(^\/+)|(\/+$)/, '')
19 | filename_without_trailing_slashes + '.json'
20 | end
21 |
22 | def file_content(mock_picker)
23 | JSON.stable_pretty_generate(mock_picker.mock_body)
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/services/post_slack_news.rb:
--------------------------------------------------------------------------------
1 | class PostSlackNews
2 | def initialize(project)
3 | @project = project
4 | @attachment_mapper = SlackAttachmentMapper.new
5 | end
6 |
7 | def execute(since = @project.slack_updated_at)
8 | since ||= 1.day.ago
9 | audits = AuditsRepository.new
10 | .news_of_project(@project)
11 | .where('created_at > ?', since)
12 | .order(:created_at)
13 | .map(&:decorate)
14 |
15 | return 0 if audits.empty?
16 |
17 | attachments = audits.map { |a| @attachment_mapper.map(a) }
18 | HTTP.post(@project.slack_incoming_webhook_url, json: { attachments: attachments })
19 | @project.update(slack_updated_at: audits.map(&:created_at).max)
20 | audits.length
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/views/users/index.html.haml:
--------------------------------------------------------------------------------
1 | = content_for :title, 'Users'
2 |
3 | %h1 Users
4 |
5 | %table{class: "table table-striped"}
6 | %thead
7 | %tr
8 | %th Email
9 | %th Internal
10 | %th Projects
11 | %th
12 | %tbody
13 | - @users.each do |user|
14 | %tr
15 | %td= user.email
16 | %td
17 | = form_with model: user, format: :json, class: 'user_form' do |f|
18 | = f.check_box :internal, onchange: '$(this.form).trigger("submit.rails");'
19 |
20 | %td= user.projects.map { |p| link_to(p.title, project_path(p)) }.to_sentence.html_safe
21 | %td= link_to 'Delete', user_path(user), method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-default'
22 |
23 |
24 |
--------------------------------------------------------------------------------
/db/migrate/20171229133209_remove_json_errors.rb:
--------------------------------------------------------------------------------
1 | class RemoveJsonErrors < ActiveRecord::Migration[5.0]
2 | def up
3 | drop_table :json_errors
4 | ActiveRecord::Base.connection.execute("TRUNCATE validations")
5 | change_column :validations, :json_schema, 'json USING CAST(json_schema AS json)'
6 | change_column :validations, :json_instance, 'json USING CAST(json_instance AS json)'
7 | end
8 |
9 | def down
10 | change_column :validations, :json_schema, :text
11 | change_column :validations, :json_instance, :text
12 | create_table :json_errors do |t|
13 | t.text :description
14 | t.string :type
15 | t.references :validation, foreign_key: true
16 |
17 | t.timestamps
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/test/models/query_parameter_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class QueryParameterTest < ActiveSupport::TestCase
4 | test "shouldn't exist without a name" do
5 | assert_not build(:query_parameter, name: nil).valid?
6 | end
7 |
8 | test "shouldn't exist without a primitive_type" do
9 | assert_not build(:query_parameter, primitive_type: nil).valid?
10 | end
11 |
12 | test "shouldn't exist without a route" do
13 | assert_not build(:query_parameter, route: nil).valid?
14 | end
15 |
16 | test 'QueryParameter should be valid with all attributes set correctly' do
17 | assert build(:query_parameter, name: 'user_id', description: 'New test QueryParameter', primitive_type: :string, is_optional: false).valid?
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/services/set_slack_webhook.rb:
--------------------------------------------------------------------------------
1 | class SetSlackWebhook
2 | def initialize(project)
3 | @project = project
4 | end
5 |
6 | def execute(code, redirect_uri)
7 | form = {
8 | client_id: Rails.application.secrets.slack[:client_id],
9 | client_secret: Rails.application.secrets.slack[:client_secret],
10 | code: code,
11 | redirect_uri: redirect_uri
12 | }
13 | resp = HTTP.post('https://slack.com/api/oauth.access', form: form)
14 | incoming_webhook = JSON.parse(resp.body).dig('incoming_webhook')
15 | return unless incoming_webhook
16 |
17 | @project.update(
18 | slack_channel: incoming_webhook.dig('channel'),
19 | slack_incoming_webhook_url: incoming_webhook.dig('url')
20 | )
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/views/security_schemes/_security_scheme.html.haml:
--------------------------------------------------------------------------------
1 | .flexspace-and-center
2 | %dl.flex-150
3 | %dt Key
4 | %dd
5 | %strong= security_scheme.key
6 | %dl.flex-150
7 | %dt Type
8 | %dd
9 | %strong= security_scheme.security_scheme_type
10 | %dl.flex
11 | %dt Description
12 | %dd{class: 'markdown'}
13 | %vue-markdown{source: security_scheme.description}
14 | .btn-group{ role: "group" }
15 | = link_to 'Edit', edit_project_security_scheme_path(project, security_scheme), class: 'btn btn-primary' if user.can_edit? security_scheme
16 | = link_to 'Delete', project_security_scheme_path(project, security_scheme), method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-primary' if user.can_delete? security_scheme
17 |
--------------------------------------------------------------------------------
/db/migrate/20171116161642_create_api_errors_and_errors_instances.rb:
--------------------------------------------------------------------------------
1 | class CreateApiErrorsAndErrorsInstances < ActiveRecord::Migration[5.0]
2 | def change
3 | create_table :api_errors do |t|
4 | t.string :name
5 | t.json :json_schema
6 | t.references :project
7 |
8 | t.timestamps
9 | end
10 |
11 | add_reference :responses, :api_error, foreign_key: true
12 |
13 | create_table :api_error_instances do |t|
14 | t.string :name
15 | t.json :body
16 | t.references :api_error, foreign_key: true
17 |
18 | t.timestamps
19 | end
20 |
21 | create_join_table :api_error_instances, :mock_pickers do |t|
22 | t.index :api_error_instance_id
23 | t.index :mock_picker_id
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/test/decorators/json_schema/resource_representation_decorator_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class ResourceRepresentationDecoratorTest < ActiveSupport::TestCase
4 | test 'json schema is not looping' do
5 | user = create(:resource)
6 | representation = user.resource_representations.first
7 | managee = create(:attribute, parent_resource: user, resource: user, name: 'managee', primitive_type: nil)
8 | create(
9 | :attributes_resource_representation,
10 | resource_attribute: managee,
11 | parent_resource_representation: representation,
12 | resource_representation: representation
13 | )
14 |
15 | assert_not_looping { JSONSchema::ResourceRepresentationDecorator.new(representation.reload).json_schema }
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/view_models/body_error/required_error_view_model.rb:
--------------------------------------------------------------------------------
1 | module BodyError
2 | class RequiredErrorViewModel < BodyErrorViewModel
3 | attr_reader :required_properties
4 |
5 | def initialize(path, required_properties)
6 | super(path)
7 | @required_properties = required_properties
8 | end
9 |
10 | def description
11 | if @required_properties.count == 1
12 | "#{path} - missing property #{@required_properties.first}"
13 | else
14 | "#{path} - missing properties #{@required_properties.join(', ')}"
15 | end
16 | end
17 |
18 | def type
19 | :required
20 | end
21 |
22 | def merge(vm)
23 | RequiredErrorViewModel.new(path, required_properties + vm.required_properties)
24 | end
25 | end
26 | end
--------------------------------------------------------------------------------
/app/view_models/body_error/type_error_view_model.rb:
--------------------------------------------------------------------------------
1 | module BodyError
2 | class TypeErrorViewModel < BodyErrorViewModel
3 | attr_reader :current_type, :target_types
4 |
5 | def initialize(path, current_type, target_types)
6 | super(path)
7 | @current_type = current_type
8 | @target_types = target_types
9 | end
10 |
11 | def type
12 | :type
13 | end
14 |
15 | def description
16 | if @current_type == 'null'
17 | "#{path} - cannot be null"
18 | else
19 | "#{path} - wrong type: #{@current_type} instead of #{@target_types.join(' or ')}"
20 | end
21 | end
22 |
23 | def merge(vm)
24 | TypeErrorViewModel.new(path, current_type, target_types + vm.target_types)
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/views/validations/new.html.haml:
--------------------------------------------------------------------------------
1 | = content_for :title, "New Validation"
2 |
3 | - content_for :page_scripts do
4 | = javascript_include_tag("validate_instance")
5 | = javascript_include_tag("validations/new")
6 |
7 |
8 | %h1 Validate a JSON instance
9 |
10 | .row
11 | .col-xs-12
12 | #json-validation-result
13 |
14 | .row
15 | .col-xs-6
16 | %h2 JSON Schema
17 | %p
18 | = text_area_tag(:json_schema, @default_json_instance, size: "60x20")
19 | .col-xs-6
20 | %h2 JSON instance
21 | %p
22 | = text_area_tag(:json_instance, @default_json_instance, size: "60x20")
23 |
24 | .row
25 | .col-xs-12
26 | %p
27 | = submit_tag("Validate", id: 'validate_json_instance')
28 |
29 | = link_to 'See Validations History', validations_path
30 |
--------------------------------------------------------------------------------
/app/decorators/json_schema/response_decorator.rb:
--------------------------------------------------------------------------------
1 | module JSONSchema
2 | class ResponseDecorator < Draper::Decorator
3 | delegate_all
4 | decorates_association :metadata_responses, with: JSONSchema::MetadataResponseDecorator
5 |
6 | def json_schema
7 | if resource_representation
8 | contextual_representation = JSONSchema::ResourceRepresentationDecorator.new(
9 | resource_representation, context: context
10 | )
11 | end
12 |
13 | json_schema_source = contextual_representation || api_error
14 | JSONSchemaBuilder.new(
15 | json_schema_source,
16 | is_collection: is_collection,
17 | root_key: root_key,
18 | metadata_responses: metadata_responses
19 | ).execute
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/helpers/responses_helper.rb:
--------------------------------------------------------------------------------
1 | module ResponsesHelper
2 | def response_status_code_class(status_code)
3 | if status_code < 400
4 | 'valid-response'
5 | else
6 | 'invalid-response'
7 | end
8 | end
9 |
10 | def schema_summary(root_key, representation, is_collection)
11 | return '' if representation&.name.blank?
12 |
13 | summary = link_to(
14 | representation.name,
15 | project_resource_path(representation.resource.project, representation.resource, anchor: "rep-#{representation.id}")
16 | )
17 |
18 | summary = "[ #{summary} ]" if is_collection
19 | summary = "\"#{root_key}\": #{summary}" if root_key.present?
20 | summary = "{ #{summary} }" unless is_collection && root_key.blank?
21 |
22 | summary
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/views/schemes/index.html.haml:
--------------------------------------------------------------------------------
1 | = content_for :title, "Schemes"
2 |
3 | %h1 Schemes
4 |
5 | - if user.can_create? Scheme
6 | .flexcontainer.flexcontainer-justify-end
7 | = link_to 'New Scheme', new_scheme_path, class: "btn btn-primary btn-lg"
8 |
9 |
10 | - if @schemes.any?
11 | %table{class: "table table-striped"}
12 | %thead
13 | %tr
14 | %th Name
15 | %th Regexp
16 | %th
17 | %th
18 | %tbody
19 | - @schemes.each do |scheme|
20 | %tr
21 | %td= scheme.name
22 | %td= scheme.regexp
23 | %td= link_to 'Edit', edit_scheme_path(scheme) if user.can_edit? scheme
24 | %td= link_to 'Delete', scheme_path(scheme), method: :delete, data: { confirm: 'Are you sure?' } if user.can_delete? scheme
--------------------------------------------------------------------------------
/app/models/mock_profile.rb:
--------------------------------------------------------------------------------
1 | class MockProfile < ApplicationRecord
2 | belongs_to :project
3 | has_many :mock_pickers, -> { order(:priority) }, dependent: :destroy
4 | has_many :responses, through: :mock_pickers
5 | has_ancestry
6 |
7 | accepts_nested_attributes_for :mock_pickers, allow_destroy: true
8 |
9 | validates :name, presence: true
10 |
11 | def inherited_and_self_mock_pickers_of(route)
12 | sorted_profiles = (ancestors.ordered_by_ancestry.to_a << self).reverse
13 | # Note: Clément Villain 12/12/17 sorted_profiles is a very short list (<30 items max) so there is not that many n+1 queries
14 | # Feel free to fix it !
15 | sorted_profiles.map { |profile| profile.mock_pickers.joins(:response).where(responses: { route: route }) }.flatten
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/styles/audits.scss:
--------------------------------------------------------------------------------
1 | #news {
2 | .card {
3 | min-height: 88px;
4 | margin: 24px;
5 | padding: 24px;
6 | background-color: white;
7 |
8 | box-shadow: 0 2px 4px 0 #f1f2f4;
9 |
10 | border-left-style: solid;
11 |
12 | &.create {
13 | border-color: rgb(0, 181, 147);
14 | }
15 |
16 | &.update {
17 | border-color: rgb(255, 172, 20);
18 | }
19 |
20 | &.destroy {
21 | border-color: rgb(193, 19, 37);
22 | }
23 | }
24 |
25 | .right {
26 | float: right;
27 | color: $grey;
28 |
29 | text-align: right;
30 | font-style: italic;
31 | }
32 |
33 | li {
34 | span {
35 | margin: 0 0.5em;
36 | }
37 | }
38 |
39 | .stroke {
40 | text-decoration: line-through;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/decorators/news/response_audit_decorator.rb:
--------------------------------------------------------------------------------
1 | module News
2 | class ResponseAuditDecorator < AuditDecorator
3 | def partial_name
4 | if audit.action == 'create'
5 | 'audits/response/create'
6 | elsif audit.action == 'update'
7 | 'audits/response/update'
8 | elsif audit.action == 'destroy'
9 | 'audits/response/destroy'
10 | end
11 | end
12 |
13 | def type
14 | audit.auditable_type
15 | end
16 | def name
17 | audit.audited_changes['status_code']
18 | end
19 |
20 | def link_name
21 | route = audit.associated or return
22 | route.url
23 | end
24 |
25 | def url
26 | route = audit.associated or return
27 | h.project_route_url(route.project, route)
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/test/transactions/create_rest_routes_transaction_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class CreateRestRoutesTransactionTest < ActiveSupport::TestCase
4 | setup do
5 | @project = create(:project)
6 | @resource = create(:resource, project: @project)
7 | end
8 |
9 | test 'create 5 routes and 5 responses' do
10 | assert_difference 'Route.count', 5 do
11 | assert_difference 'Response.count', 5 do
12 | CreateRestRoutesTransaction.new.call(
13 | url: '/route',
14 | resource: @resource,
15 | request_resource_representation: create(:resource_representation, resource: @resource),
16 | response_resource_representation: create(:resource_representation, resource: @resource)
17 | )
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/db/migrate/20170921083345_create_schemes.rb:
--------------------------------------------------------------------------------
1 | class CreateSchemes < ActiveRecord::Migration[5.0]
2 | def change
3 | create_table :schemes do |t|
4 | t.string :name
5 | t.string :regexp
6 |
7 | t.timestamps
8 | end
9 |
10 | add_reference :attributes, :scheme, foreign_key: true
11 | reversible do |dir|
12 | Attribute.find_each do |a|
13 | dir.up do
14 | unless a.pattern.blank?
15 | a.scheme_id = Scheme.find_or_create_by(name: a.pattern, regexp: a.pattern).id
16 | a.save
17 | end
18 | end
19 | dir.down do
20 | a.pattern = Scheme.find_by(id: a.scheme_id)&.regexp
21 | a.save
22 | end
23 | end
24 | end
25 | remove_column :attributes, :pattern, :string
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/test/models/metadatum_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class MetadatumTest < ActiveSupport::TestCase
4 | test "shouldn't exist without a name" do
5 | assert_not build(:metadatum, name: nil).valid?
6 | end
7 |
8 | test "shouldn't exist without a primitive_type" do
9 | assert_not build(:metadatum, primitive_type: nil).valid?
10 | end
11 |
12 | test "shouldn't exist without a project" do
13 | assert_not build(:metadatum, project: nil).valid?
14 | end
15 |
16 | test "Two Metadata within the same project shouldn't have the same name" do
17 | metadatum = create(:metadatum)
18 | assert_not build(:metadatum, name: metadatum.name, project: metadatum.project).valid?
19 | assert build(:metadatum, name: metadatum.name.upcase, project: metadatum.project).valid?
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/views/api_errors/index.html.haml:
--------------------------------------------------------------------------------
1 | = content_for :title, "API Errors"
2 |
3 | %h1 Errors
4 |
5 |
6 | - if user.can_create? ApiError, project: project
7 | .flexcontainer.flexcontainer-justify-end
8 | = link_to 'New Error', new_project_api_error_path(project), class: "btn btn-primary btn-lg"
9 |
10 | .list-group.margin-top-element
11 | - @api_errors.each do |api_error|
12 | %a{class: "list-group-item", href: project_api_error_path(project, api_error)}
13 | .flexcontainer.flexcontainer-space-between
14 | .name
15 | = api_error.name
16 | - if api_error.has_invalid_mocks?
17 | .error
18 | .tool-tip{'data-toggle' => "tooltip", 'data-placement' => "top", title: "Invalid mocks !"}
19 | \/!\
20 |
21 | - if @api_errors.empty?
22 | %p= t('.no_errors')
--------------------------------------------------------------------------------
/db/migrate/20171004090741_create_report.rb:
--------------------------------------------------------------------------------
1 | class CreateReport < ActiveRecord::Migration[5.0]
2 | def change
3 | create_table :reports do |t|
4 | t.integer :response_status_code
5 | t.string :response_body
6 | t.json :response_headers
7 | t.string :request_body
8 | t.json :request_headers
9 | t.string :request_method
10 | t.string :url
11 | t.references :route, foreign_key: true
12 | t.references :response, foreign_key: true
13 | t.references :project, foreign_key: true
14 |
15 | t.timestamps null: false
16 | end
17 |
18 | create_table :validation_errors do |t|
19 | t.integer :category
20 | t.string :description
21 | t.references :report, foreign_key: true
22 |
23 | t.timestamps null: false
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/decorators/code/data_class.rb:
--------------------------------------------------------------------------------
1 | module Code
2 | module DataClass
3 | extend ActiveSupport::Concern
4 |
5 | def rest_name
6 | "Rest#{pascal_name}"
7 | end
8 |
9 | def graphql_name
10 | "#{pascal_name}Type"
11 | end
12 |
13 | def pascal_name
14 | name.parameterize(separator: '_', preserve_case: true).camelcase
15 | end
16 |
17 | def kotlin_filename
18 | "#{rest_name}.kt"
19 | end
20 |
21 | def swift_filename
22 | "#{rest_name}.swift"
23 | end
24 |
25 | def ruby_filename
26 | "#{name.underscore}_serializer.rb"
27 | end
28 |
29 | def typescript_filename
30 | "#{name.underscore}.ts"
31 | end
32 |
33 | def should_import_nullable_annotation
34 | resource_attributes.any?(&:code_nullable)
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/app/models/report.rb:
--------------------------------------------------------------------------------
1 | class Report < ApplicationRecord
2 | belongs_to :project
3 | belongs_to :route, optional: true
4 | belongs_to :response, optional: true
5 |
6 | has_many :validation_errors, dependent: :delete_all
7 |
8 | default_scope { includes(:validation_errors) }
9 |
10 | validates :response_status_code, :response_headers, :request_headers, presence: true
11 |
12 | def correct?
13 | validation_errors.empty?
14 | end
15 |
16 | def errors?
17 | !correct?
18 | end
19 |
20 | def valid_status?
21 | status_error.blank?
22 | end
23 |
24 | def status_error
25 | validation_errors.find(&:status_code?)
26 | end
27 |
28 | def header_errors
29 | validation_errors.select(&:header?)
30 | end
31 |
32 | def body_errors
33 | validation_errors.select(&:body?)
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/app/views/resource_instances/_form.html.haml:
--------------------------------------------------------------------------------
1 | = bootstrap_form_for(model_instance.persisted? ? model_instance : [model, model_instance]) do |f|
2 | .flexcontainer-justify-end
3 | = f.submit class: 'btn btn-primary'
4 | = f.text_field :name
5 | .flexcontainer.flexwrap.flex-v-center
6 | - model.resource_representations.each_with_index do |r, i|
7 | .btn.selected{id: r.id, class: "color-#{i}"}=r.name
8 | #error.error
9 | = f.text_area :body, value: format_json(model_instance.body), class: 'json'
10 |
11 | - model_instance.body_errors_by_representations.each_pair do |rep, body_errors|
12 | = link_to rep.name, project_resource_path(project, rep.resource, anchor: "rep-#{rep.id}")
13 | = render partial: 'shared/schema_errors', locals: { body_errors: body_errors }
14 |
15 | = javascript_include_tag 'resources/new_representation'
--------------------------------------------------------------------------------
/app/controllers/users_controller.rb:
--------------------------------------------------------------------------------
1 | class UsersController < ApplicationController
2 | include Authenticated
3 |
4 | lazy_controller_of :user
5 |
6 | def index
7 | authorize User
8 | @users = policy_scope(User).order(:email)
9 | end
10 |
11 | def update
12 | if user.update(permitted_attributes(user))
13 | respond_to do |format|
14 | format.json { render json: user }
15 | end
16 | else
17 | respond_to do |format|
18 | format.json { render json: user.errors, status: :unprocessable_entity }
19 | end
20 | end
21 | end
22 |
23 | def destroy
24 | if user.destroy
25 | redirect_to users_path, notice: "#{user.email} has been deleted."
26 | else
27 | redirect_to users_path, alert: users_path.errors.full_messages
28 | end
29 | end
30 |
31 | def show; end
32 | end
33 |
--------------------------------------------------------------------------------
/app/decorators/news/attribute_audit_decorator.rb:
--------------------------------------------------------------------------------
1 | module News
2 | class AttributeAuditDecorator < AuditDecorator
3 | def partial_name
4 | if audit.action == 'create'
5 | 'audits/attribute/create'
6 | elsif audit.action == 'update'
7 | 'audits/attribute/update'
8 | elsif audit.action == 'destroy'
9 | 'audits/attribute/destroy'
10 | end
11 | end
12 |
13 | def name
14 | audit.audited_changes['name']
15 | end
16 |
17 | def type
18 | resource = audit.auditable or return
19 | resource.type
20 | end
21 |
22 | def url
23 | resource = audit.associated or return
24 | h.project_resource_url(resource.project, resource)
25 | end
26 |
27 | def link_name
28 | resource = audit.associated or return
29 | resource.name
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------