ps_utilities 0.3.2 → 1.0.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 +4 -4
- data/lib/ps_utilities/connection.rb +57 -50
- data/lib/ps_utilities/pre_built_get.rb +27 -23
- data/lib/ps_utilities/pre_built_post.rb +20 -16
- data/lib/ps_utilities/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30083f1244c4e8ea1a53ab81b8f88ac3c10d177fb651218cfcbe9dca8bdc219f
|
4
|
+
data.tar.gz: c7f3991b61f85c224c48ca2bb3c172b8a2b24b50d28a8e14e1b80e9e5f831c4d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 :
|
28
|
-
attr_reader :
|
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['
|
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(
|
39
|
-
@version
|
40
|
-
@
|
41
|
-
@
|
42
|
-
@
|
43
|
-
@
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
raise ArgumentError, "missing
|
50
|
-
|
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
|
61
|
-
|
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)
|
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
|
-
"#{
|
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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
125
|
-
return false if
|
126
|
-
return false if
|
127
|
-
return false if
|
128
|
-
return false if
|
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(
|
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 ' +
|
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
|
140
|
-
ps_auth_text = [
|
141
|
-
|
142
|
-
|
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
|
147
|
-
{
|
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] -
|
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
|
-
|
10
|
-
|
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
|
-
|
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
|
93
|
-
students = params[:students] if params[:students]
|
94
|
-
|
95
|
-
return {"errorMessage"=>{"message"=>"
|
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
|
|
data/lib/ps_utilities/version.rb
CHANGED
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.
|
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-
|
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:
|