ps_utilities 0.3.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4abd4b17bf893e811f7e920887cc881baad21ffc7b43e112de022ac5240269e2
4
- data.tar.gz: da43f677c4b59e491e352894061a96290406dea47ad6c6bb0f395715ebead669
3
+ metadata.gz: 30083f1244c4e8ea1a53ab81b8f88ac3c10d177fb651218cfcbe9dca8bdc219f
4
+ data.tar.gz: c7f3991b61f85c224c48ca2bb3c172b8a2b24b50d28a8e14e1b80e9e5f831c4d
5
5
  SHA512:
6
- metadata.gz: 299bbefec900eef22d92c605838e90358f6cae6a561828ba2229d813e0e5fcdccc0df95d145c13e2e40d6b7b0b03d79e54f9c03e531ddfb90c5911a6622f1382
7
- data.tar.gz: edb76484fc23806fd56b1dd67573a8e2466513023340535f79437751c808423bb0a30025a182ba4141221eb8483efc0b2f5a22d80f1d08fed2faec0f0c4ae238
6
+ metadata.gz: 848cfcbb6cf7b71d1e205ac04c21ac14a7b75be92b47db43effa58d6d455ac5b20a90e0d022070e0f9c0e4e1925599fca75493ec5a8f30047205b13a758fbae3
7
+ data.tar.gz: f72b28802331533bb855536a2dcbf1ece95bab2fc3a4d09b7aeca883b4a1003c9d3ef1d2d403dd86d55b64ff5e641467f84125b86766370f8e10de41cdda0f66
@@ -24,30 +24,32 @@ module PsUtilities
24
24
 
25
25
  class Connection
26
26
 
27
- attr_reader :credentials, :headers, :base_uri
28
- attr_reader :auth_path, :auth_token
27
+ attr_reader :auth_path, :auth_token, :auth_info, :headers
28
+ attr_reader :api_data, :base_uri, :auth_path
29
+ attr_reader :client, :client_id, :client_secret
29
30
  attr_reader :version
30
31
 
31
32
  include PsUtilities::PreBuiltGet
32
33
  include PsUtilities::PreBuiltPut
33
34
  include PsUtilities::PreBuiltPost
34
35
 
35
- # @param attributes: [Hash] - options include: { base_uri: ENV['PS_URL'], auth_endpoint: (ENV['PS_AUTH_ENDPOINT'] || '/oauth/access_token'), client_id: ENV['PS_CLIENT_ID'], client_secret: ENV['PS_CLIENT_SECRET'] }
36
+ # @param attributes: [Hash] - options include: { base_uri: ENV['PS_BASE_URL'], auth_endpoint: (ENV['PS_AUTH_ENDPOINT'] || '/oauth/access_token'), client_id: ENV['PS_CLIENT_ID'], client_secret: ENV['PS_CLIENT_SECRET'] }
36
37
  # @param headers: [Hash] - allows to change from json to xml (only do this if you are doing direct api calls and not using pre-built calls) returns and use a different useragent: { 'User-Agent' => "PsUtilities - #{version}", 'Accept' => 'application/json', 'Content-Type' => 'application/json'}
37
38
  # @note preference is to use environment variables to initialize your server.
38
- def initialize(attributes: {}, headers: {})
39
- @version = "v#{PsUtilities::Version::VERSION}"
40
- @credentials = attr_defaults.merge(attributes)
41
- @base_uri = credentials[:base_uri]
42
- @auth_path = credentials[:auth_endpoint]
43
- @headers = header_defaults.merge(headers)
44
-
45
- raise ArgumentError, "missing client_secret" if credentials[:client_secret].nil? or
46
- credentials[:client_secret].empty?
47
- raise ArgumentError, "missing client_id" if credentials[:client_id].nil? or
48
- credentials[:client_id].empty?
49
- raise ArgumentError, "missing base_uri" if credentials[:base_uri].nil? or
50
- credentials[:base_uri].empty?
39
+ def initialize( header_info: {}, api_info: {}, client_info: {})
40
+ @version = "v#{PsUtilities::Version::VERSION}"
41
+ @client = client_defaults.merge(client_info)
42
+ @client_id = client[:client_id]
43
+ @client_secret = client[:client_secret]
44
+ @api_data = api_defaults.merge(api_info)
45
+ @base_uri = api_data[:base_uri]
46
+ @auth_path = api_data[:auth_endpoint]
47
+ @headers = header_defaults.merge(header_info)
48
+
49
+ raise ArgumentError, "missing client_secret" if client_secret.nil? or client_secret.empty?
50
+ raise ArgumentError, "missing client_id" if client_id.nil? or client_id.empty?
51
+ raise ArgumentError, "missing auth endpoint" if auth_path.nil? or auth_path.empty?
52
+ raise ArgumentError, "missing base_uri" if base_uri.nil? or base_uri.empty?
51
53
  end
