pyxecm 1.5__py3-none-any.whl → 2.0.0__py3-none-any.whl

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.

Potentially problematic release.


This version of pyxecm might be problematic. Click here for more details.

Files changed (56) hide show
  1. pyxecm/__init__.py +6 -2
  2. pyxecm/avts.py +1492 -0
  3. pyxecm/coreshare.py +1075 -960
  4. pyxecm/customizer/__init__.py +16 -4
  5. pyxecm/customizer/__main__.py +58 -0
  6. pyxecm/customizer/api/__init__.py +5 -0
  7. pyxecm/customizer/api/__main__.py +6 -0
  8. pyxecm/customizer/api/app.py +914 -0
  9. pyxecm/customizer/api/auth.py +154 -0
  10. pyxecm/customizer/api/metrics.py +92 -0
  11. pyxecm/customizer/api/models.py +13 -0
  12. pyxecm/customizer/api/payload_list.py +865 -0
  13. pyxecm/customizer/api/settings.py +103 -0
  14. pyxecm/customizer/browser_automation.py +332 -139
  15. pyxecm/customizer/customizer.py +1075 -1057
  16. pyxecm/customizer/exceptions.py +35 -0
  17. pyxecm/customizer/guidewire.py +322 -0
  18. pyxecm/customizer/k8s.py +787 -338
  19. pyxecm/customizer/log.py +107 -0
  20. pyxecm/customizer/m365.py +3424 -2270
  21. pyxecm/customizer/nhc.py +1169 -0
  22. pyxecm/customizer/openapi.py +258 -0
  23. pyxecm/customizer/payload.py +18201 -7030
  24. pyxecm/customizer/pht.py +1047 -210
  25. pyxecm/customizer/salesforce.py +836 -727
  26. pyxecm/customizer/sap.py +58 -41
  27. pyxecm/customizer/servicenow.py +851 -383
  28. pyxecm/customizer/settings.py +442 -0
  29. pyxecm/customizer/successfactors.py +408 -346
  30. pyxecm/customizer/translate.py +83 -48
  31. pyxecm/helper/__init__.py +5 -2
  32. pyxecm/helper/assoc.py +98 -38
  33. pyxecm/helper/data.py +2482 -742
  34. pyxecm/helper/logadapter.py +27 -0
  35. pyxecm/helper/web.py +229 -101
  36. pyxecm/helper/xml.py +528 -172
  37. pyxecm/maintenance_page/__init__.py +5 -0
  38. pyxecm/maintenance_page/__main__.py +6 -0
  39. pyxecm/maintenance_page/app.py +51 -0
  40. pyxecm/maintenance_page/settings.py +28 -0
  41. pyxecm/maintenance_page/static/favicon.avif +0 -0
  42. pyxecm/maintenance_page/templates/maintenance.html +165 -0
  43. pyxecm/otac.py +234 -140
  44. pyxecm/otawp.py +2689 -0
  45. pyxecm/otcs.py +12344 -7547
  46. pyxecm/otds.py +3166 -2219
  47. pyxecm/otiv.py +36 -21
  48. pyxecm/otmm.py +1363 -296
  49. pyxecm/otpd.py +231 -127
  50. pyxecm-2.0.0.dist-info/METADATA +145 -0
  51. pyxecm-2.0.0.dist-info/RECORD +54 -0
  52. {pyxecm-1.5.dist-info → pyxecm-2.0.0.dist-info}/WHEEL +1 -1
  53. pyxecm-1.5.dist-info/METADATA +0 -51
  54. pyxecm-1.5.dist-info/RECORD +0 -30
  55. {pyxecm-1.5.dist-info → pyxecm-2.0.0.dist-info/licenses}/LICENSE +0 -0
  56. {pyxecm-1.5.dist-info → pyxecm-2.0.0.dist-info}/top_level.txt +0 -0
@@ -1,39 +1,12 @@
1
- """
2
- SuccessFactors Module to interact with the SuccessFactors API
3
-
4
- See:
5
- https://community.sap.com/t5/enterprise-resource-planning-blogs-by-members/how-to-initiate-an-oauth-connection-to-successfactors-employee-central/ba-p/13332388
6
- https://help.sap.com/docs/SAP_SUCCESSFACTORS_PLATFORM/d599f15995d348a1b45ba5603e2aba9b/78b1d8aac783455684a7de7a8a5b0c04.html
7
-
8
- Class: SuccessFactors
9
- Methods:
10
-
11
- __init__ : class initializer
12
- config : Returns config data set
13
- credentials: Returns the token data
14
- idp_data: Return the IDP data used to request the SAML assertion
15
- request_header: Returns the request header for SuccessFactors API calls
16
- parse_request_response: Parse the REST API responses and convert
17
- them to Python dict in a safe way
18
- exist_result_item: Check if an dict item is in the response
19
- of the SuccessFactors API call
20
- get_result_value: Check if a defined value (based on a key) is in the SuccessFactors API response
21
-
22
- get_saml_assertion: Get SAML Assertion for SuccessFactors authentication
23
- authenticate : Authenticates at SuccessFactors API
24
-
25
- get_country: Get information for a Country / Countries
26
- get_user: Get a SuccessFactors user based on its ID.
27
- get_user_account: Get information for a SuccessFactors User Account
28
- update_user: Update user data. E.g. update the user password or email.
29
- get_employee: Get a list of employee(s) matching given criterias.
30
- get_entities_metadata: Get the schema (metadata) for a list of entities
31
- (list can be empty to get it for all)
32
- get_entity_metadata: Get the schema (metadata) for an entity
1
+ """SuccessFactors Module to interact with the SuccessFactors API.
2
+
3
+ See:
4
+ https://community.sap.com/t5/enterprise-resource-planning-blogs-by-members/how-to-initiate-an-oauth-connection-to-successfactors-employee-central/ba-p/13332388
5
+ https://help.sap.com/docs/SAP_SUCCESSFACTORS_PLATFORM/d599f15995d348a1b45ba5603e2aba9b/78b1d8aac783455684a7de7a8a5b0c04.html
33
6
  """
34
7
 
35
8
  __author__ = "Dr. Marc Diefenbruch"
36
- __copyright__ = "Copyright 2024, OpenText"
9
+ __copyright__ = "Copyright (C) 2024-2025, OpenText"
37
10
  __credits__ = ["Kai-Philip Gatzweiler"]
38
11
  __maintainer__ = "Dr. Marc Diefenbruch"
39
12
  __email__ = "mdiefenb@opentext.com"
@@ -42,11 +15,11 @@ import json
42
15
  import logging
43
16
  import time
44
17
  import urllib.parse
45
- import requests
46
18
 
19
+ import requests
47
20
  import xmltodict
48
21
 
49
- logger = logging.getLogger("pyxecm.customizer.sucessfactors")
22
+ default_logger = logging.getLogger("pyxecm.customizer.sucessfactors")
50
23
 
51
24
  request_login_headers = {
52
25
  "Content-Type": "application/x-www-form-urlencoded", # "application/json",
@@ -57,8 +30,11 @@ REQUEST_TIMEOUT = 60
57
30
  REQUEST_MAX_RETRIES = 5
58
31
  REQUEST_RETRY_DELAY = 60
59
32
 
60
- class SuccessFactors(object):
61
- """Used to retrieve and automate stettings in SuccessFactors."""
33
+
34
+ class SuccessFactors:
35
+ """Class SuccessFactors is used to retrieve and automate stettings in SuccessFactors."""
36
+
37
+ logger: logging.Logger = default_logger
62
38
 
63
39
  _config: dict
64
40
  _access_token = None
@@ -74,35 +50,49 @@ class SuccessFactors(object):
74
50
  password: str = "",
75
51
  company_id: str = "",
76
52
  authorization_url: str = "",
77
- ):
78
- """Initialize the SuccessFactors object
53
+ logger: logging.Logger = default_logger,
54
+ ) -> None:
55
+ """Initialize the SuccessFactors object.
79
56
 
80
57
  Args:
81
- base_url (str): base URL of the SuccessFactors tenant
82
- authorization_url (str): authorization URL of the SuccessFactors tenant, typically ending with "/services/oauth2/token"
83
- client_id (str): SuccessFactors Client ID
84
- client_secret (str): SuccessFactors Client Secret
85
- username (str): user name in SuccessFactors
86
- password (str): password of the user
87
- authorization_url (str, optional): URL for SuccessFactors login. If not given it will be constructed with default values
88
- using base_url
58
+ base_url (str):
59
+ The base URL of the SuccessFactors tenant.
60
+ as_url (str):
61
+ The Application Service URL of the SuccessFactors tenant.
62
+ client_id (str):
63
+ The SuccessFactors Client ID.
64
+ client_secret (str):
65
+ The SuccessFactors Client Secret.
66
+ username (str):
67
+ The user name in SuccessFactors.
68
+ password (str):
69
+ The password of the SuccessFactors user.
70
+ company_id (str):
71
+ The SuccessFactors company ID.
72
+ authorization_url (str, optional):
73
+ URL for SuccessFactors login.
74
+ If not given it will be constructed with default values using base_url.
75
+ logger (logging.Logger, optional):
76
+ The logging object to use for all log messages. Defaults to default_logger.
77
+
89
78
  """
