datasourcelib 0.1.10__tar.gz → 0.1.12__tar.gz

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.
Files changed (44) hide show
  1. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/PKG-INFO +1 -1
  2. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/setup.py +1 -1
  3. datasourcelib-0.1.12/src/datasourcelib/datasources/azure_devops_source.py +402 -0
  4. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib.egg-info/PKG-INFO +1 -1
  5. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib.egg-info/SOURCES.txt +1 -0
  6. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/LICENSE +0 -0
  7. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/MANIFEST.in +0 -0
  8. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/README.md +0 -0
  9. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/pyproject.toml +0 -0
  10. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/setup.cfg +0 -0
  11. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/__init__.py +0 -0
  12. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/core/__init__.py +0 -0
  13. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/core/sync_base.py +0 -0
  14. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/core/sync_manager.py +0 -0
  15. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/core/sync_types.py +0 -0
  16. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/datasources/__init__.py +0 -0
  17. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/datasources/azure_devops_source copy.py +0 -0
  18. /datasourcelib-0.1.10/src/datasourcelib/datasources/azure_devops_source.py → /datasourcelib-0.1.12/src/datasourcelib/datasources/azure_devops_source10dec.py +0 -0
  19. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/datasources/blob_source.py +0 -0
  20. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/datasources/datasource_base.py +0 -0
  21. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/datasources/datasource_types.py +0 -0
  22. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/datasources/dataverse_source.py +0 -0
  23. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/datasources/sharepoint_source - Copy.py +0 -0
  24. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/datasources/sharepoint_source.py +0 -0
  25. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/datasources/sql_source.py +0 -0
  26. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/datasources/sql_source_bkup.py +0 -0
  27. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/indexes/__init__.py +0 -0
  28. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/indexes/azure_search_index.py +0 -0
  29. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/strategies/__init__.py +0 -0
  30. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/strategies/daily_load.py +0 -0
  31. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/strategies/full_load.py +0 -0
  32. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/strategies/incremental_load.py +0 -0
  33. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/strategies/ondemand_load.py +0 -0
  34. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/strategies/timerange_load.py +0 -0
  35. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/utils/__init__.py +0 -0
  36. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/utils/aggregation.py +0 -0
  37. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/utils/byte_reader.py +0 -0
  38. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/utils/exceptions.py +0 -0
  39. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/utils/file_reader.py +0 -0
  40. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/utils/logger.py +0 -0
  41. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib/utils/validators.py +0 -0
  42. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib.egg-info/dependency_links.txt +0 -0
  43. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib.egg-info/requires.txt +0 -0
  44. {datasourcelib-0.1.10 → datasourcelib-0.1.12}/src/datasourcelib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datasourcelib
3
- Version: 0.1.10
3
+ Version: 0.1.12
4
4
  Summary: Data source sync strategies for vector DBs
5
5
  Home-page: https://github.com/akashmaurya0217/datasourcelib
6
6
  Author: Akash Kumar Maurya
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="datasourcelib",
5
- version="0.1.10",
5
+ version="0.1.12",
6
6
  packages=find_packages(where="src", exclude=["tests.*", "tests", "examples.*", "examples"]),
7
7
  package_dir={"": "src"},
