praxis 2.0.pre.10 → 2.0.pre.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +1 -3
- data/CHANGELOG.md +26 -0
- data/bin/praxis +65 -2
- data/lib/praxis/api_definition.rb +8 -4
- data/lib/praxis/bootloader_stages/environment.rb +1 -0
- data/lib/praxis/collection.rb +11 -0
- data/lib/praxis/docs/open_api/response_object.rb +21 -6
- data/lib/praxis/docs/open_api_generator.rb +1 -1
- data/lib/praxis/extensions/attribute_filtering.rb +14 -1
- data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +206 -66
- data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +3 -2
- data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +45 -41
- data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +193 -0
- data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +20 -8
- data/lib/praxis/extensions/pagination.rb +5 -32
- 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 +1 -0
- data/lib/praxis/mapper/sequel_compat.rb +7 -0
- data/lib/praxis/media_type_identifier.rb +11 -1
- data/lib/praxis/plugins/mapper_plugin.rb +22 -13
- data/lib/praxis/plugins/pagination_plugin.rb +34 -4
- data/lib/praxis/response_definition.rb +46 -66
- data/lib/praxis/responses/http.rb +3 -1
- data/lib/praxis/tasks/api_docs.rb +4 -1
- data/lib/praxis/tasks/routes.rb +6 -6
- data/lib/praxis/version.rb +1 -1
- data/spec/praxis/action_definition_spec.rb +3 -1
- data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +267 -167
- data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +25 -6
- data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +100 -17
- data/spec/praxis/extensions/attribute_filtering/filters_parser_spec.rb +148 -0
- 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/mapper/selector_generator_spec.rb +1 -1
- data/spec/praxis/media_type_identifier_spec.rb +15 -1
- data/spec/praxis/response_definition_spec.rb +37 -129
- data/tasks/thor/example.rb +12 -6
- data/tasks/thor/model.rb +40 -0
- data/tasks/thor/scaffold.rb +117 -0
- data/tasks/thor/templates/generator/empty_app/config/environment.rb +1 -0
- data/tasks/thor/templates/generator/example_app/Rakefile +9 -2
- data/tasks/thor/templates/generator/example_app/app/v1/concerns/controller_base.rb +24 -0
- data/tasks/thor/templates/generator/example_app/app/v1/concerns/href.rb +33 -0
- data/tasks/thor/templates/generator/example_app/app/v1/controllers/users.rb +2 -2
- data/tasks/thor/templates/generator/example_app/app/v1/resources/base.rb +15 -0
- data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +7 -28
- data/tasks/thor/templates/generator/example_app/config.ru +1 -2
- data/tasks/thor/templates/generator/example_app/config/environment.rb +3 -2
- data/tasks/thor/templates/generator/example_app/db/migrate/20201010101010_create_users_table.rb +3 -2
- data/tasks/thor/templates/generator/example_app/db/seeds.rb +6 -0
- data/tasks/thor/templates/generator/example_app/design/v1/endpoints/users.rb +4 -4
- data/tasks/thor/templates/generator/example_app/design/v1/media_types/user.rb +1 -6
- data/tasks/thor/templates/generator/example_app/spec/helpers/database_helper.rb +4 -2
- data/tasks/thor/templates/generator/example_app/spec/spec_helper.rb +2 -2
- data/tasks/thor/templates/generator/example_app/spec/v1/controllers/users_spec.rb +2 -2
- 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 +21 -6
data/tasks/thor/example.rb
CHANGED
@@ -33,16 +33,22 @@ module PraxisGen
|
|
33
33
|
puts
|
34
34
|
puts " cd #{app_name}"
|
35
35
|
puts " bundle"
|
36
|
-
puts " bundle exec rake db:
|
37
|
-
puts " bundle exec rackup
|
36
|
+
puts " bundle exec rake db:recreate # To create/migrate/seed the dev DB"
|
37
|
+
puts " bundle exec rackup # To start the web server"
|
38
38
|
puts
|
39
39
|
puts "From another terminal/app, use curl (or your favorite HTTP client) to retrieve data from the API"
|
40
40
|
puts " For example: "
|
41
|
-
puts " Get all users without filters or limit, and display only
|
42
|
-
puts " curl -H 'X-Api-Version: 1' http://localhost:9292/users
|
41
|
+
puts " Get all users without filters or limit, and display only id, and first_name fields"
|
42
|
+
puts " curl -G -H 'X-Api-Version: 1' http://localhost:9292/users \\"
|
43
|
+
puts " --data-urlencode \"fields=id,first_name\""
|
43
44
|
puts
|
44
|
-
puts " Get the last 5 users, ordered by
|
45
|
-
puts "
|
45
|
+
puts " Get the last 5 users, with last_names starting with \"L\" ordered by first_name (descending)"
|
46
|
+
puts " and display only id, first_name, last_name, and email fields"
|
47
|
+
puts " curl -G -H 'X-Api-Version: 1' http://localhost:9292/users \\"
|
48
|
+
puts " --data-urlencode \"filters=last_name=L*\" \\"
|
49
|
+
puts " --data-urlencode \"pagination=by=first_name,items=5\" \\"
|
50
|
+
puts " --data-urlencode \"order=-first_name\" \\"
|
51
|
+
puts " --data-urlencode \"fields=id,first_name,last_name,email\""
|
46
52
|
puts " (Note: To list all routes use: bundle exec rake praxis:routes)"
|
47
53
|
puts
|
48
54
|
nil
|
data/tasks/thor/model.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PraxisGen
|
4
|
+
class Model < Thor
|
5
|
+
require 'active_support/inflector'
|
6
|
+
include Thor::Actions
|
7
|
+
|
8
|
+
def self.source_root
|
9
|
+
File.dirname(__FILE__) + "/templates/generator/scaffold"
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "gmodel", "Generates a skeleton model file under app/models for ActiveRecord or Sequel."
|
13
|
+
argument :model_name, required: true
|
14
|
+
option :orm, required: false, default: 'activerecord', enum: ['activerecord','sequel']
|
15
|
+
def g
|
16
|
+
#self.class.check_name(model_name)
|
17
|
+
template_file = \
|
18
|
+
if options[:orm] == 'activerecord'
|
19
|
+
'models/active_record.rb'
|
20
|
+
else
|
21
|
+
'models/sequel.rb'
|
22
|
+
end
|
23
|
+
puts "Generating Model for #{model_name}"
|
24
|
+
template template_file, "app/models/#{model_name}.rb"
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
# Helper functions (which are available in the ERB contexts)
|
28
|
+
no_commands do
|
29
|
+
def model_class
|
30
|
+
model_name.camelize
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# TODO: do we want the argument to be camelcase? or snake case?
|
35
|
+
def self.check_name(name)
|
36
|
+
sanitized = name.downcase.gsub(/[^a-z0-9_]/, '')
|
37
|
+
raise "Please use only downcase letters, numbers and underscores for the model" unless sanitized == name
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PraxisGen
|
4
|
+
class Scaffold < Thor
|
5
|
+
require 'active_support/inflector'
|
6
|
+
include Thor::Actions
|
7
|
+
|
8
|
+
attr_reader :actions_hash
|
9
|
+
|
10
|
+
def self.source_root
|
11
|
+
File.dirname(__FILE__) + "/templates/generator/scaffold"
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "g","Generates an API design and implementation scaffold for managing a collection of <collection_name>"
|
15
|
+
argument :collection_name, required: true
|
16
|
+
option :version, required: false, default: '1',
|
17
|
+
desc: 'Version string for the API endpoint. This also dictates the directory structure (i.e., v1/endpoints/...))'
|
18
|
+
option :design, type: :boolean, default: true,
|
19
|
+
desc: 'Include the Endpoint and MediaType files for the collection'
|
20
|
+
option :implementation, type: :boolean, default: true,
|
21
|
+
desc: 'Include the Controller and (possibly the) Resource files for the collection (see --no-resource)'
|
22
|
+
option :resource, type: :boolean, default: true,
|
23
|
+
desc: 'Disable (or enable) the creation of the Resource files when generating implementation'
|
24
|
+
option :model, type: :string, enum: ['activerecord','sequel'],
|
25
|
+
desc: 'It also generates a model for the given ORM. An empty --model flag will default to activerecord'
|
26
|
+
option :actions, type: :string, default: 'crud', enum: ['cr','cru','crud','u','ud','d'],
|
27
|
+
desc: 'Specifies the actions to generate for the API. cr=create, u=update, d=delete. Index and show actions are always generated'
|
28
|
+
def g
|
29
|
+
self.class.check_name(collection_name)
|
30
|
+
@actions_hash = self.class.compose_actions_hash(options[:actions])
|
31
|
+
env_rb = Pathname.new(destination_root)+Pathname.new("config/environment.rb")
|
32
|
+
@pagination_plugin_found = File.open(env_rb).grep(/Praxis::Plugins::PaginationPlugin.*/).reject{|l| l.strip[0] == '#'}.present?
|
33
|
+
if options[:design]
|
34
|
+
say_status 'Design', "Generating scaffold for #{plural_class}", :blue
|
35
|
+
template 'design/media_types/item.rb', "design/#{version_dir}/media_types/#{collection_name.singularize}.rb"
|
36
|
+
template 'design/endpoints/collection.rb', "design/#{version_dir}/endpoints/#{collection_name}.rb"
|
37
|
+
end
|
38
|
+
if options[:implementation]
|
39
|
+
say_status 'Implement', "Generating scaffold for #{plural_class}", :blue
|
40
|
+
if options[:resource]
|
41
|
+
base_resource = Pathname.new(destination_root)+Pathname.new("app/#{version_dir}/resources/base.rb")
|
42
|
+
unless base_resource.exist?
|
43
|
+
# Copy an appropriate base resource for the version (resources within same version must share same base)
|
44
|
+
say_status "NOTE:",
|
45
|
+
"Creating a base resource file for resources to inherit from (at 'app/#{version_dir}/resources/base.rb')",
|
46
|
+
:yellow
|
47
|
+
say_status "",
|
48
|
+
"If you had already other resources in the app, change them to derive from this Base"
|
49
|
+
template 'implementation/resources/base.rb', "app/#{version_dir}/resources/base.rb"
|
50
|
+
end
|
51
|
+
template 'implementation/resources/item.rb', "app/#{version_dir}/resources/#{collection_name.singularize}.rb"
|
52
|
+
end
|
53
|
+
template 'implementation/controllers/collection.rb', "app/#{version_dir}/controllers/#{collection_name}.rb"
|
54
|
+
end
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
# Helper functions (which are available in the ERB contexts)
|
59
|
+
no_commands do
|
60
|
+
def plural_class
|
61
|
+
collection_name.camelize
|
62
|
+
end
|
63
|
+
|
64
|
+
def singular_class
|
65
|
+
collection_name.singularize.camelize
|
66
|
+
end
|
67
|
+
|
68
|
+
def version
|
69
|
+
options[:version]
|
70
|
+
end
|
71
|
+
|
72
|
+
def version_module
|
73
|
+
"V#{version}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def version_dir
|
77
|
+
version_module.camelize(:lower)
|
78
|
+
end
|
79
|
+
|
80
|
+
def action_enabled?(action)
|
81
|
+
@actions_hash[action.to_sym]
|
82
|
+
end
|
83
|
+
|
84
|
+
def pagination_enabled?
|
85
|
+
@pagination_plugin_found
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.compose_actions_hash(actions_opt)
|
90
|
+
required = { index: true, show: true }
|
91
|
+
case actions_opt
|
92
|
+
when nil
|
93
|
+
required
|
94
|
+
when 'cr'
|
95
|
+
required.merge(create: true)
|
96
|
+
when 'cru'
|
97
|
+
required.merge(create: true, update: true)
|
98
|
+
when 'crud'
|
99
|
+
required.merge(create: true, update: true, delete: true)
|
100
|
+
when 'u'
|
101
|
+
required.merge(update: true)
|
102
|
+
when 'ud'
|
103
|
+
required.merge(update: true, delete: true)
|
104
|
+
when 'd'
|
105
|
+
required.merge(delete: true)
|
106
|
+
else
|
107
|
+
raise "actions option does not support the string #{actions_opt}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.check_name(name)
|
112
|
+
sanitized = name.downcase.gsub(/[^a-z0-9_]/, '')
|
113
|
+
# TODO: bail or support CamelCase collections (for now only snake case)
|
114
|
+
raise "Please use only downcase letters, numbers and underscores for the collection" unless sanitized == name
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -18,6 +18,7 @@ Praxis::Application.configure do |application|
|
|
18
18
|
# map :models, 'models/**/*'
|
19
19
|
# map :responses, '**/responses/**/*'
|
20
20
|
# map :exceptions, '**/exceptions/**/*'
|
21
|
+
# map :concerns, '**/concerns/**/*'
|
21
22
|
# map :resources, '**/resources/**/*'
|
22
23
|
# map :controllers, '**/controllers/**/*'
|
23
24
|
# end
|
@@ -32,10 +32,17 @@ namespace :db do
|
|
32
32
|
puts "Database migrated."
|
33
33
|
end
|
34
34
|
|
35
|
+
desc 'Fully receate, migrate and seed the DB'
|
36
|
+
task :recreate do
|
37
|
+
Rake::Task['db:drop'].invoke rescue nil
|
38
|
+
Rake::Task['db:create'].invoke
|
39
|
+
Rake::Task['db:migrate'].invoke
|
40
|
+
Rake::Task['db:seed'].invoke
|
41
|
+
end
|
42
|
+
|
35
43
|
desc 'seed with example data'
|
36
44
|
task seed: 'praxis:environment' do
|
37
|
-
require_relative '
|
38
|
-
DatabaseHelper.seed!
|
45
|
+
require_relative 'db/seeds.rb'
|
39
46
|
end
|
40
47
|
|
41
48
|
desc 'drops current database'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module V1
|
2
|
+
module Concerns
|
3
|
+
module ControllerBase
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
# Controller concen that wraps an API with a transaction, and automatically rolls it back
|
6
|
+
# for non-2xx (or 3xx) responses
|
7
|
+
included do
|
8
|
+
around :action do |controller, callee|
|
9
|
+
begin
|
10
|
+
# TODO: Support Sequel as well
|
11
|
+
ActiveRecord::Base.transaction do
|
12
|
+
callee.call
|
13
|
+
res = controller.response
|
14
|
+
# Force a rollback for non 2xx or 3xx responses
|
15
|
+
raise ActiveRecord::Rollback unless res.status >= 200 && res.status < 400
|
16
|
+
end
|
17
|
+
rescue ActiveRecord::Rollback
|
18
|
+
# No need to do anything, let the responses flow normally
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module V1
|
4
|
+
module Resources
|
5
|
+
module Concerns
|
6
|
+
module Href
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
# Base module where the href concern will grab constants from
|
10
|
+
included do
|
11
|
+
def self.base_module
|
12
|
+
::V1
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def endpoint_path_template
|
18
|
+
# memoize a templated path for an endpoint, like
|
19
|
+
# /im/contacts/%{id}
|
20
|
+
return @endpoint_path_template if @endpoint_path_template
|
21
|
+
|
22
|
+
path = self.base_module.const_get(:Endpoints).const_get(model.name.split(':').last.pluralize).canonical_path.route.path
|
23
|
+
@endpoint_path_template = path.names.inject(path.to_s) { |p, name| p.sub(':' + name, "%{#{name}}") }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def href
|
28
|
+
format(self.class.endpoint_path_template, id: id)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../concerns/href'
|
4
|
+
|
5
|
+
module V1
|
6
|
+
module Resources
|
7
|
+
class Base < Praxis::Mapper::Resource
|
8
|
+
include Resources::Concerns::Href
|
9
|
+
|
10
|
+
# Base for all V1 resources.
|
11
|
+
# Resources withing a single version should have resource mappings separate from other versions
|
12
|
+
# and the Mapper::Resource will appropriately maintain different model_maps for each Base classes
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -2,42 +2,21 @@
|
|
2
2
|
|
3
3
|
module V1
|
4
4
|
module Resources
|
5
|
-
class User <
|
5
|
+
class User < Base
|
6
6
|
model ::User
|
7
7
|
|
8
|
-
# Mappings for the allowed
|
8
|
+
# Mappings for the allowed filters
|
9
9
|
filters_mapping(
|
10
|
-
'
|
11
|
-
'first_name': 'first_name',
|
12
|
-
|
13
|
-
'
|
14
|
-
case spec[:value].to_s
|
15
|
-
when 'pending' # Pending users do not have a uuid
|
16
|
-
{ name: :uuid, value: nil, op: spec[:op] }
|
17
|
-
when 'active' # Active users do not have a uuid (so "flip" the original equality condition)
|
18
|
-
opposite_op = spec[:op] == '=' ? '!=' : '='
|
19
|
-
{ name: :uuid, value: nil, op: opposite_op }
|
20
|
-
else
|
21
|
-
raise "Cannot filter users by state #{spec[:value]}"
|
22
|
-
end
|
23
|
-
end,
|
10
|
+
'uuid': 'uuid',
|
11
|
+
'first_name': 'first_name',
|
12
|
+
'last_name': 'last_name',
|
13
|
+
'email': 'email'
|
24
14
|
)
|
25
15
|
|
26
|
-
# Example of a property that depends on a differently named DB field
|
27
|
-
property :uid, dependencies: %i[id]
|
28
16
|
# To compute the full_name (method below) we need to load first and last names from the DB
|
29
17
|
property :full_name, dependencies: %i[first_name last_name]
|
30
18
|
|
31
|
-
|
32
|
-
id # underlying id field of the model
|
33
|
-
end
|
34
|
-
|
35
|
-
# Computed attribute: if uuid nil, user in in a pending stat, else active
|
36
|
-
def state
|
37
|
-
self.uuid.nil? ? 'pending' : 'active'
|
38
|
-
end
|
39
|
-
|
40
|
-
# Computed attribute the combines first and last
|
19
|
+
# Computed attribute that combines first and last
|
41
20
|
def full_name
|
42
21
|
[first_name, last_name].join(' ')
|
43
22
|
end
|
@@ -8,10 +8,9 @@ Bundler.require(:default, ENV['RACK_ENV'])
|
|
8
8
|
# API field selection (a la GraphQL) - for querying and rendering
|
9
9
|
# API filtering extensions (to add "where clauses") in listings
|
10
10
|
# Views and partial rendering (for ActiveRecord models)
|
11
|
-
|
12
11
|
require 'praxis/plugins/mapper_plugin'
|
13
12
|
require 'praxis/mapper/active_model_compat'
|
14
|
-
|
13
|
+
# Want to take advantage of the pagination and sorting extensions as well
|
15
14
|
require 'praxis/plugins/pagination_plugin'
|
16
15
|
|
17
16
|
# Start the sqlite DB
|
@@ -3,8 +3,8 @@ Praxis::Application.configure do |application|
|
|
3
3
|
|
4
4
|
# Configure the Mapper plugin (if we want to use all the filtering/field_selection extensions)
|
5
5
|
application.bootloader.use Praxis::Plugins::MapperPlugin
|
6
|
-
#
|
7
|
-
application.bootloader.use Praxis::Plugins::PaginationPlugin, {
|
6
|
+
# Configure the Pagination plugin (if we want to use all the pagination/ordering extensions)
|
7
|
+
application.bootloader.use Praxis::Plugins::PaginationPlugin, **{
|
8
8
|
# max_items: 500, # Unlimited by default,
|
9
9
|
# default_page_size: 100,
|
10
10
|
# paging_default_mode: {by: :id},
|
@@ -33,6 +33,7 @@ Praxis::Application.configure do |application|
|
|
33
33
|
# map :models, 'models/**/*'
|
34
34
|
# map :responses, '**/responses/**/*'
|
35
35
|
# map :exceptions, '**/exceptions/**/*'
|
36
|
+
# map :concerns, '**/concerns/**/*'
|
36
37
|
# map :resources, '**/resources/**/*'
|
37
38
|
# map :controllers, '**/controllers/**/*'
|
38
39
|
# end
|
data/tasks/thor/templates/generator/example_app/db/migrate/20201010101010_create_users_table.rb
CHANGED
@@ -3,9 +3,10 @@
|
|
3
3
|
class CreateUsersTable < ActiveRecord::Migration[5.2]
|
4
4
|
def change
|
5
5
|
create_table :users do |table|
|
6
|
-
table.column :uuid, :
|
7
|
-
table.column :first_name, :string
|
6
|
+
table.column :uuid, :string, null: false
|
7
|
+
table.column :first_name, :string, null: false
|
8
8
|
table.column :last_name, :string
|
9
|
+
table.column :email, :string
|
9
10
|
end
|
10
11
|
end
|
11
12
|
end
|
@@ -4,7 +4,6 @@ module V1
|
|
4
4
|
module Endpoints
|
5
5
|
class Users
|
6
6
|
include Praxis::EndpointDefinition
|
7
|
-
# include AuthenticatedEndpoint
|
8
7
|
|
9
8
|
media_type MediaTypes::User
|
10
9
|
version '1'
|
@@ -18,15 +17,16 @@ module V1
|
|
18
17
|
attribute :fields, Praxis::Types::FieldSelector.for(MediaTypes::User),
|
19
18
|
description: 'Fields with which to render the result.'
|
20
19
|
attribute :filters, Praxis::Types::FilteringParams.for(MediaTypes::User) do
|
21
|
-
filter '
|
20
|
+
filter 'uuid', using: ['=', '!=']
|
22
21
|
filter 'first_name', using: ['=', '!='], fuzzy: true
|
23
22
|
filter 'last_name', using: ['=', '!='], fuzzy: true
|
23
|
+
filter 'email', using: ['=', '!=']
|
24
24
|
end
|
25
25
|
attribute :pagination, Praxis::Types::PaginationParams.for(MediaTypes::User) do
|
26
|
-
by_fields :
|
26
|
+
by_fields :uuid, :first_name, :last_name
|
27
27
|
end
|
28
28
|
attribute :order, Praxis::Extensions::Pagination::OrderingParams.for(MediaTypes::User) do
|
29
|
-
by_fields :
|
29
|
+
by_fields :uuid, :last_name, :first_name
|
30
30
|
end
|
31
31
|
end
|
32
32
|
response :ok, media_type: Praxis::Collection.of(MediaTypes::User)
|
@@ -9,16 +9,11 @@ module V1
|
|
9
9
|
description 'A user in the system'
|
10
10
|
|
11
11
|
attributes do
|
12
|
-
attribute :
|
12
|
+
attribute :id, Integer
|
13
13
|
attribute :uuid, String
|
14
14
|
attribute :email, String
|
15
15
|
attribute :first_name, String
|
16
16
|
attribute :last_name, String
|
17
|
-
attribute :state, String, values: %i[pending active]
|
18
|
-
end
|
19
|
-
|
20
|
-
default_fieldset do
|
21
|
-
attribute :uid
|
22
17
|
end
|
23
18
|
end
|
24
19
|
end
|