79
+ if logger != default_logger:
80
+ self.logger = logger.getChild("successfactors")
81
+ for logfilter in logger.filters:
82
+ self.logger.addFilter(logfilter)
90
83
 
91
84
  successfactors_config = {}
92
85
 
93
86
  # this class assumes that the base URL is provided without
94
87
  # a trailing "/". Otherwise the trailing slash is removed.
95
- if base_url.endswith("/"):
96
- base_url = base_url[:-1]
88
+ base_url = base_url.removesuffix("/")
97
89
 
98
90
  # Set the authentication endpoints and credentials
99
91
  successfactors_config["baseUrl"] = base_url
100
92
  successfactors_config["asUrl"] = as_url
101
93
  successfactors_config["clientId"] = client_id
102
94
  successfactors_config["clientSecret"] = client_secret
103
- successfactors_config["username"] = username.split("@")[
104
- 0
105
- ] # we don't want the company ID in the user name
95
+ successfactors_config["username"] = username.split("@")[0] # we don't want the company ID in the user name
106
96
  successfactors_config["password"] = password
107
97
  if company_id:
108
98
  successfactors_config["companyId"] = company_id
@@ -114,13 +104,9 @@ class SuccessFactors(object):
114
104
  if authorization_url:
115
105
  successfactors_config["authenticationUrl"] = authorization_url
116
106
  else:
117
- successfactors_config["authenticationUrl"] = (
118
- successfactors_config["baseUrl"] + "/oauth/token"
119
- )
107
+ successfactors_config["authenticationUrl"] = successfactors_config["baseUrl"] + "/oauth/token"
120
108
 
121
- successfactors_config["idpUrl"] = (
122
- successfactors_config["baseUrl"] + "/oauth/idp"
123
- )
109
+ successfactors_config["idpUrl"] = successfactors_config["baseUrl"] + "/oauth/idp"
124
110
 
125
111
  if not username:
126
112
  # Set the data for the token request
@@ -128,13 +114,10 @@ class SuccessFactors(object):
128
114
  "grant_type": "client_credentials",
129
115
  "client_id": client_id,
130
116
  "client_secret": client_secret,
131
- # "username": successfactors_config["username"],
132
- # "password": password,
133
117
  }
134
118
  else:
135
119
  # Set the data for the token request
