introspective_grape 0.0.3
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 +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +35 -0
- data/Gemfile +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +103 -0
- data/Rakefile +26 -0
- data/app/assets/images/introspective_grape/.keep +0 -0
- data/app/assets/stylesheets/introspective_grape/.keep +0 -0
- data/app/controllers/.keep +0 -0
- data/app/helpers/.keep +0 -0
- data/app/mailers/.keep +0 -0
- data/app/models/.keep +0 -0
- data/app/views/.keep +0 -0
- data/bin/rails +12 -0
- data/introspective_grape.gemspec +49 -0
- data/lib/introspective_grape/api.rb +445 -0
- data/lib/introspective_grape/camel_snake.rb +71 -0
- data/lib/introspective_grape/version.rb +3 -0
- data/lib/introspective_grape.rb +4 -0
- data/lib/tasks/introspective_grape_tasks.rake +4 -0
- data/spec/dummy/Gemfile +4 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/api/active_record_helpers.rb +17 -0
- data/spec/dummy/app/api/api_helpers.rb +36 -0
- data/spec/dummy/app/api/dummy/chat_api.rb +108 -0
- data/spec/dummy/app/api/dummy/company_api.rb +8 -0
- data/spec/dummy/app/api/dummy/entities.rb +25 -0
- data/spec/dummy/app/api/dummy/location_api.rb +37 -0
- data/spec/dummy/app/api/dummy/project_api.rb +51 -0
- data/spec/dummy/app/api/dummy/role_api.rb +7 -0
- data/spec/dummy/app/api/dummy/sessions.rb +55 -0
- data/spec/dummy/app/api/dummy/user_api.rb +32 -0
- data/spec/dummy/app/api/dummy_api.rb +57 -0
- data/spec/dummy/app/api/error_handlers.rb +28 -0
- data/spec/dummy/app/api/permissions_helper.rb +7 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/abstract_adapter.rb +13 -0
- data/spec/dummy/app/models/admin_user.rb +6 -0
- data/spec/dummy/app/models/chat.rb +18 -0
- data/spec/dummy/app/models/chat_message.rb +34 -0
- data/spec/dummy/app/models/chat_message_user.rb +17 -0
- data/spec/dummy/app/models/chat_user.rb +16 -0
- data/spec/dummy/app/models/company.rb +14 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/models/image.rb +21 -0
- data/spec/dummy/app/models/job.rb +10 -0
- data/spec/dummy/app/models/locatable.rb +6 -0
- data/spec/dummy/app/models/location.rb +26 -0
- data/spec/dummy/app/models/location_beacon.rb +16 -0
- data/spec/dummy/app/models/location_gps.rb +14 -0
- data/spec/dummy/app/models/project.rb +20 -0
- data/spec/dummy/app/models/project_job.rb +7 -0
- data/spec/dummy/app/models/role.rb +30 -0
- data/spec/dummy/app/models/super_user.rb +11 -0
- data/spec/dummy/app/models/team.rb +9 -0
- data/spec/dummy/app/models/team_user.rb +13 -0
- data/spec/dummy/app/models/user/chatter.rb +79 -0
- data/spec/dummy/app/models/user.rb +84 -0
- data/spec/dummy/app/models/user_location.rb +28 -0
- data/spec/dummy/app/models/user_project_job.rb +16 -0
- data/spec/dummy/app/policies/application_policy.rb +47 -0
- data/spec/dummy/app/policies/chat_policy.rb +22 -0
- data/spec/dummy/app/policies/company_policy.rb +32 -0
- data/spec/dummy/app/policies/location_policy.rb +29 -0
- data/spec/dummy/app/policies/project_policy.rb +42 -0
- data/spec/dummy/app/policies/role_policy.rb +33 -0
- data/spec/dummy/app/policies/user_location_policy.rb +12 -0
- data/spec/dummy/app/policies/user_policy.rb +8 -0
- data/spec/dummy/app/views/layouts/application.html.erb +13 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config/application.rb +38 -0
- data/spec/dummy/config/boot.rb +6 -0
- data/spec/dummy/config/database.yml +23 -0
- data/spec/dummy/config/environment.rb +11 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +43 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/devise.rb +262 -0
- data/spec/dummy/config/initializers/devise_async.rb +2 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/paperclip.rb +13 -0
- data/spec/dummy/config/initializers/paperclip_adapter.rb +13 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/devise.en.yml +60 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +8 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20141002205024_devise_create_users.rb +42 -0
- data/spec/dummy/db/migrate/20141002211055_devise_create_admin_users.rb +48 -0
- data/spec/dummy/db/migrate/20141002211057_create_active_admin_comments.rb +19 -0
- data/spec/dummy/db/migrate/20141002220722_add_lockable_to_users.rb +8 -0
- data/spec/dummy/db/migrate/20150406213646_create_companies.rb +11 -0
- data/spec/dummy/db/migrate/20150414213154_add_user_authentication_token.rb +11 -0
- data/spec/dummy/db/migrate/20150415222005_create_roles.rb +12 -0
- data/spec/dummy/db/migrate/20150505181635_create_chats.rb +9 -0
- data/spec/dummy/db/migrate/20150505181636_create_chat_users.rb +11 -0
- data/spec/dummy/db/migrate/20150505181640_create_chat_messages.rb +11 -0
- data/spec/dummy/db/migrate/20150507191529_create_chat_message_users.rb +11 -0
- data/spec/dummy/db/migrate/20150601200526_create_locations.rb +12 -0
- data/spec/dummy/db/migrate/20150601200533_create_locatables.rb +10 -0
- data/spec/dummy/db/migrate/20150601212924_create_location_beacons.rb +15 -0
- data/spec/dummy/db/migrate/20150601213542_create_location_gps.rb +12 -0
- data/spec/dummy/db/migrate/20150609201823_create_user_locations.rb +14 -0
- data/spec/dummy/db/migrate/20150616205336_add_role_user_constraint.rb +9 -0
- data/spec/dummy/db/migrate/20150617232519_create_projects.rb +10 -0
- data/spec/dummy/db/migrate/20150617232521_create_jobs.rb +9 -0
- data/spec/dummy/db/migrate/20150617232522_create_project_jobs.rb +11 -0
- data/spec/dummy/db/migrate/20150623170133_create_user_project_jobs.rb +12 -0
- data/spec/dummy/db/migrate/20150701234929_create_teams.rb +11 -0
- data/spec/dummy/db/migrate/20150701234930_create_team_users.rb +11 -0
- data/spec/dummy/db/migrate/20150727214950_add_confirmable_to_devise.rb +11 -0
- data/spec/dummy/db/migrate/20150820190524_add_user_names.rb +6 -0
- data/spec/dummy/db/migrate/20150824215701_create_images.rb +15 -0
- data/spec/dummy/db/migrate/20150909225019_add_password_to_project.rb +5 -0
- data/spec/dummy/db/schema.rb +278 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/fixtures/images/avatar.jpeg +0 -0
- data/spec/fixtures/images/exif.jpeg +0 -0
- data/spec/models/chat_spec.rb +32 -0
- data/spec/models/image_spec.rb +14 -0
- data/spec/models/locatable_spec.rb +10 -0
- data/spec/models/project_spec.rb +17 -0
- data/spec/models/role_spec.rb +63 -0
- data/spec/models/team_spec.rb +17 -0
- data/spec/models/team_user_spec.rb +20 -0
- data/spec/models/user_location_spec.rb +35 -0
- data/spec/models/user_project_job_spec.rb +30 -0
- data/spec/models/user_spec.rb +125 -0
- data/spec/rails_helper.rb +23 -0
- data/spec/requests/chat_api_spec.rb +174 -0
- data/spec/requests/company_api_spec.rb +61 -0
- data/spec/requests/location_api_spec.rb +96 -0
- data/spec/requests/project_api_spec.rb +151 -0
- data/spec/requests/role_api_spec.rb +37 -0
- data/spec/requests/sessions_api_spec.rb +55 -0
- data/spec/requests/user_api_spec.rb +191 -0
- data/spec/support/blueprints.rb +103 -0
- data/spec/support/location_helper.rb +56 -0
- data/spec/support/pundit_helpers.rb +13 -0
- data/spec/support/request_helpers.rb +22 -0
- metadata +562 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
class Dummy::ChatAPI < DummyAPI
|
|
2
|
+
Chat.default_includes :chat
|
|
3
|
+
|
|
4
|
+
before do
|
|
5
|
+
authorize!
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
resource :chats do
|
|
9
|
+
|
|
10
|
+
desc "list the current user's existing chats"
|
|
11
|
+
get '/' do
|
|
12
|
+
authorize Chat.new, :index?
|
|
13
|
+
present current_user.chats.includes(:users), with: ChatEntity
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
desc "get new chat notifications"
|
|
17
|
+
params {
|
|
18
|
+
optional :id, type: Integer, desc: "Chat ID"
|
|
19
|
+
}
|
|
20
|
+
get '/notifications' do
|
|
21
|
+
authorize Chat.new, :show?
|
|
22
|
+
present current_user.new_messages?(params[:id])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
desc "get messages"
|
|
27
|
+
params {
|
|
28
|
+
optional :id, type: Integer, desc: "Chat ID"
|
|
29
|
+
optional :new, type: Boolean, desc: "Get only new messages"
|
|
30
|
+
optional :mark_as_read, type: Boolean, desc: "Mark new messages as read"
|
|
31
|
+
}
|
|
32
|
+
get '/messages' do
|
|
33
|
+
authorize Chat.new, :show?
|
|
34
|
+
present current_user.read_messages(params[:id], mark_as_read: params[:mark_as_read], new: params[:new]), with: MessageEntity
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
desc "list the users in a chat"
|
|
39
|
+
params {
|
|
40
|
+
requires :id, type: Integer, desc: "Chat ID"
|
|
41
|
+
}
|
|
42
|
+
get '/users' do
|
|
43
|
+
authorize Chat.new, :show?
|
|
44
|
+
present current_user.chats.find(params[:id]).active_users, with: UserEntity
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
desc "add user(s) to a chat"
|
|
49
|
+
params {
|
|
50
|
+
requires :id, type: Integer, desc: "Chat ID"
|
|
51
|
+
requires :user_ids, desc: 'Comma separated list of User IDs', type: String, regexp: /^\d+(,\d+)*$/
|
|
52
|
+
}
|
|
53
|
+
post '/users' do
|
|
54
|
+
authorize Chat.new, :create?
|
|
55
|
+
user_ids = params[:user_ids].split(',')
|
|
56
|
+
present status: current_user.add_chatters(params[:id], user_ids)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
desc "start a new chat"
|
|
61
|
+
params {
|
|
62
|
+
requires :message, type: String
|
|
63
|
+
requires :user_ids, desc: 'Comma separated list of User IDs', type: String, regexp: /^\d+(,\d+)*$/
|
|
64
|
+
}
|
|
65
|
+
post do
|
|
66
|
+
authorize Chat.new, :create?
|
|
67
|
+
user_ids = params[:user_ids].split(',')
|
|
68
|
+
present current_user.chat(user_ids, params[:message])
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
desc "reply to a chat"
|
|
73
|
+
params {
|
|
74
|
+
requires :id, type: Integer, desc: "Chat ID"
|
|
75
|
+
requires :message, type: String
|
|
76
|
+
}
|
|
77
|
+
put ':id' do
|
|
78
|
+
authorize Chat.new, :create?
|
|
79
|
+
current_user.reply(params[:id], params[:message])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
desc "drop out of a chat"
|
|
84
|
+
params {
|
|
85
|
+
requires :id, type: Integer, desc: "Chat ID"
|
|
86
|
+
}
|
|
87
|
+
delete ':id' do
|
|
88
|
+
authorize Chat.new, :create?
|
|
89
|
+
present status: current_user.leave_chat(params[:id])
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class UserEntity < Grape::Entity
|
|
95
|
+
expose :id, :name, :email, :avatar_url
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class ChatEntity < Grape::Entity
|
|
99
|
+
expose :id, :creator_id
|
|
100
|
+
expose :users, using: UserEntity
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class MessageEntity < Grape::Entity
|
|
104
|
+
expose :id, :chat_id, :message
|
|
105
|
+
expose :author, using: UserEntity
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Grape Entity monkey patch to ensure that all keys are exposed as camelCased instead of the actual Ruby snake_case names.
|
|
2
|
+
# Note that the keys are not touched if the :as option is used, so
|
|
3
|
+
# expose :some_name, :as => 'some_name'
|
|
4
|
+
# will remain snake_cased
|
|
5
|
+
|
|
6
|
+
#class Grape::Entity
|
|
7
|
+
# protected
|
|
8
|
+
# def self.key_for(attribute)
|
|
9
|
+
# (exposures[attribute.to_sym][:as] || attribute).to_s.camelize(:lower).to_sym
|
|
10
|
+
# end
|
|
11
|
+
#end
|
|
12
|
+
|
|
13
|
+
module Dummy::Entities
|
|
14
|
+
|
|
15
|
+
# base class for entities
|
|
16
|
+
class DummyEntity < Grape::Entity
|
|
17
|
+
# common formatters can go here
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# User entities
|
|
21
|
+
class User < DummyEntity
|
|
22
|
+
expose :id, :email, :first_name, :last_name, :avatar_url, :authentication_token
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
class Dummy::LocationAPI < IntrospectiveGrape::API
|
|
2
|
+
|
|
3
|
+
default_includes Location, :child_locations, :gps, :beacons, :locatables
|
|
4
|
+
|
|
5
|
+
exclude_actions LocationBeacon, :show,:create,:update,:destroy
|
|
6
|
+
exclude_actions LocationGps, :show,:create,:update,:destroy
|
|
7
|
+
|
|
8
|
+
restful Location, [:name, :kind,
|
|
9
|
+
{gps_attributes: [:id, :lat, :lng, :alt, :_destroy]},
|
|
10
|
+
{beacons_attributes: [:id, :company_id, :mac_address, :uuid, :major, :minor, :_destroy]},
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
class Locatable < Grape::Entity
|
|
14
|
+
expose :id, :locatable_id, :locatable_type, :updated_at, :created_at
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class LocationBeaconEntity < Grape::Entity
|
|
18
|
+
expose :id, :uuid, :major, :minor, :company_id, :mac_address, :created_at
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class LocationGpsEntity < Grape::Entity
|
|
22
|
+
expose :id, :lat, :lng, :alt, :updated_at
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class ChildLocationEntity < Grape::Entity
|
|
26
|
+
expose :id, :name, :kind, :created_at, :updated_at
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class LocationEntity < Grape::Entity
|
|
30
|
+
expose :id, :name, :kind, :parent_location_id, :created_at, :updated_at
|
|
31
|
+
expose :locatables, using: Locatable
|
|
32
|
+
expose :child_locations, using: ChildLocationEntity
|
|
33
|
+
expose :gps, using: LocationGpsEntity
|
|
34
|
+
expose :beacons, using: LocationBeaconEntity
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
class Dummy::ProjectAPI < IntrospectiveGrape::API
|
|
2
|
+
|
|
3
|
+
default_includes Project, :owner, :admins, :user_project_jobs, project_jobs: [:job], teams: [:team_users]
|
|
4
|
+
default_includes Team, :team_users
|
|
5
|
+
default_includes TeamUser, user: [:projects], team: [:project]
|
|
6
|
+
default_includes ProjectJob, :job
|
|
7
|
+
|
|
8
|
+
exclude_actions Project, :create,:destroy,:update
|
|
9
|
+
exclude_actions Team, :show
|
|
10
|
+
exclude_actions TeamUser, :show,:update
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
restful Project, [:id, teams_attributes: [:id,:name,:_destroy, team_users_attributes: [:id, :user_id, :_destroy] ]]
|
|
14
|
+
|
|
15
|
+
class AdminEntity < Grape::Entity
|
|
16
|
+
expose :id, as: :user_id
|
|
17
|
+
expose :email, :name, :avatar_url
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class UserJobEntity < Grape::Entity
|
|
21
|
+
expose :user_id, :name, :email, :avatar_url, :title, :created_at
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class JobEntity < Grape::Entity
|
|
25
|
+
expose :title
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class UserEntity < Grape::Entity
|
|
29
|
+
expose :id, :name, :avatar_url
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class TeamUserEntity < Grape::Entity
|
|
33
|
+
expose :id
|
|
34
|
+
expose :user, using: UserEntity
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class TeamEntity < Grape::Entity
|
|
38
|
+
expose :id, :name, :created_at, :updated_at
|
|
39
|
+
expose :team_users, using: TeamUserEntity
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class ProjectEntity < Grape::Entity
|
|
43
|
+
expose :id, :name, :created_at, :updated_at
|
|
44
|
+
expose :owner, using: Dummy::CompanyAPI::CompanyEntity
|
|
45
|
+
expose :admins, using: AdminEntity
|
|
46
|
+
expose :project_jobs, as: :jobs, using: JobEntity
|
|
47
|
+
expose :user_project_jobs, as: :user_jobs, using: UserJobEntity
|
|
48
|
+
expose :teams, using: TeamEntity
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
class Dummy::Sessions < Grape::API
|
|
2
|
+
|
|
3
|
+
resource :sessions do
|
|
4
|
+
|
|
5
|
+
desc "create a user session" do
|
|
6
|
+
detail "sign in a user"
|
|
7
|
+
end
|
|
8
|
+
params do
|
|
9
|
+
requires :login, type: String, desc: "email address"
|
|
10
|
+
requires :password, type: String, desc: "password"
|
|
11
|
+
optional :token, type: Boolean, desc: "set to true to generate and return Firebase secure token", default: false
|
|
12
|
+
end
|
|
13
|
+
post '/' do
|
|
14
|
+
authorize User.new, :sessions?
|
|
15
|
+
user = User.find_first_by_auth_conditions({email: params[:login]})
|
|
16
|
+
if user && user.valid_password?(params[:password]) && user.valid_for_authentication?
|
|
17
|
+
|
|
18
|
+
# commented out for now, User model is not yet confirmable
|
|
19
|
+
#unauthorized! DummyAPI::USER_NOT_CONFIRMED unless user.confirmed?
|
|
20
|
+
|
|
21
|
+
token = nil
|
|
22
|
+
if params[:token]
|
|
23
|
+
payload = {
|
|
24
|
+
uid: "#{user.id}", # uid must be a string
|
|
25
|
+
email: user.email,
|
|
26
|
+
avatar_url: user.avatar_url
|
|
27
|
+
}
|
|
28
|
+
user.authentication_token = SecureRandom.urlsafe_base64(nil, false)
|
|
29
|
+
user.save
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#user.ensure_authentication_token!
|
|
33
|
+
env['warden'].set_user(user, scope: :user)
|
|
34
|
+
present user, with: Dummy::Entities::User, token: token
|
|
35
|
+
else
|
|
36
|
+
unauthorized! DummyAPI::BAD_LOGIN
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
desc "delete a user session" do
|
|
42
|
+
detail "sign out the current user"
|
|
43
|
+
end
|
|
44
|
+
delete '/' do
|
|
45
|
+
authorize User.new, :sessions?
|
|
46
|
+
if u = User.find_by_authentication_token(params[:api_key])
|
|
47
|
+
u.authentication_token = nil
|
|
48
|
+
{status: u.save!}
|
|
49
|
+
else
|
|
50
|
+
{status: true } # the user is already logged out
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class Dummy::UserAPI < IntrospectiveGrape::API
|
|
2
|
+
|
|
3
|
+
skip_presence_validations :password
|
|
4
|
+
|
|
5
|
+
exclude_actions Role, :show,:update
|
|
6
|
+
exclude_actions UserProjectJob, :show,:update
|
|
7
|
+
|
|
8
|
+
restful User, [:id, :email, :password, :first_name, :last_name, :skip_confirmation_email,
|
|
9
|
+
user_project_jobs_attributes: [:id, :job_id, :project_id, :_destroy],
|
|
10
|
+
roles_attributes: [:id, :ownable_type, :ownable_id, :_destroy],
|
|
11
|
+
avatar_attributes: [:id, :file, :_destroy]
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
class RoleEntity < Grape::Entity
|
|
15
|
+
expose :id, :ownable_type, :ownable_id
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class ImageEntity < Grape::Entity
|
|
19
|
+
expose :id, :file_processing #, 'file.url'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class UserProjectJobEntity < Grape::Entity
|
|
23
|
+
expose :id, :name, :title, :job_id, :project_id
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class UserEntity < Grape::Entity
|
|
27
|
+
expose :id, :email, :first_name, :last_name, :avatar_url
|
|
28
|
+
expose :roles, as: :roles_attributes, using: RoleEntity
|
|
29
|
+
expose :user_project_jobs, as: :user_project_jobs_attributes, using: UserProjectJobEntity
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#require 'grape-swagger'
|
|
2
|
+
#require 'grape-entity'
|
|
3
|
+
require 'active_record_helpers'
|
|
4
|
+
#require 'introspective_grape/camel_snake'
|
|
5
|
+
|
|
6
|
+
class DummyAPI < Grape::API
|
|
7
|
+
version 'v1', using: :path
|
|
8
|
+
format :json
|
|
9
|
+
default_format :json
|
|
10
|
+
|
|
11
|
+
include ErrorHandlers
|
|
12
|
+
helpers PermissionsHelper
|
|
13
|
+
helpers ApiHelpers
|
|
14
|
+
|
|
15
|
+
USER_NOT_CONFIRMED = 'user_not_confirmed' # "Your account must be confirmed, please check your email inbox."
|
|
16
|
+
BAD_LOGIN = 'bad_login' # incorrect username or password'
|
|
17
|
+
|
|
18
|
+
before do
|
|
19
|
+
# sets server date in response header. This can be used on the client side
|
|
20
|
+
header "X-Server-Date", "#{Time.now.to_i}"
|
|
21
|
+
header "Expires", 1.year.ago.httpdate
|
|
22
|
+
# Convert incoming camel case params to snake case: grape will totally blow this
|
|
23
|
+
# if the params hash is not a Hashie::Mash, so make it one of those:
|
|
24
|
+
#@params = Hashie::Mash.new(snake_keys(params))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
before_validation do
|
|
28
|
+
Rails.logger.info "With params: #{params.to_hash.inspect}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
after do
|
|
32
|
+
unless self.options[:path].first =~ /swagger/
|
|
33
|
+
verify_authorized # Ensure that all endpoints are authorized by a policy class
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Load the in-memory database for the test app
|
|
38
|
+
load "#{Rails.root}/db/schema.rb"
|
|
39
|
+
|
|
40
|
+
# Mount every api endpoint under app/api/dummy/.
|
|
41
|
+
Dir.glob(Rails.root+"app"+"api"+'dummy'+'*.rb').each do |f|
|
|
42
|
+
api = "Dummy::#{File.basename(f, '.rb').camelize.sub(/Api$/,'API')}".constantize
|
|
43
|
+
mount api if api.respond_to? :endpoints
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# configure grape-swagger to auto-generate swagger docs
|
|
47
|
+
protocol = Rails.application.config.force_ssl ? 'https' : 'http'
|
|
48
|
+
add_swagger_documentation({
|
|
49
|
+
base_path: "#{protocol}://localhost:3000/api",
|
|
50
|
+
api_version: 'v1',
|
|
51
|
+
hide_documentation_path: true,
|
|
52
|
+
format: :json,
|
|
53
|
+
hide_format: true
|
|
54
|
+
#markdown: true
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'pundit'
|
|
2
|
+
module ErrorHandlers
|
|
3
|
+
def self.included(m)
|
|
4
|
+
m.rescue_from ActiveRecord::RecordInvalid do |e|
|
|
5
|
+
error_response message: e.record.errors.to_a.uniq.join(', '), status: 400
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
m.rescue_from Grape::Exceptions::ValidationErrors do |e|
|
|
9
|
+
error_response message: e.message, status: 400
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
m.rescue_from ActiveRecord::RecordNotFound do |e|
|
|
13
|
+
error_response message: "Record not found! #{e.message}", status: 404
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
m.rescue_from ActiveRecord::InvalidForeignKey do |e|
|
|
17
|
+
error_response message: "Join record not found! #{e.message}", status: 404
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
m.rescue_from Pundit::NotAuthorizedError do |e|
|
|
21
|
+
error_response message: "Forbidden", status: 403
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
m.rescue_from Pundit::NotDefinedError do |e|
|
|
25
|
+
error_response message: "Policy not implemented", status: 501
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
|
2
|
+
// listed below.
|
|
3
|
+
//
|
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
|
6
|
+
//
|
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
|
8
|
+
// compiled file.
|
|
9
|
+
//
|
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
|
11
|
+
// about supported directives.
|
|
12
|
+
//
|
|
13
|
+
//= require_tree .
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
|
3
|
+
* listed below.
|
|
4
|
+
*
|
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
|
7
|
+
*
|
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
|
11
|
+
* file per style scope.
|
|
12
|
+
*
|
|
13
|
+
*= require_tree .
|
|
14
|
+
*= require_self
|
|
15
|
+
*/
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class AbstractAdapter < ActiveRecord::Base
|
|
2
|
+
self.abstract_class = true
|
|
3
|
+
|
|
4
|
+
class << self
|
|
5
|
+
def human_attribute_name(attr, options = {})
|
|
6
|
+
# The default formatting of validation errors sucks, this helps a little syntatically:
|
|
7
|
+
super.titleize+":"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class Chat < AbstractAdapter
|
|
2
|
+
belongs_to :creator, foreign_key: :creator_id, :class_name => "User", inverse_of: :own_chats
|
|
3
|
+
|
|
4
|
+
has_many :chat_users, dependent: :destroy
|
|
5
|
+
has_many :users, through: :chat_users
|
|
6
|
+
has_many :chat_messages, dependent: :destroy
|
|
7
|
+
has_many :messages, class_name: 'ChatMessage', dependent: :destroy
|
|
8
|
+
|
|
9
|
+
def active_users
|
|
10
|
+
chat_users.includes(:user).select {|cu| cu.departed_at.nil? }.map(&:user)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
before_create :add_creator_to_conversation
|
|
14
|
+
def add_creator_to_conversation
|
|
15
|
+
users.push creator
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
class ChatMessage < AbstractAdapter
|
|
2
|
+
belongs_to :chat
|
|
3
|
+
belongs_to :author, class_name: 'User'
|
|
4
|
+
|
|
5
|
+
has_many :chat_users, through: :chat
|
|
6
|
+
has_many :recipients, lambda {|message| where(':created_at >= chat_users.created_at and (chat_users.departed_at IS NULL OR :created_at <= chat_users.departed_at)', created_at: message.created_at ) }, through: :chat_users, source: :user, class_name: 'User'
|
|
7
|
+
|
|
8
|
+
# Create ChatUserMessage records for each recipient to track read status
|
|
9
|
+
has_many :chat_message_users, dependent: :destroy
|
|
10
|
+
|
|
11
|
+
validate :author_in_chat
|
|
12
|
+
|
|
13
|
+
def author_in_chat
|
|
14
|
+
errors[:base] << 'User not in chat session.' unless chat.active_users.include? author
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
before_save :create_message_users, if: :new_record?
|
|
18
|
+
def create_message_users
|
|
19
|
+
chat_users.merge(ChatUser.current).each do |cu|
|
|
20
|
+
chat_message_users.build(user: cu.user)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def read_by?(user)
|
|
25
|
+
chat_message_users.merge(ChatMessageUser.read).map(&:user_id).include?(user.id)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.find_chat_for_users(users)
|
|
29
|
+
# presumably much more efficient ways to run an intersecton, we want to find the last
|
|
30
|
+
# exact match with the users being messaged to append to the existing chat.
|
|
31
|
+
Chat.eager_load(:chat_users).where("chat_users.departed_at IS NULL").order('chats.created_at desc').detect {|c| c.chat_users.map(&:user_id).uniq.sort == users.map(&:id).sort }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class ChatMessageUser < AbstractAdapter
|
|
2
|
+
belongs_to :chat_message
|
|
3
|
+
belongs_to :user
|
|
4
|
+
has_one :chat, through: :chat_message
|
|
5
|
+
|
|
6
|
+
scope :read, ->{ where('read_at IS NOT NULL' ) }
|
|
7
|
+
scope :unread, ->{ where('read_at IS NULL' ) }
|
|
8
|
+
|
|
9
|
+
before_save :author_reads_message
|
|
10
|
+
def author_reads_message
|
|
11
|
+
self.read_at = Time.now if user == chat_message.author
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def read?
|
|
15
|
+
read_at.present?
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class ChatUser < AbstractAdapter
|
|
2
|
+
belongs_to :chat
|
|
3
|
+
belongs_to :user
|
|
4
|
+
|
|
5
|
+
alias_attribute :joined_at, :created_at
|
|
6
|
+
alias_attribute :left_at, :departed_at
|
|
7
|
+
|
|
8
|
+
scope :current, ->{ where(departed_at: nil) }
|
|
9
|
+
|
|
10
|
+
validate :user_not_already_active, on: :create
|
|
11
|
+
|
|
12
|
+
def user_not_already_active
|
|
13
|
+
errors[:base] << "#{user.name} is already present in this chat." if chat.chat_users.where(user_id: user.id, departed_at: nil).count > 0 if user.persisted?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class Company < AbstractAdapter
|
|
2
|
+
has_many :roles, as: :ownable
|
|
3
|
+
has_many :admins, through: :roles, source: :user
|
|
4
|
+
accepts_nested_attributes_for :roles, allow_destroy: true
|
|
5
|
+
|
|
6
|
+
has_many :beacons, class_name: 'LocationBeacon', dependent: :destroy
|
|
7
|
+
has_many :locatables
|
|
8
|
+
has_many :locations, through: :locatables, source: :locatable, source_type: 'Company'
|
|
9
|
+
|
|
10
|
+
has_many :projects, foreign_key: :owner_id, dependent: :destroy, inverse_of: :owner
|
|
11
|
+
|
|
12
|
+
validates_length_of :name, maximum: 256
|
|
13
|
+
validates_length_of :short_name, maximum: 10
|
|
14
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'paperclip'
|
|
2
|
+
#require 'delayed_paperclip'
|
|
3
|
+
class Image < ActiveRecord::Base
|
|
4
|
+
belongs_to :imageable, polymorphic: true
|
|
5
|
+
|
|
6
|
+
has_attached_file :file, styles: {medium: "300x300>", thumb: "100x100"},
|
|
7
|
+
url: "/system/:imageable_type/:imageable_id/:id/:style/:filename"
|
|
8
|
+
|
|
9
|
+
validates_attachment :file, content_type: {content_type: ["image/jpeg", "image/png", "image/gif"]}
|
|
10
|
+
validates_attachment_size :file, :less_than => 2.megabytes
|
|
11
|
+
|
|
12
|
+
#process_in_background :file, processing_image_url: 'empty_avatar.png'
|
|
13
|
+
|
|
14
|
+
Paperclip.interpolates :imageable_type do |attachment, style|
|
|
15
|
+
attachment.instance.imageable_type.try(:pluralize)
|
|
16
|
+
end
|
|
17
|
+
Paperclip.interpolates :imageable_id do |attachment, style|
|
|
18
|
+
attachment.instance.imageable_id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class Job < AbstractAdapter
|
|
2
|
+
|
|
3
|
+
has_many :project_jobs, dependent: :destroy
|
|
4
|
+
accepts_nested_attributes_for :project_jobs, allow_destroy: true
|
|
5
|
+
|
|
6
|
+
has_many :user_project_jobs, dependent: :destroy
|
|
7
|
+
has_many :users, through: :user_project_jobs
|
|
8
|
+
has_many :projects, through: :user_project_jobs
|
|
9
|
+
|
|
10
|
+
end
|