collibra-connector 1.0.10b0__py3-none-any.whl → 1.0.11__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.
@@ -86,7 +86,7 @@ class Asset(BaseAPI):
86
86
  response = self._post(url=self.__base_api, data=data)
87
87
  return self._handle_response(response)
88
88
 
89
- def update_asset_properties(
89
+ def change_asset(
90
90
  self,
91
91
  asset_id: str,
92
92
  name: str = None,
@@ -146,16 +146,7 @@ class Asset(BaseAPI):
146
146
  "typePublicId": type_public_id,
147
147
  }
148
148
 
149
- # Use PATCH method through requests directly since BaseAPI doesn't have _patch
150
- import requests
151
- response = requests.patch(
152
- f"{self.__base_api}/{asset_id}",
153
- auth=self.__connector.auth,
154
- json=data,
155
- headers=self.__header,
156
- timeout=30
157
- )
158
-
149
+ response = self._patch(url=f"{self.__base_api}/{asset_id}", data=data)
159
150
  return self._handle_response(response)
160
151
 
161
152
  def update_asset_attribute(self, asset_id: str, attribute_id: str, value):
@@ -182,16 +173,203 @@ class Asset(BaseAPI):
182
173
  "typeId": attribute_id
183
174
  }
184
175
 
185
- # Use PUT method through requests directly since BaseAPI doesn't have _put
186
- import requests
187
- response = requests.put(
188
- f"{self.__base_api}/{asset_id}/attributes",
189
- auth=self.__connector.auth,
190
- json=data,
191
- headers=self.__header,
192
- timeout=30
193
- )
176
+ response = self._put(url=f"{self.__base_api}/{asset_id}/attributes", data=data)
177
+ return self._handle_response(response)
178
+
179
+ def set_asset_attributes(self, asset_id: str, type_id: str = None, type_public_id: str = None, values: list = None):
180
+ """
181
+ Set asset attributes. Replaces all attributes of the asset with the given ID
182
+ (of given attribute type) with the attributes from the request.
183
+ :param asset_id: The ID of the asset.
184
+ :param type_id: The ID of the attribute type for the new attribute.
185
+ :param type_public_id: The public ID of the attribute type for the new attribute.
186
+ :param values: The values for the new attribute (list of objects).
187
+ :return: The response from setting the attributes.
188
+ """
189
+ if not asset_id:
190
+ raise ValueError("asset_id is required")
191
+ if not isinstance(asset_id, str):
192
+ raise ValueError("asset_id must be a string")
193
+
194
+ try:
195
+ uuid.UUID(asset_id)
196
+ except ValueError as exc:
197
+ raise ValueError("asset_id must be a valid UUID") from exc
198
+
199
+ if not values:
200
+ raise ValueError("values is required")
201
+ if not isinstance(values, list):
202
+ raise ValueError("values must be a list")
203
+
204
+ # Validate that either type_id or type_public_id is provided
205
+ if not type_id and not type_public_id:
206
+ raise ValueError("Either type_id or type_public_id must be provided")
207
+
208
+ # Validate type_id if provided
209
+ if type_id:
210
+ if not isinstance(type_id, str):
211
+ raise ValueError("type_id must be a string")
212
+ try:
213
+ uuid.UUID(type_id)
214
+ except ValueError as exc:
215
+ raise ValueError("type_id must be a valid UUID") from exc
216
+
217
+ # Validate type_public_id if provided
218
+ if type_public_id and not isinstance(type_public_id, str):
219
+ raise ValueError("type_public_id must be a string")
220
+
221
+ data = {
222
+ "values": values
223
+ }
224
+
225
+ # Add type_id or type_public_id to the data
226
+ if type_id:
227
+ data["typeId"] = type_id
228
+ if type_public_id:
229
+ data["typePublicId"] = type_public_id
230
+
231
+ response = self._put(url=f"{self.__base_api}/{asset_id}/attributes", data=data)
232
+ return self._handle_response(response)
233
+
234
+ def remove_asset(self, asset_id: str):
235
+ """
236
+ Remove an asset identified by given ID.
237
+ :param asset_id: The ID of the asset to remove.
238
+ :return: The response from removing the asset.
239
+ """
240
+ if not asset_id:
241
+ raise ValueError("asset_id is required")
242
+ if not isinstance(asset_id, str):
243
+ raise ValueError("asset_id must be a string")
244
+
245
+ try:
246
+ uuid.UUID(asset_id)
247
+ except ValueError as exc:
248
+ raise ValueError("asset_id must be a valid UUID") from exc
249
+
250
+ response = self._delete(url=f"{self.__base_api}/{asset_id}")
251
+ return self._handle_response(response)
252
+
253
+ def set_asset_relations(self, asset_id: str, related_asset_ids: list, relation_direction: str,
254
+ type_id: str = None, type_public_id: str = None):
255
+ """
256
+ Set relations for the asset with the given ID. All relations described by this request
257
+ will replace the existing ones (identified with asset as one end, relation type and direction).
258
+ :param asset_id: The ID of the asset.
259
+ :param related_asset_ids: The IDs of the related assets (list of UUIDs).
260
+ :param relation_direction: The relation direction ('TO_TARGET' or 'TO_SOURCE').
261
+ :param type_id: The ID of the relation type for the relations to be set.
262
+ :param type_public_id: The public ID of the relation type for the relations to be set.
263
+ :return: The response from setting the relations.
264
+ """
265
+ if not asset_id:
266
+ raise ValueError("asset_id is required")
267
+ if not isinstance(asset_id, str):
268
+ raise ValueError("asset_id must be a string")
269
+
270
+ try:
271
+ uuid.UUID(asset_id)
272
+ except ValueError as exc:
273
+ raise ValueError("asset_id must be a valid UUID") from exc
274
+
275
+ if not related_asset_ids:
276
+ raise ValueError("related_asset_ids is required")
277
+ if not isinstance(related_asset_ids, list):
278
+ raise ValueError("related_asset_ids must be a list")
279
+
280
+ # Validate all related asset IDs are valid UUIDs
281
+ for i, related_id in enumerate(related_asset_ids):
282
+ if not isinstance(related_id, str):
283
+ raise ValueError(f"related_asset_ids[{i}] must be a string")
284
+ try:
285
+ uuid.UUID(related_id)
286
+ except ValueError as exc:
287
+ raise ValueError(f"related_asset_ids[{i}] must be a valid UUID") from exc
288
+
289
+ if not relation_direction:
290
+ raise ValueError("relation_direction is required")
291
+ if relation_direction not in ["TO_TARGET", "TO_SOURCE"]:
292
+ raise ValueError("relation_direction must be either 'TO_TARGET' or 'TO_SOURCE'")
293
+
294
+ # Validate that either type_id or type_public_id is provided
295
+ if not type_id and not type_public_id:
296
+ raise ValueError("Either type_id or type_public_id must be provided")
297
+
298
+ # Validate type_id if provided
299
+ if type_id:
300
+ if not isinstance(type_id, str):
301
+ raise ValueError("type_id must be a string")
302
+ try:
303
+ uuid.UUID(type_id)
304
+ except ValueError as exc:
305
+ raise ValueError("type_id must be a valid UUID") from exc
306
+
307
+ # Validate type_public_id if provided
308
+ if type_public_id and not isinstance(type_public_id, str):
309
+ raise ValueError("type_public_id must be a string")
310
+
311
+ data = {
312
+ "relatedAssetIds": related_asset_ids,
313
+ "relationDirection": relation_direction
314
+ }
315
+
316
+ # Add type_id or type_public_id to the data
317
+ if type_id:
318
+ data["typeId"] = type_id
319
+ if type_public_id:
320
+ data["typePublicId"] = type_public_id
321
+
322
+ response = self._put(url=f"{self.__base_api}/{asset_id}/relations", data=data)
323
+ return self._handle_response(response)
324
+
325
+ def set_asset_responsibilities(self, asset_id: str, role_id: str, owner_ids: list):
326
+ """
327
+ Set responsibilities for the asset with the given ID.
328
+ :param asset_id: The ID of the asset.
329
+ :param role_id: The ID of the role for the responsibilities to be set.
330
+ :param owner_ids: The IDs of the owners (list of UUIDs). An owner is either user or group.
331
+ :return: The response from setting the responsibilities.
332
+ """
333
+ if not asset_id:
334
+ raise ValueError("asset_id is required")
335
+ if not isinstance(asset_id, str):
336
+ raise ValueError("asset_id must be a string")
337
+
338
+ try:
339
+ uuid.UUID(asset_id)
340
+ except ValueError as exc:
341
+ raise ValueError("asset_id must be a valid UUID") from exc
342
+
343
+ if not role_id:
344
+ raise ValueError("role_id is required")
345
+ if not isinstance(role_id, str):
346
+ raise ValueError("role_id must be a string")
347
+
348
+ try:
349
+ uuid.UUID(role_id)
350
+ except ValueError as exc:
351
+ raise ValueError("role_id must be a valid UUID") from exc
352
+
353
+ if not owner_ids:
354
+ raise ValueError("owner_ids is required")
355
+ if not isinstance(owner_ids, list):
356
+ raise ValueError("owner_ids must be a list")
357
+
358
+ # Validate all owner IDs are valid UUIDs
359
+ for i, owner_id in enumerate(owner_ids):
360
+ if not isinstance(owner_id, str):
361
+ raise ValueError(f"owner_ids[{i}] must be a string")
362
+ try:
363
+ uuid.UUID(owner_id)
364
+ except ValueError as exc:
365
+ raise ValueError(f"owner_ids[{i}] must be a valid UUID") from exc
366
+
367
+ data = {
368
+ "roleId": role_id,
369
+ "ownerIds": owner_ids
370
+ }
194
371
 