8
8
  install_requires=[
@@ -0,0 +1,402 @@
1
+ from typing import Any, Dict, List, Optional
2
+ from datasourcelib.datasources.datasource_base import DataSourceBase
3
+ from datasourcelib.utils.logger import get_logger
4
+ from datasourcelib.utils.validators import require_keys
5
+ import base64
6
+ import json
7
+ from bs4 import BeautifulSoup
8
+ import regex as re
9
+
10
+ logger = get_logger(__name__)
11
+
12
+ try:
13
+ import requests # type: ignore
14
+ except Exception:
15
+ requests = None # lazy import handled at runtime
16
+
17
+ class AzureDevOpsSource(DataSourceBase):
18
+
19
+ def validate_config(self) -> bool:
20
+ try:
21
+ require_keys(self.config, ["ado_organization", "ado_personal_access_token"])
22
+ return True
23
+ except Exception as ex:
24
+ logger.error("AzureDevOpsSource.validate_config: %s", ex)
25
+ return False
26
+
27
+ def connect(self) -> bool:
28
+ if requests is None:
29
+ raise RuntimeError("requests package is required for AzureDevOpsSource")
30
+ # No persistent connection; store auth header
31
+ pat = self.config.get("ado_personal_access_token")
32
+ token = pat
33
+ token_b64 = base64.b64encode(token.encode("utf-8")).decode("utf-8")
34
+ self._headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
35
+ self._connected = True
36
+ logger.info("AzureDevOpsSource ready (no persistent connection required)")
37
+ return True
38
+
39
+ @staticmethod
40
+ def sanitize(s: str) -> str:
41
+ """Keep only A-Z a-z 0-9 underscore/dash/equals in a safe way."""
42
+ # using the `regex` import already present as `re`
43
+ return re.sub(r'[^A-Za-z0-9_\-=]', '', s)
44
+
45
+ def disconnect(self) -> None:
46
+ self._headers = {}
47
+ self._connected = False
48
+ logger.info("AzureDevOpsSource cleared")
49
+
50
+ def fetch_query_data(self, query: Optional[str] = None, **kwargs) -> List[Dict[str, Any]]:
51
+ if requests is None:
52
+ raise RuntimeError("requests package is required for AzureDevOpsSource")
53
+ if not getattr(self, "_connected", False):
54
+ self.connect()
55
+
56
+ org = self.config.get("ado_organization")
57
+ project = self.config.get("ado_project")
58
+ query_id = self.config.get("ado_query_id")
59
+ api_version = self.config.get("api_version", "7.1")
60
+ if not query_id:
61
+ raise ValueError("AzureDevOpsSource.fetch_data requires 'query_id' or query argument")
62
+
63
+ base = f"https://dev.azure.com/{org}/"
64
+ if project:
65
+ base = f"{base}{project}/"
66
+ # WIQL query by id (returns list of work item refs)
67
+ wiql_url = f"{base}_apis/wit/wiql/{query_id}"
68
+ params = {"api-version": api_version}
69
+ method = self.config.get("method", "GET").upper()
70
+ query_response = requests.request(method, wiql_url, headers=getattr(self, "_headers", {}), params=params)
71
+ query_response.raise_for_status()
72
+
73
+ if query_response.status_code != 200:
74
+ raise RuntimeError(f"Error: {query_response.status_code}")
75
+
76
+ work_items_refs = query_response.json().get('workItems', []) or []
77
+ if not work_items_refs:
78
+ return []
79
+
80
+ # collect ids and fetch details in batch to get all fields for all work item types
81
+ ids = [str(item.get('id')) for item in work_items_refs if item.get('id')]
82
+ if not ids:
83
+ return []
84
+
85
+ details_url = f"https://dev.azure.com/{org}/{project}/_apis/wit/workitems"
86
+ # expand=all to include fields, relations, and attachments
87
+ params = {
88
+ "ids": ",".join(ids),
89
+ "api-version": api_version,
90
+ "$expand": "all"
91
+ }
92
+ details_resp = requests.get(details_url, headers=getattr(self, "_headers", {}), params=params)
93
+ details_resp.raise_for_status()
94
+ items = details_resp.json().get("value", [])
95
+
96
+ work_item_details: List[Dict[str, Any]] = []
97
+ for item in items:
98
+ item_id = item.get("id")
99
+ fields = item.get("fields", {}) or {}
100
+
101
+ # Normalize field keys to safe snake_case-like keys
102
+ norm_fields: Dict[str, Any] = {}
103
+ for k, v in fields.items():
104
+ nk = k.replace(".", "_")
105
+ nk = nk.lower()
106
+ norm_fields[nk] = v
107
+
108
+ # Helper to safely extract nested displayName for assigned to
109
+ assigned = norm_fields.get("system_assignedto")
110
+ if isinstance(assigned, dict):
111
+ assigned_to = assigned.get("displayName") or assigned.get("uniqueName") or str(assigned)
112
+ else:
113
+ assigned_to = assigned
114
+
115
+ # find a description-like field (some types use different field names)
116
+ desc = ""
117
+ for fk in ["system_description", "microsoft_vsts_createdby", "html_description"]:
118
+ if fk in norm_fields:
119
+ desc = norm_fields.get(fk) or ""
120
+ break
121
+ if not desc:
122
+ # fallback: first field key that contains 'description'
123
+ for kf, vf in norm_fields.items():
124
+ if "description" in kf and vf:
125
+ desc = vf
126
+ break
127
+
128
+ # clean HTML description to text
129
+ try:
130
+ c_desc = BeautifulSoup(desc or "", "html.parser").get_text()
131
+ except Exception:
132
+ c_desc = desc or ""
133
+
134
+ # Build common convenience values (use available fields)
135
+ wi_type = norm_fields.get("system_workitemtype") or norm_fields.get("system_witype") or ""
136
+ title = norm_fields.get("system_title") or ""
137
+ status = norm_fields.get("system_state") or ""
138
+ created = norm_fields.get("system_createddate") or norm_fields.get("system_created") or ""
139
+ changed = norm_fields.get("system_changeddate") or norm_fields.get("system_changed") or ""
140
+ tags = norm_fields.get("system_tags", "")
141
+ project_name = norm_fields.get("custom.projectname") or norm_fields.get("system_teamproject") or ""
142
+
143
+ rtype = norm_fields.get("custom.releasetype") or norm_fields.get("custom_releasetype") or ""
144
+ target_date = norm_fields.get("microsoft_vsts_scheduling_targetdate") or norm_fields.get("microsoft.vsts.scheduling.targetdate") or ""
145
+
146
+ # Construct a 'full' description string using available pieces
147
+ parts = []
148
+ if wi_type:
149
+ parts.append(f"{wi_type} ID {item_id}")
150
+ else:
151
+ parts.append(f"WorkItem {item_id}")
152
+ if created:
153
+ parts.append(f"was created on {created}")
154
+ if title:
155
+ parts.append(f"and has Title '{title}'")
156
+ if status:
157
+ parts.append(f"is currently in {status} state")
158
+ if assigned_to:
159
+ parts.append(f"is assigned to {assigned_to}")
160
+ if project_name:
161
+ parts.append(f"for Project '{project_name}'")
162
+ if rtype:
163
+ parts.append(f"release type '{rtype}'")
164
+ if target_date:
165
+ parts.append(f"with target date '{target_date}'")
166
+ if tags:
167
+ parts.append(f"Tags: {tags}")
168
+ if c_desc:
169
+ parts.append(f"Description: [{c_desc}]")
170
+ fullfeature = ". ".join(parts)
171
+
172
+ # include all normalized fields in the returned object for completeness
173
+ entry = {
174
+ "id": item_id,
175
+ "type": wi_type,
176
+ "title": title,
177
+ "status": status,
178
+ "assigned_to": assigned_to,
179
+ "created": created,
180
+ "changed_date": changed,
181
+ "tags": tags,
182
+ "project": project_name,
183
+ "release_type": rtype,
184
+ "target_date": target_date,
185
+ "description": c_desc,
186
+ "full": fullfeature
187
+ }
188
+ work_item_details.append(entry)
189
+
190
+ return work_item_details
191
+
192
+ def fetch_wiki_data(self, wiki_name: Optional[str] = None, max_depth: int = 3, **kwargs) -> List[Dict[str, Any]]:
193
+ """
194
+ Crawl wiki pages in the configured Azure DevOps organization/project and return a list of
195
+ dicts: {"display_name": str, "url": str, "content": str, "wiki": str, "project": str}.
196
+ - wiki_name: optional filter to select a single wiki by name
197
+ - max_depth: how many child levels to traverse (>=1)
198
+ - If ado_project is configured, only fetch wikis from that project.
199
+ - Otherwise, fetch wikis from all projects in the organization.
200
+ """
201
+ if requests is None:
202
+ raise RuntimeError("requests package is required for AzureDevOpsSource")
203
+ if not getattr(self, "_connected", False):
204
+ self.connect()
205
+
206
+ org = self.config.get("ado_organization")
207
+ configured_project = self.config.get("ado_project") # Rename to avoid overwriting in loop
208
+ api_version = self.config.get("api_version", "7.1")
209
+ headers = getattr(self, "_headers", {})
210
+
211
+ results: List[Dict[str, Any]] = []
212
+ seen_paths = set()
213
+
214
+ # Determine which projects to process
215
+ projects_to_process = []
216
+ if configured_project:
217
+ # Use only the configured project
218
+ projects_to_process = [configured_project]
219
+ logger.info("fetch_wiki_data: Using configured project: %s", configured_project)
220
+ else:
221
+ # Fetch all projects in the organization
222
+ try:
223
+ projects_url = f"https://dev.azure.com/{org}/_apis/projects?api-version={api_version}"
224
+ proj_resp = requests.get(projects_url, headers=headers, timeout=30)
225
+ proj_resp.raise_for_status()
226
+ proj_json = proj_resp.json()
227
+ projects_list = proj_json.get("value", [])
228
+ projects_to_process = [p.get("name") or p.get("id") for p in projects_list if p.get("name") or p.get("id")]
229
+ logger.info("fetch_wiki_data: Found %d projects in organization", len(projects_to_process))
230
+ except Exception as ex:
231
+ logger.exception("Failed to list projects in organization: %s", ex)
232
+ return []
233
+
234
+ # Process each project
235
+ for project_name in projects_to_process:
236
+ logger.info("fetch_wiki_data: Processing project: %s", project_name)
237
+
238
+ # 1) List wikis in this project
239
+ wikis_url = f"https://dev.azure.com/{org}/{project_name}/_apis/wiki/wikis?api-version={api_version}"
240
+ try:
241
+ resp = requests.get(wikis_url, headers=headers, timeout=30)
242
+ resp.raise_for_status()
243
+ wikis_json = resp.json()
244
+ wikis = wikis_json.get("value", []) if isinstance(wikis_json, dict) else []
245
+ except Exception as ex:
246
+ logger.warning("Failed to list wikis for project %s: %s", project_name, ex)
247
+ continue
248
+
249
+ # Filter selected wikis by name if specified
250
+ selected_wikis = []
251
+ for w in wikis:
252
+ name = w.get("name") or w.get("wikiName") or ""
253
+ if wiki_name:
254
+ if name.lower() == wiki_name.lower():
255
+ selected_wikis.append(w)
256
+ else:
257
+ # Include all wikis for this project
258
+ selected_wikis.append(w)
259
+
260
+ if not selected_wikis:
261
+ logger.debug("No wikis found in project %s matching filter (wiki_name=%s)", project_name, wiki_name)
262
+ continue
263
+
264
+ # 2) Crawl pages in each wiki
265
+ for wiki in selected_wikis:
266
+ wiki_id = wiki.get("id") or wiki.get("name")
267
+ wiki_display = wiki.get("name") or wiki.get("wikiName") or str(wiki_id)
268
+ logger.info("fetch_wiki_data: Crawling wiki '%s' in project '%s'", wiki_display, project_name)
269
+
270
+ # BFS queue of (path, depth). Start at root path "/"
271
+ queue = [("/", 1)]
272
+
273
+ while queue:
274
+ path, depth = queue.pop(0)
275
+ if depth > max_depth:
276
+ continue
277
+
278
+ # Pages listing for this path with recursionLevel=1 to get direct children
279
+ pages_url = (
280
+ f"https://dev.azure.com/{org}/{project_name}/_apis/wiki/wikis/{wiki_id}/pages"
281
+ f"?path={path}&recursionLevel=1&api-version={api_version}"
282
+ )
283
+ try:
284
+ p_resp = requests.get(pages_url, headers=headers, timeout=30)
285
+ p_resp.raise_for_status()
286
+ p_json = p_resp.json()
287
+ pages = p_json.get("value") or p_json.get("subPages") or []
288
+ except Exception as ex:
289
+ logger.warning("Failed to list pages for wiki %s path %s in project %s: %s",
290
+ wiki_display, path, project_name, ex)
291
+ pages = []
292
+
293
+ for page in pages:
294
+ page_path = page.get("path") or "/"
295
+ # Dedupe by wiki id + project + path
296
+ key = f"{project_name}:{wiki_id}:{page_path}"
297
+ if key in seen_paths:
298
+ continue
299
+ seen_paths.add(key)
300
+
301
+ # Display name and url
302
+ display_name = page.get("name") or page.get("pageName") or page_path.strip("/") or "/"
303
+ new_display_name = self.sanitize(display_name.replace(" ", "_").strip()),
304
+ url = (
305
+ page.get("remoteUrl")
306
+ or page.get("url")
307
+ or (page.get("_links") or {}).get("web", {}).get("href")
308
+ or ""
309
+ )
310
+
311
+ # Fetch page content (includeContent)
312
+ content_text = ""
313
+ try:
314
+ content_url = (
315
+ f"https://dev.azure.com/{org}/{project_name}/_apis/wiki/wikis/{wiki_id}/pages"
316
+ f"?path={page_path}&includeContent=true&api-version={api_version}"
317
+ )
318
+ c_resp = requests.get(content_url, headers=headers, timeout=30)
319
+ c_resp.raise_for_status()
320
+ c_json = c_resp.json()
321
+
322
+ # Page content may be in several places depending on API version
323
+ if isinstance(c_json, dict):
324
+ # If API returns page object
325
+ content_text = (
326
+ c_json.get("content")
327
+ or (c_json.get("value", [{}])[0].get("content", "") if c_json.get("value") else "")
328
+ or c_json.get("text", "")
329
+ )
330
+ else:
331
+ # Fallback to raw bytes
332
+ content_text = c_resp.content.decode("utf-8", errors="ignore")
333
+ except Exception as fetch_ex:
334
+ logger.debug("Failed to fetch content for page %s: %s", display_name, fetch_ex)
335
+ # Best-effort fallback: try to GET the web url (may return HTML)
336
+ if url:
337
+ try:
338
+ w_resp = requests.get(url, headers=headers, timeout=30)
339
+ w_resp.raise_for_status()
340
+ content_text = w_resp.content.decode("utf-8", errors="ignore")
341
+ except Exception:
342
+ content_text = ""
343
+ # Construct a 'full' description string using available pieces
344
+ content_text = BeautifulSoup(content_text or "", "html.parser").get_text(),
345
+ parts = []
346
+ if new_display_name:
347
+ parts.append(f"Wiki Page Name is {display_name}. Page has information about {display_name}")
348
+ if project_name:
349
+ parts.append(f"This page is documented by for Project '{project_name}' and by the team '{project_name}'")
350
+ if url:
351
+ parts.append(f"The devops wiki page (url) link to access this page is {url}")
352
+ if project_name:
353
+ parts.append(f"These wiki page content refers sharepoint site links and other documents from sharepoint. So to get full detailed steps or contents you need to refer those links with appropriate permissions. This page contents are available on wiki are [{content_text}].")
354
+
355
+ index_content = ". ".join(parts)
356
+ results.append({
357
+ "display_name": new_display_name,
358
+ "url": url,
359
+ "content": index_content,
360
+ "project": project_name
361
+ })
362
+
363
+ # Enqueue child pages
364
+ if depth < max_depth:
365
+ # If page has children field, use it
366
+ children = page.get("children") or []
367
+ if children:
368
+ for ch in children:
369
+ ch_path = ch.get("path") or ch
370
+ queue.append((ch_path, depth + 1))
371
+ else:
372
+ # Fallback: attempt to list sub-path under current page path
373
+ sub_path = page_path.rstrip("/") + "/"
374
+ queue.append((sub_path, depth + 1))
375
+
376
+ logger.info("fetch_wiki_data completed: Retrieved %d wiki pages", len(results))
377
+ return results
378
+
379
+ def fetch_data(self, query: Optional[str] = None, **kwargs) -> List[Dict[str, Any]]:
380
+ """
381
+ Dispatch fetch call to either wiki downloader or WIQL/query fetcher.
382
+
383
+ Priority:
384
+ 1. kwargs['ado_download_wiki'] if provided
385
+ 2. self.config['ado_download_wiki'] otherwise
386
+
387
+ Accepts same params as fetch_query_data / fetch_wiki_data and returns their output.
388
+ """
389
+ # Determine flag from kwargs first, then config
390
+ download_flag = kwargs.pop("ado_download_wiki", None)
391
+ if download_flag is None:
392
+ download_flag = self.config.get("ado_download_wiki", False)
393
+
394
+ # normalize boolean-like strings
395
+ if isinstance(download_flag, str):
396
+ download_flag = download_flag.strip().lower() in ("1", "true", "yes", "y", "on")
397
+
398
+ if download_flag:
399
+ # pass query as wiki_name if caller intended, otherwise kwargs forwarded
400
+ return self.fetch_wiki_data(wiki_name=query, **kwargs)
401
+ else:
402
+ return self.fetch_query_data(query=query, **kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datasourcelib
3
- Version: 0.1.10
3
+ Version: 0.1.12
4
4
  Summary: Data source sync strategies for vector DBs
5
5
  Home-page: https://github.com/akashmaurya0217/datasourcelib
6
6
  Author: Akash Kumar Maurya
@@ -16,6 +16,7 @@ src/datasourcelib/core/sync_types.py
16
16
  src/datasourcelib/datasources/__init__.py
17
17
  src/datasourcelib/datasources/azure_devops_source copy.py
18
18
  src/datasourcelib/datasources/azure_devops_source.py
19
+ src/datasourcelib/datasources/azure_devops_source10dec.py
19
20
  src/datasourcelib/datasources/blob_source.py
20
21
  src/datasourcelib/datasources/datasource_base.py
21
22
  src/datasourcelib/datasources/datasource_types.py
File without changes
File without changes
File without changes