comparison sat/plugins/plugin_comp_ap_gateway/__init__.py @ 3742:bf0505d41c09

comp AP: helper methods to get AP objects: rel 364
author Goffi <goffi@goffi.org>
date Tue, 22 Mar 2022 17:00:42 +0100
parents 86eea17cafa7
children a8c7e5cef0cb
comparison
equal deleted inserted replaced
3741:eddab3798aca 3742:bf0505d41c09
208 except Exception as e: 208 except Exception as e:
209 raise error.StanzaError( 209 raise error.StanzaError(
210 "service-unavailable", 210 "service-unavailable",
211 text=f"Can't get AP data at {url}: {e}" 211 text=f"Can't get AP data at {url}: {e}"
212 ) 212 )
213
214 @overload
215 async def apGetObject(self, data: dict, key: str) -> Optional[dict]:
216 ...
217
218 @overload
219 async def apGetObject(
220 self, data: Union[str, dict], key: None = None
221 ) -> dict:
222 ...
223
224 async def apGetObject(self, data, key = None):
225 """Retrieve an AP object, dereferencing when necessary
226
227 This method is to be used with attributes marked as "Functional" in
228 https://www.w3.org/TR/activitystreams-vocabulary
229 @param data: AP object where an other object is looked for, or the object itself
230 @param key: name of the object to look for, or None if data is the object directly
231 @return: found object if any
232 """
233 if key is not None:
234 value = data.get(key)
235 else:
236 value = data
237 if value is None:
238 if key is None:
239 raise ValueError("None can't be used with apGetObject is key is None")
240 return None
241 elif isinstance(value, dict):
242 return value
243 elif isinstance(value, str):
244 return await self.apGet(value)
245 else:
246 raise NotImplementedError(
247 "was expecting a string or a dict, got {type(value)}: {value!r}}"
248 )
249
250 async def apGetList(self, data: dict, key: str) -> Optional[List[dict]]:
251 """Retrieve a list of objects from AP data, dereferencing when necessary
252
253 This method is to be used with non functional vocabularies. Use ``apGetObject``
254 otherwise.
255 If the value is a dictionary, it will be wrapped in a list
256 @param data: AP object where a list of objects is looked for
257 @param key: key of the list to look for
258 @return: list of objects, or None if the key is not present
259 """
260 value = data.get(key)
261 if value is None:
262 return None
263 elif isinstance(value, str):
264 value = await self.apGet(value)
265 if isinstance(value, dict):
266 return [value]
267 if not isinstance(value, list):
268 raise ValueError(f"A list was expected, got {type(value)}: {value!r}")
269 return [await self.apGetObject(i) for i in value]
270
271 async def apGetActors(
272 self,
273 data: dict,
274 key: str,
275 as_account: bool = True
276 ) -> List[str]:
277 """Retrieve AP actors from data
278
279 @param data: AP object containing a field with actors
280 @param key: field to use to retrieve actors
281 @param as_account: if True returns account handles, otherwise will return actor
282 IDs
283 @raise exceptions.DataError: there is not actor data or it is invalid
284 """
285 value = data.get(key)
286 if value is None:
287 raise exceptions.DataError(
288 f"no actor associated to object {data.get('id')!r}"
289 )
290 elif isinstance(value, dict):
291 actor_id = value.get("id")
292 if actor_id is None:
293 raise exceptions.DataError(
294 f"invalid actor associated to object {data.get('id')!r}: {value!r}"
295 )
296 value = [actor_id]
297 elif isinstance(value, str):
298 value = [value]
299 elif isinstance(value, list):
300 try:
301 value = [a if isinstance(a, str) else a["id"] for a in value]
302 except (TypeError, KeyError):
303 raise exceptions.DataError(
304 f"invalid actors list to object {data.get('id')!r}: {value!r}"
305 )
306 if not value:
307 raise exceptions.DataError(
308 f"list of actors is empty"
309 )
310 if as_account:
311 return [await self.getAPAccountFromId(actor_id) for actor_id in value]
312 else:
313 return value
314
315 async def apGetSenderActor(
316 self,
317 data: dict,
318 ) -> str:
319 """Retrieve actor who sent data
320
321 This is done by checking "attributedTo" field first, then "actor" field.
322 Only the first found actor is taken into accoun
323 @param data: AP object
324 @return: actor id of the sender
325 @raise exceptions.NotFound: no actor has been found in data
326 """
327 try:
328 actors = await self.apGetActors(data, "attributedTo", as_account=False)
329 except exceptions.DataError:
330 actors = None
331 if not actors:
332 try:
333 actors = await self.apGetActors(data, "actor", as_account=False)
334 except exceptions.DataError:
335 raise exceptions.NotFound(
336 'actor not specified in "attributedTo" or "actor"'
337 )
338 try:
339 return actors[0]
340 except IndexError:
341 raise exceptions.NotFound("list of actors is empty")
213 342
214 def mustEncode(self, text: str) -> bool: 343 def mustEncode(self, text: str) -> bool:
215 """Indicate if a text must be period encoded""" 344 """Indicate if a text must be period encoded"""
216 return ( 345 return (
217 not RE_ALLOWED_UNQUOTED.match(text) 346 not RE_ALLOWED_UNQUOTED.match(text)