praxis 2.0.pre.10 → 2.0.pre.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|