gaku_api 0.3.0.pre.4

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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +17 -0
  3. data/app/controllers/gaku/api/application_controller.rb +3 -0
  4. data/app/controllers/gaku/api/v1/authentication_controller.rb +34 -0
  5. data/app/controllers/gaku/api/v1/base_controller.rb +53 -0
  6. data/app/controllers/gaku/api/v1/class_groups/enrollments_controller.rb +44 -0
  7. data/app/controllers/gaku/api/v1/class_groups/students_controller.rb +22 -0
  8. data/app/controllers/gaku/api/v1/courses/enrollments_controller.rb +44 -0
  9. data/app/controllers/gaku/api/v1/courses/students_controller.rb +22 -0
  10. data/app/controllers/gaku/api/v1/courses_controller.rb +54 -0
  11. data/app/controllers/gaku/api/v1/enrollments_controller.rb +22 -0
  12. data/app/controllers/gaku/api/v1/statuses_controller.rb +24 -0
  13. data/app/controllers/gaku/api/v1/students/class_groups_controller.rb +27 -0
  14. data/app/controllers/gaku/api/v1/students/courses_controller.rb +27 -0
  15. data/app/controllers/gaku/api/v1/students/exam_sessions_controller.rb +27 -0
  16. data/app/controllers/gaku/api/v1/students/extracurricular_activities_controller.rb +27 -0
  17. data/app/controllers/gaku/api/v1/students/guardians_controller.rb +27 -0
  18. data/app/controllers/gaku/api/v1/students/student_guardians_controller.rb +29 -0
  19. data/app/controllers/gaku/api/v1/students_controller.rb +67 -0
  20. data/app/controllers/gaku/api/v1/syllabuses_controller.rb +54 -0
  21. data/app/serializers/gaku/class_group_serializer.rb +6 -0
  22. data/app/serializers/gaku/course_serializer.rb +7 -0
  23. data/app/serializers/gaku/enrollment_serializer.rb +14 -0
  24. data/app/serializers/gaku/exam_session_serializer.rb +5 -0
  25. data/app/serializers/gaku/extracurricular_activity_serializer.rb +5 -0
  26. data/app/serializers/gaku/guardian_serializer.rb +9 -0
  27. data/app/serializers/gaku/student_serializer.rb +12 -0
  28. data/app/serializers/gaku/syllabus_serializer.rb +5 -0
  29. data/app/services/gaku/api/authenticate_user.rb +42 -0
  30. data/app/services/gaku/api/authorize_api_request.rb +39 -0
  31. data/app/services/gaku/api/refresh_authenticate_user.rb +50 -0
  32. data/config/initializers/kaminari.rb +3 -0
  33. data/config/initializers/mime_types.rb +1 -0
  34. data/config/routes.rb +31 -0
  35. data/gaku_api.gemspec +25 -0
  36. data/lib/gaku/api/engine.rb +28 -0
  37. data/lib/gaku/api/version.rb +5 -0
  38. data/lib/gaku/api.rb +15 -0
  39. data/lib/gaku_api.rb +1 -0
  40. data/lib/json_web_token.rb +24 -0
  41. data/lib/tasks/api_tasks.rake +4 -0
  42. metadata +184 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2022049a291ae30871d7cb99cd7e634c4793337a99da7b223b88247cc12da3a4
