api_engine_base 0.1.1 → 0.2.0

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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -6
  3. data/app/controllers/api_engine_base/admin_controller.rb +104 -0
  4. data/app/controllers/api_engine_base/application_controller.rb +45 -11
  5. data/app/controllers/api_engine_base/auth/plain_text_controller.rb +1 -1
  6. data/app/controllers/api_engine_base/inbox/message_blast_controller.rb +89 -0
  7. data/app/controllers/api_engine_base/inbox/message_controller.rb +79 -0
  8. data/app/controllers/api_engine_base/user_controller.rb +49 -0
  9. data/app/models/api_engine_base/application_record.rb +38 -0
  10. data/app/models/message.rb +30 -0
  11. data/app/models/message_blast.rb +27 -0
  12. data/app/models/user.rb +15 -4
  13. data/app/services/api_engine_base/README.md +49 -0
  14. data/app/services/api_engine_base/argument_validation/README.md +192 -0
  15. data/app/services/api_engine_base/argument_validation/class_methods.rb +2 -3
  16. data/app/services/api_engine_base/argument_validation/instance_methods.rb +13 -1
  17. data/app/services/api_engine_base/authorize/validate.rb +49 -0
  18. data/app/services/api_engine_base/inbox_service/blast/delete.rb +23 -0
  19. data/app/services/api_engine_base/inbox_service/blast/metadata.rb +26 -0
  20. data/app/services/api_engine_base/inbox_service/blast/new_user_blaster.rb +24 -0
  21. data/app/services/api_engine_base/inbox_service/blast/retrieve.rb +30 -0
  22. data/app/services/api_engine_base/inbox_service/blast/upsert.rb +67 -0
  23. data/app/services/api_engine_base/inbox_service/message/metadata.rb +35 -0
  24. data/app/services/api_engine_base/inbox_service/message/modify.rb +44 -0
  25. data/app/services/api_engine_base/inbox_service/message/retrieve.rb +36 -0
  26. data/app/services/api_engine_base/inbox_service/message/send.rb +33 -0
  27. data/app/services/api_engine_base/jwt/authenticate_user.rb +22 -7
  28. data/app/services/api_engine_base/jwt/login_create.rb +1 -1
  29. data/app/services/api_engine_base/login_strategy/plain_text/create.rb +1 -0
  30. data/app/services/api_engine_base/service_base.rb +4 -5
  31. data/app/services/api_engine_base/user_attributes/modify.rb +68 -0
  32. data/app/services/api_engine_base/user_attributes/roles.rb +27 -0
  33. data/config/routes.rb +32 -0
  34. data/db/migrate/20241117043720_create_api_engine_base_users.rb +2 -0
  35. data/db/migrate/20250223023306_create_api_engine_base_messages.rb +12 -0
  36. data/db/migrate/20250223023313_create_api_engine_base_message_blasts.rb +14 -0
  37. data/lib/api_engine_base/authorization/default.yml +42 -0
  38. data/lib/api_engine_base/authorization/entity.rb +101 -0
  39. data/lib/api_engine_base/authorization/role.rb +101 -0
  40. data/lib/api_engine_base/authorization.rb +85 -0
  41. data/lib/api_engine_base/configuration/admin/config.rb +18 -0
  42. data/lib/api_engine_base/configuration/application/config.rb +2 -2
  43. data/lib/api_engine_base/configuration/authorization/config.rb +24 -0
  44. data/lib/api_engine_base/configuration/config.rb +19 -1
  45. data/lib/api_engine_base/configuration/user/config.rb +56 -0
  46. data/lib/api_engine_base/engine.rb +38 -6
  47. data/lib/api_engine_base/error.rb +5 -0
  48. data/lib/api_engine_base/schema/admin/users.rb +15 -0
  49. data/lib/api_engine_base/schema/error/invalid_argument_response.rb +1 -1
  50. data/lib/api_engine_base/schema/inbox/blast_request.rb +15 -0
  51. data/lib/api_engine_base/schema/inbox/blast_response.rb +16 -0
  52. data/lib/api_engine_base/schema/inbox/message_blast_entity.rb +16 -0
  53. data/lib/api_engine_base/schema/inbox/message_blast_metadata.rb +16 -0
  54. data/lib/api_engine_base/schema/inbox/message_entity.rb +14 -0
  55. data/lib/api_engine_base/schema/inbox/metadata.rb +18 -0
  56. data/lib/api_engine_base/schema/inbox/modified.rb +13 -0
  57. data/lib/api_engine_base/schema/page.rb +14 -0
  58. data/lib/api_engine_base/schema/user.rb +28 -0
  59. data/lib/api_engine_base/schema.rb +13 -0
  60. data/lib/api_engine_base/spec_helper.rb +4 -3
  61. data/lib/api_engine_base/version.rb +1 -1
  62. data/lib/api_engine_base.rb +2 -2
  63. metadata +44 -5
  64. data/app/controllers/concerns/api_engine_base/schematizable.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 237d043d261233c9e45d4b7466dcacbfb4792636246fa2e211e765ee21b75141
