jobshop 0.0.163 → 0.0.167

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +3 -3
  3. data/Rakefile +8 -17
  4. data/app/controllers/jobshop/application_controller.rb +5 -4
  5. data/app/controllers/jobshop/places_controller.rb +0 -4
  6. data/app/controllers/jobshop/users_controller.rb +0 -3
  7. data/app/mailers/jobshop/application_mailer.rb +0 -2
  8. data/app/mailers/jobshop/rfq_responder_mailer.rb +18 -0
  9. data/app/models/jobshop/ability.rb +1 -1
  10. data/app/models/jobshop/customer.rb +30 -0
  11. data/app/models/jobshop/customer/category.rb +19 -0
  12. data/app/models/jobshop/customer/contact.rb +28 -0
  13. data/app/models/jobshop/employee.rb +46 -0
  14. data/app/models/jobshop/employment.rb +25 -0
  15. data/app/models/jobshop/employment_version.rb +10 -0
  16. data/app/models/jobshop/inspection/boolean_criterion.rb +2 -2
  17. data/app/models/jobshop/inspection/deviation_criterion.rb +4 -4
  18. data/app/models/jobshop/inspection/limit_criterion.rb +6 -4
  19. data/app/models/jobshop/inspection/report.rb +2 -0
  20. data/app/models/jobshop/inspection/result.rb +2 -2
  21. data/app/models/jobshop/mailman.rb +22 -0
  22. data/app/models/jobshop/order.rb +2 -6
  23. data/app/models/jobshop/order_line.rb +2 -3
  24. data/app/models/jobshop/organization.rb +15 -6
  25. data/app/models/jobshop/place.rb +2 -0
  26. data/app/models/jobshop/product.rb +0 -3
  27. data/app/models/jobshop/rfq.rb +12 -6
  28. data/app/models/jobshop/user.rb +2 -17
  29. data/app/views/jobshop/rfq_responder_mailer/verified_contact.html.erb +5 -0
  30. data/app/views/jobshop/rfq_responder_mailer/verified_contact.text.erb +3 -0
  31. data/app/views/layouts/jobshop/application.html.haml +15 -0
  32. data/app/views/layouts/jobshop/mailer.html.erb +13 -0
  33. data/config/initializers/devise.rb +290 -0
  34. data/config/locales/devise.en.yml +64 -0
  35. data/config/routes.rb +1 -2
  36. data/db/migrate/20170311194758_initialize_jobshop.rb +42 -2
  37. data/db/migrate/20171216021339_create_organizations.rb +1 -4
  38. data/db/migrate/20171216021400_create_employees.rb +47 -0
  39. data/db/migrate/20171216021554_create_people.rb +13 -18
  40. data/db/migrate/20171216021853_create_customers.rb +57 -0
  41. data/db/migrate/20171216022020_create_places.rb +8 -9
  42. data/db/migrate/20171216022135_create_products.rb +6 -13
  43. data/db/migrate/20171216022605_create_orders.rb +19 -36
  44. data/db/migrate/20171216023018_create_roles.rb +5 -3
  45. data/db/migrate/20171216035357_create_things.rb +4 -4
  46. data/db/migrate/20171219022118_create_routing_processes.rb +15 -15
  47. data/db/migrate/20180107203241_create_inspections.rb +15 -14
  48. data/db/migrate/20181117023949_create_rfqs.rb +27 -19
  49. data/db/migrate/20181118014603_create_mailmen.rb +6 -4
  50. data/db/seeds.rb +126 -0
  51. data/lib/generators/jobshop/app/app_generator.rb +1 -1
  52. data/lib/generators/jobshop/app/templates/Procfile.tt +1 -0
  53. data/lib/generators/jobshop/app/templates/config/database.yml.tt +5 -5
  54. data/lib/generators/jobshop/{dummy/dummy_generator.rb → canary/canary_generator.rb} +13 -14
  55. data/lib/generators/jobshop/{dummy → canary}/templates/config/boot.rb.tt +0 -0
  56. data/lib/jobshop.rb +41 -12
  57. data/lib/jobshop/cli.rb +16 -82
  58. data/lib/jobshop/cli/app_generator.rb +38 -0
  59. data/lib/jobshop/cli/application.rb +14 -0
  60. data/lib/jobshop/cli/canary.rb +52 -0
  61. data/lib/jobshop/configuration.rb +16 -11
  62. data/lib/jobshop/engine.rb +39 -28
  63. data/lib/jobshop/helpers/migration.rb +78 -2
  64. data/lib/jobshop/mailroom/base_handler.rb +33 -0
  65. data/lib/jobshop/mailroom/null_handler.rb +10 -0
  66. data/lib/jobshop/mailroom/postmaster.rb +77 -0
  67. data/lib/jobshop/mailroom/rfq_handler.rb +39 -0
  68. data/lib/jobshop/version.rb +3 -1
  69. data/lib/tasks/jobshop_tasks.rake +7 -6
  70. metadata +98 -69
  71. data/app/controllers/concerns/email_token_validation.rb +0 -59
  72. data/app/controllers/concerns/jobshop/authentication_handler.rb +0 -15
  73. data/app/controllers/concerns/jobshop/authorization_handler.rb +0 -29
  74. data/app/controllers/jobshop/session_activations_controller.rb +0 -13
  75. data/app/controllers/jobshop/sessions_controller.rb +0 -20
  76. data/app/email_handlers/jobshop/rfq_handler.rb +0 -43
  77. data/app/models/jobshop/company.rb +0 -35
  78. data/app/models/jobshop/company/type.rb +0 -17
  79. data/app/models/jobshop/company_person.rb +0 -15
  80. data/app/models/jobshop/person.rb +0 -30
  81. data/app/models/jobshop/session_activation.rb +0 -30
  82. data/app/serializers/jobshop/test_user_serializer.rb +0 -10
  83. data/app/services/jobshop/authentication_service.rb +0 -20
  84. data/app/services/jobshop/authorization_service.rb +0 -30
  85. data/app/services/jobshop/jwt_service.rb +0 -17
  86. data/db/migrate/20171216021853_create_companies.rb +0 -64
  87. data/db/migrate/20171216023022_create_sessions.rb +0 -23
  88. data/lib/jobshop/cli/spinner.rb +0 -21
  89. data/lib/jobshop/dummy_app.rb +0 -190
  90. data/lib/jobshop/postmaster.rb +0 -89