372
+ response = self._put(url=f"{self.__base_api}/{asset_id}/responsibilities", data=data)
195
373
  return self._handle_response(response)
196
374
 
197
375
  def find_assets(
@@ -38,7 +38,7 @@ class BaseAPI:
38
38
  timeout=self.__connector.timeout
39
39
  )
40
40
 
41
- def _post(self, url: str, data: dict, headers: dict = None):
41
+ def _post(self, url: str, data: dict, headers: dict = None, params: dict = None):
42
42
  """
43
43
  Makes a POST request to the specified URL with the given data.
44
44
  :param url: The URL to send the POST request to.
@@ -56,6 +56,7 @@ class BaseAPI:
56
56
  auth=self.__connector.auth,
57
57
  json=data,
58
58
  headers=headers,
59
+ params=params,
59
60
  timeout=self.__connector.timeout
60
61
  )
61
62
 
@@ -110,3 +110,183 @@ class Community(BaseAPI):
110
110
 
111
111
  response = self._get(params=params)
112
112
  return self._handle_response(response)
113
+
114
+ def add_community(
115
+ self,
116
+ name: str,
117
+ parent_id: str = None,
118
+ description: str = None,
119
+ community_id: str = None
120
+ ):
121
+ """
122
+ Adds a new community with the given parameters.
123
+ :param name: The name of the new community. Should be unique across all communities
124
+ (required, 1-255 characters).
125
+ :param parent_id: The ID of the parent for the new community (optional UUID).
126
+ :param description: The description of the new community (optional).
127
+ :param community_id: The ID of the new community. Should be unique within all communities
128
+ (optional UUID).
129
+ :return: Details of the created community.
130
+ """
131
+ # Validate required parameters
132
+ if not name:
133
+ raise ValueError("name is required")
134
+ if not isinstance(name, str):
135
+ raise ValueError("name must be a string")
136
+ if len(name.strip()) < 1 or len(name) > 255:
137
+ raise ValueError("name must be between 1 and 255 characters")
138
+
139
+ # Validate parent_id if provided
140
+ if parent_id is not None:
141
+ if not isinstance(parent_id, str):
142
+ raise ValueError("parent_id must be a string")
143
+ try:
144
+ uuid.UUID(parent_id)
145
+ except ValueError as exc:
146
+ raise ValueError("parent_id must be a valid UUID") from exc
147
+
148
+ # Validate description if provided
149
+ if description is not None and not isinstance(description, str):
150
+ raise ValueError("description must be a string")
151
+
152
+ # Validate community_id if provided
153
+ if community_id is not None:
154
+ if not isinstance(community_id, str):
155
+ raise ValueError("community_id must be a string")
156
+ try:
157
+ parsed_uuid = uuid.UUID(community_id)
158
+ # Check if UUID starts with reserved prefix
159
+ if str(parsed_uuid).startswith("00000000-0000-0000-"):
160
+ raise ValueError("community_id cannot start with reserved prefix '00000000-0000-0000-'")
161
+ except ValueError as exc:
162
+ if "reserved prefix" in str(exc):
163
+ raise exc
164
+ raise ValueError("community_id must be a valid UUID") from exc
165
+
166
+ # Build request body - only include provided values
167
+ data = {"name": name.strip()}
168
+
169
+ if parent_id is not None:
170
+ data["parentId"] = parent_id
171
+ if description is not None:
172
+ data["description"] = description
173
+ if community_id is not None:
174
+ data["id"] = community_id
175
+
176
+ response = self._post(url=self.__base_api, data=data)
177
+ return self._handle_response(response)
178
+
179
+ def change_community(
180
+ self,
181
+ community_id: str,
182
+ name: str = None,
183
+ parent_id: str = None,
184
+ description: str = None,
185
+ remove_scope_overlap_on_move: bool = None
186
+ ):
187
+ """
188
+ Changes the community with the information that is present in the request.
189
+ Only properties that are specified in this request and have non-null values are updated.
190
+ :param community_id: The ID of the community to be changed (required UUID).
191
+ :param name: The new name for the community (optional, 1-255 characters).
192
+ :param parent_id: The ID of the new parent community (optional UUID).
193
+ :param description: The new description for the community (optional).
194
+ :param remove_scope_overlap_on_move: Whether scopes assigned to domain community
195
+ and its children should be removed on move if there
196
+ are any inherited scopes in new parent community.
197
+ :return: Details of the updated community.
198
+ """
199
+ # Validate required parameters
200
+ if not community_id:
201
+ raise ValueError("community_id is required")
202
+ if not isinstance(community_id, str):
203
+ raise ValueError("community_id must be a string")
204
+
205
+ try:
206
+ uuid.UUID(community_id)
207
+ except ValueError as exc:
208
+ raise ValueError("community_id must be a valid UUID") from exc
209
+
210
+ # Validate name if provided
211
+ if name is not None:
212
+ if not isinstance(name, str):
213
+ raise ValueError("name must be a string")
214
+ if len(name.strip()) < 1 or len(name) > 255:
215
+ raise ValueError("name must be between 1 and 255 characters")
216
+
217
+ # Validate parent_id if provided
218
+ if parent_id is not None:
219
+ if not isinstance(parent_id, str):
220
+ raise ValueError("parent_id must be a string")
221
+ try:
222
+ uuid.UUID(parent_id)
223
+ except ValueError as exc:
224
+ raise ValueError("parent_id must be a valid UUID") from exc
225
+
226
+ # Validate description if provided
227
+ if description is not None and not isinstance(description, str):
228
+ raise ValueError("description must be a string")
229
+
230
+ # Validate remove_scope_overlap_on_move if provided
231
+ if remove_scope_overlap_on_move is not None and not isinstance(remove_scope_overlap_on_move, bool):
232
+ raise ValueError("remove_scope_overlap_on_move must be a boolean")
233
+
234
+ # Build request body - only include provided values
235
+ # Include the community_id in the body as required by the API
236
+ data = {"id": community_id}
237
+
238
+ if name is not None:
239
+ data["name"] = name.strip()
240
+ if parent_id is not None:
241
+ data["parentId"] = parent_id
242
+ if description is not None:
243
+ data["description"] = description
244
+ if remove_scope_overlap_on_move is not None:
245
+ data["removeScopeOverlapOnMove"] = remove_scope_overlap_on_move
246
+
247
+ response = self._patch(url=f"{self.__base_api}/{community_id}", data=data)
248
+ return self._handle_response(response)
249
+
250
+ def remove_community(self, community_id: str):
251
+ """
252
+ Remove a community by its ID.
253
+
254
+ **DEPRECATED**: This endpoint will be removed in the future.
255
+ Please use POST /communities/removalJobs instead.
256
+
257
+ :param community_id: The ID of the community to remove (required UUID).
258
+ :return: Response from the removal operation.
259
+ """
260
+ # Validate required parameters
261
+ if not community_id:
262
+ raise ValueError("community_id is required")
263
+ if not isinstance(community_id, str):
264
+ raise ValueError("community_id must be a string")
265
+
266
+ try:
267
+ uuid.UUID(community_id)
268
+ except ValueError as exc:
269
+ raise ValueError("community_id must be a valid UUID") from exc
270
+
271
+ response = self._delete(url=f"{self.__base_api}/{community_id}")
272
+ return self._handle_response(response)
273
+
274
+ def change_to_root_community(self, community_id: str):
275
+ """
276
+ Changes the community with given ID to a root community.
277
+ :param community_id: The ID of the community that will be changed to a root community (required UUID).
278
+ :return: Details of the updated community.
279
+ """
280
+ # Validate required parameters
281
+ if not community_id:
282
+ raise ValueError("community_id is required")
283
+ if not isinstance(community_id, str):
284
+ raise ValueError("community_id must be a string")
285
+
286
+ try:
287
+ uuid.UUID(community_id)
288
+ except ValueError as exc:
289
+ raise ValueError("community_id must be a valid UUID") from exc
290
+
291
+ response = self._post(url=f"{self.__base_api}/{community_id}/root", data={})
292
+ return self._handle_response(response)
@@ -112,5 +112,200 @@ class Domain(BaseAPI):
112
112
  if type_public_id is not None:
113
113
  params["typePublicId"] = type_public_id
114
114
 
115
- response = self._get(params=params)
115
+ response = self._get(url=self.__base_api, params=params)
116
+ return self._handle_response(response)
117
+
118
+ def add_domain(
119
+ self,
120
+ name: str,
121
+ community_id: str,
122
+ type_id: str = None,
123
+ description: str = None,
124
+ domain_id: str = None,
125
+ excluded_from_auto_hyperlinking: bool = None,
126
+ type_public_id: str = None
127
+ ):
128
+ """
129
+ Adds a new domain with given data into a community.
130
+ :param name: The name of the new domain. Should be unique within the community
131
+ (required, 1-255 characters).
132
+ :param community_id: The ID of the community that the new domain should be added to (required UUID).
133
+ :param type_id: The ID of the domain type of the new domain (optional UUID).
134
+ :param description: The description of the new domain (optional).
135
+ :param domain_id: The ID of the new domain. Should be unique within all domains (optional UUID).
136
+ :param excluded_from_auto_hyperlinking: Whether to exclude from auto hyperlinking (optional boolean).
137
+ :param type_public_id: The public ID of the domain type of the new domain (optional).
138
+ :return: Details of the created domain.
139
+ """
140
+ # Validate required parameters
141
+ if not name:
142
+ raise ValueError("name is required")
143
+ if not isinstance(name, str):
144
+ raise ValueError("name must be a string")
145
+ if len(name.strip()) < 1 or len(name) > 255:
146
+ raise ValueError("name must be between 1 and 255 characters")
147
+
148
+ if not community_id:
149
+ raise ValueError("community_id is required")
150
+ if not isinstance(community_id, str):
151
+ raise ValueError("community_id must be a string")
152
+ try:
153
+ uuid.UUID(community_id)
154
+ except ValueError as exc:
155
+ raise ValueError("community_id must be a valid UUID") from exc
156
+
157
+ # Validate type_id if provided
158
+ if type_id is not None:
159
+ if not isinstance(type_id, str):
160
+ raise ValueError("type_id must be a string")
161
+ try:
162
+ uuid.UUID(type_id)
163
+ except ValueError as exc:
164
+ raise ValueError("type_id must be a valid UUID") from exc
165
+
166
+ # Validate description if provided
167
+ if description is not None and not isinstance(description, str):
168
+ raise ValueError("description must be a string")
169
+
170
+ # Validate domain_id if provided
171
+ if domain_id is not None:
172
+ if not isinstance(domain_id, str):
173
+ raise ValueError("domain_id must be a string")
174
+ try:
175
+ parsed_uuid = uuid.UUID(domain_id)
176
+ # Check if UUID starts with reserved prefix
177
+ if str(parsed_uuid).startswith("00000000-0000-0000-"):
178
+ raise ValueError("domain_id cannot start with reserved prefix '00000000-0000-0000-'")
179
+ except ValueError as exc:
180
+ if "reserved prefix" in str(exc):
181
+ raise exc
182
+ raise ValueError("domain_id must be a valid UUID") from exc
183
+
184
+ # Validate excluded_from_auto_hyperlinking if provided
185
+ if excluded_from_auto_hyperlinking is not None and not isinstance(excluded_from_auto_hyperlinking, bool):
186
+ raise ValueError("excluded_from_auto_hyperlinking must be a boolean")
187
+
188
+ # Validate type_public_id if provided
189
+ if type_public_id is not None and not isinstance(type_public_id, str):
190
+ raise ValueError("type_public_id must be a string")
191
+
192
+ # Build request body - only include provided values
193
+ data = {
194
+ "name": name.strip(),
195
+ "communityId": community_id
196
+ }
197
+
198
+ if type_id is not None:
199
+ data["typeId"] = type_id
200
+ if description is not None:
201
+ data["description"] = description
202
+ if domain_id is not None:
203
+ data["id"] = domain_id
204
+ if excluded_from_auto_hyperlinking is not None:
205
+ data["excludedFromAutoHyperlinking"] = excluded_from_auto_hyperlinking
206
+ if type_public_id is not None:
207
+ data["typePublicId"] = type_public_id
208
+
209
+ response = self._post(url=self.__base_api, data=data)
210
+ return self._handle_response(response)
211
+
212
+ def remove_domain(self, domain_id: str):
213
+ """
214
+ Remove a domain by its ID.
215
+
216
+ **DEPRECATED**: This endpoint will be removed in the future.
217
+ Please use POST /domains/removalJobs instead.
218
+
219
+ :param domain_id: The ID of the domain to remove (required UUID).
220
+ :return: Response from the removal operation.
221
+ """
222
+ # Validate required parameters
223
+ if not domain_id:
224
+ raise ValueError("domain_id is required")
225
+ if not isinstance(domain_id, str):
226
+ raise ValueError("domain_id must be a string")
227
+
228
+ try:
229
+ uuid.UUID(domain_id)
230
+ except ValueError as exc:
231
+ raise ValueError("domain_id must be a valid UUID") from exc
232
+
233
+ response = self._delete(url=f"{self.__base_api}/{domain_id}")
234
+ return self._handle_response(response)
235
+
236
+ def change_domain(
237
+ self,
238
+ domain_id: str,
239
+ name: str = None,
240
+ description: str = None,
241
+ type_id: str = None,
242
+ type_public_id: str = None,
243
+ excluded_from_auto_hyperlinking: bool = None
244
+ ):
245
+ """
246
+ Changes the domain with the information that is present in the request.
247
+ Only properties that are specified in this request and have non-null values are updated.
248
+ :param domain_id: The ID of the domain to be changed (required UUID).
249
+ :param name: The new name for the domain (optional, 1-255 characters).
250
+ :param description: The new description for the domain (optional).
251
+ :param type_id: The ID of the domain type (optional UUID).
252
+ :param type_public_id: The public ID of the domain type (optional).
253
+ :param excluded_from_auto_hyperlinking: Whether to exclude from auto hyperlinking (optional boolean).
254
+ :return: Details of the updated domain.
255
+ """
256
+ # Validate required parameters
257
+ if not domain_id:
258
+ raise ValueError("domain_id is required")
259
+ if not isinstance(domain_id, str):
260
+ raise ValueError("domain_id must be a string")
261
+
262
+ try:
263
+ uuid.UUID(domain_id)
264
+ except ValueError as exc:
265
+ raise ValueError("domain_id must be a valid UUID") from exc
266
+
267
+ # Validate name if provided
268
+ if name is not None:
269
+ if not isinstance(name, str):
270
+ raise ValueError("name must be a string")
271
+ if len(name.strip()) < 1 or len(name) > 255:
272
+ raise ValueError("name must be between 1 and 255 characters")
273
+
274
+ # Validate description if provided
275
+ if description is not None and not isinstance(description, str):
276
+ raise ValueError("description must be a string")
277
+
278
+ # Validate type_id if provided
279
+ if type_id is not None:
280
+ if not isinstance(type_id, str):
281
+ raise ValueError("type_id must be a string")
282
+ try:
283
+ uuid.UUID(type_id)
284
+ except ValueError as exc:
285
+ raise ValueError("type_id must be a valid UUID") from exc
286
+
287
+ # Validate type_public_id if provided
288
+ if type_public_id is not None and not isinstance(type_public_id, str):
289
+ raise ValueError("type_public_id must be a string")
290
+
291
+ # Validate excluded_from_auto_hyperlinking if provided
292
+ if excluded_from_auto_hyperlinking is not None and not isinstance(excluded_from_auto_hyperlinking, bool):
293
+ raise ValueError("excluded_from_auto_hyperlinking must be a boolean")
294
+
295
+ # Build request body - only include provided values
296
+ # Include the domain_id in the body as required by the API
297
+ data = {"id": domain_id}
298
+
299
+ if name is not None:
300
+ data["name"] = name.strip()
301
+ if description is not None:
302
+ data["description"] = description
303
+ if type_id is not None:
304
+ data["typeId"] = type_id
305
+ if type_public_id is not None:
306
+ data["typePublicId"] = type_public_id
307
+ if excluded_from_auto_hyperlinking is not None:
308
+ data["excludedFromAutoHyperlinking"] = excluded_from_auto_hyperlinking
309
+
310
+ response = self._patch(url=f"{self.__base_api}/{domain_id}", data=data)
116
311
  return self._handle_response(response)
@@ -72,8 +72,8 @@ class Metadata(BaseAPI):
72
72
  for domain in domains_data.get("results", []):
73
73
  metadata["Domain"][domain["name"]] = domain["id"]
74
74
  if (
75
- domains_data.get("offset", 0) + domains_data.get("limit", 0) >=
76
- domains_data.get("total", 0)
75
+ domains_data.get("offset", 0) + domains_data.get("limit", 0)
76
+ >= domains_data.get("total", 0)
77
77
  ):
78
78
  break
79
79
  domains_params["offset"] += domains_params["limit"]
@@ -0,0 +1,48 @@
1
+ import requests
2
+ from typing import Optional, Dict, Any
3
+ from .Base import BaseAPI
4
+
5
+
6
+ class OutputModule(BaseAPI):
7
+ """
8
+ Output Module API endpoints for Collibra DGC.
9
+ """
10
+ def __init__(self, connector):
11
+ super().__init__(connector)
12
+ self.__base_api = connector.api + "/outputModule"
13
+
14
+ def export_json(
15
+ self,
16
+ body: str,
17
+ validation_enabled: bool = False
18
+ ) -> Dict[Any, Any]:
19
+ """
20
+ Exports results in JSON format, returns the results immediately.
21
+
22
+ Performs an Output Module query and exports the returns results immediately in JSON format.
23
+
24
+ Please note that the ViewConfig/TableViewConfig's syntax validation is not executed by default.
25
+ DGC admin console settings may impact the execution of the query (especially in terms of timeout
26
+ and a limit on the number of results).
27
+
28
+ Args:
29
+ view_config (str): The JSON/YAML representation of ViewConfig/TableViewConfig
30
+ that describes the query to be performed.
31
+ validation_enabled (bool): Determines if the ViewConfig's syntax should be validated
32
+ (True) or not (False). Default value is False for backward
33
+ compatibility reasons but it is strongly advised to always
34
+ enable this validation.
35
+
36
+ Returns:
37
+ Dict[Any, Any]: The exported results in JSON format.
38
+
39
+ Raises:
40
+ requests.exceptions.RequestException: If the API request fails.
41
+ """
42
+ endpoint = f"{self.__base_api}/export/json"
43
+
44
+ headers = {
45
+ 'Content-Type': 'application/json'
46
+ }
47
+
48
+ return self._post(url=endpoint, data=body, headers=headers).json()
@@ -0,0 +1,206 @@
1
+ import uuid
2
+ from .Base import BaseAPI
3
+
4
+
5
+ class Relation(BaseAPI):
6
+ def __init__(self, connector):
7
+ super().__init__(connector)
8
+ self.__base_api = connector.api + "/relations"
9
+
10
+ def add_relation(
11
+ self,
12
+ source_id: str,
13
+ target_id: str,
14
+ type_id: str = None,
15
+ starting_date: int = None,
16
+ ending_date: int = None,
17
+ type_public_id: str = None
18
+ ):
19
+ """
20
+ Adds a new relation.
21
+ :param source_id: The ID of the source of the relation (required UUID).
22
+ :param target_id: The ID of the target of the relation (required UUID).
23
+ :param type_id: The ID of the type of the relation (optional UUID).
24
+ :param starting_date: The starting date of the relation (deprecated, int64).
25
+ :param ending_date: The ending date of the relation (deprecated, int64).
26
+ :param type_public_id: The public ID of the type of the relation (optional).
27
+ :return: Details of the created relation.
28
+ """
29
+ # Validate required parameters
30
+ if not source_id:
31
+ raise ValueError("source_id is required")
32
+ if not isinstance(source_id, str):
33
+ raise ValueError("source_id must be a string")
34
+ try:
35
+ uuid.UUID(source_id)
36
+ except ValueError as exc:
37
+ raise ValueError("source_id must be a valid UUID") from exc
38
+
39
+ if not target_id:
40
+ raise ValueError("target_id is required")
41
+ if not isinstance(target_id, str):
42
+ raise ValueError("target_id must be a string")
43
+ try:
44
+ uuid.UUID(target_id)
45
+ except ValueError as exc:
46
+ raise ValueError("target_id must be a valid UUID") from exc
47
+
48
+ # Validate type_id if provided
49
+ if type_id is not None:
50
+ if not isinstance(type_id, str):
51
+ raise ValueError("type_id must be a string")
52
+ try:
53
+ uuid.UUID(type_id)
54
+ except ValueError as exc:
55
+ raise ValueError("type_id must be a valid UUID") from exc
56
+
57
+ # Validate starting_date if provided
58
+ if starting_date is not None:
59
+ if not isinstance(starting_date, int):
60
+ raise ValueError("starting_date must be an integer")
61
+ if starting_date < 0:
62
+ raise ValueError("starting_date must be a positive integer")
63
+
64
+ # Validate ending_date if provided
65
+ if ending_date is not None:
66
+ if not isinstance(ending_date, int):
67
+ raise ValueError("ending_date must be an integer")
68
+ if ending_date < 0:
69
+ raise ValueError("ending_date must be a positive integer")
70
+
71
+ # Validate type_public_id if provided
72
+ if type_public_id is not None and not isinstance(type_public_id, str):
73
+ raise ValueError("type_public_id must be a string")
74
+
75
+ # Build request data - only include provided values
76
+ data = {
77
+ "sourceId": source_id,
78
+ "targetId": target_id
79
+ }
80
+
81
+ if type_id is not None:
82
+ data["typeId"] = type_id
83
+ if starting_date is not None:
84
+ data["startingDate"] = starting_date
85
+ if ending_date is not None:
86
+ data["endingDate"] = ending_date
87
+ if type_public_id is not None:
88
+ data["typePublicId"] = type_public_id
89
+
90
+ response = self._post(url=self.__base_api, data=data)
91
+ return self._handle_response(response)
92
+
93
+ def get_relation(self, relation_id: str):
94
+ """
95
+ Returns a relation identified by given id.
96
+ :param relation_id: The ID of the relation (required UUID).
97
+ :return: Relation details.
98
+ """
99
+ if not relation_id:
100
+ raise ValueError("relation_id is required")
101
+ if not isinstance(relation_id, str):
102
+ raise ValueError("relation_id must be a string")
103
+
104
+ try:
105
+ uuid.UUID(relation_id)
106
+ except ValueError as exc:
107
+ raise ValueError("relation_id must be a valid UUID") from exc
108
+
109
+ response = self._get(url=f"{self.__base_api}/{relation_id}")
110
+ return self._handle_response(response)
111
+
112
+ def remove_relation(self, relation_id: str):
113
+ """
114
+ Removes a relation identified by given id.
115
+ :param relation_id: The ID of the relation to remove (required UUID).
116
+ :return: Response from the removal operation.
117
+ """
118
+ if not relation_id:
119
+ raise ValueError("relation_id is required")
120
+ if not isinstance(relation_id, str):
121
+ raise ValueError("relation_id must be a string")
122
+
123
+ try:
124
+ uuid.UUID(relation_id)
125
+ except ValueError as exc:
126
+ raise ValueError("relation_id must be a valid UUID") from exc
127
+
128
+ response = self._delete(url=f"{self.__base_api}/{relation_id}")
129
+ return self._handle_response(response)
130
+
131
+ def change_relation(
132
+ self,
133
+ relation_id: str,
134
+ source_id: str = None,
135
+ target_id: str = None,
136
+ starting_date: int = None,
137
+ ending_date: int = None
138
+ ):
139
+ """
140
+ Changes the relation with the information that is present in the request.
141
+ Only properties that are specified in this request and have non-null values are updated.
142
+ :param relation_id: The ID of the relation to be changed (required UUID).
143
+ :param source_id: The ID of the new source for the relation (optional UUID).
144
+ :param target_id: The ID of the new target for the relation (optional UUID).
145
+ :param starting_date: The new starting date for the relation (deprecated, int64).
146
+ :param ending_date: The new ending date for the relation (deprecated, int64).
147
+ :return: Details of the updated relation.
148
+ """
149
+ # Validate required parameters
150
+ if not relation_id:
151
+ raise ValueError("relation_id is required")
152
+ if not isinstance(relation_id, str):
153
+ raise ValueError("relation_id must be a string")
154
+
155
+ try:
156
+ uuid.UUID(relation_id)
157
+ except ValueError as exc:
158
+ raise ValueError("relation_id must be a valid UUID") from exc
159
+
160
+ # Validate source_id if provided
161
+ if source_id is not None:
162
+ if not isinstance(source_id, str):
163
+ raise ValueError("source_id must be a string")
164
+ try:
165
+ uuid.UUID(source_id)
166
+ except ValueError as exc:
167
+ raise ValueError("source_id must be a valid UUID") from exc
168
+
169
+ # Validate target_id if provided
170
+ if target_id is not None:
171
+ if not isinstance(target_id, str):
172
+ raise ValueError("target_id must be a string")
173
+ try:
174
+ uuid.UUID(target_id)
175
+ except ValueError as exc:
176
+ raise ValueError("target_id must be a valid UUID") from exc
177
+
178
+ # Validate starting_date if provided
179
+ if starting_date is not None:
180
+ if not isinstance(starting_date, int):
181
+ raise ValueError("starting_date must be an integer")
182
+ if starting_date < 0:
183
+ raise ValueError("starting_date must be a positive integer")
184
+
185
+ # Validate ending_date if provided
186
+ if ending_date is not None:
187
+ if not isinstance(ending_date, int):
188
+ raise ValueError("ending_date must be an integer")
189
+ if ending_date < 0:
190
+ raise ValueError("ending_date must be a positive integer")
191
+
192
+ # Build request body - only include provided values
193
+ # Include the relation_id in the body as required by the API
194
+ data = {"id": relation_id}
195
+
196
+ if source_id is not None:
197
+ data["sourceId"] = source_id
198
+ if target_id is not None:
199
+ data["targetId"] = target_id
200
+ if starting_date is not None:
201
+ data["startingDate"] = starting_date
202
+ if ending_date is not None:
203
+ data["endingDate"] = ending_date
204
+
205
+ response = self._patch(url=f"{self.__base_api}/{relation_id}", data=data)
206
+ return self._handle_response(response)
@@ -113,6 +113,25 @@ class Responsibility(BaseAPI):
113
113
  response = self._get(url=f"{self.__base_api}/{responsibility_id}")
114
114
  return self._handle_response(response)
115
115
 
116
+ def delete_responsibility(self, responsibility_id: str):
117
+ """
118
+ Remove the responsibility identified by the given id.
119
+ :param responsibility_id: The unique identifier of the responsibility.
120
+ :return: None
121
+ """
122
+ if not responsibility_id:
123
+ raise ValueError("responsibility_id is required")
124
+ if not isinstance(responsibility_id, str):
125
+ raise ValueError("responsibility_id must be a string")
126
+
127
+ try:
128
+ uuid.UUID(responsibility_id)
129
+ except ValueError as exc:
130
+ raise ValueError("responsibility_id must be a valid UUID") from exc
131
+
132
+ response = self._delete(url=f"{self.__base_api}/{responsibility_id}")
133
+ self._handle_response(response)
134
+
116
135
  def find_responsibilities(
117
136
  self,
118
137
  count_limit: int = -1,
@@ -0,0 +1,103 @@
1
+ import re
2
+ import logging
3
+ from .Base import BaseAPI
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ class Utils(BaseAPI):
9
+ def __init__(self, connector):
10
+ super().__init__(connector)
11
+ self.__base_api = connector.api
12
+
13
+ def get_uuids(self):
14
+ """
15
+ Retrieves UUIDs of asset types, relation types, responsibilities, statuses, and attributes from Collibra,
16
+ returning a dictionary with names as keys and UUIDs as values. Relation type names are used.
17
+
18
+ Returns:
19
+ A dictionary containing dictionaries of named UUIDs for asset types, relation types,
20
+ responsibilities, statuses, and attributes.
21
+ """
22
+ metadata = {
23
+ "AssetType": {},
24
+ "Relation": {},
25
+ "Responsability": {},
26
+ "Status": {},
27
+ "Attribute": {},
28
+ "Community": {},
29
+ "Domain": {},
30
+ "DomainType": {},
31
+ }
32
+ try:
33
+ # Get Asset Type UUIDs
34
+ asset_types_url = f"{self.__base_api}/assetTypes"
35
+ asset_types_response = self._get(url=asset_types_url)
36
+ asset_types_data = self._handle_response(asset_types_response)
37
+ for asset_type in asset_types_data["results"]:
38
+ metadata["AssetType"][asset_type["name"]] = asset_type["id"]
39
+
40
+ # Get Relation Type UUIDs
41
+ relation_types_url = f"{self.__base_api}/relationTypes"
42
+ relation_types_response = self._get(url=relation_types_url)
43
+ relation_types_data = self._handle_response(relation_types_response)
44
+ for relation_type in relation_types_data["results"]:
45
+ source_name = re.sub(" ", "", relation_type["sourceType"]["name"])
46
+ target_name = re.sub(" ", "", relation_type["targetType"]["name"])
47
+ metadata["Relation"][f"{source_name}_{target_name}"] = relation_type["id"]
48
+
49
+ # Get Roles
50
+ resource_roles_url = f"{self.__base_api}/roles"
51
+ resource_roles_response = self._get(url=resource_roles_url)
52
+ resource_roles_data = self._handle_response(resource_roles_response)
53
+ for resource_role in resource_roles_data["results"]:
54
+ metadata["Responsability"][resource_role["name"]] = resource_role["id"]
55
+
56
+ # Get Status UUIDs
57
+ statuses_url = f"{self.__base_api}/statuses"
58
+ statuses_response = self._get(url=statuses_url)
59
+ statuses_data = self._handle_response(statuses_response)
60
+ for status in statuses_data["results"]:
61
+ metadata["Status"][status["name"]] = status["id"]
62
+
63
+ # Get Attribute UUIDs
64
+ attributes_url = f"{self.__base_api}/attributeTypes"
65
+ attributes_response = self._get(url=attributes_url)
66
+ attributes_data = self._handle_response(attributes_response)
67
+ for attribute in attributes_data["results"]:
68
+ metadata["Attribute"][attribute["name"]] = attribute["id"]
69
+
70
+ # Get Community UUIDs
71
+ communities_url = f"{self.__base_api}/communities"
72
+ communities_response = self._get(url=communities_url)
73
+ communities_data = self._handle_response(communities_response)
74
+ for community in communities_data["results"]:
75
+ metadata["Community"][community["name"]] = community["id"]
76
+
77
+ # Get Domain UUIDs
78
+ domains_url = f"{self.__base_api}/domains"
79
+ domains_params = {"limit": 1000, "offset": 0}
80
+ while True:
81
+ domains_response = self._get(url=domains_url, params=domains_params)
82
+ domains_data = self._handle_response(domains_response)
83
+ for domain in domains_data["results"]:
84
+ metadata["Domain"][domain["name"]] = domain["id"]
85
+ if domains_data.get("offset") + domains_data.get(
86
+ "limit"
87
+ ) >= domains_data.get("total"):
88
+ break
89
+ domains_params["offset"] += domains_params["limit"]
90
+
91
+ # Get Domain Type UUIDs
92
+ domain_types_url = f"{self.__base_api}/domainTypes"
93
+ domain_types_response = self._get(url=domain_types_url)
94
+ domain_types_data = self._handle_response(domain_types_response)
95
+ for domain_type in domain_types_data["results"]:
96
+ metadata["DomainType"][domain_type["name"]] = domain_type["id"]
97
+
98
+ logger.info("Collibra UUIDS fetched successfully")
99
+ return metadata
100
+
101
+ except (KeyError, ValueError, AttributeError) as e:
102
+ logger.error("Error fetching Collibra UUIDs: %s", e)
103
+ return None
@@ -1,12 +1,8 @@
1
1
  from .Asset import Asset
2
2
  from .Base import BaseAPI
3
3
  from .Community import Community
4
- from .Domain import Domain
5
- from .User import User
6
- from .Responsibility import Responsibility
7
- from .Workflow import Workflow
8
- from .Metadata import Metadata
9
4
  from .Comment import Comment
5
+ from .Domain import Domain
10
6
  from .Exceptions import (
11
7
  CollibraAPIError,
12
8
  UnauthorizedError,
@@ -14,3 +10,10 @@ from .Exceptions import (
14
10
  NotFoundError,
15
11
  ServerError
16
12
  )
13
+ from .Metadata import Metadata
14
+ from .OutputModule import OutputModule
15
+ from .Relation import Relation
16
+ from .Responsibility import Responsibility
17
+ from .User import User
18
+ from .Utils import Utils
19
+ from .Workflow import Workflow
@@ -10,7 +10,8 @@ from .api import (
10
10
  Responsibility,
11
11
  Workflow,
12
12
  Metadata,
13
- Comment
13
+ Comment,
14
+ Relation
14
15
  )
15
16
 
16
17
 
@@ -42,6 +43,7 @@ class CollibraConnector():
42
43
  self.workflow = Workflow(self)
43
44
  self.metadata = Metadata(self)
44
45
  self.comment = Comment(self)
46
+ self.relation = Relation(self)
45
47
 
46
48
  logging.basicConfig(level=logging.INFO)
47
49
  self.logger = logging.getLogger(__name__)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: collibra-connector
3
- Version: 1.0.10b0
3
+ Version: 1.0.11
4
4
  Summary: An UNOFFICIAL standard Python connector for the Collibra Data Governance Center API.
5
5
  Author-email: Raül Dalgamonni <rauldalgamonnialonso@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/rauldaal/collibra-python-connector
@@ -0,0 +1,21 @@
1
+ collibra_connector/__init__.py,sha256=vzF9epFfmdnFjOcr_qZhvfiN9Z0cIBB48xldnvIiKjo,1694
2
+ collibra_connector/connector.py,sha256=oNN8WCI1TI6ffSEfl6_SX7SdrznwA5UeN7U13m-O_hU,2224
3
+ collibra_connector/api/Asset.py,sha256=aPhnsRxzdGGFtaytpLueDgMrELMpIs4cuOJoc2DR-p4,17927
4
+ collibra_connector/api/Base.py,sha256=2gHpieP8N_PK-R63zUv38Wpdfqdw9tHcH59hlXUZOWM,5929
5
+ collibra_connector/api/Comment.py,sha256=4ZzXiKj3pDOw_JRhfrUj-gcF8nZVdW_z7X2jJnq7LcU,4994
6
+ collibra_connector/api/Community.py,sha256=kM4j0LGYCXQygOZ4zHuu10JzJzvXtbUDz9litzKeKUI,12132
7
+ collibra_connector/api/Domain.py,sha256=U0i103g1WceCn9qAFUWDLgeXrtOvfBqX3vMCn4sQqRE,13012
8
+ collibra_connector/api/Exceptions.py,sha256=lX8yU3BnlOiWWifIBKlZdXVZaTI5pvtbrffyDtEUtoc,429
9
+ collibra_connector/api/Metadata.py,sha256=4uzb4vnZuMOg_ISCEnCfmAEVbsIcuD4FvupDlcxrfT4,5213
10
+ collibra_connector/api/OutputModule.py,sha256=97TzIbeoC5V63x5fi5fEcGfviUydwKwJC3NobLuNF5w,1791
11
+ collibra_connector/api/Relation.py,sha256=Hw0PlAQrU2Zv-RAbvHZhZpZb2_m6uDWngx2ySwo_mv0,8079
12
+ collibra_connector/api/Responsibility.py,sha256=PJu1Pe1u8mKzgpGGjzHtZNfRTfpgWWbaQn5bSifZOMo,14123
13
+ collibra_connector/api/User.py,sha256=nPl8q6l-H8hq2sR7gLelXq09jl3QRWFzI_WU3OUIHhQ,7755
14
+ collibra_connector/api/Utils.py,sha256=F8ZWrW8urzDX8QfiL16kWRnBYGRiLqeKUbYXlQpXNiM,4674
15
+ collibra_connector/api/Workflow.py,sha256=rsD9mPut69eEy35TNGf2mUKTrgWUmoakgnsXa85XzNY,10417
16
+ collibra_connector/api/__init__.py,sha256=yBJcKzcRraE-UYbBviisbdvQ1asFBnud5Vo1oJcCv_w,491
17
+ collibra_connector-1.0.11.dist-info/licenses/LICENSE,sha256=6KmWWtAu_q58gerPlrnkgsmGM2l8j6Wc_VL0y4S_ip4,1079
18
+ collibra_connector-1.0.11.dist-info/METADATA,sha256=HXkSXZVVmm1z57evuwsuWoOzFI6lSjrVuhd2hZKSHEU,4081
19
+ collibra_connector-1.0.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
+ collibra_connector-1.0.11.dist-info/top_level.txt,sha256=Vs-kR64zf__ebL2j3_AEx7rhO6xkwgmHUFRzxlQPgTQ,19
21
+ collibra_connector-1.0.11.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- collibra_connector/__init__.py,sha256=vzF9epFfmdnFjOcr_qZhvfiN9Z0cIBB48xldnvIiKjo,1694
2
- collibra_connector/connector.py,sha256=VjoFk0Jouz6vEhP74_9MJv6f8EQxKg_durmkbDLKxbI,2171
3
- collibra_connector/api/Asset.py,sha256=JJkImmgxYN3zGacSlnXegVnO2xHZqUhTiOyjhQFnvEU,10359
4
- collibra_connector/api/Base.py,sha256=8sYTPrIlHZP8smfYKl2rWSL63aKqHrtFca_M6gEOVns,5881
5
- collibra_connector/api/Comment.py,sha256=4ZzXiKj3pDOw_JRhfrUj-gcF8nZVdW_z7X2jJnq7LcU,4994
6
- collibra_connector/api/Community.py,sha256=yEDbYEmEi7ZhVsodTtCTi2DpEa-NOojgsOK3ihn7wfY,4463
7
- collibra_connector/api/Domain.py,sha256=CXDVheIcJ_7cU_h_gOozAg2nliFoAEMoeprZRIba_i8,4605
8
- collibra_connector/api/Exceptions.py,sha256=lX8yU3BnlOiWWifIBKlZdXVZaTI5pvtbrffyDtEUtoc,429
9
- collibra_connector/api/Metadata.py,sha256=lOVXu0pcHSZNhInb4nR4q7lBB1MwtRuoL4Qb-2sr8aI,5213
10
- collibra_connector/api/Responsibility.py,sha256=s9W2oa11CS2_CEDYrgdjpHOjX83ysXnQ9CNDUFlzGTQ,13372
11
- collibra_connector/api/User.py,sha256=nPl8q6l-H8hq2sR7gLelXq09jl3QRWFzI_WU3OUIHhQ,7755
12
- collibra_connector/api/Workflow.py,sha256=rsD9mPut69eEy35TNGf2mUKTrgWUmoakgnsXa85XzNY,10417
13
- collibra_connector/api/__init__.py,sha256=61ZNTfMTMRMPCy8WABptw152RriLZ_B1HV455vipFDk,396
14
- collibra_connector-1.0.10b0.dist-info/licenses/LICENSE,sha256=6KmWWtAu_q58gerPlrnkgsmGM2l8j6Wc_VL0y4S_ip4,1079
15
- collibra_connector-1.0.10b0.dist-info/METADATA,sha256=jIfBXEZlypct5cyb9E1H878_-MhF9gbxesqoOQTEPUQ,4083
16
- collibra_connector-1.0.10b0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
- collibra_connector-1.0.10b0.dist-info/top_level.txt,sha256=Vs-kR64zf__ebL2j3_AEx7rhO6xkwgmHUFRzxlQPgTQ,19
18
- collibra_connector-1.0.10b0.dist-info/RECORD,,