4
- data.tar.gz: 55d598e1abaf7a12a253fbc9ea8d010c9a11ac9ca5ea293e2a30f6cb42ffc759
3
+ metadata.gz: d10e7cc64ff7f99aa912731ce7200ebfbff18e90b0cc05b948a8f6034d15e54f
4
+ data.tar.gz: 07baec4ba6dc487ab145d68e87e489181153550b3e22f1db594d56e1fcc86e3d
5
5
  SHA512:
6
- metadata.gz: 53180702bea719474e8f9922858da3c1af7b6c44ce3ad1db78bb5c633c039b9b5bb7a524984a637e41dd16499478f896731f033a8c8beece0441eff76ac40fa9
7
- data.tar.gz: '03397ea3efb5e7a68554ae600d4eda0d0f467d2990a14dbb4e1bf2ea2cd13a96e82abd679894e2943c51e097eab095c10434116303c1004404626d84d2805c5a'
6
+ metadata.gz: 93d925277dac2e33eff40c8f3e4669f4ea162051ca6f9ec0d9d66791ed9e937384262fd983a04d4895c7891d1ec763d41f092a3242c4e685f9237f6b8af463ba
7
+ data.tar.gz: 7e7bd57ff6c8a991fe251c6afb01d4d3a8900a002973dc8c661313fd94ceac1f81e17f7dc80ec96f175bc110f4c0ec74638c75b9eb32b4a871815dff2c20e21f
data/README.md CHANGED
@@ -1,8 +1,7 @@
1
1
  # ApiEngineBase
2
- Short description and motivation.
2
+ This is an API only base engine to build on top of. This Engine takes care of all Authentication, Token Refresh, and RBAC Roles so that you do not have to! For all applications, you can get right to work on implementing the code directly related to your project rather than dealing with the administrative overhead.
3
3
 
4
- ## Usage
5
- How to use my plugin.
4
+ While this gem is heavily opinionated, everything can be configured to your liking.
6
5
 
7
6
  ## Installation
8
7
  Add this line to your application's Gemfile:
@@ -21,8 +20,40 @@ Or install it yourself as:
21
20
  $ gem install api_engine_base