52
54
 
53
55
  # this runs the various options:
@@ -57,12 +59,14 @@ module PsUtilities
57
59
  # @param params: [Hash] - this is the data needed for using pre-built commands - see the individual command for details
58
60
  # @note with no command an authenticatation check is done
59
61
  def run(command: nil, api_path: "", options: {}, params: {})
60
- authenticate unless token_valid?
61
- @headers[:headers].merge!('Authorization' => 'Bearer ' + authorized_token)
62
+ authenticate unless auth_valid?
63
+
62
64
  case command
63
65
  when nil, :authenticate
66
+ authenticate
64
67
  when :delete, :get, :patch, :post, :put
65
- api(command, api_path, options) unless api_path.empty?
68
+ api(command, api_path, options) unless api_path.empty?
69
+ # TODO: panick if api_path empty
66
70
  else
67
71
  send(command, params)
68
72
  end
@@ -71,7 +75,7 @@ module PsUtilities
71
75
  private
72
76
 
73
77
  def authorized_token
74
- "#{credentials[:access_token]}"
78
+ "#{auth_info['access_token']}"
75
79
  end
76
80
 
77
81
  # verb = :delete, :get, :patch, :post, :put
@@ -81,10 +85,6 @@ module PsUtilities
81
85
  retries = 3
82
86
  ps_url = base_uri + api_path
83
87
  options = options.merge(headers)
84
- pp "api-url"
85
- pp ps_url
86
- pp "api-options"
87
- pp options
88
88
  begin
89
89
  HTTParty.send(verb, ps_url, options)
90
90
  rescue Net::ReadTimeout, Net::OpenTimeout
@@ -101,7 +101,8 @@ module PsUtilities
101
101
  { headers:
102
102
  { 'User-Agent' => "PsUtilities - #{version}",
103
103
  'Accept' => 'application/json',
104
- 'Content-Type' => 'application/json'}
104
+ 'Content-Type' => 'application/json'
105
+ }
105
106
  }
106
107
  end
107
108
 
@@ -110,46 +111,52 @@ module PsUtilities
110
111
  ps_url = base_uri + auth_path
111
112
  response = HTTParty.post(ps_url, {headers: auth_headers,
112
113
  body: 'grant_type=client_credentials'})
113
-
114
- @credentials[:token_expires] = Time.now + response.parsed_response['expires_in'].to_i
115
- @credentials[:access_token] = response.parsed_response['access_token'].to_s
116
- # @headers[:headers].merge!('Authorization' => 'Bearer ' + credentials[:access_token])
117
-
118
- # throw error if no token returned -- nothing else will work
119
- raise AuthError.new("No Auth Token Returned",
120
- ps_url, credentials
121
- ) if credentials[:access_token].empty?
114
+ if response.code.to_s.eql? "200"
115
+ @auth_info = response.parsed_response
116
+ @auth_info['token_expires'] = Time.now + response.parsed_response['expires_in'].to_i
117
+ @headers[:headers].merge!('Authorization' => 'Bearer ' + auth_info['access_token'])
118
+ return auth_info
119
+ else
120
+ # throw error if - error returned -- nothing else will work
121
+ raise AuthError.new("No Auth Token Returned", ps_url, client )
122
+ end
122
123
  end
123
124
 
124
- def token_valid?(tokens = credentials)
125
- return false if tokens[:access_token].nil?
126
- return false if tokens[:access_token].empty?
127
- return false if tokens[:token_expires].nil?
128
- return false if tokens[:token_expires] <= Time.now
125
+ def auth_valid?(auth = auth_info)
126
+ return false if auth.nil?
127
+ return false if auth.empty?
128
+ return false if auth['access_token'].nil?
129
+ return false if auth['access_token'].empty?
130
+ return false if auth['token_expires'].nil?
131
+ return false if auth['token_expires'] < Time.now
129
132
  return true
