praxis 2.0.pre.6 → 2.0.pre.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +1 -3
- data/CHANGELOG.md +25 -0
- data/TODO.md +1 -4
- data/bin/praxis +67 -12
- data/lib/praxis.rb +10 -3
- data/lib/praxis/action_definition.rb +15 -13
- data/lib/praxis/action_definition/headers_dsl_compiler.rb +0 -7
- data/lib/praxis/api_general_info.rb +1 -1
- data/lib/praxis/application.rb +6 -2
- data/lib/praxis/blueprint.rb +357 -0
- data/lib/praxis/bootloader.rb +9 -3
- data/lib/praxis/bootloader_stages/environment.rb +16 -13
- data/lib/praxis/collection.rb +1 -11
- data/lib/praxis/config_hash.rb +44 -0
- data/lib/praxis/docs/{openapi → open_api}/info_object.rb +18 -10
- data/lib/praxis/docs/{openapi → open_api}/media_type_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/operation_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/parameter_object.rb +2 -2
- data/lib/praxis/docs/{openapi → open_api}/paths_object.rb +12 -15
- data/lib/praxis/docs/{openapi → open_api}/request_body_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/response_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/responses_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/schema_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/server_object.rb +0 -0
- data/lib/praxis/docs/{openapi → open_api}/tag_object.rb +0 -0
- data/lib/praxis/docs/open_api_generator.rb +91 -6
- data/lib/praxis/endpoint_definition.rb +273 -0
- data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +57 -8
- data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +3 -16
- data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +20 -8
- data/lib/praxis/extensions/field_expansion.rb +3 -36
- data/lib/praxis/extensions/pagination.rb +5 -32
- data/lib/praxis/extensions/pagination/ordering_params.rb +5 -1
- data/lib/praxis/extensions/pagination/pagination_params.rb +10 -4
- data/lib/praxis/field_expander.rb +90 -0
- data/lib/praxis/finalizable.rb +34 -0
- data/lib/praxis/mapper/active_model_compat.rb +4 -0
- data/lib/praxis/mapper/resource.rb +18 -2
- data/lib/praxis/mapper/selector_generator.rb +2 -1
- data/lib/praxis/mapper/sequel_compat.rb +7 -0
- data/lib/praxis/media_type.rb +3 -68
- data/lib/praxis/plugin_concern.rb +1 -1
- data/lib/praxis/plugins/mapper_plugin.rb +24 -15
- data/lib/praxis/plugins/pagination_plugin.rb +34 -4
- data/lib/praxis/renderer.rb +88 -0
- data/lib/praxis/request.rb +1 -1
- data/lib/praxis/resource_definition.rb +2 -311
- data/lib/praxis/response_definition.rb +2 -10
- data/lib/praxis/response_template.rb +3 -3
- data/lib/praxis/router.rb +2 -2
- data/lib/praxis/routing_config.rb +1 -1
- data/lib/praxis/tasks/api_docs.rb +17 -64
- data/lib/praxis/tasks/routes.rb +2 -2
- data/lib/praxis/types/media_type_common.rb +1 -11
- data/lib/praxis/version.rb +1 -1
- data/praxis.gemspec +0 -1
- data/spec/functional_spec.rb +5 -9
- data/spec/praxis/action_definition_spec.rb +12 -20
- data/spec/praxis/blueprint_spec.rb +373 -0
- data/spec/praxis/bootloader_spec.rb +10 -2
- data/spec/praxis/collection_spec.rb +0 -13
- data/spec/praxis/config_hash_spec.rb +64 -0
- data/spec/praxis/{resource_definition_spec.rb → endpoint_definition_spec.rb} +37 -64
- data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +19 -8
- data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +106 -0
- data/spec/praxis/extensions/field_expansion_spec.rb +5 -24
- data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +1 -1
- data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +1 -1
- data/spec/praxis/extensions/support/spec_resources_active_model.rb +1 -1
- data/spec/praxis/field_expander_spec.rb +149 -0
- data/spec/praxis/mapper/selector_generator_spec.rb +1 -1
- data/spec/praxis/media_type_identifier_spec.rb +5 -4
- data/spec/praxis/media_type_spec.rb +4 -93
- data/spec/praxis/renderer_spec.rb +188 -0
- data/spec/praxis/response_definition_spec.rb +0 -31
- data/spec/praxis/response_spec.rb +1 -1
- data/spec/praxis/router_spec.rb +8 -8
- data/spec/praxis/routing_config_spec.rb +3 -3
- data/spec/spec_app/app/controllers/instances.rb +13 -7
- data/spec/spec_app/design/media_types/instance.rb +1 -19
- data/spec/spec_app/design/media_types/volume.rb +1 -1
- data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -14
- data/spec/spec_app/design/resources/instances.rb +5 -8
- data/spec/spec_app/design/resources/volume_snapshots.rb +1 -1
- data/spec/spec_app/design/resources/volumes.rb +1 -1
- data/spec/support/spec_authorization_plugin.rb +1 -1
- data/spec/support/spec_blueprints.rb +72 -0
- data/spec/support/{spec_resource_definitions.rb → spec_endpoint_definitions.rb} +2 -2
- data/spec/support/spec_media_types.rb +6 -26
- data/tasks/thor/app.rb +8 -34
- data/tasks/thor/example.rb +51 -285
- data/tasks/thor/model.rb +40 -0
- data/tasks/thor/scaffold.rb +117 -0
- data/tasks/thor/templates/generator/empty_app/.gitignore +0 -1
- data/tasks/thor/templates/generator/empty_app/Gemfile +7 -23
- data/tasks/thor/templates/generator/empty_app/README.md +1 -1
- data/tasks/thor/templates/generator/empty_app/Rakefile +4 -13
- data/tasks/thor/templates/generator/empty_app/{design/response_templates → app/v1/resources}/.empty_directory +0 -0
- data/tasks/thor/templates/generator/empty_app/{design/response_templates → app/v1/resources}/.gitkeep +0 -0
- data/tasks/thor/templates/generator/empty_app/config/environment.rb +26 -17
- data/tasks/thor/templates/generator/empty_app/{design/v1/resources → config/initializers}/.empty_directory +0 -0
- data/tasks/thor/templates/generator/empty_app/{design/v1/resources → config/initializers}/.gitkeep +0 -0
- data/tasks/thor/templates/generator/empty_app/design/v1/endpoints/.empty_directory +0 -0
- data/tasks/thor/templates/generator/empty_app/design/v1/endpoints/.gitkeep +0 -0
- data/tasks/thor/templates/generator/empty_app/docs/.empty_directory +0 -0
- data/tasks/thor/templates/generator/empty_app/docs/.gitkeep +0 -0
- data/tasks/thor/templates/generator/empty_app/spec/spec_helper.rb +14 -9
- data/tasks/thor/templates/generator/example_app/.gitignore +1 -0
- data/tasks/thor/templates/generator/example_app/Gemfile +19 -0
- data/tasks/thor/templates/generator/example_app/Rakefile +61 -0
- data/tasks/thor/templates/generator/example_app/app/models/user.rb +6 -0
- data/tasks/thor/templates/generator/example_app/app/v1/concerns/controller_base.rb +24 -0
- data/tasks/thor/templates/generator/example_app/app/v1/controllers/users.rb +17 -0
- data/tasks/thor/templates/generator/example_app/app/v1/resources/base.rb +11 -0
- data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +25 -0
- data/tasks/thor/templates/generator/example_app/config.ru +30 -0
- data/tasks/thor/templates/generator/example_app/config/environment.rb +41 -0
- data/tasks/thor/templates/generator/example_app/db/migrate/20201010101010_create_users_table.rb +12 -0
- data/tasks/thor/templates/generator/example_app/db/seeds.rb +6 -0
- data/tasks/thor/templates/generator/example_app/design/api.rb +18 -0
- data/tasks/thor/templates/generator/example_app/design/v1/endpoints/users.rb +37 -0
- data/tasks/thor/templates/generator/example_app/design/v1/media_types/user.rb +21 -0
- data/tasks/thor/templates/generator/example_app/spec/helpers/database_helper.rb +20 -0
- data/tasks/thor/templates/generator/example_app/spec/spec_helper.rb +42 -0
- data/tasks/thor/templates/generator/example_app/spec/v1/controllers/users_spec.rb +37 -0
- data/tasks/thor/templates/generator/scaffold/design/endpoints/collection.rb +98 -0
- data/tasks/thor/templates/generator/scaffold/design/media_types/item.rb +18 -0
- data/tasks/thor/templates/generator/scaffold/implementation/controllers/collection.rb +77 -0
- data/tasks/thor/templates/generator/scaffold/implementation/resources/base.rb +11 -0
- data/tasks/thor/templates/generator/scaffold/implementation/resources/item.rb +45 -0
- data/tasks/thor/templates/generator/scaffold/models/active_record.rb +6 -0
- data/tasks/thor/templates/generator/scaffold/models/sequel.rb +6 -0
- metadata +62 -136
- data/lib/api_browser/.bowerrc +0 -3
- data/lib/api_browser/.editorconfig +0 -21
- data/lib/api_browser/Gruntfile.js +0 -581
- data/lib/api_browser/app/index.html +0 -59
- data/lib/api_browser/app/js/app.js +0 -48
- data/lib/api_browser/app/js/controllers/action.js +0 -47
- data/lib/api_browser/app/js/controllers/controller.js +0 -10
- data/lib/api_browser/app/js/controllers/menu.js +0 -93
- data/lib/api_browser/app/js/controllers/trait.js +0 -10
- data/lib/api_browser/app/js/controllers/type.js +0 -24
- data/lib/api_browser/app/js/directives/attribute_description.js +0 -56
- data/lib/api_browser/app/js/directives/attribute_table.js +0 -28
- data/lib/api_browser/app/js/directives/conditional_requirements.js +0 -13
- data/lib/api_browser/app/js/directives/fixed_if_fits.js +0 -38
- data/lib/api_browser/app/js/directives/highlight.js +0 -14
- data/lib/api_browser/app/js/directives/menu_item.js +0 -59
- data/lib/api_browser/app/js/directives/no_container.js +0 -8
- data/lib/api_browser/app/js/directives/readable_list.js +0 -87
- data/lib/api_browser/app/js/directives/request_examples.js +0 -31
- data/lib/api_browser/app/js/directives/type_placeholder.js +0 -30
- data/lib/api_browser/app/js/directives/url.js +0 -15
- data/lib/api_browser/app/js/factories/Configuration.js +0 -12
- data/lib/api_browser/app/js/factories/Documentation.js +0 -61
- data/lib/api_browser/app/js/factories/Example.js +0 -51
- data/lib/api_browser/app/js/factories/PageInfo.js +0 -9
- data/lib/api_browser/app/js/factories/normalize_attributes.js +0 -20
- data/lib/api_browser/app/js/factories/prepare_template.js +0 -15
- data/lib/api_browser/app/js/factories/template_for.js +0 -128
- data/lib/api_browser/app/js/filters/attribute_name.js +0 -10
- data/lib/api_browser/app/js/filters/friendly_json.js +0 -5
- data/lib/api_browser/app/js/filters/has_requirement.js +0 -14
- data/lib/api_browser/app/js/filters/header_info.js +0 -9
- data/lib/api_browser/app/js/filters/is_empty.js +0 -8
- data/lib/api_browser/app/js/filters/markdown.js +0 -6
- data/lib/api_browser/app/js/filters/resource_name.js +0 -5
- data/lib/api_browser/app/js/filters/tag_requirement.js +0 -13
- data/lib/api_browser/app/sass/modules/_body.scss +0 -40
- data/lib/api_browser/app/sass/modules/_cloke.scss +0 -8
- data/lib/api_browser/app/sass/modules/_header.scss +0 -10
- data/lib/api_browser/app/sass/modules/_nav.scss +0 -7
- data/lib/api_browser/app/sass/modules/_sidebar.scss +0 -134
- data/lib/api_browser/app/sass/modules/_switch.scss +0 -55
- data/lib/api_browser/app/sass/modules/_table.scss +0 -13
- data/lib/api_browser/app/sass/praxis.scss +0 -70
- data/lib/api_browser/app/sass/variables/_bootstrap-variables.scss +0 -774
- data/lib/api_browser/app/views/action.html +0 -97
- data/lib/api_browser/app/views/builtin/field-selector.html +0 -24
- data/lib/api_browser/app/views/controller.html +0 -55
- data/lib/api_browser/app/views/directives/attribute_description.html +0 -2
- data/lib/api_browser/app/views/directives/attribute_description/default.html +0 -2
- data/lib/api_browser/app/views/directives/attribute_description/example.html +0 -13
- data/lib/api_browser/app/views/directives/attribute_description/headers.html +0 -8
- data/lib/api_browser/app/views/directives/attribute_description/member_options.html +0 -4
- data/lib/api_browser/app/views/directives/attribute_description/values.html +0 -14
- data/lib/api_browser/app/views/directives/attribute_table.html +0 -17
- data/lib/api_browser/app/views/directives/menu_item.html +0 -8
- data/lib/api_browser/app/views/directives/url.html +0 -3
- data/lib/api_browser/app/views/examples/general.html +0 -26
- data/lib/api_browser/app/views/home.html +0 -5
- data/lib/api_browser/app/views/layout.html +0 -8
- data/lib/api_browser/app/views/menu.html +0 -42
- data/lib/api_browser/app/views/navbar.html +0 -9
- data/lib/api_browser/app/views/trait.html +0 -13
- data/lib/api_browser/app/views/type.html +0 -6
- data/lib/api_browser/app/views/type/details.html +0 -33
- data/lib/api_browser/app/views/types/embedded/array.html +0 -2
- data/lib/api_browser/app/views/types/embedded/default.html +0 -12
- data/lib/api_browser/app/views/types/embedded/field-selector.html +0 -13
- data/lib/api_browser/app/views/types/embedded/links.html +0 -11
- data/lib/api_browser/app/views/types/embedded/requirements.html +0 -6
- data/lib/api_browser/app/views/types/embedded/single_req.html +0 -9
- data/lib/api_browser/app/views/types/embedded/struct.html +0 -14
- data/lib/api_browser/app/views/types/label/link.html +0 -1
- data/lib/api_browser/app/views/types/label/primitive.html +0 -1
- data/lib/api_browser/app/views/types/label/primitive_collection.html +0 -1
- data/lib/api_browser/app/views/types/label/type.html +0 -1
- data/lib/api_browser/app/views/types/label/type_collection.html +0 -1
- data/lib/api_browser/app/views/types/main/array.html +0 -22
- data/lib/api_browser/app/views/types/main/default.html +0 -23
- data/lib/api_browser/app/views/types/main/hash.html +0 -23
- data/lib/api_browser/app/views/types/standalone/array.html +0 -3
- data/lib/api_browser/app/views/types/standalone/default.html +0 -18
- data/lib/api_browser/app/views/types/standalone/struct.html +0 -2
- data/lib/api_browser/bower_template.json +0 -41
- data/lib/api_browser/package-lock.json +0 -7110
- data/lib/api_browser/package.json +0 -43
- data/lib/praxis/docs/generator.rb +0 -243
- data/lib/praxis/docs/link_builder.rb +0 -30
- data/lib/praxis/links.rb +0 -135
- data/lib/praxis/types/multipart.rb +0 -109
- data/spec/api_browser/directives/type_placeholder_spec.js +0 -134
- data/spec/api_browser/factories/configuration_spec.js +0 -32
- data/spec/api_browser/factories/documentation_spec.js +0 -100
- data/spec/api_browser/factories/normalize_attributes_spec.js +0 -92
- data/spec/api_browser/factories/template_for_spec.js +0 -67
- data/spec/api_browser/filters/attribute_name_spec.js +0 -23
- data/spec/praxis/types/multipart_spec.rb +0 -112
- data/tasks/thor/templates/generator/empty_app/.rspec +0 -1
- data/tasks/thor/templates/generator/empty_app/Guardfile +0 -3
- data/tasks/thor/templates/generator/empty_app/config/rainbows.rb +0 -57
- data/tasks/thor/templates/generator/empty_app/docs/app.js +0 -1
- data/tasks/thor/templates/generator/empty_app/docs/styles.scss +0 -3
@@ -0,0 +1,18 @@
|
|
1
|
+
# Use this file to define your overall api behavior, response templates and traits.
|
2
|
+
Praxis::ApiDefinition.define do
|
3
|
+
info do
|
4
|
+
name 'example'
|
5
|
+
title 'Example API'
|
6
|
+
|
7
|
+
# Attributes for OpenAPI docs
|
8
|
+
termsOfService 'https://mysitehere.com'
|
9
|
+
contact name: 'API Info', email: 'info@mysitehere.com'
|
10
|
+
end
|
11
|
+
|
12
|
+
# Trait that when included will require a Bearer authorization header to be passed in.
|
13
|
+
trait :authorized do
|
14
|
+
headers do
|
15
|
+
key "Authorization", String, regexp: /^.*Bearer\s/, required: true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module V1
|
4
|
+
module Endpoints
|
5
|
+
class Users
|
6
|
+
include Praxis::EndpointDefinition
|
7
|
+
|
8
|
+
media_type MediaTypes::User
|
9
|
+
version '1'
|
10
|
+
|
11
|
+
description 'Endpoints that allow the listing and manipulation of users'
|
12
|
+
|
13
|
+
action :index do
|
14
|
+
description 'List users'
|
15
|
+
routing { get '' }
|
16
|
+
params do
|
17
|
+
attribute :fields, Praxis::Types::FieldSelector.for(MediaTypes::User),
|
18
|
+
description: 'Fields with which to render the result.'
|
19
|
+
attribute :filters, Praxis::Types::FilteringParams.for(MediaTypes::User) do
|
20
|
+
filter 'uuid', using: ['=', '!=']
|
21
|
+
filter 'first_name', using: ['=', '!='], fuzzy: true
|
22
|
+
filter 'last_name', using: ['=', '!='], fuzzy: true
|
23
|
+
filter 'email', using: ['=', '!=']
|
24
|
+
end
|
25
|
+
attribute :pagination, Praxis::Types::PaginationParams.for(MediaTypes::User) do
|
26
|
+
by_fields :uuid, :first_name, :last_name
|
27
|
+
end
|
28
|
+
attribute :order, Praxis::Extensions::Pagination::OrderingParams.for(MediaTypes::User) do
|
29
|
+
by_fields :uuid, :last_name, :first_name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
response :ok, media_type: Praxis::Collection.of(MediaTypes::User)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module V1
|
4
|
+
module MediaTypes
|
5
|
+
class User < Praxis::MediaType
|
6
|
+
identifier 'application/json'
|
7
|
+
|
8
|
+
domain_model 'V1::Resources::User'
|
9
|
+
description 'A user in the system'
|
10
|
+
|
11
|
+
attributes do
|
12
|
+
attribute :id, Integer
|
13
|
+
attribute :uuid, String
|
14
|
+
attribute :email, String
|
15
|
+
attribute :first_name, String
|
16
|
+
attribute :last_name, String
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
class DatabaseHelper
|
4
|
+
# Simple DB seeding to avoid bringing in other gems like FactoryGirl etc.
|
5
|
+
# This does the job for an example seeder
|
6
|
+
def self.seed!
|
7
|
+
user_data = [
|
8
|
+
{id: 11, first_name: 'Peter', last_name: 'Praxis', uuid: 'deadbeef', email: 'peter@pan.com'},
|
9
|
+
{id: 12, first_name: 'Alice', last_name: 'Trellis', uuid: 'beefdead', email: 'alice@wonderland.com'},
|
10
|
+
{id: 13, first_name: 'Wellington', last_name: 'Lofty', uuid: 'beefbeef', email: 'well@lofty.com'},
|
11
|
+
]
|
12
|
+
(100..199).each do |i|
|
13
|
+
user_data.push id: i, first_name: "User-#{i}", last_name: "Last-#{i}", uuid: SecureRandom.hex(16).to_s
|
14
|
+
end
|
15
|
+
user_data.each_with_index do |data, i|
|
16
|
+
::User.create(**data)
|
17
|
+
end
|
18
|
+
puts "Database seeded."
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
Bundler.setup :default, :test
|
2
|
+
Bundler.require :default, :test
|
3
|
+
ENV['RACK_ENV'] = 'test'
|
4
|
+
|
5
|
+
begin
|
6
|
+
APP=Rack::Builder.parse_file(File.expand_path('../config.ru', __dir__)).first
|
7
|
+
rescue => e
|
8
|
+
puts "Application failed to initialize:"
|
9
|
+
raise e
|
10
|
+
exit 1
|
11
|
+
end
|
12
|
+
|
13
|
+
# Migrate and seed the DB (only an empty in-memory DB)
|
14
|
+
|
15
|
+
ActiveRecord::Migration.verbose = false # ?? does not seem to work like this
|
16
|
+
ActiveRecord::Tasks::DatabaseTasks.migrate
|
17
|
+
require_relative '../db/seeds.rb'
|
18
|
+
|
19
|
+
RSpec.configure do |config|
|
20
|
+
config.include Rack::Test::Methods
|
21
|
+
|
22
|
+
config.before(:suite) do
|
23
|
+
# Praxis::Blueprint.caching_enabled = true
|
24
|
+
DatabaseCleaner.strategy = :transaction
|
25
|
+
end
|
26
|
+
config.around(:each) do |example|
|
27
|
+
DatabaseCleaner.cleaning do
|
28
|
+
example.run
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# config.before(:each) do
|
33
|
+
# Praxis::Blueprint.cache = Hash.new do |hash, key|
|
34
|
+
# hash[key] = Hash.new
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
|
38
|
+
def app
|
39
|
+
APP
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
describe V1::Controllers::Users do
|
6
|
+
before do
|
7
|
+
header 'X-API-Version', '1'
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:response) { subject; last_response }
|
11
|
+
let(:json_payload) { JSON.dump(payload) }
|
12
|
+
let(:parsed_body) { JSON.parse(response.body, symbolize_names: true) }
|
13
|
+
|
14
|
+
context 'index' do
|
15
|
+
let(:filters_q) { '' }
|
16
|
+
let(:fields_q) { 'id' }
|
17
|
+
let(:query_string) do
|
18
|
+
"filters=#{CGI.escape(filters_q)}&fields=#{CGI.escape(fields_q)}"
|
19
|
+
end
|
20
|
+
subject { get "/users?#{query_string}" }
|
21
|
+
|
22
|
+
context 'without filters' do
|
23
|
+
it { expect(response.status).to eq 200 }
|
24
|
+
it 'returns all users' do
|
25
|
+
expect(parsed_body.size).to eq(2+100)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
context 'using filters' do
|
29
|
+
let(:filters_q) { 'first_name=Peter' }
|
30
|
+
it 'returns only peter' do
|
31
|
+
expect(parsed_body.size).to eq(1)
|
32
|
+
# Peter has id 11 from our seeds
|
33
|
+
expect(parsed_body.map{|u| u[:id]}).to eq([11])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module <%= version_module %>
|
4
|
+
module Endpoints
|
5
|
+
class <%= plural_class %>
|
6
|
+
include Praxis::EndpointDefinition
|
7
|
+
|
8
|
+
media_type MediaTypes::<%= singular_class %>
|
9
|
+
version '<%= version %>'
|
10
|
+
|
11
|
+
description 'Praxis-generated endpoint for managing <%= plural_class %>'
|
12
|
+
|
13
|
+
<%- if action_enabled?(:index) -%>
|
14
|
+
action :index do
|
15
|
+
description 'List <%= plural_class %>'
|
16
|
+
routing { get '' }
|
17
|
+
params do
|
18
|
+
attribute :fields, Praxis::Types::FieldSelector.for(MediaTypes::<%= singular_class %>),
|
19
|
+
description: 'Fields with which to render the result.'
|
20
|
+
<%- if !pagination_enabled? -%>
|
21
|
+
=begin
|
22
|
+
# You can use pagination/ordering by enabling the PaginationPlugin, and uncommenting these lines
|
23
|
+
<%- end -%>
|
24
|
+
attribute :pagination, Praxis::Types::PaginationParams.for(MediaTypes::<%= singular_class %>)
|
25
|
+
attribute :order, Praxis::Extensions::Pagination::OrderingParams.for(MediaTypes::<%= singular_class %>)
|
26
|
+
<%- if !pagination_enabled? -%>
|
27
|
+
=end
|
28
|
+
<%- end -%>
|
29
|
+
# # Filter by attributes. Add an allowed filter per line, with the allowed operators to use
|
30
|
+
# # Also, remember to add a mapping for each in `filters_mapping` method of Resources::<%= singular_class %> class
|
31
|
+
# attribute :filters, Praxis::Types::FilteringParams.for(MediaTypes::<%= singular_class %>) do
|
32
|
+
# filter 'first_name', using: ['=', '!='], fuzzy: true
|
33
|
+
# end
|
34
|
+
end
|
35
|
+
response :ok, media_type: Praxis::Collection.of(MediaTypes::<%= singular_class %>)
|
36
|
+
end
|
37
|
+
<%- end -%>
|
38
|
+
|
39
|
+
<%- if action_enabled?(:index) -%>
|
40
|
+
action :show do
|
41
|
+
description 'Retrieve details for a specific <%= singular_class %>'
|
42
|
+
routing { get '/:id' }
|
43
|
+
params do
|
44
|
+
attribute :id, required: true
|
45
|
+
attribute :fields, Praxis::Types::FieldSelector.for(MediaTypes::<%= singular_class %>),
|
46
|
+
description: 'Fields with which to render the result.'
|
47
|
+
end
|
48
|
+
response :ok
|
49
|
+
response :not_found
|
50
|
+
end
|
51
|
+
<%- end -%>
|
52
|
+
|
53
|
+
<%- if action_enabled?(:create) -%>
|
54
|
+
action :create do
|
55
|
+
description 'Create a new <%= singular_class %>'
|
56
|
+
routing { post '' }
|
57
|
+
payload reference: MediaTypes::<%= singular_class %> do
|
58
|
+
# List the attributes you accept from the one existing in the <%= singular_class %> Mediatype
|
59
|
+
# and/or fully define any other ones you allow at creation time
|
60
|
+
# attribute :name
|
61
|
+
end
|
62
|
+
response :created
|
63
|
+
response :bad_request
|
64
|
+
end
|
65
|
+
<%- end -%>
|
66
|
+
|
67
|
+
<%- if action_enabled?(:update) -%>
|
68
|
+
action :update do
|
69
|
+
description 'Update one or more attributes of an existing <%= singular_class %>'
|
70
|
+
routing { patch '/:id' }
|
71
|
+
params do
|
72
|
+
attribute :id, required: true
|
73
|
+
end
|
74
|
+
payload reference: MediaTypes::<%= singular_class %> do
|
75
|
+
# List the attributes you accept from the one existing in the <%= singular_class %> Mediatype
|
76
|
+
# and/or fully define any other ones you allow to change
|
77
|
+
# attribute :name
|
78
|
+
end
|
79
|
+
response :no_content
|
80
|
+
response :bad_request
|
81
|
+
end
|
82
|
+
<%- end -%>
|
83
|
+
|
84
|
+
<%- if action_enabled?(:update) -%>
|
85
|
+
action :delete do
|
86
|
+
description 'Deletes a <%= singular_class %>'
|
87
|
+
routing { delete '/:id' }
|
88
|
+
params do
|
89
|
+
attribute :id, required: true
|
90
|
+
end
|
91
|
+
response :no_content
|
92
|
+
response :not_found
|
93
|
+
end
|
94
|
+
<%- end -%>
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module <%= version_module %>
|
4
|
+
module MediaTypes
|
5
|
+
class <%= singular_class %> < Praxis::MediaType
|
6
|
+
identifier 'application/json'
|
7
|
+
|
8
|
+
domain_model '<%= version_module %>::Resources::<%= singular_class %>'
|
9
|
+
description 'Structural definition of a <%= singular_class %>'
|
10
|
+
|
11
|
+
attributes do
|
12
|
+
attribute :id, Integer, description: '<%= singular_class %> identifier'
|
13
|
+
# <INSERT MORE ATTRIBUTES HERE>
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module <%= version_module %>
|
4
|
+
module Controllers
|
5
|
+
class <%= plural_class %>
|
6
|
+
include Praxis::Controller
|
7
|
+
|
8
|
+
implements Endpoints::<%= plural_class %>
|
9
|
+
|
10
|
+
<%- if action_enabled?(:index) -%>
|
11
|
+
# Retrieve all <%= plural_class %> with the right necessary associations
|
12
|
+
# and render them appropriately with the requested field selection
|
13
|
+
def index
|
14
|
+
objects = build_query(model_class).all
|
15
|
+
display(objects)
|
16
|
+
end
|
17
|
+
<%- end -%>
|
18
|
+
|
19
|
+
<%- if action_enabled?(:show) -%>
|
20
|
+
# Retrieve a single <%= singular_class %> with the right necessary associations
|
21
|
+
# and render them appropriately with the requested field selection
|
22
|
+
def show(id:, **_args)
|
23
|
+
model = build_query(model_class.where(id: id)).first
|
24
|
+
return Praxis::Responses::NotFound.new if model.nil?
|
25
|
+
|
26
|
+
display(model)
|
27
|
+
end
|
28
|
+
<%- end -%>
|
29
|
+
|
30
|
+
<%- if action_enabled?(:create) -%>
|
31
|
+
# Creates a new <%= singular_class %>
|
32
|
+
def create
|
33
|
+
# A good pattern is to call the same name method on the corresponding resource,
|
34
|
+
# passing the incoming payload, or massaging it first
|
35
|
+
created_resource = Resources::<%= singular_class%>.create(request.payload)
|
36
|
+
|
37
|
+
# Respond with a created if it successfully finished
|
38
|
+
Praxis::Responses::Created.new(location: created_resource.href)
|
39
|
+
end
|
40
|
+
<%- end -%>
|
41
|
+
|
42
|
+
<%- if action_enabled?(:update) -%>
|
43
|
+
# Updates some of the information of a <%= singular_class %>
|
44
|
+
def update(id:)
|
45
|
+
# A good pattern is to call the same name method on the corresponding resource,
|
46
|
+
# passing the incoming id and payload (or massaging it first)
|
47
|
+
updated_resource = Resources::<%= singular_class %>.update(
|
48
|
+
id: id,
|
49
|
+
payload: request.payload,
|
50
|
+
)
|
51
|
+
return Praxis::Responses::NotFound.new unless updated_resource
|
52
|
+
|
53
|
+
Praxis::Responses::NoContent.new
|
54
|
+
end
|
55
|
+
<%- end -%>
|
56
|
+
|
57
|
+
<%- if action_enabled?(:delete) -%>
|
58
|
+
# Deletes an existing <%= singular_class %>
|
59
|
+
def delete(id:)
|
60
|
+
# A good pattern is to call the same name method on the corresponding resource,
|
61
|
+
# maybe passing the already loaded model
|
62
|
+
deleted_resource = Resources::<%= singular_class %>.delete(
|
63
|
+
id: id
|
64
|
+
)
|
65
|
+
return Praxis::Responses::NotFound.new unless deleted_resource
|
66
|
+
|
67
|
+
Praxis::Responses::NoContent.new
|
68
|
+
end
|
69
|
+
<%- end -%>
|
70
|
+
|
71
|
+
# Use the model class as the base query but you might want to change that
|
72
|
+
def model_class
|
73
|
+
::<%= singular_class %> #Change it to the appropriate DB model class
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module <%= version_module %>
|
4
|
+
module Resources
|
5
|
+
class Base < Praxis::Mapper::Resource
|
6
|
+
# Base for all <%= version_module %> resources.
|
7
|
+
# Resources withing a single version should have resource mappings separate from other versions
|
8
|
+
# and the Mapper::Resource will appropriately maintain different model_maps for each Base classes
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module <%= version_module %>
|
4
|
+
module Resources
|
5
|
+
class <%= singular_class %> < Base
|
6
|
+
model ::<%= singular_class %> # Change it if it maps to a different DB model class
|
7
|
+
|
8
|
+
# Define the name mapping from API filter params, to model attribute/associations
|
9
|
+
# when they aren't 1:1
|
10
|
+
# filters_mapping(
|
11
|
+
# 'name': 'name',
|
12
|
+
# 'label': 'association.label_name'
|
13
|
+
# )
|
14
|
+
|
15
|
+
# Add dependencies for resource attributes to other attributes and/or model associations
|
16
|
+
# property :href, dependencies: %i[id]
|
17
|
+
|
18
|
+
<%- if action_enabled?(:create) -%>
|
19
|
+
def self.create(payload)
|
20
|
+
# Assuming the API field names directly map the the model attributes. Massage if appropriate.
|
21
|
+
self.new(model.create(*payload.to_h))
|
22
|
+
end
|
23
|
+
<%- end -%>
|
24
|
+
|
25
|
+
<%- if action_enabled?(:update) -%>
|
26
|
+
def self.update(id:, payload:)
|
27
|
+
record = model.find_by(id: id)
|
28
|
+
return nil unless record
|
29
|
+
# Assuming the API field names directly map the the model attributes. Massage if appropriate.
|
30
|
+
record.update(*payload.to_h)
|
31
|
+
self.new(record)
|
32
|
+
end
|
33
|
+
<%- end -%>
|
34
|
+
|
35
|
+
<%- if action_enabled?(:delete) -%>
|
36
|
+
def self.delete(id:)
|
37
|
+
record = model.find_by(id: id)
|
38
|
+
return nil unless record
|
39
|
+
record.destroy
|
40
|
+
self.new(record)
|
41
|
+
end
|
42
|
+
<%- end -%>
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|