22
21
  ```
23
22
 
24
- ## Contributing
25
- Contribution directions go here.
23
+ ## Initializing ApiEngineBase
24
+ Please follow all steps in [Initializing ApiEngineBase](docs/initializing.md)
25
+
26
+
27
+ ## Available Routes
28
+
29
+ For more info, check out [Controllers ReadMe](docs/controllers.md)
30
+
31
+ Additionally, You can check out [RSpec Integration Testing](/spec/integration_test)
32
+
33
+ ## Available Models
34
+
35
+ ApiEngineBase provides several Models at the in the root namespace. Core Models like `User` and `UserSecret` are readily available. Don't forget! You can add additional methods to these classes by opening them back up.
36
+
37
+ For more info, check out [Models ReadMe](doc/models.md)
38
+
39
+ ## Authentication (JWT BearerToken)
40
+ Authentication ensures that we know which user is requesting the action. When the Engine is unable to authenticate, a `401` status code is returned.
41
+
42
+ For more info, check out [Authentication ReadMe](docs/authentication.md)
43
+
44
+ ## Authorization (RBAC)
45
+ Authorization is only done after authentication. This is the act of ensuring that the user can perform the action it is requesting. Put differently, I know who you are, but I need to validate you have permissions to complete the action. When the engine is unable to authorize the user, a `403` status code is returned.
46
+
47
+ For more info, check out [Authentication ReadMe](docs/authorization.md)
48
+
49
+ ## Sensitive Changes
50
+
51
+ For more info, check out [Sensitive Routes](docs/sensitive_routes.md)
52
+
53
+ ## ServiceBase
54
+ ServiceBase is built on top of Interactor. The ServiceBase is the heart of all logic for ApiEngineBase. It includes Logging and enhanced ArgumentValidation that can directly return back to the API request.
55
+
56
+ For more info, check out [ServiceBase ReadMe](app/services/api_engine_base/README.md)
26
57
 
27
58
  ## License
28
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
59
+ The engine is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiEngineBase
4
+ class AdminController < ::ApiEngineBase::ApplicationController
5
+ include ApiEngineBase::SchemaHelper
6
+
7
+ before_action :authenticate_user!
8
+ before_action :authorize_user!
9
+ before_action :user!, only: [:modify, :modify_role]
10
+
11
+ # Pagination is needed here
12
+ def show
13
+ schemafied_users = User.all.map { ApiEngineBase::Schema::User.convert_user_object(user: _1) }
14
+ schema = ApiEngineBase::Schema::Admin::Users.new(users: schemafied_users)
15
+ schema_succesful!(status: 200, schema:)
16
+ end
17
+
18
+ def modify
19
+ result = ApiEngineBase::UserAttributes::Modify.(user:, admin_user:, **modify_params)
20
+ if result.success?
21
+ schema = ApiEngineBase::Schema::User.convert_user_object(user: user.reload)
22
+ status = 201
23
+ schema_succesful!(status:, schema:)
24
+ else
25
+ if result.invalid_arguments
26
+ invalid_arguments!(
27
+ status: 400,
28
+ message: result.msg,
29
+ argument_object: result.invalid_argument_hash,
30
+ schema: ApiEngineBase::Schema::PlainText::LoginRequest
31
+ )
32
+ else
33
+ server_error!(result:)
34
+ end
35
+ end
36
+ end
37
+
38
+ def modify_role
39
+ result = ApiEngineBase::UserAttributes::Roles.(user:, admin_user:, roles: params[:roles] || [])
40
+ if result.success?
41
+ schema = ApiEngineBase::Schema::User.convert_user_object(user: user.reload)
42
+ status = 201
43
+ schema_succesful!(status:, schema:)
44
+ else
45
+ if result.invalid_arguments
46
+ invalid_arguments!(
47
+ status: 400,
48
+ message: result.msg,
49
+ argument_object: result.invalid_argument_hash,
50
+ schema: ApiEngineBase::Schema::PlainText::LoginRequest
51
+ )
52
+ else
53
+ server_error!(result:)
54
+ end
55
+ end
56
+ end
57
+
58
+ def impersonate
59
+ # TODO: @matt-taylor
60
+ end
61
+
62
+ private
63
+
64
+ def server_error!(result:)
65
+ status = 500
66
+ schema = ApiEngineBase::Schema::Error::Base.new(status:, message: result.msg)
67
+ render(json: schema.to_h, status:)
68
+ end
69
+
70
+ def modify_params
71
+ {
72
+ email: params[:email],
73
+ email_validated: safe_boolean(value: params[:email_validated]),
74
+ first_name: params[:first_name],
75
+ last_name: params[:last_name],
76
+ username: params[:username],
77
+ verifier_token: safe_boolean(value: params[:verifier_token]),
78
+ }.compact
79
+ end
80
+
81
+ def admin_user
82
+ # current_user is defined via authenticate_user! before action
83
+ current_user
84
+ end
85
+
86
+ def user!
87
+ _user = User.where(id: params[:user_id]).first
88
+ if _user
89
+ @user = _user
90
+ return true
91
+ end
92
+
93
+ status = 400
94
+ schema = ApiEngineBase::Schema::Error::Base.new(status:, message: "Invalid user")
95
+ render(json: schema.to_h, status:)
96
+ # Must return false so callbacks know to halt propagation
97
+ false
98
+ end
99
+
100
+ def user
101
+ @user ||= nil
102
+ end
103
+ end
104
+ end
@@ -2,12 +2,21 @@
2
2
 
3
3
  module ApiEngineBase
4
4
  class ApplicationController < ActionController::API
5
- AUTHORIZATION_HEADER = "AUTHORIZATION"
5
+ AUTHENTICATION_HEADER = "Authentication"
6
+ AUTHENTICATION_EXPIRE_HEADER = "X-Authentication-Expire"
7
+ AUTHENTICATION_WITH_RESET = "X-Authentication-Reset"
8
+
9
+ def safe_boolean(value:)
10
+ return nil unless [true, false, "true", "false", "0", "1", 0, 1].include?(value)
11
+
12
+ ActiveModel::Type::Boolean.new.cast(value)
13
+ end
6
14
 
7
15
  ###
8
- # AUTHORIZATION_HEADER="Bearer: {token value}"
16
+ # Authenticate user via the passed in header
17
+ # AUTHENTICATION_HEADER="Bearer: {token value}"
9
18
  def authenticate_user!(bypass_email_validation: false)
10
- raw_token = request.headers[AUTHORIZATION_HEADER]
19
+ raw_token = request.headers[AUTHENTICATION_HEADER]
11
20
  if raw_token.nil?
12
21
  status = 401
13
22
  schema = ApiEngineBase::Schema::Error::Base.new(status:, message: "Bearer token missing")
@@ -16,9 +25,14 @@ module ApiEngineBase
16
25
  end
17
26
 
18
27
  token = raw_token.split("Bearer:")[1].strip
19
- result = ApiEngineBase::Jwt::AuthenticateUser.(token:, bypass_email_validation:)
28
+ with_reset = safe_boolean(value: request.headers[AUTHENTICATION_WITH_RESET])
29
+ result = ApiEngineBase::Jwt::AuthenticateUser.(token:, bypass_email_validation:, with_reset:)
20
30
  if result.success?
21
31
  @current_user = result.user
32
+ response.set_header(AUTHENTICATION_EXPIRE_HEADER, result.expires_at)
33
+ if with_reset
34
+ response.set_header(AUTHENTICATION_WITH_RESET, result.generated_token)
35
+ end
22
36
  true
23
37
  else
24
38
  status = 401
@@ -29,19 +43,39 @@ module ApiEngineBase
29
43
  end
30
44
  end
31
45
 
46
+ ###
47
+ # Authenticate user via the passed in header without validating email
32
48
  def authenticate_user_without_email_verification!
33
49
  authenticate_user!(bypass_email_validation: true)
34
50
  end
35
51
 
36
- def current_user
37
- @current_user ||= nil
52
+ ###
53
+ # After Authenticating user, see if the user needs authorization on the route
54
+ def authorize_user!
55
+ if current_user.nil?
56
+ Rails.logger.error { "Current User is not defined. This means that authenticate_user! was not called" }
57
+ status = 401
58
+ schema = ApiEngineBase::Schema::Error::Base.new(status:, message: "Bearer token missing")
59
+ render(json: schema.to_h, status:)
60
+ return false
61
+ end
62
+ result = ApiEngineBase::Authorize::Validate.(user: current_user, controller: self.class, method: params[:action])
63
+
64
+ if result.success?
65
+ @current_user = result.user
66
+ true
67
+ else
68
+ # Current user is not authorized for the current Controller#action
69
+ status = 403
70
+ schema = ApiEngineBase::Schema::Error::Base.new(status:, message: result.msg)
71
+ render(json: schema.to_h, status:)
72
+ # Must return false so callbacks know to halt propagation
73
+ false
74
+ end
38
75
  end
39
76
 
40
- def add_to_body
41
- # {
42
- # token_valid_till:,
43
- # needs_email_verification:,
44
- # }
77
+ def current_user
78
+ @current_user ||= nil
45
79
  end
46
80
  end
47
81
  end
@@ -12,7 +12,7 @@ module ApiEngineBase
12
12
  if result.success?
13
13
  schema = ApiEngineBase::Schema::PlainText::LoginResponse.new(
14
14
  token: result.token,
15
- header_name: AUTHORIZATION_HEADER,
15
+ header_name: AUTHENTICATION_HEADER,
16
16
  message: "Successfully logged user in"
17
17
  )
18
18
  status = 201
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiEngineBase
4
+ module Inbox
5
+ class MessageBlastController < ::ApiEngineBase::ApplicationController
6
+ include ApiEngineBase::SchemaHelper
7
+
8
+ before_action :authenticate_user!
9
+ before_action :authorize_user!
10
+
11
+ # GET /inbox/blast
12
+ def metadata
13
+ result = ApiEngineBase::InboxService::Blast::Metadata.(id: params[:id].to_i)
14
+ schema_succesful!(status: 200, schema: result.metadata)
15
+ end
16
+
17
+ # GET /inbox/blast/:id
18
+ def blast
19
+ result = ApiEngineBase::InboxService::Blast::Retrieve.(id: params[:id].to_i)
20
+ if result.success?
21
+ schema = result.message_blast
22
+ status = 200
23
+ schema_succesful!(status:, schema:)
24
+ else
25
+ invalid_arguments!(
26
+ status: 400,
27
+ message: result.msg,
28
+ argument_object: result.invalid_argument_hash,
29
+ schema: ApiEngineBase::Schema::PlainText::LoginRequest,
30
+ )
31
+ end
32
+ end
33
+
34
+ # POST /inbox/blast
35
+ def create
36
+ upsert
37
+ end
38
+
39
+ # PATCH /inbox/blast/:id
40
+ def modify
41
+ upsert(id: params[:id].to_i)
42
+ end
43
+
44
+ # DELETE /inbox/blast/:id
45
+ def delete
46
+ result = ApiEngineBase::InboxService::Blast::Delete.(id: params[:id].to_i)
47
+ if result.success?
48
+ schema = result.message
49
+ status = 200
50
+ render :json, { id: params[:id], msg: "Message Blast message deleted" }
51
+ else
52
+ invalid_arguments!(
53
+ status: 400,
54
+ message: result.msg,
55
+ argument_object: result.invalid_argument_hash,
56
+ schema: ApiEngineBase::Schema::PlainText::LoginRequest,
57
+ )
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def upsert(id: nil)
64
+ upsert_params = {
65
+ user: current_user,
66
+ existing_users: safe_boolean(value: params[:existing_users]),
67
+ new_users: safe_boolean(value: params[:new_users]),
68
+ text: params[:text],
69
+ title: params[:title],
70
+ id:,
71
+ }.compact
72
+ result = ApiEngineBase::InboxService::Blast::Upsert.(**upsert_params)
73
+
74
+ if result.success?
75
+ schema = result.blast
76
+ status = 200
77
+ schema_succesful!(status:, schema:)
78
+ else
79
+ invalid_arguments!(
80
+ status: 400,
81
+ message: result.msg,
82
+ argument_object: result.invalid_argument_hash,
83
+ schema: ApiEngineBase::Schema::Inbox::BlastRequest
84
+ )
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiEngineBase
4
+ module Inbox
5
+ class MessageController < ::ApiEngineBase::ApplicationController
6
+ include ApiEngineBase::SchemaHelper
7
+
8
+ before_action :authenticate_user!
9
+
10
+ # GET: /inbox/messages
11
+ def metadata
12
+ result = ApiEngineBase::InboxService::Message::Metadata.(user: current_user)
13
+ if result.success?
14
+ schema = result.metadata
15
+ status = 200
16
+ schema_succesful!(status:, schema:)
17
+ else
18
+ invalid_arguments!(
19
+ status: 400,
20
+ message: result.msg,
21
+ argument_object: result.invalid_argument_hash,
22
+ schema: ApiEngineBase::Schema::PlainText::LoginRequest
23
+ )
24
+ end
25
+ end
26
+
27
+ # GET: /inbox/messages/:id
28
+ def message
29
+ result = ApiEngineBase::InboxService::Message::Retrieve.(user: current_user, id: params[:id].to_i)
30
+ if result.success?
31
+ schema = result.message
32
+ status = 200
33
+ schema_succesful!(status:, schema:)
34
+ else
35
+ invalid_arguments!(
36
+ status: 400,
37
+ message: result.msg,
38
+ argument_object: result.invalid_argument_hash,
39
+ schema: ApiEngineBase::Schema::PlainText::LoginRequest
40
+ )
41
+ end
42
+ end
43
+
44
+ # POST: /inbox/messages/ack
45
+ # Body { ids: [<list of ids to ack>] }
46
+ def ack
47
+ modify(type: ApiEngineBase::InboxService::Message::Modify::VIEWED)
48
+ end
49
+
50
+ # POST: /inbox/messages/delete
51
+ # Body { ids: [<list of ids to delete>] }
52
+ def delete
53
+ modify(type: ApiEngineBase::InboxService::Message::Modify::DELETE)
54
+ end
55
+
56
+ private
57
+
58
+ def modify(type:)
59
+ result = ApiEngineBase::InboxService::Message::Modify.(
60
+ user: current_user,
61
+ ids: params[:ids],
62
+ type:,
63
+ )
64
+ if result.success?
65
+ schema = result.modified
66
+ status = 200
67
+ schema_succesful!(status:, schema:)
68
+ else
69
+ invalid_arguments!(
70
+ status: 400,
71
+ message: result.msg,
72
+ argument_object: result.invalid_argument_hash,
73
+ schema: ApiEngineBase::Schema::PlainText::LoginRequest
74
+ )
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiEngineBase
4
+ class UserController < ::ApiEngineBase::ApplicationController
5
+ include ApiEngineBase::SchemaHelper
6
+
7
+ before_action :authenticate_user!
8
+
9
+ def show
10
+ schema = ApiEngineBase::Schema::User.convert_user_object(user: current_user)
11
+ schema_succesful!(status: 200, schema:)
12
+ end
13
+
14
+ def modify
15
+ result = ApiEngineBase::UserAttributes::Modify.(user: current_user, **modify_params)
16
+ if result.success?
17
+ schema = ApiEngineBase::Schema::User.convert_user_object(user: current_user.reload)
18
+ status = 201
19
+ schema_succesful!(status:, schema:)
20
+ else
21
+ if result.invalid_arguments
22
+ invalid_arguments!(
23
+ status: 400,
24
+ message: result.msg,
25
+ argument_object: result.invalid_argument_hash,
26
+ schema: ApiEngineBase::Schema::PlainText::LoginRequest
27
+ )
28
+ else
29
+ status = 500
30
+ schema = ApiEngineBase::Schema::Error::Base.new(status:, message: result.msg)
31
+ render(json: schema.to_h, status:)
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def modify_params
39
+ {
40
+ email: params[:email],
41
+ email_validated: safe_boolean(value: params[:email_validated]),
42
+ first_name: params[:first_name],
43
+ last_name: params[:last_name],
44
+ username: params[:username],
45
+ verifier_token: safe_boolean(value: params[:verifier_token]),
46
+ }.compact
47
+ end
48
+ end
49
+ end
@@ -3,5 +3,43 @@
3
3
  module ApiEngineBase
4
4
  class ApplicationRecord < ActiveRecord::Base
5
5
  self.abstract_class = true
6
+
7
+ def self.attribute_to_type_mapping
8
+ @attribute_to_type_mapping ||= begin
9
+ mapping = ActiveSupport::HashWithIndifferentAccess.new
10
+ columns_hash.each do |attribute_name, metadata|
11
+ base = nil
12
+ ruby_type = nil
13
+ allowed_types = nil
14
+ serialized_type = nil
15
+ case metadata.type
16
+ when :string, :text
17
+ base = ruby_type = String
18
+ when :integer, :bigint
19
+ base = ruby_type = Integer
20
+ when :datetime, :time, :date
21
+ base = String
22
+ ruby_type = [DateTime, Time]
23
+ when :float, :decimal
24
+ base = ruby_type = Float
25
+ when :boolean
26
+ base = "Boolean"
27
+ ruby_type = [TrueClass, FalseClass]
28
+ allowed_types = [true, false]
29
+ else
30
+ # All else fails convert to String and continue
31
+ base = ruby_type = String
32
+ end
33
+
34
+ attribute_type = attribute_types[attribute_name]
35
+ if attribute_type.is_a?(ActiveRecord::Type::Serialized)
36
+ serialized_type = attribute_type.coder.object_class
37
+ end
38
+ mapping[attribute_name] = { serialized_type:, base:, ruby_type:, allowed_types: }.compact
39
+ end
40
+
41
+ mapping
42
+ end
43
+ end
6
44
  end
7
45
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: messages
6
+ #
7
+ # id :bigint not null, primary key
8
+ # pushed :boolean default(FALSE)
9
+ # text :text(65535)
10
+ # title :string(255)
11
+ # viewed :boolean default(FALSE)
12
+ # created_at :datetime not null
13
+ # updated_at :datetime not null
14
+ # message_blast_id :bigint
15
+ # user_id :bigint not null
16
+ #
17
+ # Indexes
18
+ #
19
+ # index_messages_on_message_blast_id (message_blast_id)
20
+ # index_messages_on_user_id (user_id)
21
+ #
22
+ # Foreign Keys
23
+ #
24
+ # fk_rails_... (message_blast_id => message_blasts.id)
25
+ # fk_rails_... (user_id => users.id)
26
+ #
27
+ class Message < ApplicationRecord
28
+ belongs_to :user
29
+ belongs_to :message_blast, optional: true
30
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Schema Information
4
+ #
5
+ # Table name: message_blasts
6
+ #
7
+ # id :bigint not null, primary key
8
+ # existing_users :boolean default(FALSE)
9
+ # new_users :boolean default(FALSE)
10
+ # text :text(65535)
11
+ # title :string(255)
12
+ # created_at :datetime not null
13
+ # updated_at :datetime not null
14
+ # user_id :bigint not null
15
+ #
16
+ # Indexes
17
+ #
18
+ # index_message_blasts_on_user_id (user_id)
19
+ #
20
+ # Foreign Keys
21
+ #
22
+ # fk_rails_... (user_id => users.id)
23
+ #
24
+ class MessageBlast < ApplicationRecord
25
+ has_many :messages
26
+ belongs_to :user
27
+ end
data/app/models/user.rb CHANGED
@@ -16,6 +16,7 @@
16
16
  # password_consecutive_fail :integer default(0)
17
17
  # password_digest :string(255) default(""), not null
18
18
  # recovery_password_digest :string(255) default(""), not null
19
+ # roles :string(255) default([])
19
20
  # successful_login :integer default(0)
20
21
  # username :string(255)
21
22
  # verifier_token :string(255)
@@ -35,16 +36,26 @@ class User < ApiEngineBase::ApplicationRecord
35
36
  validates :username, uniqueness: true
36
37
  validates :email, uniqueness: true
37
38
 
39
+ ###
40
+ # Serialize the roles column to check for inclusion easily
41
+ serialize :roles, coder: JSON, type: Array
42
+
43
+ has_many :messages
44
+
38
45
  def full_name
39
46
  "#{first_name} #{last_name}"
40
47
  end
41
48
 
42
- def retreive_verifier_token!
43
- return verifier_token if verifier_token
44
-
49
+ def reset_verifier_token!
45
50
  value = SecureRandom.alphanumeric(32)
46
- update!(verifier_token: value)
51
+ update!(verifier_token: value, verifier_token_last_reset: Time.now)
47
52
 
48
53
  value
49
54
  end
55
+
56
+ def retreive_verifier_token!
57
+ return verifier_token if verifier_token
58
+
59
+ reset_verifier_token!
60
+ end
50
61
  end
@@ -0,0 +1,49 @@
1
+ # ApiEngineBase Service
2
+
3
+ `ApiEngineBase::ServiceBase` an abstraction around the Ruby Gem Interactor. It dds custom functionality to the base Service and is intended to be an inherited Class to create Application logic code. All Services in `ApiEngineBase` utilize this base Service class for convenience and DRYness.
4
+
5
+ ## What does ServiceBase offer
6
+
7
+ ### Logging
8
+ `ServiceBase` offers a convenient way to tag logs. It keeps track of:
9
+ - The start of the the Logic call
10
+ - The time it took to complete the logic
11
+ - The status of the logic
12
+
13
+ Additionally, it provides some convenience methods for logging
14
+ - `log_debug`
15
+ - `log_info`
16
+ - `log_warn`
17
+ - `log_error`
18
+
19
+ ### Argument Validation
20
+ Argument Validation is the powerhouse behind ServiceBase
21
+
22
+ Customized argument validation can be created by adding the method `validate!`
23
+ ```ruby
24
+ class MyServiceClass < ApiEngineBase::ServiceBase
25
+
26
+ def call
27
+ end
28
+
29
+ def validate!
30
+ # run custom validations before executing call
31
+ end
32
+ end
33
+ ```
34
+
35
+ Other more complex Argument validation includes:
36
+ - Validating Presence of Argument
37
+ - Validating Type of argument
38
+ - Validating a composition of argument values (At least, At Most, Exactly)
39
+ - Delegate context variable to the class for simplicity
40
+ - Validating Argument length or size is `<` `≤` `==` `>` `≥`
41
+
42
+ For More information, Check out the [ArgumentValidation ReadMe](argument_validation/README.md)
43
+
44
+
45
+ ## Basic Examples:
46
+ Check out the examples used in this directory!
47
+
48
+
49
+