school21_api_sdk 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e94bb6054dc91242b21c99220f3c30931f0b6b24418ef8f0ba140052f2220fb3
4
- data.tar.gz: f4fc58b78bfe7a25291cab9f34de83f5361ecd8afc929a500b7bb4958bbb54a2
3
+ metadata.gz: 8c369d9fa76c0c36cb3142d0807256d9be76d9b706aedc32207899f355721d0b
4
+ data.tar.gz: 3b314e7f8a1235eadfe9ce7cd5147bc0889c4926169a3a29740613f72f2aeb94
5
5
  SHA512:
6
- metadata.gz: 379828e0c0f1bc42e3d66751ee73db4163fbaf35232d7200aa7fe552f36c08bf2f31dc702aeeac37cd72d0ea02dff01c0c89937f55284115ca10264d88ce9611
7
- data.tar.gz: 707c86e1a6fb0bc5b4c92e3f71ed720b3219ddf5c569a5fc4614f7f2c78a96a45356900f6f6df942dad41f89c06ed31cac2e28a6e4caf77f7715bf8ca02657c8
6
+ metadata.gz: ddaecba86f46485941eb2a9d508ff6a2961db296657bbc28cf3b31dee962af1986648d1226cf1e46425475ef48935b2716810d4d74b34a627eba5e637d00cb4c
7
+ data.tar.gz: cf9e4fc8918d2b64e4361cf45a19bc8a6fc6f6e2c520f476e71b3e8c5025222bea1074a303971ff3f24ddd00e9c57b2e5d04f032bfd650c269b358e137fc9ebc
data/README.md CHANGED
@@ -4,6 +4,16 @@
4
4
  [![codecov](https://codecov.io/github/ikael21/school21_api_sdk/branch/main/graph/badge.svg?token=O7I31Q7N96)](https://codecov.io/github/ikael21/school21_api_sdk)
5
5
  ![rubocop](https://github.com/ikael21/school21_api_sdk/actions/workflows/rubocop.yml/badge.svg)
6
6
 
7
+ > [!NOTE]
8
+ > Development is still in progress, implementation may change in future versions.
9
+
10
+ > [!WARNING]
11
+ > **Do not store your login and password as raw string values, use environment variables instead!**
12
+ > Currently platform provides only one way to authorize API calls with `login:password` pair
13
+ > you're using for the school everyday tasks.
14
+ > As soon as other types of authorization will be added (such as using API token), they'll be implemented for this gem as well.
15
+
16
+
7
17
  ## Installation
8
18
 
9
19
  Install the gem and add to the application's Gemfile by executing:
@@ -30,17 +40,19 @@ require 'school21'
30
40
  login = 'your_login_here'
31
41
  password = 'your_password_here'
32
42
 
33
- client = School21::Client.configure do |config|
43
+ School21.configure do |config|
34
44
  config.credentials = { login: login, password: password }
35
- config.enable_logging = true # false by default
45
+
46
+ # If you want to log client's requests and responses (turned off by default)
47
+ config.enable_logging = true
36
48
  end
37
49
  ```
38
50
 
39
51
  - Select the domain specific API that you want to use. This API has all endpoints related to that domain. Here's an example of `Participant API` and a call to `/participants/:login`
40
52
 
41
53
  ```ruby
42
- participants_api = client.participants_api
43
- response = participants_api.participants('ikael@student.21-school.ru')
54
+ participants_api = School21.participants_api
55
+ response = participants_api.participant('peer_nickname')
44
56
 
45
57
  if response.success?
46
58
  puts response.data
@@ -57,4 +69,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
57
69
 
58
70
  ## Contributing
59
71
 
60
- Bug reports and pull requests are welcome on GitHub at https://github.com/ikael21/school21_api_sdk.
72
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/ikael21/school21_api_sdk>.
@@ -4,7 +4,7 @@ module School21
4
4
  class BaseApi
5
5
  include CoreLibrary
6
6
 
7
- SINGLE_AUTH_PARTICIPANT = :global
7
+ PLATFORM_AUTH_PARTICIPANT = :platform
8
8
 
9
9
  def self.base_uri(server)
10
10
  case server
@@ -16,6 +16,8 @@ module School21
16
16
  end
17
17
 
18
18
  def self.response_convertor(api_response)
19
+ return api_response unless api_response.data.present? && api_response.data.is_a?(Hash)
20
+
19
21
  api_response.data.deep_transform_keys! do |key|
20
22
  key.underscore.to_sym
21
23
  end
@@ -23,12 +25,13 @@ module School21
23
25
  api_response
24
26
  end
25
27
 
26
- def initialize(global_configuration)
27
- @global_configuration = global_configuration
28
- @api_call = ApiCall.new(@global_configuration)
28
+ def initialize
29
+ @api_call = ApiCall.new(School21.config)
29
30
  end
30
31
 
31
- def new_api_call_builder = @api_call.new_builder
32
+ def new_api_call_builder
33
+ @api_call.new_builder
34
+ end
32
35
 
33
36
  def new_response_handler
34
37
  ResponseHandler.new
@@ -49,5 +52,20 @@ module School21
49
52
  .key(key)
50
53
  .value(value)
51
54
  end
55
+
56
+ def request_with_auth_participant(http_method, path, server)
57
+ auth_participant = CoreLibrary::Single.new(PLATFORM_AUTH_PARTICIPANT)
58
+
59
+ new_request_builder(http_method, path, server).auth(auth_participant)
60
+ end
61
+
62
+ def execute_request(new_request)
63
+ Authenticator.call
64
+
65
+ new_api_call_builder
66
+ .request(new_request)
67
+ .response(new_response_handler)
68
+ .execute
69
+ end
52
70
  end
53
71
  end
@@ -3,65 +3,43 @@
3
3
  module School21
4
4
  class CampusesApi < BaseApi
5
5
  def campuses
6
- path = '/campuses'
7
- new_request = new_request_builder(HttpMethod::GET, path, :api_v1)
8
- .auth(CoreLibrary::Single.new(SINGLE_AUTH_PARTICIPANT))
9
-
10
- new_api_call_builder
11
- .request(new_request)
12
- .response(new_response_handler)
13
- .execute
6
+ execute_request(request_with_auth_participant(HttpMethod::GET, '/campuses', :api_v1))
14
7
  end
15
8
 
16
- def campuses_participants(campus_id, options: {})
17
- path = "/campuses/#{campus_id}/participants"
9
+ def campus_participants(campus_id, options: {})
10
+ path = ['/campuses/', campus_id, '/participants'].join
18
11
  default_options = { limit: 50, offset: 0 }.merge(options)
19
-
20
- new_request = new_request_builder(HttpMethod::GET, path, :api_v1)
21
- .auth(CoreLibrary::Single.new(SINGLE_AUTH_PARTICIPANT))
12
+ new_request = request_with_auth_participant(HttpMethod::GET, path, :api_v1)
22
13
 
23
14
  default_options.each do |key, value|
24
15
  new_request.query_param(new_parameter(value, key:))
25
16
  end
26
17
 
27
- new_api_call_builder
28
- .request(new_request)
29
- .response(new_response_handler)
30
- .execute
18
+ execute_request(new_request)
31
19
  end
32
20
 
33
- def campuses_coalitions(campus_id, options: {})
34
- path = "/campuses/#{campus_id}/coalitions"
21
+ def campus_coalitions(campus_id, options: {})
22
+ path = ['/campuses/', campus_id, '/coalitions'].join
35
23
  default_options = { limit: 50, offset: 0 }.merge(options)
36
-
37
- new_request = new_request_builder(HttpMethod::GET, path, :api_v1)
38
- .auth(CoreLibrary::Single.new(SINGLE_AUTH_PARTICIPANT))
24
+ new_request = request_with_auth_participant(HttpMethod::GET, path, :api_v1)
39
25
 
40
26
  default_options.each do |key, value|
41
27
  new_request.query_param(new_parameter(value, key:))
42
28
  end
43
29
 
44
- new_api_call_builder
45
- .request(new_request)
46
- .response(new_response_handler)
47
- .execute
30
+ execute_request(new_request)
48
31
  end
49
32
 
50
- def campuses_clusters(campus_id, options: {})
51
- path = "/campuses/#{campus_id}/clusters"
33
+ def campus_clusters(campus_id, options: {})
34
+ path = ['/campuses/', campus_id, '/clusters'].join
52
35
  default_options = { limit: 50, offset: 0 }.merge(options)
53
-
54
- new_request = new_request_builder(HttpMethod::GET, path, :api_v1)
55
- .auth(CoreLibrary::Single.new(SINGLE_AUTH_PARTICIPANT))
36
+ new_request = request_with_auth_participant(HttpMethod::GET, path, :api_v1)
56
37
 
57
38
  default_options.each do |key, value|
58
39
  new_request.query_param(new_parameter(value, key:))
59
40
  end
60
41
 
61
- new_api_call_builder
62
- .request(new_request)
63
- .response(new_response_handler)
64
- .execute
42
+ execute_request(new_request)
65
43
  end
66
44
  end
67
45
  end
@@ -2,21 +2,16 @@
2
2
 
3
3
  module School21
4
4
  class ClustersApi < BaseApi
5
- def clusters_map(cluster_id, options: {})
6
- path = "/clusters/#{cluster_id}/map"
7
- default_options = { limit: 50, offset: 0, occupied: true }.merge(options)
8
-
9
- new_request = new_request_builder(HttpMethod::GET, path, :api_v1)
10
- .auth(CoreLibrary::Single.new(SINGLE_AUTH_PARTICIPANT))
5
+ def map(cluster_id, options: {})
6
+ path = ['/clusters/', cluster_id, '/map'].join
7
+ default_options = { limit: 50, offset: 0 }.merge(options)
8
+ new_request = request_with_auth_participant(HttpMethod::GET, path, :api_v1)
11
9
 
12
10
  default_options.each do |key, value|
13
11
  new_request.query_param(new_parameter(value, key:))
14
12
  end
15
13
 
16
- new_api_call_builder
17
- .request(new_request)
18
- .response(new_response_handler)
19
- .execute
14
+ execute_request(new_request)
20
15
  end
21
16
  end
22
17
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module School21
4
+ class GraphApi < BaseApi
5
+ def graph
6
+ new_request = request_with_auth_participant(HttpMethod::GET, '/graph', :api_v1)
7
+
8
+ execute_request(new_request)
9
+ end
10
+ end
11
+ end
@@ -2,32 +2,30 @@
2
2
 
3
3
  module School21
4
4
  class ParticipantsApi < BaseApi
5
- def participants(login)
6
- path = "/participants/#{login}"
7
- new_request = new_request_builder(HttpMethod::GET, path, :api_v1)
8
- .auth(CoreLibrary::Single.new(SINGLE_AUTH_PARTICIPANT))
5
+ def participant(login)
6
+ path = ['/participants/', login].join
7
+ new_request = request_with_auth_participant(HttpMethod::GET, path, :api_v1)
9
8
 
10
- new_api_call_builder
11
- .request(new_request)
12
- .response(new_response_handler)
13
- .execute
9
+ execute_request(new_request)
14
10
  end
15
11
 
16
- def participants_projects(login, options: {})
17
- path = "/participants/#{login}/projects"
12
+ def participant_projects(login, options: {})
13
+ path = ['/participants/', login, '/projects'].join
18
14
  default_options = { limit: 10, offset: 0 }.merge(options)
19
-
20
- new_request = new_request_builder(HttpMethod::GET, path, :api_v1)
21
- .auth(CoreLibrary::Single.new(SINGLE_AUTH_PARTICIPANT))
15
+ new_request = request_with_auth_participant(HttpMethod::GET, path, :api_v1)
22
16
 
23
17
  default_options.each do |key, value|
24
18
  new_request.query_param(new_parameter(value, key:))
25
19
  end
26
20
 
27
- new_api_call_builder
28
- .request(new_request)
29
- .response(new_response_handler)
30
- .execute
21
+ execute_request(new_request)
22
+ end
23
+
24
+ def participant_project(login, project_id)
25
+ path = ['/participants/', login, '/projects/', project_id].join
26
+ new_request = request_with_auth_participant(HttpMethod::GET, path, :api_v1)
27
+
28
+ execute_request(new_request)
31
29
  end
32
30
  end
33
31
  end
@@ -2,32 +2,24 @@
2
2
 
3
3
  module School21
4
4
  class ProjectsApi < BaseApi
5
- def projects(project_id)
6
- path = "/projects/#{project_id}"
7
- new_request = new_request_builder(HttpMethod::GET, path, :api_v1)
8
- .auth(CoreLibrary::Single.new(SINGLE_AUTH_PARTICIPANT))
5
+ def project(project_id)
6
+ path = ['/projects/', project_id].join
7
+ new_request = request_with_auth_participant(HttpMethod::GET, path, :api_v1)
9
8
 
10
- new_api_call_builder
11
- .request(new_request)
12
- .response(new_response_handler)
13
- .execute
9
+ execute_request(new_request)
14
10
  end
15
11
 
16
- def projects_participants(project_id, options: {})
17
- path = "/projects/#{project_id}/participants"
18
- default_options = { limit: 10, offset: 0 }.merge(options)
12
+ def project_participants(project_id, options: {})
13
+ path = ['/projects/', project_id, '/participants'].join
14
+ default_options = { limit: 10, offset: 0 }
15
+ new_request = request_with_auth_participant(HttpMethod::GET, path, :api_v1)
19
16
 
20
- new_request = new_request_builder(HttpMethod::GET, path, :api_v1)
21
- .auth(CoreLibrary::Single.new(SINGLE_AUTH_PARTICIPANT))
22
-
23
- default_options.each do |key, value|
17
+ options.reverse_merge!(default_options)
18
+ options.each do |key, value|
24
19
  new_request.query_param(new_parameter(value, key:))
25
20
  end
26
21
 
27
- new_api_call_builder
28
- .request(new_request)
29
- .response(new_response_handler)
30
- .execute
22
+ execute_request(new_request)
31
23
  end
32
24
  end
33
25
  end
@@ -10,7 +10,7 @@ module School21
10
10
  end
11
11
 
12
12
  def expired?
13
- Time.now >= @expires_at
13
+ access_token.nil? || Time.now >= @expires_at
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module School21
4
+ module Authenticator
5
+ def self.call
6
+ return unless School21.config.access_token.expired?
7
+
8
+ auth_api_response = School21.auth_api.token(
9
+ login: School21.config.credentials[:login],
10
+ password: School21.config.credentials[:password]
11
+ )
12
+
13
+ raise 'Access Token Error' unless auth_api_response.success?
14
+
15
+ access_token = AccessToken.new(*auth_api_response.data.values_at(:access_token, :expires_in))
16
+ bearer_auth_credentials = BearerAuthCredentials.new(access_token:)
17
+ auth_header = AuthorizationHeader.new(bearer_auth_credentials)
18
+
19
+ School21.config.access_token = access_token
20
+ School21.config.auth_managers[BaseApi::PLATFORM_AUTH_PARTICIPANT] = auth_header
21
+ end
22
+ end
23
+ end
@@ -7,12 +7,9 @@ module School21
7
7
  end
8
8
 
9
9
  def initialize(bearer_auth_credentials)
10
+ access_token = bearer_auth_credentials.access_token
10
11
  auth_params = {}
11
-
12
- @access_token = bearer_auth_credentials.access_token unless
13
- bearer_auth_credentials.nil? || bearer_auth_credentials.access_token.nil?
14
-
15
- auth_params['Authorization'] = "Bearer #{@access_token}" unless @access_token.nil?
12
+ auth_params['Authorization'] = "Bearer #{access_token}"
16
13
 
17
14
  super(auth_params)
18
15
  end
@@ -2,14 +2,38 @@
2
2
 
3
3
  module School21
4
4
  class GlobalConfig < CoreLibrary::GlobalConfiguration
5
- attr_accessor :auth_managers, :enable_logging, :credentials
6
- attr_writer :client_configuration
5
+ attr_accessor :auth_managers, :enable_logging, :credentials, :logger, :access_token
7
6
 
8
7
  def initialize(client_configuration: ClientConfig.new)
9
8
  super
10
9
 
11
10
  @enable_logging = false
12
11
  @credentials = {}
12
+ @access_token = AccessToken.new(nil, 0)
13
+
14
+ http_client = CoreLibrary::FaradayClient.new(client_configuration)
15
+ client_configuration.set_http_client(http_client)
16
+ end
17
+
18
+ def initialize_logger
19
+ logger ||= semantic_logger
20
+
21
+ excluded_headers = ['Authorization']
22
+ api_logging_config = School21::ApiLoggingConfig.new(logger, nil, nil, nil, false)
23
+ api_logging_config.request_logging_config =
24
+ CoreLibrary::ApiRequestLoggingConfiguration.new(true, true, excluded_headers, nil, nil, true)
25
+ api_logging_config.response_logging_config =
26
+ CoreLibrary::ApiResponseLoggingConfiguration.new(true, true, nil, nil, nil)
27
+ client_configuration.logging_configuration = api_logging_config
28
+ end
29
+
30
+ private
31
+
32
+ def semantic_logger
33
+ SemanticLogger.default_level = :trace
34
+ SemanticLogger.add_appender(io: $stdout, formatter: :color)
35
+
36
+ SemanticLogger[School21.name]
13
37
  end
14
38
  end
15
39
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module School21
4
- VERSION = '0.3.0'
4
+ VERSION = '0.5.0'
5
5
  end
data/lib/school21.rb CHANGED
@@ -2,12 +2,14 @@
2
2
 
3
3
  require 'apimatic_core'
4
4
  require 'apimatic_faraday_client_adapter'
5
+ require 'semantic_logger'
5
6
 
6
7
  require 'active_support/all'
7
8
 
8
9
  require_relative 'school21/auth/access_token'
9
10
  require_relative 'school21/auth/authorization_header'
10
11
  require_relative 'school21/auth/bearer_auth_credentials'
12
+ require_relative 'school21/auth/authenticator'
11
13
 
12
14
  require_relative 'school21/api/base_api'
13
15
  require_relative 'school21/api/participants_api'
@@ -15,9 +17,41 @@ require_relative 'school21/api/auth_api'
15
17
  require_relative 'school21/api/projects_api'
16
18
  require_relative 'school21/api/campuses_api'
17
19
  require_relative 'school21/api/clusters_api'
20
+ require_relative 'school21/api/graph_api'
18
21
 
19
22
  require_relative 'school21/config/api_logging_config'
20
23
  require_relative 'school21/config/client_config'
21
24
  require_relative 'school21/config/global_config'
22
25
 
23
- require_relative 'school21/client'
26
+ module School21
27
+ class << self
28
+ def configure
29
+ yield config
30
+ config.initialize_logger if config.enable_logging
31
+ config
32
+ end
33
+
34
+ def config
35
+ @config ||= GlobalConfig.new.base_uri_executor(BaseApi.method(:base_uri))
36
+ end
37
+
38
+ def reset!
39
+ @config = nil
40
+ end
41
+
42
+ API_CLASSES = {
43
+ auth: AuthApi,
44
+ participants: ParticipantsApi,
45
+ projects: ProjectsApi,
46
+ campuses: CampusesApi,
47
+ clusters: ClustersApi,
48
+ graph: GraphApi
49
+ }.freeze
50
+
51
+ API_CLASSES.each do |name, klass|
52
+ define_method("#{name}_api") do
53
+ klass.new
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ describe 'Auth API Test' do
6
+ include AuthStub
7
+
8
+ with_configured_client do
9
+ let(:auth_api_call) do
10
+ School21.auth_api.token(
11
+ login: School21.config.credentials[:login],
12
+ password: School21.config.credentials[:password]
13
+ )
14
+ end
15
+
16
+ it 'calls stub token success' do
17
+ stub = stub_token_success
18
+
19
+ auth_api_call
20
+
21
+ assert_requested(stub)
22
+ end
23
+
24
+ it 'calls stub token fail' do
25
+ stub = stub_token_fail
26
+
27
+ response = auth_api_call
28
+
29
+ assert_requested(stub)
30
+ assert_equal(500, response.status_code)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ describe 'Configuration test' do
6
+ include SharedData
7
+
8
+ it 'successfully configures client' do
9
+ School21.configure do |config|
10
+ config.credentials = credentials_fixtures
11
+ config.enable_logging = false
12
+ end
13
+
14
+ assert_equal credentials_fixtures, School21.config.credentials
15
+ refute School21.config.enable_logging
16
+ end
17
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ describe 'Participants API Test' do
6
+ include AuthStub
7
+ include ParticipantsStub
8
+
9
+ with_configured_client do
10
+ let(:login) { 'test_login' }
11
+ let(:participants_api_call) do
12
+ School21.participants_api.participant(login)
13
+ end
14
+
15
+ it 'calls stub participant success' do
16
+ stubs = [
17
+ stub_token_success,
18
+ stub_participant_success
19
+ ]
20
+
21
+ participants_api_call
22
+
23
+ stubs.each { |stub| assert_requested(stub) }
24
+ end
25
+
26
+ it 'calls stub participant fail' do
27
+ stubs = [
28
+ stub_token_success,
29
+ stub_participant_fail
30
+ ]
31
+
32
+ participants_api_call
33
+
34
+ stubs.each { |stub| assert_requested(stub) }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SharedData
4
+ def credentials_fixtures
5
+ { login: 'login', password: 'password' }
6
+ end
7
+ end
@@ -3,18 +3,17 @@
3
3
  module AuthStub
4
4
  include BaseStub
5
5
 
6
- def stub_token
7
- body = {
8
- access_token: 'access_token',
9
- expires_in: 36_000
10
- }
6
+ STUBBED_AUTH_URL = [BASE_AUTH_URL, '/auth/realms/EduPowerKeycloak/protocol/openid-connect/token'].join
11
7
 
12
- stub_request(:post, stubbed_auth_url).to_return_json(body:)
8
+ def stub_token_success
9
+ stub_request(:post, STUBBED_AUTH_URL)
10
+ .to_return_json(body: {
11
+ access_token: 'access_token',
12
+ expires_in: 36_000
13
+ })
13
14
  end
14
15
 
15
- private
16
-
17
- def stubbed_auth_url
18
- [BASE_AUTH_URL, '/auth/realms/EduPowerKeycloak/protocol/openid-connect/token'].join
16
+ def stub_token_fail
17
+ base_stub_fail(:post, STUBBED_AUTH_URL)
19
18
  end
20
19
  end
@@ -2,5 +2,10 @@
2
2
 
3
3
  module BaseStub
4
4
  BASE_AUTH_URL = 'https://auth.sberclass.ru'
5
- API_V1_URL = 'https://edu-api.21-school.ru/services/21-school/api/v1'
5
+ BASE_API_V1_URL = 'https://edu-api.21-school.ru/services/21-school/api/v1'
6
+
7
+ def base_stub_fail(http_method, url)
8
+ stub_request(http_method, url)
9
+ .to_return(status: [500, 'Internal Server Error'])
10
+ end
6
11
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ParticipantsStub
4
+ include BaseStub
5
+
6
+ TEST_LOGIN = 'test_login'
7
+ STUBBED_PARTICIPANT_URL = [BASE_API_V1_URL, '/participants/', TEST_LOGIN].join
8
+
9
+ def stub_participant_success
10
+ stub_request(:get, STUBBED_PARTICIPANT_URL)
11
+ .to_return_json(body: { login: TEST_LOGIN })
12
+ end
13
+
14
+ def stub_participant_fail
15
+ stub_request(:get, STUBBED_PARTICIPANT_URL)
16
+ .to_return(status: [500, 'Internal Server Error'])
17
+ end
18
+ end
data/test/test_helper.rb CHANGED
@@ -3,9 +3,13 @@
3
3
  require 'simplecov'
4
4
  require 'simplecov-cobertura'
5
5
  require 'webmock/minitest'
6
+ require 'debug'
7
+
8
+ require_relative 'support/shared_data'
6
9
 
7
10
  require_relative 'support/stubs/base_stub'
8
11
  require_relative 'support/stubs/auth_stub'
12
+ require_relative 'support/stubs/participants_stub'
9
13
 
10
14
  SimpleCov.start
11
15
  SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter
@@ -17,3 +21,20 @@ require 'minitest/autorun'
17
21
  require 'minitest/reporters'
18
22
 
19
23
  Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
24
+
25
+ module Minitest
26
+ class Spec
27
+ include SharedData
28
+
29
+ def self.with_configured_client(&block)
30
+ describe 'with configured client' do
31
+ before do
32
+ School21.reset!
33
+ School21.configure { |c| c.credentials = credentials_fixtures }
34
+ end
35
+
36
+ class_eval(&block)
37
+ end
38
+ end
39
+ end
40
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: school21_api_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Yudin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-15 00:00:00.000000000 Z
11
+ date: 2025-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: semantic_logger
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  description: ''
84
98
  email:
85
99
  - ikael.fess@gmail.com
@@ -96,19 +110,24 @@ files:
96
110
  - lib/school21/api/base_api.rb
97
111
  - lib/school21/api/campuses_api.rb
98
112
  - lib/school21/api/clusters_api.rb
113
+ - lib/school21/api/graph_api.rb
99
114
  - lib/school21/api/participants_api.rb
100
115
  - lib/school21/api/projects_api.rb
101
116
  - lib/school21/auth/access_token.rb
117
+ - lib/school21/auth/authenticator.rb
102
118
  - lib/school21/auth/authorization_header.rb
103
119
  - lib/school21/auth/bearer_auth_credentials.rb
104
- - lib/school21/client.rb
105
120
  - lib/school21/config/api_logging_config.rb
106
121
  - lib/school21/config/client_config.rb
107
122
  - lib/school21/config/global_config.rb
108
123
  - lib/school21/version.rb
109
- - test/client_test.rb
124
+ - test/auth_api_test.rb
125
+ - test/configuration_test.rb
126
+ - test/participants_api_test.rb
127
+ - test/support/shared_data.rb
110
128
  - test/support/stubs/auth_stub.rb
111
129
  - test/support/stubs/base_stub.rb
130
+ - test/support/stubs/participants_stub.rb
112
131
  - test/test_helper.rb
113
132
  homepage: https://github.com/ikael21/school21_api_sdk
114
133
  licenses:
@@ -1,99 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module School21
4
- class Client
5
- include CoreLibrary
6
-
7
- def self.configure(&)
8
- new(&)
9
- end
10
-
11
- def initialize(&block)
12
- raise 'A block must be given' unless block_given?
13
- raise 'You must provide block argument that represents config instance' if block.arity != 1
14
-
15
- @config = GlobalConfig
16
- .new
17
- .base_uri_executor(BaseApi.method(:base_uri))
18
-
19
- yield(@config)
20
-
21
- validate_credentials!
22
-
23
- initialize_logging
24
-
25
- http_client = FaradayClient.new(@config.client_configuration)
26
- @config.client_configuration.set_http_client(http_client)
27
-
28
- initialize_auth!
29
- end
30
-
31
- def auth_api
32
- @auth_api ||= AuthApi.new(@config)
33
- end
34
-
35
- def participants_api
36
- initialize_auth! if @access_token.expired?
37
- ParticipantsApi.new(@config)
38
- end
39
-
40
- def projects_api
41
- initialize_auth! if @access_token.expired?
42
- ProjectsApi.new(@config)
43
- end
44
-
45
- def campuses_api
46
- initialize_auth! if @access_token.expired?
47
- CampusesApi.new(@config)
48
- end
49
-
50
- def clusters_api
51
- initialize_auth! if @access_token.expired?
52
- ClustersApi.new(@config)
53
- end
54
-
55
- private
56
-
57
- def validate_credentials!
58
- login = @config.credentials[:login]
59
- password = @config.credentials[:password]
60
-
61
- raise "Login can't be nil or empty" if login.nil? || login.empty?
62
- raise "Password can't be nil or empty" if password.nil? || password.empty?
63
- end
64
-
65
- def initialize_auth!
66
- @access_token = request_access_token!
67
-
68
- initialize_auth_managers
69
- end
70
-
71
- def initialize_auth_managers
72
- auth_managers = {}
73
-
74
- creds = BearerAuthCredentials.new(access_token: @access_token)
75
- auth_managers[BaseApi::SINGLE_AUTH_PARTICIPANT] = AuthorizationHeader.new(creds)
76
-
77
- @config.auth_managers = auth_managers
78
- end
79
-
80
- def request_access_token!
81
- auth_api_response = auth_api.token(login: @config.credentials[:login], password: @config.credentials[:password])
82
- raise 'Access Token Error' unless auth_api_response.success?
83
-
84
- AccessToken.new(*auth_api_response.data.values_at(:access_token, :expires_in))
85
- end
86
-
87
- def initialize_logging
88
- return unless @config.enable_logging
89
-
90
- headers_to_exclude = ['Authorization']
91
- api_logging_config = ApiLoggingConfig.new(nil, nil, nil, nil, false)
92
- api_logging_config.request_logging_config = ApiRequestLoggingConfiguration.new(true, true, headers_to_exclude,
93
- nil, nil, true)
94
- api_logging_config.response_logging_config = ApiResponseLoggingConfiguration.new(true, true, nil, nil, nil)
95
-
96
- @config.client_configuration.logging_configuration = api_logging_config
97
- end
98
- end
99
- end
data/test/client_test.rb DELETED
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- describe 'Client Test' do
6
- include AuthStub
7
-
8
- before do
9
- @login = 'login'
10
- @password = 'password'
11
- end
12
-
13
- let(:valid_init_client) do
14
- School21::Client.configure do |config|
15
- config.credentials = { login: @login, password: @password }
16
- config.enable_logging = false
17
- end
18
- end
19
-
20
- let(:init_client_without_login) do
21
- School21::Client.configure do |config|
22
- config.credentials = { login: nil, password: @password }
23
- config.enable_logging = false
24
- end
25
- end
26
-
27
- let(:init_client_without_password) do
28
- School21::Client.configure do |config|
29
- config.credentials = { login: @login, password: nil }
30
- config.enable_logging = false
31
- end
32
- end
33
-
34
- it 'successfully stubs client auth' do
35
- stub = stub_token
36
-
37
- valid_init_client
38
-
39
- assert_requested(stub)
40
- end
41
-
42
- it 'raises an exception without login' do
43
- stub = stub_token
44
-
45
- assert_raises(RuntimeError, "Login can't be nil or empty") do
46
- init_client_without_login
47
- end
48
-
49
- refute_requested(stub)
50
- end
51
-
52
- it 'raises an exception without password' do
53
- stub = stub_token
54
-
55
- assert_raises(RuntimeError, "Password can't be nil or empty") do
56
- init_client_without_password
57
- end
58
-
59
- refute_requested(stub)
60
- end
61
- end