136
120
  successfactors_config["authenticationData"] = {
137
- # "grant_type": "password",
138
121
  "grant_type": "urn:ietf:params:oauth:grant-type:saml2-bearer",
139
122
  "company_id": successfactors_config["companyId"],
140
123
  "username": successfactors_config["username"],
@@ -146,7 +129,6 @@ class SuccessFactors(object):
146
129
  successfactors_config["idpData"] = {
147
130
  "client_id": client_id,
148
131
  "user_id": successfactors_config["username"],
149
- # "use_email": True,
150
132
  "token_url": successfactors_config["authenticationUrl"],
151
133
  "private_key": client_secret,
152
134
  }
@@ -156,43 +138,54 @@ class SuccessFactors(object):
156
138
  # end method definition
157
139
 
158
140
  def config(self) -> dict:
159
- """Returns the configuration dictionary
141
+ """Return the configuration dictionary.
160
142
 
161
143
  Returns:
162
- dict: Configuration dictionary
144
+ dict:
145
+ The configuration dictionary.
146
+
163
147
  """
164
148
  return self._config
165
149
 
166
150
  # end method definition
167
151
 
168
152
  def credentials(self) -> dict:
169
- """Return the login credentials
153
+ """Return the login credentials.
170
154
 
171
155
  Returns:
172
- dict: dictionary with login credentials for SuccessFactors
156
+ dict:
157
+ A dictionary with login credentials for SuccessFactors.
158
+
173
159
  """
174
160
  return self.config()["authenticationData"]
175
161
 
176
162
  # end method definition
177
163
 
178
164
  def idp_data(self) -> dict:
179
- """Return the IDP data used to request the SAML assertion
165
+ """Return the IDP data used to request the SAML assertion.
180
166
 
181
167
  Returns:
182
- dict: dictionary with IDP data for SuccessFactors
168
+ dict:
169
+ A dictionary with IDP data for SuccessFactors.
170
+
183
171
  """
184
172
  return self.config()["idpData"]
185
173
 
186
174
  # end method definition
187
175
 
188
176
  def request_header(self, content_type: str = "application/json") -> dict:
189
- """Returns the request header used for Application calls.
190
- Consists of Bearer access token and Content Type
177
+ """Return the request header used for Application calls.
178
+
179
+ Consists of Bearer access token and Content Type
191
180
 
192
181
  Args:
193
- content_type (str, optional): content type for the request
194
- Return:
195
- dict: request header values
182
+ content_type (str, optional):
183
+ The content type for the request. Defaults to "application/json".
184
+
185
+ Returns:
186
+ dict:
187
+ The request header values.
188
+
196
189
  """
197
190
 
198
191
  request_header = {
@@ -210,43 +203,47 @@ class SuccessFactors(object):
210
203
  additional_error_message: str = "",
211
204
  show_error: bool = True,
212
205
  ) -> dict | None:
213
- """Converts the request response (JSon) to a Python dict in a safe way
214
- that also handles exceptions. It first tries to load the response.text
215
- via json.loads() that produces a dict output. Only if response.text is
216
- not set or is empty it just converts the response_object to a dict using
217
- the vars() built-in method.
206
+ """Convert the request response (JSon) to a Python dict in a safe way.
207
+
208
+ It also handles exceptions. It first tries to load the response.text
209
+ via json.loads() that produces a dict output. Only if response.text is
210
+ not set or is empty it just converts the response_object to a dict using
211
+ the vars() built-in method.
218
212
 
219
213
  Args:
220
- response_object (object): this is reponse object delivered by the request call
221
- additional_error_message (str, optional): use a more specific error message
222
- in case of an error
223
- show_error (bool): True: write an error to the log file
224
- False: write a warning to the log file
214
+ response_object (object):
215
+ This is reponse object delivered by the request call.
216
+ additional_error_message (str, optional):
217
+ Provide a more specific error message in case of an error.
218
+ show_error (bool):
219
+ True: write an error to the log file
220
+ False: write a warning to the log file
221
+
225
222
  Returns:
226
- dict: response information or None in case of an error
223
+ dict:
224
+ The response information or None in case of an error.
225
+
227
226
  """
228
227
 
229
228
  if not response_object:
230
229
  return None
231
230
 
232
231
  try:
233
- if response_object.text:
234
- dict_object = json.loads(response_object.text)
235
- else:
236
- dict_object = vars(response_object)
232
+ dict_object = json.loads(response_object.text) if response_object.text else vars(response_object)
237
233
  except json.JSONDecodeError as exception:
238
234
  if additional_error_message:
239
235
  message = "Cannot decode response as JSon. {}; error -> {}".format(
240
- additional_error_message, exception
236
+ additional_error_message,
237
+ exception,
241
238
  )
242
239
  else:
243
240
  message = "Cannot decode response as JSon; error -> {}".format(
244
- exception
241
+ exception,
245
242
  )
246
243
  if show_error:
247
- logger.error(message)
244
+ self.logger.error(message)
248
245
  else:
249
- logger.warning(message)
246
+ self.logger.warning(message)
250
247
  return None
251
248
  else:
252
249
  return dict_object
@@ -262,6 +259,7 @@ class SuccessFactors(object):
262
259
  value (str): value to find in the item with the matching key
263
260
  Returns:
264
261
  bool: True if the value was found, False otherwise
262
+
265
263
  """
266
264
 
267
265
  if not response:
@@ -269,12 +267,12 @@ class SuccessFactors(object):
269
267
 
270
268
  if "d" in response:
271
269
  data = response["d"]
272
- if not key in data:
270
+ if key not in data:
273
271
  return False
274
272
  if value == data[key]:
275
273
  return True
276
274
  else:
277
- if not key in response:
275
+ if key not in response:
278
276
  return False
279
277
  if value == response[key]:
280
278
  return True
@@ -296,11 +294,13 @@ class SuccessFactors(object):
296
294
  key (str): property name (key)
297
295
  index (int, optional): Index to use (1st element has index 0).
298
296
  Defaults to 0.
297
+
299
298
  Returns:
300
299
  str: value for the key, None otherwise
300
+
301
301
  """
302
302
 
303
- if not response or not "d" in response:
303
+ if not response or "d" not in response:
304
304
  return None
305
305
 
306
306
  data = response["d"]
@@ -313,24 +313,22 @@ class SuccessFactors(object):
313
313
  return None
314
314
  try:
315
315
  value = results[index][key]
316
- except IndexError as e:
317
- logger.error(
318
- "Index error with index -> %s and key -> %s: %s",
316
+ except IndexError:
317
+ self.logger.error(
318
+ "Index error with index -> %s and key -> %s",
319
319
  str(index),
320
320
  key,
321
- str(e),
322
321
  )
323
322
  return None
324
- except KeyError as e:
325
- logger.error(
326
- "Key error with index -> %s and key -> %s: %s",
323
+ except KeyError:
324
+ self.logger.error(
325
+ "Key error with index -> %s and key -> %s",
327
326
  str(index),
328
327
  key,
329
- str(e),
330
328
  )
331
329
  return None
332
330
  else: # simple response - try to find key in response directly:
333
- if not key in data:
331
+ if key not in data:
334
332
  return None
335
333
  value = data[key]
336
334
 
@@ -345,13 +343,15 @@ class SuccessFactors(object):
345
343
  None
346
344
  Returns:
347
345
  str: Assertion. Also stores access token in self._assertion. None in case of error
346
+
348
347
  """
349
348
 
350
349
  request_url = self.config()["idpUrl"]
351
350
 
352
- # request_header = request_login_headers
353
-
354
- logger.debug("Requesting SuccessFactors SAML Assertion from -> %s", request_url)
351
+ self.logger.debug(
352
+ "Requesting SuccessFactors SAML Assertion from -> %s",
353
+ request_url,
354
+ )
355
355
 
356
356
  idp_post_body = self.config()["idpData"]
357
357
 
@@ -362,24 +362,22 @@ class SuccessFactors(object):
362
362
  response = requests.post(
363
363
  request_url,
364
364
  data=idp_post_body,
365
- # headers=request_header,
366
365
  timeout=REQUEST_TIMEOUT,
367
366
  )
368
- except requests.exceptions.ConnectionError as exception:
369
- logger.error(
370
- "Unable to get SAML assertion from -> %s : %s",
367
+ except requests.exceptions.ConnectionError:
368
+ self.logger.error(
369
+ "Unable to get SAML assertion from -> %s",
371
370
  self.config()["idpUrl"],
372
- exception,
373
371
  )
374
372
  return None
375
373
 
376
374
  if response.ok:
377
375
  assertion = response.text
378
376
  self._assertion = assertion
379
- logger.debug("Assertion -> %s", self._assertion)
377
+ self.logger.debug("Assertion -> %s", self._assertion)
380
378
  return assertion
381
379
 
382
- logger.error(
380
+ self.logger.error(
383
381
  "Failed to request an SuccessFactors SAML Assertion; error -> %s",
384
382
  response.text,
385
383
  )
@@ -391,10 +389,14 @@ class SuccessFactors(object):
391
389
  """Authenticate at SuccessFactors with client ID and client secret.
392
390
 
393
391
  Args:
394
- revalidate (bool, optional): determinse if a re-athentication is enforced
395
- (e.g. if session has timed out with 401 error)
392
+ revalidate (bool, optional):
393
+ Determine if a re-athentication is enforced
394
+ (e.g. if session has timed out with 401 error).
395
+
396
396
  Returns:
397
- str: Access token. Also stores access token in self._access_token. None in case of error
397
+ str | None:
398
+ Access token. Also stores access token in self._access_token. None in case of error
399
+
398
400
  """
399
401
 
400
402
  if not self._assertion:
@@ -402,7 +404,7 @@ class SuccessFactors(object):
402
404
 
403
405
  # Already authenticated and session still valid?
404
406
  if self._access_token and not revalidate:
405
- logger.debug(
407
+ self.logger.debug(
406
408
  "Session still valid - return existing access token -> %s",
407
409
  str(self._access_token),
408
410
  )
@@ -410,9 +412,10 @@ class SuccessFactors(object):
410
412
 
411
413
  request_url = self.config()["authenticationUrl"]
412
414
 
413
- # request_header = request_login_headers
414
-
415
- logger.debug("Requesting SuccessFactors Access Token from -> %s", request_url)
415
+ self.logger.debug(
416
+ "Requesting SuccessFactors Access Token from -> %s",
417
+ request_url,
418
+ )
416
419
 
417
420
  authenticate_post_body = self.credentials()
418
421
  authenticate_post_body["assertion"] = self._assertion
@@ -421,14 +424,14 @@ class SuccessFactors(object):
421
424
  self._access_token = None
422
425
 
423
426
  try:
427
+ # Don't use header here:
424
428
  response = requests.post(
425
429
  request_url,
426
430
  data=authenticate_post_body,
427
- # headers=request_header,
428
431
  timeout=REQUEST_TIMEOUT,
429
432
  )
430
433
  except requests.exceptions.ConnectionError as exception:
431
- logger.warning(
434
+ self.logger.warning(
432
435
  "Unable to connect to -> %s : %s",
433
436
  self.config()["authenticationUrl"],
434
437
  exception,
@@ -437,13 +440,13 @@ class SuccessFactors(object):
437
440
 
438
441
  if response.ok:
439
442
  authenticate_dict = self.parse_request_response(response)
440
- if not authenticate_dict or not "access_token" in authenticate_dict:
443
+ if not authenticate_dict or "access_token" not in authenticate_dict:
441
444
  return None
442
445
  # Store authentication access_token:
443
446
  self._access_token = authenticate_dict["access_token"]
444
- logger.debug("Access Token -> %s", self._access_token)
447
+ self.logger.debug("Access Token -> %s", self._access_token)
445
448
  else:
446
- logger.error(
449
+ self.logger.error(
447
450
  "Failed to request an SuccessFactors Access Token; error -> %s",
448
451
  response.text,
449
452
  )
@@ -454,59 +457,65 @@ class SuccessFactors(object):
454
457
  # end method definition
455
458
 
456
459
  def get_country(self, code: str = "") -> dict | None:
457
- """Get information for a Country / Countries
460
+ """Get information for a country / countries.
458
461
 
459
462
  Args:
460
- code (str): 3 character code for contry selection, like "USA"
463
+ code (str):
464
+ 3 character code for contry selection, like "USA"
461
465
 
462
466
  Returns:
463
- dict | None: Country details
464
-
465
- Example return data in "d" dictionary:
467
+ dict | None:
468
+ Country details
469
+
470
+ Example return data in "d" dictionary:
471
+ {
472
+ '__metadata': {
473
+ 'uri': "https://apisalesdemo2.successfactors.eu/odata/v2/UserAccount('twalker')",
474
+ 'type': 'SFOData.UserAccount'
475
+ },
476
+ 'username': 'twalker',
477
+ 'lastModifiedDateTime': '/Date(1692701804000+0000)/',
478
+ 'accountUuid': '5c7390e0-d9d2-e348-1700-2b02b3a61aa5',
479
+ 'createdDateTime': '/Date(1420745485000+0000)/',
480
+ 'timeZone': 'US/Eastern',
481
+ 'lastInactivationDateTime': None,
482
+ 'accountIsLocked': 'FALSE',
483
+ 'accountStatus': 'ACTIVE',
484
+ 'defaultLocale': 'en_US',
485
+ 'lastLoginFailedDateTime': None,
486
+ 'accountId': '90',
487
+ 'sapGlobalUserId': None,
488
+ 'personIdExternal': '82094',
489
+ 'userType': 'employee',
490
+ 'email': 'twalker@m365x41497014.onmicrosoft.com',
491
+ 'user': {'__deferred': {...}}
492
+ }
466
493
 
467
- {
468
- '__metadata': {
469
- 'uri': "https://apisalesdemo2.successfactors.eu/odata/v2/UserAccount('twalker')",
470
- 'type': 'SFOData.UserAccount'
471
- },
472
- 'username': 'twalker',
473
- 'lastModifiedDateTime': '/Date(1692701804000+0000)/',
474
- 'accountUuid': '5c7390e0-d9d2-e348-1700-2b02b3a61aa5',
475
- 'createdDateTime': '/Date(1420745485000+0000)/',
476
- 'timeZone': 'US/Eastern',
477
- 'lastInactivationDateTime': None,
478
- 'accountIsLocked': 'FALSE',
479
- 'accountStatus': 'ACTIVE',
480
- 'defaultLocale': 'en_US',
481
- 'lastLoginFailedDateTime': None,
482
- 'accountId': '90',
483
- 'sapGlobalUserId': None,
484
- 'personIdExternal': '82094',
485
- 'userType': 'employee',
486
- 'email': 'twalker@m365x41497014.onmicrosoft.com',
487
- 'user': {'__deferred': {...}}
488
- }
489
494
  """
490
495
 
491
496
  if not self._access_token:
492
497
  self.authenticate()
493
498
 
494
- if code:
495
- request_url = self.config()["asUrl"] + "Country(code='{}')".format(
496
- code
497
- ) # ,effectiveStartDate=datetime'1900-01-01T00:00:00'
498
- else:
499
- request_url = self.config()["asUrl"] + "Country"
499
+ request_url = (
500
+ self.config()["asUrl"]
501
+ + "Country(code='{}')".format(
502
+ code,
503
+ )
504
+ if code
505
+ else self.config()["asUrl"] + "Country"
506
+ )
500
507
 
501
508
  request_header = self.request_header()
502
509
 
503
510
  response = requests.get(
504
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
511
+ request_url,
512
+ headers=request_header,
513
+ timeout=REQUEST_TIMEOUT,
505
514
  )
506
515
  if response.status_code == 200:
507
516
  return self.parse_request_response(response)
508
517
  else:
509
- logger.error(
518
+ self.logger.error(
510
519
  "Failed to retrieve country data; status -> %s; error -> %s",
511
520
  response.status_code,
512
521
  response.text,
@@ -523,13 +532,23 @@ class SuccessFactors(object):
523
532
  field_operation: str = "eq",
524
533
  max_results: int = 1,
525
534
  ) -> dict | None:
526
- """Get information for a User Account
527
- Inactive users are not returned by default. To query inactive users,
528
- you can explicitly include the status in a $filter or use a key predicate.
529
- If you want to query all users, use query option $filter=status in 't','f','T','F','e','d'.
535
+ """Get information for a SuccessFactors user account.
536
+
537
+ Inactive users are not returned by default. To query inactive users,
538
+ you can explicitly include the status in a $filter or use a key predicate.
539
+ If you want to query all users, use query option $filter=status in 't','f','T','F','e','d'.
530
540
 
531
541
  Args:
532
- user_id (str): login name of the user (e.g. "twalker")
542
+ user_id (str):
543
+ The login name of the user (e.g. "twalker")
544
+ field_name (str):
545
+ The field name of the filter.
546
+ field_value (str):
547
+ The filter value to compare the field with.
548
+ field_operation (str):
549
+ The operation of the filter. Like "in".
550
+ max_results (int):
551
+ The maximum number of results to return. Default is 1.
533
552
 
534
553
  Returns:
535
554
  dict | None: User Account details
@@ -600,6 +619,7 @@ class SuccessFactors(object):
600
619
  'salaryBudgetTotalRaisePercentage': None,
601
620
  ...
602
621
  }
622
+
603
623
  """
604
624
 
605
625
  if not self._access_token:
@@ -614,7 +634,9 @@ class SuccessFactors(object):
614
634
  query = {}
615
635
  if field_name and field_value:
616
636
  query["$filter"] = "{} {} {}".format(
617
- field_name, field_operation, field_value
637
+ field_name,
638
+ field_operation,
639
+ field_value,
618
640
  )
619
641
  if max_results > 0:
620
642
  query["$top"] = max_results
@@ -625,12 +647,14 @@ class SuccessFactors(object):
625
647
  request_header = self.request_header()
626
648
 
627
649
  response = requests.get(
628
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
650
+ request_url,
651
+ headers=request_header,
652
+ timeout=REQUEST_TIMEOUT,
629
653
  )
630
654
  if response.status_code == 200:
631
655
  return self.parse_request_response(response)
632
656
  else:
633
- logger.error(
657
+ self.logger.error(
634
658
  "Failed to retrieve user data; status -> %s; error -> %s",
635
659
  response.status_code,
636
660
  response.text,
@@ -640,41 +664,44 @@ class SuccessFactors(object):
640
664
  # end method definition
641
665
 
642
666
  def get_user_account(self, username: str) -> dict | None:
643
- """Get information for a SuccessFactors User Account
644
- Inactive users are not returned by default. To query inactive users,
645
- you can explicitly include the status in a $filter or use a key predicate.
646
- If you want to query all users, use query option $filter=status in 't','f','T','F','e','d'.
667
+ """Get information for a SuccessFactors User Account.
668
+
669
+ Inactive users are not returned by default. To query inactive users,
670
+ you can explicitly include the status in a $filter or use a key predicate.
671
+ If you want to query all users, use query option $filter=status in 't','f','T','F','e','d'.
647
672
 
648
673
  Args:
649
- username (str): login name of the user (e.g. "twalker")
674
+ username (str):
675
+ The login name of the user (e.g. "twalker").
650
676
 
651
677
  Returns:
652
- dict | None: User Account details
653
-
654
- Example return data in "d" dictionary:
678
+ dict | None:
679
+ User Account details.
680
+
681
+ Example return data in "d" dictionary:
682
+ {
683
+ '__metadata': {
684
+ 'uri': "https://apisalesdemo2.successfactors.eu/odata/v2/UserAccount('twalker')",
685
+ 'type': 'SFOData.UserAccount'
686
+ },
687
+ 'username': 'twalker',
688
+ 'lastModifiedDateTime': '/Date(1692701804000+0000)/',
689
+ 'accountUuid': '5c7390e0-d9d2-e348-1700-2b02b3a61aa5',
690
+ 'createdDateTime': '/Date(1420745485000+0000)/',
691
+ 'timeZone': 'US/Eastern',
692
+ 'lastInactivationDateTime': None,
693
+ 'accountIsLocked': 'FALSE',
694
+ 'accountStatus': 'ACTIVE',
695
+ 'defaultLocale': 'en_US',
696
+ 'lastLoginFailedDateTime': None,
697
+ 'accountId': '90',
698
+ 'sapGlobalUserId': None,
699
+ 'personIdExternal': '82094',
700
+ 'userType': 'employee',
701
+ 'email': 'twalker@m365x41497014.onmicrosoft.com',
702
+ 'user': {'__deferred': {...}}
703
+ }
655
704
 
656
- {
657
- '__metadata': {
658
- 'uri': "https://apisalesdemo2.successfactors.eu/odata/v2/UserAccount('twalker')",
659
- 'type': 'SFOData.UserAccount'
660
- },
661
- 'username': 'twalker',
662
- 'lastModifiedDateTime': '/Date(1692701804000+0000)/',
663
- 'accountUuid': '5c7390e0-d9d2-e348-1700-2b02b3a61aa5',
664
- 'createdDateTime': '/Date(1420745485000+0000)/',
665
- 'timeZone': 'US/Eastern',
666
- 'lastInactivationDateTime': None,
667
- 'accountIsLocked': 'FALSE',
668
- 'accountStatus': 'ACTIVE',
669
- 'defaultLocale': 'en_US',
670
- 'lastLoginFailedDateTime': None,
671
- 'accountId': '90',
672
- 'sapGlobalUserId': None,
673
- 'personIdExternal': '82094',
674
- 'userType': 'employee',
675
- 'email': 'twalker@m365x41497014.onmicrosoft.com',
676
- 'user': {'__deferred': {...}}
677
- }
678
705
  """
679
706
 
680
707
  if not self._access_token:
@@ -689,35 +716,34 @@ class SuccessFactors(object):
689
716
  while True:
690
717
  try:
691
718
  response = requests.get(
692
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
719
+ request_url,
720
+ headers=request_header,
721
+ timeout=REQUEST_TIMEOUT,
693
722
  )
694
723
  response.raise_for_status() # This will raise an HTTPError for bad responses
695
724
  return self.parse_request_response(response)
696
- except requests.exceptions.HTTPError as http_err:
697
- logger.error(
698
- "Failed to retrieve user data from SuccessFactors; status -> %s; error -> %s",
725
+ except requests.exceptions.HTTPError:
726
+ self.logger.error(
727
+ "Failed to retrieve user data from SuccessFactors; status -> %s",
699
728
  response.status_code,
700
- str(http_err),
701
729
  )
702
730
  except requests.exceptions.Timeout:
703
- logger.warning(
731
+ self.logger.warning(
704
732
  "Failed to retrieve user data from SuccessFactors. The request timed out.",
705
733
  )
706
- except requests.exceptions.ConnectionError as conn_err:
707
- logger.error(
708
- "Cannot connect to SuccessFactors to retrieve user data; status -> %s; error -> %s",
734
+ except requests.exceptions.ConnectionError:
735
+ self.logger.error(
736
+ "Cannot connect to SuccessFactors to retrieve user data; status -> %s",
709
737
  response.status_code,
710
- str(conn_err),
711
738
  )
712
- except requests.exceptions.RequestException as req_err:
713
- logger.error(
714
- "Failed to retrieve user data from SuccessFactors; status -> %s; error -> %s",
739
+ except requests.exceptions.RequestException:
740
+ self.logger.error(
741
+ "Failed to retrieve user data from SuccessFactors; status -> %s",
715
742
  response.status_code,
716
- str(req_err),
717
743
  )
718
744
  retries += 1
719
745
  if retries <= REQUEST_MAX_RETRIES:
720
- logger.info("Retrying in %s seconds...", str(REQUEST_RETRY_DELAY))
746
+ self.logger.info("Retrying in %s seconds...", str(REQUEST_RETRY_DELAY))
721
747
  time.sleep(retries * REQUEST_RETRY_DELAY)
722
748
  else:
723
749
  break
@@ -732,13 +758,19 @@ class SuccessFactors(object):
732
758
  update_data: dict,
733
759
  ) -> dict:
734
760
  """Update user data. E.g. update the user password or email.
735
- See: https://help.sap.com/docs/SAP_SUCCESSFACTORS_PLATFORM/d599f15995d348a1b45ba5603e2aba9b/47c39724e7654b99a6be2f71fce1c50b.html?locale=en-US
761
+
762
+ See: https://help.sap.com/docs/SAP_SUCCESSFACTORS_PLATFORM/d599f15995d348a1b45ba5603e2aba9b/47c39724e7654b99a6be2f71fce1c50b.html?locale=en-US
736
763
 
737
764
  Args:
738
- user_id (str): ID of the user (e.g. 106020)
739
- update_data (dict): Update data
765
+ user_id (str):
766
+ The ID of the user (e.g. 106020)
767
+ update_data (dict):
768
+ The data to update the user with.
769
+
740
770
  Returns:
741
- dict: Request response or None if an error occured.
771
+ dict:
772
+ Request response or None if an error occured.
773
+
742
774
  """
743
775
 
744
776
  if not self._access_token:
@@ -758,10 +790,10 @@ class SuccessFactors(object):
758
790
  timeout=REQUEST_TIMEOUT,
759
791
  )
760
792
  if response.ok:
761
- logger.debug("User with ID -> %s updated successfully.", user_id)
793
+ self.logger.debug("User with ID -> %s updated successfully.", user_id)
762
794
  return self.parse_request_response(response)
763
795
  else:
764
- logger.error(
796
+ self.logger.error(
765
797
  "Failed to update user with ID -> %s; status -> %s; error -> %s",
766
798
  user_id,
767
799
  response.status_code,
@@ -782,109 +814,116 @@ class SuccessFactors(object):
782
814
  """Get a list of employee(s) matching given criterias.
783
815
 
784
816
  Args:
785
- entity (str, optional): Entity type to query. Examples are "PerPerson" (default),
786
- "PerPersonal", "PerEmail", "PersonKey", ...
787
- field_name (str): Field to search in. E.g. personIdExternal, firstName, lastName,
788
- fullName, email, dateOfBirth, gender, nationality, maritalStatus,
789
- employeeId
790
- field_value (str): Value to match in the Field
817
+ entity (str, optional):
818
+ Entity type to query. Examples are "PerPerson" (default),
819
+ "PerPersonal", "PerEmail", "PersonKey", ...
820
+ field_name (str):
821
+ Field to search in. E.g. personIdExternal, firstName, lastName,
822
+ fullName, email, dateOfBirth, gender, nationality, maritalStatus,
823
+ employeeId.
824
+ field_value (str):
825
+ Value to match in the Field
826
+ field_operation (str):
827
+ The operation to apply for the filter.
828
+ max_results (int):
829
+ The maximum number of results to return. Default is 1.
791
830
 
792
831
  Returns:
793
- dict | None: Dictionary with the SuccessFactors object data or None in case the request failed.
794
-
795
- Example result values for "PerPerson" inside the "d" structure:
796
-
797
- "results": [
798
- {
799
- '__metadata': {...},
800
- 'personIdExternal': '109031',
801
- 'lastModifiedDateTime': '/Date(1442346839000+0000)/',
802
- 'lastModifiedBy': 'admindlr',
803
- 'createdDateTime': '/Date(1442346265000+0000)/',
804
- 'dateOfBirth': '/Date(-501206400000)/',
805
- 'perPersonUuid': '0378B0E6F41444EBB90345B56D537D3D',
806
- 'createdOn': '/Date(1442353465000)/',
807
- 'lastModifiedOn': '/Date(1442354039000)/',
808
- 'countryOfBirth': 'RUS',
809
- 'createdBy': 'admindlr',
810
- 'regionOfBirth': None,
811
- 'personId': '771',
812
- 'personalInfoNav': {...},
813
- 'emergencyContactNav': {...},
814
- 'secondaryAssignmentsNav': {...},
815
- 'personEmpTerminationInfoNav': {...},
816
- 'phoneNav': {...},
817
- 'employmentNav': {...},
818
- ...
819
- }
820
- ]
821
-
822
- Example result values for "PerPersonal" inside the "d" structure:
823
-
824
- "results": [
825
- {
826
- '__metadata': {
827
- 'uri': "https://apisalesdemo2.successfactors.eu/odata/v2/PerPersonal(personIdExternal='108729',startDate=datetime'2017-03-13T00:00:00')",
828
- 'type': 'SFOData.PerPersonal'
829
- },
830
- 'personIdExternal': '108729',
831
- 'startDate': '/Date(1489363200000)/',
832
- 'lastModifiedDateTime': '/Date(1489442337000+0000)/',
833
- 'endDate': '/Date(253402214400000)/',
834
- 'createdDateTime': '/Date(1489442337000+0000)/',
835
- 'suffix': None,
836
- 'attachmentId': None,
837
- 'preferredName': 'Hillary',
838
- 'lastNameAlt1': None,
839
- 'firstName': 'Hillary',
840
- 'nationality': 'USA',
841
- 'salutation': '30085',
842
- 'maritalStatus': '10825',
843
- 'lastName': 'Lawson',
844
- 'gender': 'F',
845
- 'firstNameAlt1': None,
846
- 'createdOn': '/Date(1489445937000)/',
847
- 'middleNameAlt1': None,
848
- 'lastModifiedBy': '82094',
849
- 'lastModifiedOn': '/Date(1489445937000)/',
850
- 'createdBy': '82094',
851
- 'middleName': None,
852
- 'nativePreferredLang': '10249',
853
- 'localNavAUS': {'__deferred': {...}},
854
- 'localNavBGD': {'__deferred': {...}},
855
- 'localNavHKG': {'__deferred': {...}},
856
- 'localNavMYS': {'__deferred': {...}},
857
- 'localNavAUT': {'__deferred': {...}},
858
- 'localNavLKA': {'__deferred': {...}},
859
- 'localNavPOL': {'__deferred': {...}},
860
- 'localNavCZE': {'__deferred': {...}},
861
- 'localNavTWN': {'__deferred': {...}},
862
- 'localNavARE': {'__deferred': {...}},
863
- 'localNavARG': {'__deferred': {...}},
864
- 'localNavCAN': {'__deferred': {...}},
865
- 'localNavNOR': {'__deferred': {...}},
866
- 'localNavOMN': {'__deferred': {...}},
867
- 'localNavPER': {'__deferred': {...}},
868
- 'localNavSGP': {'__deferred': {...}},
869
- 'localNavVEN': {'__deferred': {...}},
870
- 'localNavZAF': {'__deferred': {...}},
871
- 'localNavCHL': {'__deferred': {...}},
872
- 'localNavCHE': {'__deferred': {...}},
873
- 'localNavDNK': {'__deferred': {...}},
874
- 'localNavGTM': {'__deferred': {...}},
875
- 'localNavNZL': {'__deferred': {...}},
876
- 'salutationNav': {'__deferred': {...}},
877
- 'localNavCHN': {'__deferred': {...}},
878
- 'localNavVNM': {'__deferred': {...}},
879
- 'localNavIDN': {'__deferred': {...}},
880
- 'localNavPRT': {'__deferred': {...}},
881
- 'localNavCOL': {'__deferred': {...}},
882
- 'localNavHUN': {'__deferred': {...}},
883
- 'localNavSWE': {'__deferred': {...}},
884
- 'localNavESP': {'__deferred': {...}},
885
- 'localNavUSA': {'__deferred': {...}},
886
- 'nativePreferredLangNav': {'__deferred': {...}},
887
- 'maritalStatusNav': {'__deferred': {...}}, ...}
832
+ dict | None:
833
+ Dictionary with the SuccessFactors object data or None in case the request failed.
834
+
835
+ Example result values for "PerPerson" inside the "d" structure:
836
+ "results": [
837
+ {
838
+ '__metadata': {...},
839
+ 'personIdExternal': '109031',
840
+ 'lastModifiedDateTime': '/Date(1442346839000+0000)/',
841
+ 'lastModifiedBy': 'admindlr',
842
+ 'createdDateTime': '/Date(1442346265000+0000)/',
843
+ 'dateOfBirth': '/Date(-501206400000)/',
844
+ 'perPersonUuid': '0378B0E6F41444EBB90345B56D537D3D',
845
+ 'createdOn': '/Date(1442353465000)/',
846
+ 'lastModifiedOn': '/Date(1442354039000)/',
847
+ 'countryOfBirth': 'RUS',
848
+ 'createdBy': 'admindlr',
849
+ 'regionOfBirth': None,
850
+ 'personId': '771',
851
+ 'personalInfoNav': {...},
852
+ 'emergencyContactNav': {...},
853
+ 'secondaryAssignmentsNav': {...},
854
+ 'personEmpTerminationInfoNav': {...},
855
+ 'phoneNav': {...},
856
+ 'employmentNav': {...},
857
+ ...
858
+ }
859
+ ]
860
+
861
+ Example result values for "PerPersonal" inside the "d" structure:
862
+ "results": [
863
+ {
864
+ '__metadata': {
865
+ 'uri': "https://apisalesdemo2.successfactors.eu/odata/v2/PerPersonal(personIdExternal='108729',startDate=datetime'2017-03-13T00:00:00')",
866
+ 'type': 'SFOData.PerPersonal'
867
+ },
868
+ 'personIdExternal': '108729',
869
+ 'startDate': '/Date(1489363200000)/',
870
+ 'lastModifiedDateTime': '/Date(1489442337000+0000)/',
871
+ 'endDate': '/Date(253402214400000)/',
872
+ 'createdDateTime': '/Date(1489442337000+0000)/',
873
+ 'suffix': None,
874
+ 'attachmentId': None,
875
+ 'preferredName': 'Hillary',
876
+ 'lastNameAlt1': None,
877
+ 'firstName': 'Hillary',
878
+ 'nationality': 'USA',
879
+ 'salutation': '30085',
880
+ 'maritalStatus': '10825',
881
+ 'lastName': 'Lawson',
882
+ 'gender': 'F',
883
+ 'firstNameAlt1': None,
884
+ 'createdOn': '/Date(1489445937000)/',
885
+ 'middleNameAlt1': None,
886
+ 'lastModifiedBy': '82094',
887
+ 'lastModifiedOn': '/Date(1489445937000)/',
888
+ 'createdBy': '82094',
889
+ 'middleName': None,
890
+ 'nativePreferredLang': '10249',
891
+ 'localNavAUS': {'__deferred': {...}},
892
+ 'localNavBGD': {'__deferred': {...}},
893
+ 'localNavHKG': {'__deferred': {...}},
894
+ 'localNavMYS': {'__deferred': {...}},
895
+ 'localNavAUT': {'__deferred': {...}},
896
+ 'localNavLKA': {'__deferred': {...}},
897
+ 'localNavPOL': {'__deferred': {...}},
898
+ 'localNavCZE': {'__deferred': {...}},
899
+ 'localNavTWN': {'__deferred': {...}},
900
+ 'localNavARE': {'__deferred': {...}},
901
+ 'localNavARG': {'__deferred': {...}},
902
+ 'localNavCAN': {'__deferred': {...}},
903
+ 'localNavNOR': {'__deferred': {...}},
904
+ 'localNavOMN': {'__deferred': {...}},
905
+ 'localNavPER': {'__deferred': {...}},
906
+ 'localNavSGP': {'__deferred': {...}},
907
+ 'localNavVEN': {'__deferred': {...}},
908
+ 'localNavZAF': {'__deferred': {...}},
909
+ 'localNavCHL': {'__deferred': {...}},
910
+ 'localNavCHE': {'__deferred': {...}},
911
+ 'localNavDNK': {'__deferred': {...}},
912
+ 'localNavGTM': {'__deferred': {...}},
913
+ 'localNavNZL': {'__deferred': {...}},
914
+ 'salutationNav': {'__deferred': {...}},
915
+ 'localNavCHN': {'__deferred': {...}},
916
+ 'localNavVNM': {'__deferred': {...}},
917
+ 'localNavIDN': {'__deferred': {...}},
918
+ 'localNavPRT': {'__deferred': {...}},
919
+ 'localNavCOL': {'__deferred': {...}},
920
+ 'localNavHUN': {'__deferred': {...}},
921
+ 'localNavSWE': {'__deferred': {...}},
922
+ 'localNavESP': {'__deferred': {...}},
923
+ 'localNavUSA': {'__deferred': {...}},
924
+ 'nativePreferredLangNav': {'__deferred': {...}},
925
+ 'maritalStatusNav': {'__deferred': {...}}, ...}
926
+
888
927
  """
889
928
 
890
929
  if not self._access_token:
@@ -894,7 +933,9 @@ class SuccessFactors(object):
894
933
  query = {}
895
934
  if field_name and field_value:
896
935
  query["$filter"] = "{} {} {}".format(
897
- field_name, field_operation, field_value
936
+ field_name,
937
+ field_operation,
938
+ field_value,
898
939
  )
899
940
  if max_results > 0:
900
941
  query["$top"] = max_results
@@ -907,12 +948,14 @@ class SuccessFactors(object):
907
948
  request_header = self.request_header()
908
949
 
909
950
  response = requests.get(
910
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
951
+ request_url,
952
+ headers=request_header,
953
+ timeout=REQUEST_TIMEOUT,
911
954
  )
912
955
  if response.status_code == 200:
913
956
  return self.parse_request_response(response)
914
957
  else:
915
- logger.error(
958
+ self.logger.error(
916
959
  "Failed to retrieve employee data; status -> %s; error -> %s",
917
960
  response.status_code,
918
961
  response.text,
@@ -922,16 +965,20 @@ class SuccessFactors(object):
922
965
  # end method definition
923
966
 
924
967
  def get_entities_metadata(self, entities: list | None = None) -> dict | None:
925
- """Get the schema (metadata) for a list of entities (list can be empty to get it for all)
926
- IMPORTANT: A metadata request using $metadata returns an XML serialization of the service,
927
- including the entity data model (EDM) and the service operation descriptions.
928
- The metadata response supports only application/xml type.
968
+ """Get the schema (metadata) for a list of entities (list can be empty to get it for all).
969
+
970
+ IMPORTANT: A metadata request using $metadata returns an XML serialization of the service,
971
+ including the entity data model (EDM) and the service operation descriptions.
972
+ The metadata response supports only application/xml type.
929
973
 
930
974
  Args:
931
- entities (list): list of entities to deliver metadata for
975
+ entities (list):
976
+ A list of entities to deliver metadata for.
932
977
 
933
978
  Returns:
934
- dict | None: Dictionary with the SuccessFactors object data or None in case the request failed.
979
+ dict | None:
980
+ Dictionary with the SuccessFactors object data or None in case the request failed.
981
+
935
982
  """
936
983
 
937
984
  if not self._access_token:
@@ -946,12 +993,14 @@ class SuccessFactors(object):
946
993
  request_header["Accept"] = "application/xml"
947
994
 
948
995
  response = requests.get(
949
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
996
+ request_url,
997
+ headers=request_header,
998
+ timeout=REQUEST_TIMEOUT,
950
999
  )
951
1000
  if response.status_code == 200:
952
1001
  return xmltodict.parse(response.text)
953
1002
  else:
954
- logger.error(
1003
+ self.logger.error(
955
1004
  "Failed to retrieve entity data; status -> %s; error -> %s",
956
1005
  response.status_code,
957
1006
  response.text,
@@ -961,13 +1010,16 @@ class SuccessFactors(object):
961
1010
  # end method definition
962
1011
 
963
1012
  def get_entity_metadata(self, entity: str) -> dict | None:
964
- """Get the schema (metadata) for an entity
1013
+ """Get the schema (metadata) for an entity.
965
1014
 
966
1015
  Args:
967
- entity (str): entity to deliver metadata for
1016
+ entity (str):
1017
+ The entity to deliver metadata for.
968
1018
 
969
1019
  Returns:
970
- dict | None: Dictionary with the SuccessFactors object data or None in case the request failed.
1020
+ dict | None:
1021
+ Dictionary with the SuccessFactors object data or None in case the request failed.
1022
+
971
1023
  """
972
1024
 
973
1025
  if not self._access_token:
@@ -977,18 +1029,20 @@ class SuccessFactors(object):
977
1029
  return None
978
1030
 
979
1031
  request_url = self.config()["asUrl"] + "Entity('{}')?$format=JSON".format(
980
- entity
1032
+ entity,
981
1033
  )
982
1034
 
983
1035
  request_header = self.request_header()
984
1036
 
985
1037
  response = requests.get(
986
- request_url, headers=request_header, timeout=REQUEST_TIMEOUT
1038
+ request_url,
1039
+ headers=request_header,
1040
+ timeout=REQUEST_TIMEOUT,
987
1041
  )
988
1042
  if response.status_code == 200:
989
1043
  return self.parse_request_response(response)
990
1044
  else:
991
- logger.error(
1045
+ self.logger.error(
992
1046
  "Failed to retrieve entity data; status -> %s; error -> %s",
993
1047
  response.status_code,
994
1048
  response.text,
@@ -1004,14 +1058,21 @@ class SuccessFactors(object):
1004
1058
  email_type: int = 8448, # 8448
1005
1059
  ) -> dict:
1006
1060
  """Update user email.
1007
- See: https://help.sap.com/docs/SAP_SUCCESSFACTORS_PLATFORM/d599f15995d348a1b45ba5603e2aba9b/7b3daeb3d77d491bb401345eede34bb5.html?locale=en-US
1061
+
1062
+ See: https://help.sap.com/docs/SAP_SUCCESSFACTORS_PLATFORM/d599f15995d348a1b45ba5603e2aba9b/7b3daeb3d77d491bb401345eede34bb5.html?locale=en-US
1008
1063
 
1009
1064
  Args:
1010
- user_id (str): ID of the user (e.g. 106020)
1011
- email_address (str): new email address of user
1012
- email_type (int): Type of the email. 8448 = Business
1065
+ user_id (str):
1066
+ The ID of the user (e.g. 106020).
1067
+ email_address (str):
1068
+ The new email address of user.
1069
+ email_type (int):
1070
+ Type of the email. 8448 = Business
1071
+
1013
1072
  Returns:
1014
- dict: Request response or None if an error occured.
1073
+ dict:
1074
+ Request response or None if an error occured.
1075
+
1015
1076
  """
1016
1077
 
1017
1078
  if not self._access_token:
@@ -1022,7 +1083,8 @@ class SuccessFactors(object):
1022
1083
  update_data = {
1023
1084
  "__metadata": {
1024
1085
  "uri": "PerEmail(emailType='{}',personIdExternal='{}')".format(
1025
- email_type, user_id
1086
+ email_type,
1087
+ user_id,
1026
1088
  ),
1027
1089
  "type": "SFOData.PerEmail",
1028
1090
  },
@@ -1038,14 +1100,14 @@ class SuccessFactors(object):
1038
1100
  timeout=REQUEST_TIMEOUT,
1039
1101
  )
1040
1102
  if response.ok:
1041
- logger.debug(
1103
+ self.logger.debug(
1042
1104
  "Email of user with ID -> %s successfully updated to -> %s.",
1043
1105
  user_id,
1044
1106
  email_address,
1045
1107
  )
1046
1108
  return self.parse_request_response(response)
1047
1109
  else:
1048
- logger.error(
1110
+ self.logger.error(
1049
1111
  "Failed to set email of user with ID -> %s; status -> %s; error -> %s",
1050
1112
  user_id,
1051
1113
  response.status_code,