@@ -93,8 +93,14 @@ a Python dictionary of its attributes.
9393
9494# # Get a List of Users
9595
96+ The following code enumerates the first 5 users, printing the email of each.
97+ This will only have fetched the first page of results. Then, after the loop,
98+ the `all_results` call will force the fetch of all remaining pages so the
99+ list of all results can be constructed. ()Once the `all_results` call has been made,
100+ you cannot enumerate again without first calling `reload`.)
101+
96102` ` ` python
97- users = QueryUsers(conn)
103+ users = umapi_client. QueryUsers(conn)
98104# print first 5 users
99105for i, user in enumerate(users):
100106 if i == 5: break
@@ -109,7 +115,7 @@ This list of groups will contain both user groups and product license
109115configuration groups.
110116
111117` ` ` python
112- groups = QueryGroups(conn)
118+ groups = umapi_client. QueryGroups(conn)
113119# print all the group details
114120for group in groups:
115121 print(group)
@@ -122,5 +128,160 @@ for group in groups:
122128
123129# Performing Operations on Users
124130
125- _...under construction..._
131+ User operations in the UMAPI are performed in three steps :
132+
133+ 1. You specify the user to be operated on.
134+ 2. You specify the operations to be performed on the user.
135+ 3. You submit the user and operations to the UMAPI server.
136+
137+ The combined specification of the user identity and the operations to be performed
138+ is called an _action_ in the UMAPI documentation, while the individual operations are called
139+ _commands_. If you read the documentation carefully, you will see that there
140+ are limits to how many actions can be submitted to the UMAPI
141+ service in a single call, how many commands there can be in a single action, and
142+ how many calls can be submitted in a given period of time. However,
143+ the `umapi_client` implementation has been design to insulate
144+ your application from these limits
145+ by
146+ packing as many commands as allowed into each action,
147+ batching up as many actions as possible into a call,
148+ and spacing calls out when required to by the server. Thus, from an
149+ application perspective, you can simply follow the three steps above for each
150+ user and not worry about the mechanics of server communication limits.
151+
152+ # # Step 1: Specify the User
153+
154+ To operate on a user, you first create a `UserAction` object that
155+ specifies the user's identity type, domain, and unique ID in the domain.
156+
157+ In most cases,
158+ the user's email ID will be his unique ID and will itself contain his domain,
159+ as in these examples :
160+
161+ ` ` ` python
162+ from umapi_client import IdentityTypes
163+ user1 = UserAction(id_type=IdentityTypes.adobeID, email="[email protected] ") 164+ user2 = UserAction(id_type=IdentityTypes.enterpriseID, email="[email protected] ") 165+ ` ` `
166+
167+ But when Federated ID is being used, and a non-email username is being
168+ used to identify users across the SAML connection, both the username
169+ and the domain must be specified separately, as in these examples :
170+
171+ ` ` ` python
172+ user3 = UserAction(id_type=IdentityTypes.federatedID,
173+ username="user347", domain="division.conglomerate.com")
174+ user4 = UserAction(id_type=IdentityTypes.federatedID,
175+ username="user348", domain="division.conglomerate.com",
176+ 177+ ` ` `
178+
179+ Note that, as in the last example, it's OK to specify the email when
180+ creating a user object even if the email is not the unique ID or
181+ doesn't use the same domain. If
182+ you later perform an operations on a user which requires the email
183+ (such as user creation on the Adobe side), the email will be remembered
184+ and supplied from the UserAction object.
185+
186+ # # Step 2: Specify the Operations
187+
188+ Once you have a `UserAction` object for a user, you can specify
189+ operations (called _commands_) to perform on that user. For
190+ example, to create a new user on the Adobe side, for the users
191+ that were specified in the last section, we could do :
192+
193+ ` ` ` python
194+ user1.create()
195+ user2.create(first_name="Geoffrey", last_name="Giraffe")
196+ user3.create(email="[email protected] ", country="US") 197+ user4.create(first_name="John", last_name="User", country="US")
198+ ` ` `
199+
200+ When creating users, the email address is mandatory if not already specified
201+ when creating the user action. First and last name and country can be optionally
202+ specified ()except for Adobe ID users),
203+ and country _must_ be specified for Federated ID users.
204+
205+ If a user has already been created, but you want to update attributes,
206+ you can use the `update` rather than the `create` command :
207+
208+ ` ` ` python
209+ user2.update(first_name="Jeff", country="AU")
210+ user4.update(username="user0347")
211+ ` ` `
212+
213+ You can also specify to create if necessary, but update if already created :
214+
215+ ` ` ` python
216+ from umapi_client import IfAlreadyExistsOptions
217+ user4.create(first_name="John", last_name="User", country="US",
218+ on_conflict=IfAlreadyExistsOptions.updateIfAlreadyExists)
219+ ` ` `
220+
221+ There are many other operations you can perform, such as adding and removing
222+ users from user groups and product configuration groups. Because each
223+ operation specifier returns the user, it's easy to chain the together :
224+
225+ ` ` ` python
226+ user2.add_group(groups=["Photoshop", "Illustrator"]).remove_group(groups=["CC All Apps"])
227+ ` ` `
228+
229+ The details of all the possible commands are specified in the code,
230+ and more user documentation will be forthcoming. In general, commands
231+ are performed in the order they are specified, except for certain special
232+ commands such as ```create``` which are always performed first regardless
233+ of when they were specified.
234+
235+ # # Step 3: Submit to the UMAPI server
236+
237+ Once you have specified all the desired operations on a given `UserAction`,
238+ you can submit it to the server as follows (recall that `conn` is an authorized
239+ connection to the UMAPI server, as created above) :
240+
241+ ` ` ` python
242+ result = conn.execute_single(user1)
243+ result = conn.execute_multiple([user2, user3, user4])
244+ ` ` `
245+
246+ By default, `execute_single` queues the action for sending to the server
247+ when a "full batch" (of 10) actions has been accumulated, but
248+ ` execute_multiple` forces a batch to be sent (including any
249+ previously queued actions as well as the specified ones). You can
250+ override these defaults with the `immediate` argument, as in :
251+
252+ ` ` ` python
253+ result = conn.execute_single(user1, immediate=True)
254+ result = conn.execute_multiple([user2, user3, user4], immediate=False)
255+ ` ` `
256+
257+ The result of either execute operation is a tuple of three numbers
258+ ` (queued, sent, succeeded)` which tell you how many actions were
259+ queued, how many were sent, and how many of those sent succeeded
260+ without errors. So, for example, in the following code :
261+
262+ ` ` ` python
263+ queued, _, _ = conn.execute_single(user1)
264+ _, sent, succeeded = conn.execute_multiple(user2, user3, user4)
265+ ` ` `
266+
267+ we would likely see `queued = 1`, `sent = 4`, and `succeeded = 4`.
268+
269+ If, for some reason, the succeeded number is not equal to the sent
270+ number for any call, it means that not all of the actions were
271+ executed successfully on the server-side : one or more of the commands
272+ failed to execute. In such cases, the server will have sent back
273+ error information which the `umapi_client` implementation records
274+ against the commands that failed, you can call the `execution_errors`
275+ method on the user actions to get a list of the failed commands
276+ and the server error information. For example, if only three
277+ of the four actions sent had succeeded, then we could execute
278+ this code :
279+
280+ ` ` ` python
281+ actions = (user1, user2, user3, user4)
282+ errors = [info for action in actions for info in action.execution_errors()]
283+ ` ` `
126284
285+ Each entry in errors would then be a dictionary giving
286+ the command that failed, the target user it failed on,
287+ and server information about the reason for the failure.
0 commit comments