@@ -1,59 +0,0 @@
1
- class EmailTokenValidation
2
- def self.before(controller)
3
- @token = EmailToken.new(
4
- controller.params.fetch(:user_email, nil),
5
- controller.params.fetch(:email_authentication_token, nil)
6
- )
7
-
8
- if @token.valid?
9
- sign_out_current_scope
10
- sign_in token.user
11
- token.destroy
12
- end
13
- end
14
-
15
- class EmailToken
16
- attr_reader :token
17
-
18
- def initialize(email, token)
19
- @email, @token = email, token
20
- end
21
-
22
- def valid?
23
- present? && user && token && !expired? && secure_compare
24
- end
25
-
26
- private
27
- def present?
28
- @email.present? && @token.present?
29
- end
30
-
31
- def user
32
- @user ||= Jobshop::User.where(email: @email)
33
- .where.not(email_authentication_token_sent_at: nil).first
34
- end
35
-
36
- def destroy
37
- user.update({
38
- email_authentication_token: nil,
39
- email_authentication_token_sent_at: nil
40
- })
41
- end
42
-
43
- def secure_compare
44
- # Notice how we use Devise.secure_compare to compare the token in the
45
- # database with the token given in the params, mitigating timing
46
- # attacks.
47
- Devise.secure_compare(user.email_authentication_token, token)
48
- end
49
-
50
- def expired?
51
- @expired ||= Time.now >= expires_on
52
- end
53
-
54
- def expires_on
55
- # TODO: Make token expiration configurable in initializers/jobshop.rb.
56
- @expires_on ||= user.email_authentication_token_sent_at + 6.hours
57
- end
58
- end
59
- end
@@ -1,15 +0,0 @@
1
- module Jobshop
2
- module AuthenticationHandler
3
- extend ActiveSupport::Concern
4
-
5
- class InvalidCredentials < StandardError; end
6
-
7
- included do
8
- rescue_from AuthenticationHandler::InvalidCredentials, with: :unauthorized
9
- end
10
-
11
- private def unauthorized(e)
12
- render({ json: { message: "Invalid credentials #{e.message}" } }, :unauthorized)
13
- end
14
- end
15
- end
@@ -1,29 +0,0 @@
1
- module Jobshop
2
- module AuthorizationHandler
3
- extend ActiveSupport::Concern
4
-
5
- class TokenMissing < StandardError; end
6
- class TokenInvalid < StandardError; end
7
-
8
- included do
9
- before_action :authorize_request
10
-
11
- rescue_from Jobshop::AuthorizationHandler::TokenMissing, with: :four_twenty_two
12
- rescue_from Jobshop::AuthorizationHandler::TokenInvalid, with: :four_twenty_two
13
- rescue_from ActiveRecord::RecordInvalid, with: :four_twenty_two
14
- rescue_from ActiveRecord::RecordNotFound do |e|
15
- render({ json: { message: e.message } }, :not_found)
16
- end
17
-
18
- attr_reader :current_user
19
- end
20
-
21
- private def four_twenty_two(e)
22
- render({ json: { message: e.message } }, :unprocessable_entity)
23
- end
24
-
25
- private def authorize_request
26
- @current_user = (AuthorizationService.new(request.headers).perform)[:user]
27
- end
28
- end
29
- end
@@ -1,13 +0,0 @@
1
- require_dependency "jobshop/application_controller"
2
-
3
- module Jobshop
4
- class SessionActivationsController < ApplicationController
5
- def destroy
6
- revoked_session.destroy
7
- end
8
-
9
- private def revoked_session
10
- @revoked_session ||= current_user.session_activations.find(params[:id])
11
- end
12
- end
13
- end
@@ -1,20 +0,0 @@
1
- require_dependency "jobshop/application_controller"
2
-
3
- module Jobshop
4
- class SessionsController < ApplicationController
5
- skip_before_action :authorize_request, only: :new
6
-
7
- def new
8
- token = AuthenticationService.new(
9
- session_params[:email],
10
- session_params[:password]
11
- ).perform
12
-
13
- render(json: { auth_token: token })
14
- end
15
-
16
- private def session_params
17
- params.permit(:email, :password)
18
- end
19
- end
20
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literals: true
2
-
3
- module Jobshop
4
- class RFQHandler
5
- def initialize(message, organization:)
6
- @from = message.from.first
7
- @subject = message.subject
8
- @content = message.text_part.decoded
9
- @organization = organization
10
- end
11
-
12
- def process
13
- new_rfq = @organization.rfqs.create({
14
- from: @from,
15
- subject: @subject,
16
- content: @content
17
- })
18
-
19
- if new_rfq.persisted?
20
- # TODO: Send response email to customer
21
- # TODO: Send rfq to quoting queue.
22
- return new_rfq
23
- else
24
- # TODO: There was some kind of failure which should be logged.
25
- return false
26
- end
27
- end
28
-
29
- # Whether or not to copy the original to a new location after processing.
30
- def copy_after_processing?
31
- true
32
- end
33
-
34
- # Whether or not to delete the original item after processing.
35
- def delete_after_processing?
36
- true
37
- end
38
-
39
- def processed_copy_prefix
40
- "rfqs/processed"
41
- end
42
- end
43
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literals: true
2
-
3
- module Jobshop
4
- class Company < ApplicationRecord
5
- self.primary_keys = %i[ organization_id company_id ]
6
-
7
- # Because we have a column named +type+, Rails wants to activate STI
8
- # Disable it manually
9
- self.inheritance_column = nil
10
-
11
- after_initialize { self.company_id ||= SecureRandom.uuid if new_record? }
12
-
13
- belongs_to :organization, inverse_of: :companies
14
-
15
- has_many :company_people, inverse_of: :company,
16
- foreign_key: %i[ organization_id company_id ]
17
-
18
- belongs_to :created_by, class_name: "Jobshop::Person",
19
- foreign_key: %i[ organization_id created_by_id ]
20
-
21
- has_many :orders, inverse_of: :company,
22
- foreign_key: %i[ organization_id company_id ]
23
-
24
- has_many :people, through: :company_people,
25
- foreign_key: %i[ organization_id person_id ]
26
-
27
- has_many :rfqs, inverse_of: :company,
28
- foreign_key: %i[ organization_id company_id ]
29
-
30
- belongs_to :type, class_name: "Jobshop::Company::Type",
31
- foreign_key: %i[ organization_id type_id ], inverse_of: :companies
32
-
33
- validates :name, presence: true
34
- end
35
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literals: true
2
-
3
- module Jobshop
4
- class Company::Type < ApplicationRecord
5
- self.primary_keys = %i[ organization_id type_id ]
6
-
7
- after_initialize { self.type_id ||= SecureRandom.uuid if new_record? }
8
-
9
- belongs_to :organization, inverse_of: :company_types
10
-
11
- has_many :companies, inverse_of: :type,
12
- foreign_key: %i[ organization_id type_id ]
13
-
14
- validates :name,
15
- uniqueness: { scope: :organization_id, case_sensitive: false }
16
- end
17
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literals: true
2
-
3
- module Jobshop
4
- class CompanyPerson < ApplicationRecord
5
- self.primary_keys = %i[ organization_id company_id person_id ]
6
-
7
- belongs_to :organization
8
-
9
- belongs_to :company, inverse_of: :company_people,
10
- foreign_key: %i[ organization_id company_id ]
11
-
12
- belongs_to :person, inverse_of: :company_person,
13
- foreign_key: %i[ organization_id person_id ]
14
- end
15
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literals: true
2
-
3
- module Jobshop
4
- class Person < ApplicationRecord
5
- self.primary_keys = %i[ organization_id person_id ]
6
-
7
- after_initialize { self.person_id ||= SecureRandom.uuid if new_record? }
8
-
9
- belongs_to :organization, inverse_of: :people
10
-
11
- has_one :company_person, inverse_of: :person,
12
- foreign_key: %i[ organization_id person_id ]
13
-
14
- has_one :company, through: :company_person,
15
- foreign_key: %i[ organization_id company_id ]
16
-
17
- validates :email,
18
- presence: { if: :email_required? },
19
- format: { if: :email_required?, with: /\A[^@\s]+@[^@\s]+\z/ },
20
- uniqueness: { if: :email_changed?, scope: :organization_id, case_sensitive: false }
21
-
22
- def name
23
- @name = [ forename, surname ].join(" ")
24
- end
25
-
26
- private def email_required?
27
- true
28
- end
29
- end
30
- end
@@ -1,30 +0,0 @@
1
- module Jobshop
2
- class SessionActivation < ApplicationRecord
3
- LIMIT = 20
4
-
5
- class << self
6
- def active?(token)
7
- token && where(activation_token: token).exists?
8
- end
9
-
10
- def activate(token)
11
- activation = create!(activation_token: token)
12
- enforce_active_session_quota
13
- activation
14
- end
15
-
16
- def deactivate(token)
17
- return unless token
18
- where(activation_token: token).delete_all
19
- end
20
-
21
- def enforce_active_session_quota
22
- order("created_at desc").offset(LIMIT).destroy_all
23
- end
24
-
25
- def exclusive(token)
26
- where("activation_token != ?", id).delete_all
27
- end
28
- end
29
- end
30
- end
@@ -1,10 +0,0 @@
1
- module Jobshop
2
- class TestUserSerializer
3
- include FastJsonapi::ObjectSerializer
4
- attributes :email, :forename, :surname, :job_title
5
-
6
- attribute :organization_name do |object|
7
- object.organization.name
8
- end
9
- end
10
- end
@@ -1,20 +0,0 @@
1
- module Jobshop
2
- class AuthenticationService
3
- attr_reader :email, :password
4
-
5
- def initialize(email, password)
6
- @email = email
7
- @password = password
8
- end
9
-
10
- def perform
11
- Jobshop::JWTService.encode(user_id: user.id) if user
12
- end
13
-
14
- private def user
15
- user = User.find_by(email: email)
16
- user && user.authenticate(password) ||
17
- raise(AuthenticationHandler::InvalidCredentials)
18
- end
19
- end
20
- end
@@ -1,30 +0,0 @@
1
- module Jobshop
2
- class AuthorizationService
3
- attr_reader :headers
4
-
5
- def initialize(headers = {})
6
- @headers = headers
7
- end
8
-
9
- def perform
10
- { user: user }
11
- end
12
-
13
- private def user
14
- @user ||= Jobshop::User.find(decoded_auth_token[:user_id]) if decoded_auth_token
15
- rescue ActiveRecord::RecordNotFound => e
16
- raise(AuthorizationHandler::TokenInvalid, e.message)
17
- end
18
-
19
- private def decoded_auth_token
20
- @decoded_auth_token ||= JWTService.decode(http_auth_header)
21
- end
22
-
23
- private def http_auth_header
24
- if headers["Authorization"].present?
25
- return headers["Authorization"].split(" ").last
26
- end
27
- raise(AuthorizationHandler::TokenMissing)
28
- end
29
- end
30
- end
@@ -1,17 +0,0 @@
1
- module Jobshop
2
- class JWTService
3
- HMAC_SECRET = "fixme"
4
-
5
- def self.encode(payload, exp = 24.hours.from_now)
6
- payload[:exp] = exp.to_i
7
- JWT.encode(payload, HMAC_SECRET)
8
- end
9
-
10
- def self.decode(token)
11
- body = JWT.decode(token, HMAC_SECRET)[0]
12
- HashWithIndifferentAccess.new body
13
- rescue JWT::DecodeError => e
14
- raise AuthorizationHandler::TokenInvalid, e.message
15
- end
16
- end
17
- end
@@ -1,64 +0,0 @@
1
- # frozen_string_literals: true
2
-
3
- require "jobshop/helpers/migration.rb"
4
-
5
- class CreateCompanies < ActiveRecord::Migration[5.2]
6
- include Jobshop::Helpers::Migration
7
-
8
- def change # rubocop:disable Metrics/MethodLength
9
- create_table :jobshop_company_types, id: false do |t|
10
- t.uuid :organization_id, null: false
11
- t.uuid :type_id, null: false, default: "gen_random_uuid()"
12
- t.index %i[ organization_id type_id ],
13
- name: :idx_jobshop_company_types_pkey, unique: true
14
-
15
- t.citext :name, null: false
16
- t.index %i[ organization_id name ], unique: true
17
-
18
- t.text :description
19
- end
20
-
21
- idx_table_name_pkey :jobshop_company_types
22
- fk_organization_id :jobshop_company_types
23
-
24
- create_table :jobshop_companies, id: false do |t|
25
- t.uuid :organization_id, null: false
26
- t.uuid :company_id, null: false, default: "gen_random_uuid()"
27
- t.index %i[ organization_id company_id ], unique: true,
28
- name: :idx_jobshop_companies_pkey
29
-
30
- t.uuid :created_by_id, null: false
31
-
32
- t.uuid :type_id, null: false
33
-
34
- t.citext :name, null: false
35
- t.index %i[ organization_id name ], unique: true
36
-
37
- t.timestamps
38
- end
39
-
40
- idx_table_name_pkey :jobshop_companies
41
- fk_organization_id :jobshop_companies
42
- fk_created_by_id :jobshop_companies
43
- foreign_key :jobshop_companies, :jobshop_company_types,
44
- %i[ organization_id type_id ]
45
-
46
- create_table :jobshop_company_people, id: false do |t|
47
- t.uuid :organization_id, null: false
48
- t.uuid :company_id, null: false
49
- t.uuid :person_id, null: false
50
- t.index %i[ organization_id company_id person_id ],
51
- name: :idx_jobshop_company_people_pkey, unique: true
52
-
53
- t.index %i[ organization_id company_id ],
54
- name: :idx_jobshop_company_organization_company_ids
55
- end
56
-
57
- idx_table_name_pkey :jobshop_company_people
58
- fk_organization_id :jobshop_company_people
59
- foreign_key :jobshop_company_people, :jobshop_companies,
60
- %i[ organization_id company_id ]
61
- foreign_key :jobshop_company_people, :jobshop_people,
62
- %i[ organization_id person_id ]
63
- end
64
- end