4
+ data.tar.gz: 539d9b8910f2ae44c1c315bb77121170bdb5940f664d3a7a0a60c5fc2ac8ab80
5
+ SHA512:
6
+ metadata.gz: d705d970e7707a30c5ac0ed92f9bcec973fff85c7a5bc01fa1cbb2efa2d6e105db378f28266643ddb4936bf64aaadfe7facf8a80159effab92b182b5bca0ddba
7
+ data.tar.gz: b8487fb3e71bc6467425a4fc9e32230cf1ba182bbfeca0a5450fbc034c05aaa61f43e289af118ab5e15c3fe31dddba1ca90c0bab170a4ff0b6ab241afa32c483
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'rake/packagetask'
6
+ require 'rubygems/package_task'
7
+ require 'gaku/testing/common_rake'
8
+
9
+ Bundler::GemHelper.install_tasks
10
+
11
+ task default: :spec
12
+
13
+ desc 'Generates a dummy app for testing'
14
+ task :test_app do
15
+ ENV['LIB_NAME'] = 'gaku/api'
16
+ Rake::Task['common:test_app'].invoke
17
+ end
@@ -0,0 +1,3 @@
1
+ class Gaku::Api::ApplicationController < ActionController::API
2
+
3
+ end
@@ -0,0 +1,34 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ class AuthenticationController < BaseController
5
+ skip_before_action :authenticate_request
6
+
7
+ def authenticate
8
+ command = Gaku::Api::AuthenticateUser.call(
9
+ username: params[:username],
10
+ password: params[:password]
11
+ )
12
+
13
+ if command.success?
14
+ render(respond_format => { tokens: command.result })
15
+ else
16
+ render(respond_format => { error: command.errors }, status: :unauthorized)
17
+ end
18
+ end
19
+
20
+ def refresh
21
+ refresh_token = params[:refresh_token]
22
+ command = Gaku::Api::RefreshAuthenticateUser.call(params[:refresh_token])
23
+
24
+ if command.success?
25
+ render respond_format => { tokens: command.result }
26
+ else
27
+ render respond_format => { error: command.errors }, status: :unauthorized
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,53 @@
1
+ class Gaku::Api::V1::BaseController < Gaku::Api::ApplicationController
2
+ include ActionController::MimeResponds
3
+ attr_reader :current_user
4
+
5
+ before_action :set_default_format
6
+ before_action :authenticate_request
7
+
8
+ # rescue_from StandardError do |exception|
9
+ # render respond_format => { error: exception.message }, status: 500
10
+ # end
11
+
12
+ # rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_response
13
+ # rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable_entity_response
14
+
15
+
16
+ private
17
+
18
+ def respond_format
19
+ request.format.to_sym
20
+ end
21
+
22
+ def member_respond_to(object, options = {})
23
+ render({respond_format => object, root: false, adapter: :attributes }.merge!(options))
24
+ end
25
+
26
+ def collection_respond_to(collection, options = {})
27
+ render({respond_format => collection, root: false, adapter: :json, meta: meta_for(collection)}.merge!(options))
28
+ end
29
+
30
+ def meta_for(collection)
31
+ { count: collection.size , total_count: collection.total_count, page: collection.current_page }
32
+ end
33
+
34
+ def authenticate_request
35
+ @current_user = Gaku::Api::AuthorizeApiRequest.call(request.headers).result
36
+ render respond_format => { error: 'Not Authorized' }, status: 401 unless @current_user
37
+ end
38
+
39
+ def set_default_format
40
+ unless [:json, :msgpack].include? request.format.symbol
41
+ request.format = :json
42
+ end
43
+ end
44
+
45
+ def render_unprocessable_entity_response(exception)
46
+ render(respond_format => exception.record.errors, status: :unprocessable_entity)
47
+ end
48
+
49
+ def render_not_found_response(exception)
50
+ render(respond_format => { error: "record not found" }, status: :not_found)
51
+ end
52
+
53
+ end
@@ -0,0 +1,44 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module ClassGroups
5
+ class EnrollmentsController < BaseController
6
+ before_action :set_class_group
7
+
8
+ def index
9
+ @enrollments = @class_group.enrollments.page(params[:page ])
10
+ collection_respond_to @enrollments, root: :enrollments
11
+
12
+ end
13
+
14
+ def create
15
+ @enrollment = @class_group.enrollments.create(create_enrollment_params)
16
+ member_respond_to @enrollment
17
+ end
18
+
19
+ def update
20
+ @enrollment = Enrollment.find(params[:id])
21
+ @enrollment.update!(update_enrollment_params)
22
+ member_respond_to @enrollment
23
+ end
24
+
25
+ private
26
+
27
+ def create_enrollment_params
28
+ params.require(:student_id)
29
+ params.permit(:student_id, :seat_number)
30
+ end
31
+
32
+ def update_enrollment_params
33
+ params.permit(:seat_number)
34
+ end
35
+
36
+ def set_class_group
37
+ @class_group = ClassGroup.find(params[:class_group_id])
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module ClassGroups
5
+ class StudentsController < BaseController
6
+ before_action :set_class_group
7
+
8
+ def index
9
+ @students = @class_group.students.page(params[:page ])
10
+ collection_respond_to @students, root: :students
11
+ end
12
+
13
+ private
14
+
15
+ def set_class_group
16
+ @class_group = ClassGroup.find(params[:class_group_id])
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,44 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Courses
5
+ class EnrollmentsController < BaseController
6
+ before_action :set_course
7
+
8
+ def index
9
+ @enrollments = @course.enrollments.page(params[:page ])
10
+ collection_respond_to @enrollments, root: :enrollments
11
+
12
+ end
13
+
14
+ def create
15
+ @enrollment = @course.enrollments.create(create_enrollment_params)
16
+ member_respond_to @enrollment
17
+ end
18
+
19
+ def update
20
+ @enrollment = Enrollment.find(params[:id])
21
+ @enrollment.update!(update_enrollment_params)
22
+ member_respond_to @enrollment
23
+ end
24
+
25
+ private
26
+
27
+ def create_enrollment_params
28
+ params.require(:student_id)
29
+ params.permit(:student_id, :seat_number)
30
+ end
31
+
32
+ def update_enrollment_params
33
+ params.permit(:seat_number)
34
+ end
35
+
36
+ def set_course
37
+ @course = Course.find(params[:course_id])
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Courses
5
+ class StudentsController < BaseController
6
+ before_action :set_course
7
+
8
+ def index
9
+ @students = @course.students.page(params[:page ])
10
+ collection_respond_to @students, root: :students
11
+ end
12
+
13
+ private
14
+
15
+ def set_course
16
+ @course = Course.find(params[:course_id])
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,54 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ class CoursesController < BaseController
5
+
6
+ before_action :set_course, only: %i( show update destroy )
7
+
8
+ def index
9
+ @courses = Course.all.page(params[:page ])
10
+ collection_respond_to @courses, root: :courses
11
+ end
12
+
13
+ def show
14
+ member_respond_to @course
15
+ end
16
+
17
+ def create
18
+ @course = Course.new(course_params)
19
+ if @course.save!
20
+ member_respond_to @course
21
+ end
22
+ end
23
+
24
+ def update
25
+ if @course.update!(course_params)
26
+ member_respond_to @course
27
+ end
28
+ end
29
+
30
+ def destroy
31
+ if @course.destroy!
32
+ member_respond_to @course
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def set_course
39
+ @course = Course.find(params[:id])
40
+ end
41
+
42
+ def course_params
43
+ params.require(:code)
44
+ params.permit(course_attrs)
45
+ end
46
+
47
+ def course_attrs
48
+ %i( syllabus_id code )
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,22 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ class EnrollmentsController < BaseController
5
+ skip_before_action :authenticate_request
6
+ before_action :set_enrollable
7
+
8
+ def index
9
+ @enrollments = @enrollable.enrollments.page(params[:page])
10
+ collection_respond_to @enrollments, root: :enrollments
11
+ end
12
+
13
+ private
14
+
15
+ def set_enrollable
16
+ resource, id = request.path.split('/').values_at(3,4)
17
+ @enrollable = "gaku/#{resource}".pluralize.classify.constantize.find(id)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ class StatusesController < ActionController::API
5
+
6
+ def show
7
+ if msgpack_request?
8
+ render msgpack: { status: :running }
9
+
10
+ else
11
+ render json: { status: :running }
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def msgpack_request?
18
+ request.format.to_sym == :msgpack
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Students
5
+ class ClassGroupsController < BaseController
6
+
7
+ before_action :set_student
8
+
9
+ def index
10
+ @class_groups = @student.class_groups
11
+ respond_to do |format|
12
+ format.json { render json: @class_groups, root: :class_groups, adapter: :json }
13
+ format.msgpack { render msgpack: @class_groups, root: :class_groups, adapter: :json }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def set_student
20
+ @student = Gaku::Student.find(params[:student_id])
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Students
5
+ class CoursesController < BaseController
6
+
7
+ before_action :set_student
8
+
9
+ def index
10
+ @courses = @student.courses
11
+ respond_to do |format|
12
+ format.json { render json: @courses, root: :courses, adapter: :json }
13
+ format.msgpack { render msgpack: @courses, root: :courses, adapter: :json }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def set_student
20
+ @student = Gaku::Student.find(params[:student_id])
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Students
5
+ class ExamSessionsController < BaseController
6
+
7
+ before_action :set_student
8
+
9
+ def index
10
+ @exam_sessions = @student.exam_sessions
11
+ respond_to do |format|
12
+ format.json { render json: @exam_sessions, root: :exam_sessions, adapter: :json }
13
+ format.msgpack { render msgpack: @exam_sessions, root: :exam_sessions, adapter: :json }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def set_student
20
+ @student = Gaku::Student.find(params[:student_id])
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Students
5
+ class ExtracurricularActivitiesController < BaseController
6
+
7
+ before_action :set_student
8
+
9
+ def index
10
+ @extracurricular_activities = @student.extracurricular_activities
11
+ respond_to do |format|
12
+ format.json { render json: @extracurricular_activities, root: :extracurricular_activities, adapter: :json }
13
+ format.msgpack { render msgpack: @extracurricular_activities, root: :extracurricular_activities, adapter: :json }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def set_student
20
+ @student = Gaku::Student.find(params[:student_id])
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Students
5
+ class GuardiansController < BaseController
6
+
7
+ before_action :set_student
8
+
9
+ def index
10
+ @guardians = @student.guardians
11
+ respond_to do |format|
12
+ format.json { render json: @guardians, root: :guardians, adapter: :json }
13
+ format.msgpack { render msgpack: @guardians, root: :guardians, adapter: :json }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def set_student
20
+ @student = Gaku::Student.find(params[:student_id])
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ module Students
5
+ class StudentGuardiansController < BaseController
6
+
7
+ before_action :set_guardian
8
+ before_action :set_student
9
+
10
+ def create
11
+ @student.guardians << @guardian
12
+ member_respond_to @guardian
13
+ end
14
+
15
+ private
16
+
17
+ def set_guardian
18
+ @guardian = Guardian.find(params[:guardian_id])
19
+ end
20
+
21
+ def set_student
22
+ @student = Student.find(params[:student_id])
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,67 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ class StudentsController < BaseController
5
+
6
+ skip_before_action :authenticate_request
7
+ before_action :set_student, except: %i( index create )
8
+
9
+ def index
10
+ @q = Student.includes(:primary_contact, :primary_address).search(params[:q])
11
+ @students = @q.result.page(params[:page])
12
+ collection_respond_to @students, root: :students
13
+ end
14
+
15
+ def show
16
+ member_respond_to @student
17
+ end
18
+
19
+ def create
20
+ @student = Student.new(create_student_params)
21
+ if @student.save!
22
+ member_respond_to @student
23
+ end
24
+ end
25
+
26
+ def update
27
+ if @student.update!(update_student_params)
28
+ member_respond_to @student
29
+ end
30
+ end
31
+
32
+ def destroy
33
+ if @student.destroy!
34
+ member_respond_to @student
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def set_student
41
+ @student = Student.find(params[:id])
42
+ end
43
+
44
+ def create_student_params
45
+ params.require(required_student_attributes)
46
+ params.permit(student_attributes)
47
+ end
48
+
49
+ def update_student_params
50
+ params.permit(student_attributes)
51
+ end
52
+
53
+ def required_student_attributes
54
+ %i( name surname )
55
+ end
56
+
57
+ def student_attributes
58
+ %i(
59
+ name surname name_reading surname_reading birth_date gender scholarship_status_id,
60
+ enrollment_status_code commute_method_type_id admitted graduated picture
61
+ )
62
+ end
63
+
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,54 @@
1
+ module Gaku
2
+ module Api
3
+ module V1
4
+ class SyllabusesController < BaseController
5
+
6
+ before_action :set_syllabus, only: %i( show update destroy )
7
+
8
+ def index
9
+ @syllabuses = Syllabus.all.page(params[:page ])
10
+ collection_respond_to @syllabuses, root: :syllabuses
11
+ end
12
+
13
+ def show
14
+ member_respond_to @syllabus
15
+ end
16
+
17
+ def create
18
+ @syllabus = Syllabus.new(syllabus_params)
19
+ if @syllabus.save!
20
+ member_respond_to @syllabus
21
+ end
22
+ end
23
+
24
+ def update
25
+ if @syllabus.update!(syllabus_params)
26
+ member_respond_to @syllabus
27
+ end
28
+ end
29
+
30
+ def destroy
31
+ if @syllabus.destroy!
32
+ member_respond_to @syllabus
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def set_syllabus
39
+ @syllabus = Syllabus.find(params[:id])
40
+ end
41
+
42
+ def syllabus_params
43
+ params.require(syllabus_attrs)
44
+ params.permit(syllabus_attrs)
45
+ end
46
+
47
+ def syllabus_attrs
48
+ %i( name code )
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,6 @@
1
+
2
+ module Gaku
3
+ class ClassGroupSerializer < ActiveModel::Serializer
4
+ attributes %i( id name grade homeroom notes_count faculty_id created_at updated_at enrollments_count )
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ module Gaku
2
+ class CourseSerializer < ActiveModel::Serializer
3
+ attributes %i( id name code notes_count students_count faculty_id
4
+ syllabus_id class_group_id created_at updated_at enrollments_count
5
+ )
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ module Gaku
2
+ class EnrollmentSerializer < ActiveModel::Serializer
3
+ attributes %i( id student_id seat_number )
4
+ attribute :course_id, if: :course_enrollment?
5
+
6
+ def course_id
7
+ object.enrollable_id
8
+ end
9
+
10
+ def course_enrollment?
11
+ object.course_type?
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ module Gaku
2
+ class ExamSessionSerializer < ActiveModel::Serializer
3
+ attributes %i( id session_time name exam_id session_start enrollments_count )
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Gaku
2
+ class ExtracurricularActivitySerializer < ActiveModel::Serializer
3
+ attributes %i( id name created_at updated_at enrollments_count )
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ module Gaku
2
+ class GuardianSerializer < ActiveModel::Serializer
3
+ attributes %i( id name surname middle_name name_reading middle_name_reading surname_reading
4
+ gender birth_date relationship picture_file_name picture_content_type picture_file_size
5
+ picture_updated_at primary_address primary_contact addresses_count contacts_count user_id
6
+ created_at updated_at
7
+ )
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module Gaku
2
+ class StudentSerializer < ActiveModel::Serializer
3
+ attributes %i( id name surname middle_name name_reading middle_name_reading surname_reading
4
+ gender birth_date admitted graduated code serial_id foreign_id_code national_registration_code
5
+ enrollment_status_code picture_file_name picture_content_type picture_file_size
6
+ picture_updated_at addresses_count contacts_count notes_count courses_count guardians_count
7
+ external_school_records_count badges_count primary_address primary_contact class_and_number
8
+ user_id faculty_id commute_method_type_id scholarship_status_id created_at updated_at
9
+ extracurricular_activities_count class_groups_count )
10
+
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ module Gaku
2
+ class SyllabusSerializer < ActiveModel::Serializer
3
+ attributes %i( id name code )
4
+ end
5
+ end
@@ -0,0 +1,42 @@
1
+ require 'json_web_token'
2
+ module Gaku
3
+ module Api
4
+ class AuthenticateUser
5
+ prepend SimpleCommand
6
+
7
+ attr_accessor :username, :password
8
+
9
+ def initialize(username:, password:)
10
+ @username = username
11
+ @password = password
12
+ end
13
+
14
+ def call
15
+ if user
16
+ secret = SecureRandom.hex(64)
17
+ $redis.set("token:#{jti}", secret, ex: 20.minutes)
18
+ {
19
+ auth_token: JsonWebToken.encode({user_id: user.id}, exp: 3.minutes.from_now, jti: jti, secret: secret),
20
+ refresh_token: JsonWebToken.encode({user_id: user.id}, exp: 20.minutes.from_now, jti: jti, secret: secret)
21
+ }
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def jti
28
+ @jti ||= SecureRandom.uuid
29
+ end
30
+
31
+ def user
32
+ user = Gaku::User.find_by(username: username)
33
+ return user if user && user.valid_password?(password)
34
+
35
+ errors.add(:user_authentication, 'invalid credentials')
36
+ nil
37
+ end
38
+
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ require 'json_web_token'
2
+
3
+ module Gaku
4
+ module Api
5
+ class AuthorizeApiRequest
6
+ prepend SimpleCommand
7
+
8
+ def initialize(headers = {})
9
+ @headers = headers
10
+ end
11
+
12
+ def call
13
+ user
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :headers
19
+
20
+ def user
21
+ @user ||= User.find(decoded_auth_token[:user_id]) if decoded_auth_token
22
+ @user || errors.add(:token, 'Invalid token') && nil
23
+ end
24
+
25
+ def decoded_auth_token
26
+ @decoded_auth_token ||= JsonWebToken.decode(http_auth_header)
27
+ end
28
+
29
+ def http_auth_header
30
+ if headers['Authorization'].present?
31
+ return headers['Authorization'].split(' ').last
32
+ else
33
+ errors.add(:token, 'Missing token')
34
+ end
35
+ nil
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,50 @@
1
+ require 'json_web_token'
2
+ module Gaku
3
+ module Api
4
+ class RefreshAuthenticateUser
5
+ prepend SimpleCommand
6
+
7
+ attr_accessor :refresh_token
8
+
9
+ def initialize(refresh_token)
10
+ @refresh_token = refresh_token
11
+ end
12
+
13
+ def call
14
+ if user
15
+ secret = SecureRandom.hex(64)
16
+ $redis.set("token:#{jti}", secret, ex: 20.minutes)
17
+ {
18
+ auth_token: JsonWebToken.encode({user_id: user.id}, exp: 3.minutes.from_now, jti: jti, secret: secret),
19
+ refresh_token: JsonWebToken.encode({user_id: user.id}, exp: 20.minutes.from_now, jti: jti, secret: secret)
20
+ }
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def jti
27
+ @jti ||= SecureRandom.uuid
28
+ end
29
+
30
+ def user
31
+ @user ||= User.find(decoded_refresh_token[:user_id]) if decoded_refresh_token
32
+ @user || errors.add(:token, 'Invalid expire token') && nil
33
+ end
34
+
35
+ def decoded_refresh_token
36
+ @decoded_refresh_token ||= JsonWebToken.decode(refresh_token, refresh: true)
37
+ end
38
+
39
+ # def user
40
+ # user = Gaku::User.find_by(email: email)
41
+ # return user if user && user.valid_password?(password)
42
+ #
43
+ # errors.add(:user_authentication, 'invalid credentials')
44
+ # nil
45
+ # end
46
+
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ Kaminari.configure do |config|
2
+ config.default_per_page = 100
3
+ end
@@ -0,0 +1 @@
1
+ Mime::Type.register "application/msgpack", :msgpack
data/config/routes.rb ADDED
@@ -0,0 +1,31 @@
1
+ Gaku::Core::Engine.routes.draw do
2
+ namespace :api do
3
+ namespace :v1 do
4
+ resource :status
5
+
6
+ post 'authenticate', to: 'authentication#authenticate'
7
+ post 'authenticate/refresh', to: 'authentication#refresh'
8
+
9
+ resources :students do
10
+ resources :guardians, controller: 'students/guardians'
11
+ resources :student_guardians, controller: 'students/student_guardians', only: %i(create destroy)
12
+ resources :courses, controller: 'students/courses'
13
+ resources :class_groups, controller: 'students/class_groups'
14
+ resources :extracurricular_activities, controller: 'students/extracurricular_activities'
15
+ resources :exam_sessions, controller: 'students/exam_sessions'
16
+ end
17
+
18
+ resources :courses do
19
+ resources :students, controller: 'courses/students'
20
+ resources :enrollments, controller: 'courses/enrollments'
21
+ end
22
+
23
+ resources :class_groups do
24
+ resources :students, controller: 'class_groups/students'
25
+ resources :enrollments, controller: 'class_groups/enrollments'
26
+ end
27
+
28
+ resources :syllabuses
29
+ end
30
+ end
31
+ end
data/gaku_api.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ require_relative '../common_gaku_gemspec_mixin'
2
+
3
+ Gem::Specification.new do |s|
4
+ set_gaku_gemspec_shared s
5
+
6
+ s.name = 'gaku_api'
7
+ s.summary = 'GAKU Engine API module'
8
+ s.description = 'API functionality for GAKU Engine. See https://github.com/GAKUEngine/gaku'
9
+
10
+ s.files = Dir.glob("{app,config,db,lib}/**/*") +
11
+ [
12
+ 'Rakefile',
13
+ 'gaku_api.gemspec'
14
+ ]
15
+
16
+ s.add_dependency 'simple_command'
17
+ s.add_dependency 'jwt'
18
+ s.add_dependency 'active_model_serializers'
19
+ s.add_dependency 'msgpack_rails'
20
+ s.add_dependency 'kaminari'
21
+
22
+ s.add_dependency 'gaku_core', s.version
23
+
24
+ s.add_development_dependency 'gaku_testing', s.version
25
+ end
@@ -0,0 +1,28 @@
1
+ module Gaku
2
+ module Api
3
+ class Engine < ::Rails::Engine
4
+ engine_name 'gaku_api'
5
+ config.generators.api_only = true
6
+
7
+ initializer 'actionpack-msgpack_parser.configure' do
8
+ ActionDispatch::Request.parameter_parsers[:msgpack] = -> (raw_post) do
9
+ ActiveSupport::MessagePack.decode(raw_post) || {}
10
+ end
11
+ end
12
+
13
+ config.after_initialize do
14
+ ActionController.add_renderer :msgpack do |resource, options|
15
+ MessagePack::DefaultFactory.register_type(0x00, Symbol)
16
+ resource_serializer = ActiveModelSerializers::SerializableResource.new(resource, options)
17
+ self.content_type = Mime[:msgpack]
18
+ if resource_serializer.serializer?
19
+ self.response_body = resource_serializer.serializable_hash.msgpack_to_msgpack
20
+ else
21
+ self.response_body = resource.msgpack_to_msgpack
22
+ end
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ module Gaku
2
+ module Api
3
+ VERSION = '0.2.4'
4
+ end
5
+ end
data/lib/gaku/api.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'gaku_core'
2
+ require 'simple_command'
3
+ require 'jwt'
4
+ require 'active_model_serializers'
5
+ require 'msgpack_rails'
6
+ require 'kaminari'
7
+
8
+ module Gaku
9
+
10
+ module Api
11
+ end
12
+
13
+ end
14
+
15
+ require 'gaku/api/engine'
data/lib/gaku_api.rb ADDED
@@ -0,0 +1 @@
1
+ require 'gaku/api'
@@ -0,0 +1,24 @@
1
+ class JsonWebToken
2
+ class << self
3
+ def encode(payload, exp:, jti:, secret:)
4
+ payload[:exp] = exp.to_i
5
+ payload[:jti] = jti
6
+ JWT.encode(payload, secret)
7
+ end
8
+
9
+ def decode(token, refresh: false)
10
+ secret = extract_secret(token, refresh: refresh)
11
+ body = JWT.decode(token, secret)[0]
12
+ HashWithIndifferentAccess.new body
13
+ rescue
14
+ nil
15
+ end
16
+
17
+ def extract_secret(token, refresh: false)
18
+ key = JSON.parse(Base64.decode64 token.split('.')[1])['jti']
19
+ $redis.get("token:#{key}").tap do
20
+ $redis.del("token:#{key}") if refresh
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :api do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,184 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gaku_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0.pre.4
5
+ platform: ruby
6
+ authors:
7
+ - Rei Kagetsuki
8
+ - Georgi Tapalilov
9
+ - Nakaya Yukiharu
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2018-02-26 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: simple_command
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: jwt
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: active_model_serializers
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: msgpack_rails
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: kaminari
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ - !ruby/object:Gem::Dependency
86
+ name: gaku_core
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - '='
90
+ - !ruby/object:Gem::Version
91
+ version: 0.3.0.pre.4
92
+ type: :runtime
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '='
97
+ - !ruby/object:Gem::Version
98
+ version: 0.3.0.pre.4
99
+ - !ruby/object:Gem::Dependency
100
+ name: gaku_testing
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '='
104
+ - !ruby/object:Gem::Version
105
+ version: 0.3.0.pre.4
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - '='
111
+ - !ruby/object:Gem::Version
112
+ version: 0.3.0.pre.4
113
+ description: API functionality for GAKU Engine. See https://github.com/GAKUEngine/gaku
114
+ email: info@gakuengine.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - Rakefile
120
+ - app/controllers/gaku/api/application_controller.rb
121
+ - app/controllers/gaku/api/v1/authentication_controller.rb
122
+ - app/controllers/gaku/api/v1/base_controller.rb
123
+ - app/controllers/gaku/api/v1/class_groups/enrollments_controller.rb
124
+ - app/controllers/gaku/api/v1/class_groups/students_controller.rb
125
+ - app/controllers/gaku/api/v1/courses/enrollments_controller.rb
126
+ - app/controllers/gaku/api/v1/courses/students_controller.rb
127
+ - app/controllers/gaku/api/v1/courses_controller.rb
128
+ - app/controllers/gaku/api/v1/enrollments_controller.rb
129
+ - app/controllers/gaku/api/v1/statuses_controller.rb
130
+ - app/controllers/gaku/api/v1/students/class_groups_controller.rb
131
+ - app/controllers/gaku/api/v1/students/courses_controller.rb
132
+ - app/controllers/gaku/api/v1/students/exam_sessions_controller.rb
133
+ - app/controllers/gaku/api/v1/students/extracurricular_activities_controller.rb
134
+ - app/controllers/gaku/api/v1/students/guardians_controller.rb
135
+ - app/controllers/gaku/api/v1/students/student_guardians_controller.rb
136
+ - app/controllers/gaku/api/v1/students_controller.rb
137
+ - app/controllers/gaku/api/v1/syllabuses_controller.rb
138
+ - app/serializers/gaku/class_group_serializer.rb
139
+ - app/serializers/gaku/course_serializer.rb
140
+ - app/serializers/gaku/enrollment_serializer.rb
141
+ - app/serializers/gaku/exam_session_serializer.rb
142
+ - app/serializers/gaku/extracurricular_activity_serializer.rb
143
+ - app/serializers/gaku/guardian_serializer.rb
144
+ - app/serializers/gaku/student_serializer.rb
145
+ - app/serializers/gaku/syllabus_serializer.rb
146
+ - app/services/gaku/api/authenticate_user.rb
147
+ - app/services/gaku/api/authorize_api_request.rb
148
+ - app/services/gaku/api/refresh_authenticate_user.rb
149
+ - config/initializers/kaminari.rb
150
+ - config/initializers/mime_types.rb
151
+ - config/routes.rb
152
+ - gaku_api.gemspec
153
+ - lib/gaku/api.rb
154
+ - lib/gaku/api/engine.rb
155
+ - lib/gaku/api/version.rb
156
+ - lib/gaku_api.rb
157
+ - lib/json_web_token.rb
158
+ - lib/tasks/api_tasks.rake
159
+ homepage: http://www.gakuengine.com
160
+ licenses:
161
+ - GPL-3.0
162
+ - AGPL-3.0
163
+ metadata: {}
164
+ post_install_message:
165
+ rdoc_options: []
166
+ require_paths:
167
+ - lib
168
+ required_ruby_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ required_rubygems_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">"
176
+ - !ruby/object:Gem::Version
177
+ version: 1.3.1
178
+ requirements: []
179
+ rubyforge_project:
180
+ rubygems_version: 2.7.3
181
+ signing_key:
182
+ specification_version: 4
183
+ summary: GAKU Engine API module
184
+ test_files: []