130
133
  end
131
134
 
132
- def auth_headers(creds64 = encode_credentials)
135
+ def auth_headers(credentials = client)
133
136
  { 'ContentType' => 'application/x-www-form-urlencoded;charset=UTF-8',
134
137
  'Accept' => 'application/json',
135
- 'Authorization' => 'Basic ' + creds64
138
+ 'Authorization' => 'Basic ' + encode64_client(credentials)
136
139
  }
140
+ # with(headers: {'Authorization' => "Basic #{ Base64.strict_encode64('user:pass').chomp}"})
137
141
  end
138
142
 
139
- def encode_credentials(creds = credentials)
140
- ps_auth_text = [ creds[:client_id],
141
- creds[:client_secret]
142
- ].join(':')
143
- Base64.encode64(ps_auth_text).gsub(/\n/, '')
143
+ def encode64_client(credentials = client)
144
+ ps_auth_text = [ credentials[:client_id], credentials[:client_secret] ].join(':')
145
+ Base64.encode64(ps_auth_text).chomp
146
+ # Base64.encode64(ps_auth_text).gsub(/\n/, '')
144
147
  end
145
148
 
146
- def attr_defaults
147
- { base_uri: ENV['PS_URL'],
148
- auth_endpoint: ENV['PS_AUTH_ENDPOINT'] || '/oauth/access_token',
149
- client_id: ENV['PS_CLIENT_ID'],
149
+ def client_defaults
150
+ { client_id: ENV['PS_CLIENT_ID'],
150
151
  client_secret: ENV['PS_CLIENT_SECRET'],
151
152
  }
152
153
  end
153
154
 
155
+ def api_defaults
156
+ { base_uri: ENV['PS_BASE_URL'],
157
+ auth_endpoint: ENV['PS_AUTH_ENDPOINT'] || '/oauth/access_token',
158
+ }
159
+ end
160
+
154
161
  end
155
162
  end
@@ -3,11 +3,11 @@ module PsUtilities
3
3
  module PreBuiltGet
4
4
 
5
5
  # return all active students within the district (special case of #get_all_matching_students) - a recursive search
6
- # @param params [Hash] - ignored - only included for the api standard
6
+ # @param params [Hash] - page_size: is the only parameter accepted - default is 100
7
7
  # @return - (see #get_all_matching_students)
8
8
  def get_all_active_students(params={})
9
- params = {status_code: 0}
10
- # params[:status_code] = 0
9
+ page_size = params[:page_size] || 100
10
+ params = {status_code: 0, page_size: page_size}
11
11
  get_all_matching_students(params)
12
12
  end
13
13
  alias_method :all_active_students, :get_all_active_students
@@ -117,12 +117,32 @@ module PsUtilities
117
117
  return {"errorMessage"=>{"message"=>"A valid dcid must be entered."}} if "#{ps_dcid.to_i}".eql? "0"
118
118
 
119
119
  answer = api(:get, api_path, options)
120
- { student: (answer["student"] || []) }
120
+ return { student: (answer["student"] || []) } if answer.code.to_s.eql? "200"
121
+ # return { student: (answer.parsed_response["student"] || []) } if answer.code.to_s.eql? "200"
122
+ return {"errorMessage"=>"#{answer.response}"}
121
123
  end
122
124
  alias_method :get_student, :get_one_student
123
125
 
124
126
  private
125
127
 
128
+ # build the api query - you can use splats to match any character
129
+ # @param params [Hash] - valid keys include: :status_code (or :enroll_status), :username, :last_name, :first_name, :student_id (or :local_id), :id (or :dcid)
130
+ # @return [String] - "id==345;name.last_name==BA*"
131
+ def build_query(params)
132
+ query = []
133
+ query << "school_enrollment.enroll_status_code==#{params[:status_code]}" if params.has_key?(:status_code)
134
+ query << "school_enrollment.enroll_status==#{params[:enroll_status]}" if params.has_key?(:enroll_status)
135
+ query << "student_username==#{params[:username]}" if params.has_key?(:username)
136
+ query << "name.last_name==#{params[:last_name]}" if params.has_key?(:last_name)
137
+ query << "name.first_name==#{params[:first_name]}" if params.has_key?(:first_name)
138
+ query << "local_id==#{params[:local_id]}" if params.has_key?(:local_id)
139
+ query << "local_id==#{params[:student_id]}" if params.has_key?(:student_id)
140
+ query << "id==#{params[:dcid]}" if params.has_key?(:dcid)
141
+ query << "id==#{params[:id]}" if params.has_key?(:id)
142
+ answer = query.join(";")
143
+ answer
144
+ end
145
+
126
146
  # given the number of students and page size calculate pages needed to return all students
127
147
  # @param count [Integer] - total number of students matching filter
128
148
  # @param page_size [Integer] - total number of students to be return per page
@@ -185,25 +205,9 @@ module PsUtilities
185
205
  options[:query]["q"] = query unless query.empty?
186
206
  return {"errorMessage"=>{"message"=>"A valid parameter must be entered."}} if query.empty?
187
207
  # pp options
188
- api(:get, api_path, options)
189
- end
190
-
191
- # build the api query - you can use splats to match any character
192
- # @param params [Hash] - valid keys include: :status_code (or :enroll_status), :username, :last_name, :first_name, :student_id (or :local_id), :id (or :dcid)
193
- # @return [String] - "id==345;name.last_name==BA*"
194
- def build_query(params)
195
- query = []
196
- query << "school_enrollment.enroll_status_code==#{params[:status_code]}" if params.has_key?(:status_code)
197
- query << "school_enrollment.enroll_status==#{params[:enroll_status]}" if params.has_key?(:enroll_status)
198
- query << "student_username==#{params[:username]}" if params.has_key?(:username)
199
- query << "name.last_name==#{params[:last_name]}" if params.has_key?(:last_name)
200
- query << "name.first_name==#{params[:first_name]}" if params.has_key?(:first_name)
201
- query << "local_id==#{params[:local_id]}" if params.has_key?(:local_id)
202
- query << "local_id==#{params[:student_id]}" if params.has_key?(:student_id)
203
- query << "id==#{params[:dcid]}" if params.has_key?(:dcid)
204
- query << "id==#{params[:id]}" if params.has_key?(:id)
205
- answer = query.join(";")
206
- answer
208
+ answer = api(:get, api_path, options)
209
+ return answer.parsed_response if answer.code.to_s.eql? "200"
210
+ return {"errorMessage"=>"#{answer.response}"}
207
211
  end
208
212
 
209
213
  end
@@ -30,28 +30,36 @@ module PsUtilities
30
30
  # ]
31
31
  # }
32
32
  # }
33
+ # @note create_students REQUIRED params are: :id
34
+ # @note create_students OPTIONAL params are:
35
+ # @note create_students INVALID params are:
33
36
  def create_students(params)
34
37
  action = "INSERT"
35
38
  kids_api_array = build_kids_api_array(action, params)
36
39
  options = { body: { students: { student: kids_api_array } }.to_json }
37
40
  answer = api(:post, "/ws/v1/student", options)
41
+ return answer.parsed_response if answer.code.to_s.eql? "200"
42
+ return {"errorMessage"=>"#{answer.response}"}
38
43
  end
39
44
  alias_method :create_student, :create_students
40
45
 
41
46
  # this updates and existing student record within PowerSchool
42
47
  # (see #create_students)
48
+ # @note update_students REQUIRED params are: :last_name, :first_name, :entry_date, :exit_date, :school_number, :grade_level
49
+ # @note update_students OPTIONAL params are: :
50
+ # @note update_students INVALID params are:
43
51
  def update_students(params)
44
- pp "update students"
45
- pp params
46
52
  action = "UPDATE"
47
53
  kids_api_array = build_kids_api_array(action, params)
48
- pp kids_api_array
49
54
  options = { body: { students: { student: kids_api_array } }.to_json }
50
- pp options
51
55
  answer = api(:post, "/ws/v1/student", options)
56
+ return answer.parsed_response if answer.code.to_s.eql? "200"
57
+ return {"errorMessage"=>"#{answer.response}"}
52
58
  end
53
59
  alias_method :update_student, :update_students
54
60
 
61
+ private
62
+
55
63
  # @param action [String] - either "INSERT" or "UPDATE"
56
64
  # @param params [Array of Hashes] - in this format -- students: [{kid_1_info}, {kid_2_info}]
57
65
  # @return [Array of Hashes] - with data like below:
@@ -85,14 +93,12 @@ module PsUtilities
85
93
  #]
86
94
  # @note this is then sent to the API call with a body tag
87
95
  def build_kids_api_array(action, params)
88
- pp "build_kids_api_array"
89
- pp params
90
96
  students = []
91
97
  api_array = []
92
- students << params[:student] if params[:student]
93
- students = params[:students] if params[:students]
94
- unless students.is_a? Array
95
- return {"errorMessage"=>{"message"=>"Student Data (in Hash format) must be in an Array."}}
98
+ # students = [params[:student]] if not params[:student].nil? && params[:student].is_a?(Hash)
99
+ students = params[:students] #if not params[:students].nil? && params[:students].is_a?(Array)
100
+ if students.empty?
101
+ return {"errorMessage"=>{"message"=>"Bad student data - USE: {students: [{kid_data1}, {kid_data2}]}"}}
96
102
  end
97
103
  students.each do |kid|
98
104
  # kid[:las_extensions] = true if params[:las_extensions]
@@ -174,13 +180,10 @@ module PsUtilities
174
180
  # ]
175
181
  # }
176
182
  def build_kid_attributes(action, kid)
177
- pp "build_kid_attributes"
178
- pp kid
179
183
  # ALWAYS NEEDED INFO
180
184
  attribs = {action: action}
181
- attribs[:id] = kid[:id] || kid[:dcid]
182
185
  attribs[:client_uid] = kid[:student_id].to_s
183
- attribs[:student_username] = kid[:username]
186
+ attribs[:student_username] = kid[:username] if kid[:username]
184
187
 
185
188
  # REQUIRED ON ENROLLMENT (optional later)
186
189
  attribs[:name] = {}
@@ -206,6 +209,7 @@ module PsUtilities
206
209
  attribs[:school_enrollment][:school_id] = kid[:school_id] if kid[:school_id]
207
210
  when 'UPDATE'
208
211
  # don't allow nil / blank name updates
212
+ attribs[:id] = kid[:id] || kid[:dcid]
209
213
  attribs[:name][:last_name] = kid[:last_name] if kid[:last_name]
210
214
  attribs[:name][:first_name] = kid[:first_name] if kid[:first_name]
211
215
  attribs[:name][:middle_name] = kid[:middle_name] if kid[:middle_name]
@@ -278,9 +282,9 @@ module PsUtilities
278
282
  # Update LAS Database Extensions as needed
279
283
  attribs["_extension_data"] = { "_table_extension" => [] }
280
284
  # built-in extensions by PowerSchool
281
- attribs["_extension_data"]["_table_extension"] << u_studentsuserfields(kid[:u_studentsuserfields])
285
+ attribs["_extension_data"]["_table_extension"] << u_studentsuserfields(kid[:u_studentsuserfields]) if kid[:u_studentsuserfields]
282
286
  # school defined database extensions
283
- attribs["_extension_data"]["_table_extension"] << u_students_extension(kid[:u_students_extension])
287
+ attribs["_extension_data"]["_table_extension"] << u_students_extension(kid[:u_students_extension]) if kid[:u_students_extension]
284
288
  # if no extension data present make it empty
285
289
  attribs["_extension_data"] = {} if attribs["_extension_data"]["_table_extension"].empty?
286
290
 
@@ -1,5 +1,5 @@
1
1
  module PsUtilities
2
2
  module Version
3
- VERSION = "0.3.2"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ps_utilities
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Weisbecker
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2018-06-27 00:00:00.000000000 Z
12
+ date: 2018-06-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: httparty
@@ -67,6 +67,20 @@ dependencies:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
69
  version: '3.7'
70
+ - !ruby/object:Gem::Dependency
71
+ name: webmock
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '3.4'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '3.4'
70
84
  description: 'Uses oauth2 to connection to the Powerschool API. Heavily refactored
71
85
  code (not dependent on Rails) starting with: https://github.com/TomK32/powerschool'
72
86
  email: