introspective_grape 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|