abs-integration-core 0.1.0__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.
@@ -0,0 +1,176 @@
1
+ Metadata-Version: 2.3
2
+ Name: abs-integration-core
3
+ Version: 0.1.0
4
+ Summary: Core utilities for building OAuth-based integrations in FastAPI applications
5
+ License: MIT
6
+ Author: AutoBridgeSystems
7
+ Author-email: info@autobridgesystems.com
8
+ Requires-Python: >=3.13,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Requires-Dist: abs-exception-core (>=0.1.2,<0.2.0)
13
+ Requires-Dist: abs-repository-core (>=0.2.2,<0.3.0)
14
+ Requires-Dist: fastapi (>=0.95.0,<2.0.0)
15
+ Requires-Dist: pydantic (>=2.0.0,<3.0.0)
16
+ Requires-Dist: sqlalchemy (>=2.0.0,<3.0.0)
17
+ Description-Content-Type: text/markdown
18
+
19
+ # Integration Core
20
+
21
+ A core library for building and managing OAuth-based third-party integrations in FastAPI applications.
22
+
23
+ ## Overview
24
+
25
+ `abs-integration-core` provides a set of reusable components to simplify the implementation of OAuth-based integrations with third-party services. It includes models, schemas, repositories, and a base service class that can be extended for specific integration providers.
26
+
27
+ ## Features
28
+
29
+ - **Integration Model**: SQLAlchemy model for storing OAuth tokens and integration metadata
30
+ - **Standard Schemas**: Pydantic models for integration data validation and serialization
31
+ - **Repository Layer**: Data access layer for CRUD operations on integration records
32
+ - **Base Service**: Abstract base class for implementing integration services for different providers
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install abs-integration-core
38
+ ```
39
+
40
+ ## Dependencies
41
+
42
+ This package depends on:
43
+
44
+ - `fastapi`: For API routing and endpoint handling
45
+ - `sqlalchemy`: For database ORM functionality
46
+ - `pydantic`: For data validation
47
+ - `abs-exception-core`: For standardized exception handling
48
+ - `abs-repository-core`: For base repository pattern implementation
49
+ - `abs-auth-rbac-core`: For authentication and base models
50
+
51
+ ## Usage
52
+
53
+ ### Models
54
+
55
+ The core model represents an OAuth integration with a third-party provider:
56
+
57
+ ```python
58
+ from abs_integration_core.models import Integration
59
+
60
+ # The Integration model includes:
61
+ # - provider_name: String(255)
62
+ # - access_token: Text
63
+ # - refresh_token: Text
64
+ # - expires_in: Integer
65
+ ```
66
+
67
+ ### Schemas
68
+
69
+ Various Pydantic schemas are available for request/response handling:
70
+
71
+ ```python
72
+ from abs_integration_core import (
73
+ TokenData,
74
+ IsConnectedResponse,
75
+ CreateIntegration,
76
+ UpdateIntegration,
77
+ ResponseSchema
78
+ )
79
+
80
+ # Example: Create a standard API response
81
+ response = ResponseSchema(
82
+ status=200,
83
+ message="Integration created successfully",
84
+ data=IsConnectedResponse(provider="sharepoint", connected=True)
85
+ )
86
+ ```
87
+
88
+ ### Repository
89
+
90
+ The `IntegrationRepository` provides data access methods:
91
+
92
+ ```python
93
+ from abs_integration_core import IntegrationRepository
94
+
95
+ # Initialize repository with a database session factory
96
+ repo = IntegrationRepository(db_session)
97
+
98
+ # Available methods:
99
+ # - create_integration(integration_data)
100
+ # - update_integration(integration_id, update_data)
101
+ # - get_by_provider(provider_name)
102
+ # - get_all()
103
+ # - delete_by_provider(provider_name)
104
+ # - refresh_token(provider_name, new_access_token, new_refresh_token, new_expires_in)
105
+ ```
106
+
107
+ ### Base Service
108
+
109
+ Extend the `IntegrationBaseService` to implement provider-specific integration services:
110
+
111
+ ```python
112
+ from abs_integration_core import IntegrationBaseService
113
+
114
+ class SharepointIntegrationService(IntegrationBaseService):
115
+ def __init__(self, provider_name, integration_repository):
116
+ super().__init__(provider_name, integration_repository)
117
+
118
+ def get_auth_url(self):
119
+ # Implementation for generating OAuth URL
120
+
121
+ async def get_token_data(self, code):
122
+ # Implementation for exchanging code for tokens
123
+
124
+ async def handle_oauth_callback(self, code):
125
+ # Implementation for processing OAuth callback
126
+
127
+ async def refresh_token(self):
128
+ # Implementation for refreshing tokens
129
+ ```
130
+
131
+ ## Implementing a New Integration
132
+
133
+ To implement a new integration provider:
134
+
135
+ 1. Create a new service class that extends `IntegrationBaseService`
136
+ 2. Implement the required abstract methods:
137
+ - `get_auth_url()`
138
+ - `get_token_data(code)`
139
+ - `handle_oauth_callback(code)`
140
+ - `refresh_token()`
141
+ 3. Register your service in your FastAPI application
142
+ 4. Create API routes to initiate auth flow, handle callbacks, etc.
143
+
144
+ ## Example: Creating API Routes
145
+
146
+ ```python
147
+ from fastapi import APIRouter, Depends
148
+ from abs_integration_core import ResponseSchema, IsConnectedResponse
149
+
150
+ router = APIRouter(prefix="/integration", tags=["integration"])
151
+
152
+ @router.get("/{provider_name}/connect")
153
+ async def integration_connect(
154
+ service: IntegrationBaseService = Depends(get_integration_service)
155
+ ):
156
+ auth_data = service.get_auth_url()
157
+ return RedirectResponse(url=auth_data["auth_url"])
158
+
159
+ @router.get("/{provider_name}/callback")
160
+ async def integration_callback(
161
+ code: str,
162
+ service: IntegrationBaseService = Depends(get_integration_service)
163
+ ):
164
+ token_data = await service.handle_oauth_callback(code)
165
+ return ResponseSchema(
166
+ data=IsConnectedResponse(
167
+ provider=service.provider_name,
168
+ connected=True
169
+ ),
170
+ message=f"Integration connected successfully"
171
+ )
172
+ ```
173
+
174
+ ## License
175
+
176
+ MIT
@@ -0,0 +1,158 @@
1
+ # Integration Core
2
+
3
+ A core library for building and managing OAuth-based third-party integrations in FastAPI applications.
4
+
5
+ ## Overview
6
+
7
+ `abs-integration-core` provides a set of reusable components to simplify the implementation of OAuth-based integrations with third-party services. It includes models, schemas, repositories, and a base service class that can be extended for specific integration providers.
8
+
9
+ ## Features
10
+
11
+ - **Integration Model**: SQLAlchemy model for storing OAuth tokens and integration metadata
12
+ - **Standard Schemas**: Pydantic models for integration data validation and serialization
13
+ - **Repository Layer**: Data access layer for CRUD operations on integration records
14
+ - **Base Service**: Abstract base class for implementing integration services for different providers
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pip install abs-integration-core
20
+ ```
21
+
22
+ ## Dependencies
23
+
24
+ This package depends on:
25
+
26
+ - `fastapi`: For API routing and endpoint handling
27
+ - `sqlalchemy`: For database ORM functionality
28
+ - `pydantic`: For data validation
29
+ - `abs-exception-core`: For standardized exception handling
30
+ - `abs-repository-core`: For base repository pattern implementation
31
+ - `abs-auth-rbac-core`: For authentication and base models
32
+
33
+ ## Usage
34
+
35
+ ### Models
36
+
37
+ The core model represents an OAuth integration with a third-party provider:
38
+
39
+ ```python
40
+ from abs_integration_core.models import Integration
41
+
42
+ # The Integration model includes:
43
+ # - provider_name: String(255)
44
+ # - access_token: Text
45
+ # - refresh_token: Text
46
+ # - expires_in: Integer
47
+ ```
48
+
49
+ ### Schemas
50
+
51
+ Various Pydantic schemas are available for request/response handling:
52
+
53
+ ```python
54
+ from abs_integration_core import (
55
+ TokenData,
56
+ IsConnectedResponse,
57
+ CreateIntegration,
58
+ UpdateIntegration,
59
+ ResponseSchema
60
+ )
61
+
62
+ # Example: Create a standard API response
63
+ response = ResponseSchema(
64
+ status=200,
65
+ message="Integration created successfully",
66
+ data=IsConnectedResponse(provider="sharepoint", connected=True)
67
+ )
68
+ ```
69
+
70
+ ### Repository
71
+
72
+ The `IntegrationRepository` provides data access methods:
73
+
74
+ ```python
75
+ from abs_integration_core import IntegrationRepository
76
+
77
+ # Initialize repository with a database session factory
78
+ repo = IntegrationRepository(db_session)
79
+
80
+ # Available methods:
81
+ # - create_integration(integration_data)
82
+ # - update_integration(integration_id, update_data)
83
+ # - get_by_provider(provider_name)
84
+ # - get_all()
85
+ # - delete_by_provider(provider_name)
86
+ # - refresh_token(provider_name, new_access_token, new_refresh_token, new_expires_in)
87
+ ```
88
+
89
+ ### Base Service
90
+
91
+ Extend the `IntegrationBaseService` to implement provider-specific integration services:
92
+
93
+ ```python
94
+ from abs_integration_core import IntegrationBaseService
95
+
96
+ class SharepointIntegrationService(IntegrationBaseService):
97
+ def __init__(self, provider_name, integration_repository):
98
+ super().__init__(provider_name, integration_repository)
99
+
100
+ def get_auth_url(self):
101
+ # Implementation for generating OAuth URL
102
+
103
+ async def get_token_data(self, code):
104
+ # Implementation for exchanging code for tokens
105
+
106
+ async def handle_oauth_callback(self, code):
107
+ # Implementation for processing OAuth callback
108
+
109
+ async def refresh_token(self):
110
+ # Implementation for refreshing tokens
111
+ ```
112
+
113
+ ## Implementing a New Integration
114
+
115
+ To implement a new integration provider:
116
+
117
+ 1. Create a new service class that extends `IntegrationBaseService`
118
+ 2. Implement the required abstract methods:
119
+ - `get_auth_url()`
120
+ - `get_token_data(code)`
121
+ - `handle_oauth_callback(code)`
122
+ - `refresh_token()`
123
+ 3. Register your service in your FastAPI application
124
+ 4. Create API routes to initiate auth flow, handle callbacks, etc.
125
+
126
+ ## Example: Creating API Routes
127
+
128
+ ```python
129
+ from fastapi import APIRouter, Depends
130
+ from abs_integration_core import ResponseSchema, IsConnectedResponse
131
+
132
+ router = APIRouter(prefix="/integration", tags=["integration"])
133
+
134
+ @router.get("/{provider_name}/connect")
135
+ async def integration_connect(
136
+ service: IntegrationBaseService = Depends(get_integration_service)
137
+ ):
138
+ auth_data = service.get_auth_url()
139
+ return RedirectResponse(url=auth_data["auth_url"])
140
+
141
+ @router.get("/{provider_name}/callback")
142
+ async def integration_callback(
143
+ code: str,
144
+ service: IntegrationBaseService = Depends(get_integration_service)
145
+ ):
146
+ token_data = await service.handle_oauth_callback(code)
147
+ return ResponseSchema(
148
+ data=IsConnectedResponse(
149
+ provider=service.provider_name,
150
+ connected=True
151
+ ),
152
+ message=f"Integration connected successfully"
153
+ )
154
+ ```
155
+
156
+ ## License
157
+
158
+ MIT
@@ -0,0 +1,3 @@
1
+ from .integration_model import Integration
2
+
3
+ __all__ = ["Integration"]
@@ -0,0 +1,14 @@
1
+ from sqlalchemy import Column, String, Integer, Text
2
+ from sqlalchemy.orm import relationship
3
+
4
+ from abs_repository_core.models import BaseModel
5
+
6
+
7
+ class Integration(BaseModel):
8
+ """Integration model"""
9
+ __tablename__ = "gov_integrations"
10
+
11
+ provider_name = Column(String(255), nullable=False)
12
+ access_token = Column(Text, nullable=False)
13
+ refresh_token = Column(Text, nullable=False)
14
+ expires_in = Column(Integer, nullable=False)
@@ -0,0 +1,3 @@
1
+ from .integration_repository import IntegrationRepository
2
+
3
+ __all__ = ["IntegrationRepository"]
@@ -0,0 +1,61 @@
1
+ from abs_repository_core.repository import BaseRepository
2
+ from abs_integration_core.models import Integration
3
+ from typing import Callable
4
+ from sqlalchemy.orm import Session
5
+ from abs_integration_core.schema import CreateIntegration
6
+ from abs_integration_core.schema import TokenData
7
+
8
+ class IntegrationRepository(BaseRepository):
9
+ def __init__(self, db: Callable[..., Session]):
10
+ self.db = db
11
+ super().__init__(db, Integration)
12
+
13
+ def create_integration(self, integration_data: CreateIntegration) -> Integration:
14
+ """
15
+ Create a new integration record.
16
+
17
+ Args:
18
+ integration_data: Integration data including provider_name, access_token, etc.
19
+
20
+ Returns:
21
+ The created integration object
22
+
23
+ Raises:
24
+ DuplicatedError: If integration with same provider already exists
25
+ """
26
+ new_integration = Integration(
27
+ provider_name=integration_data.provider_name,
28
+ access_token=integration_data.access_token,
29
+ refresh_token=integration_data.refresh_token,
30
+ expires_in=integration_data.expires_in
31
+ )
32
+
33
+ integration = super().create(new_integration)
34
+ return integration
35
+
36
+ def refresh_token(
37
+ self,
38
+ provider_name: str,
39
+ token_data: TokenData
40
+ ) -> Integration:
41
+ """
42
+ Update token information for a specific integration.
43
+
44
+ Args:
45
+ provider_name: The integration provider name
46
+ token_data: The data to update
47
+
48
+ Returns:
49
+ The updated integration object
50
+
51
+ Raises:
52
+ NotFoundError: If integration doesn't exist
53
+ """
54
+ integration = super().read_by_attr(
55
+ attr="provider_name",
56
+ value=provider_name
57
+ )
58
+
59
+ super().update(integration.id, token_data)
60
+
61
+ return integration
@@ -0,0 +1,17 @@
1
+ from .common_schema import ResponseSchema
2
+ from .integration_schema import (
3
+ TokenData,
4
+ Integration,
5
+ IsConnectedResponse,
6
+ CreateIntegration,
7
+ UpdateIntegration
8
+ )
9
+
10
+ __all__ = [
11
+ "ResponseSchema",
12
+ "TokenData",
13
+ "Integration",
14
+ "IsConnectedResponse",
15
+ "CreateIntegration",
16
+ "UpdateIntegration"
17
+ ]
@@ -0,0 +1,17 @@
1
+ from typing import Generic, Optional, TypeVar
2
+ from pydantic import BaseModel
3
+ from fastapi import status
4
+
5
+ T = TypeVar('T')
6
+
7
+
8
+ class ResponseSchema(BaseModel, Generic[T]):
9
+ """
10
+ Standard response schema for all API endpoints
11
+ """
12
+ status: int = status.HTTP_200_OK
13
+ message: str = "Success"
14
+ data: Optional[T] = None
15
+
16
+ class Config:
17
+ arbitrary_types_allowed = True
@@ -0,0 +1,42 @@
1
+ from abs_repository_core.schemas import ModelBaseInfo, make_optional
2
+ from pydantic import BaseModel
3
+ from typing import Optional
4
+
5
+
6
+ class TokenData(BaseModel):
7
+ access_token: str
8
+ refresh_token: str
9
+ expires_in: int
10
+
11
+ class Config:
12
+ extra = "allow"
13
+
14
+
15
+ class Integration(make_optional(ModelBaseInfo), TokenData):
16
+ provider_name: str
17
+
18
+
19
+ class IsConnectedResponse(BaseModel):
20
+ provider: str
21
+ connected: bool
22
+
23
+ class Config:
24
+ extra = "allow"
25
+
26
+
27
+ class CreateIntegration(BaseModel):
28
+ """Model for creating a new integration"""
29
+ provider_name: str
30
+ access_token: str
31
+ refresh_token: str
32
+ expires_in: int
33
+
34
+
35
+ class UpdateIntegration(BaseModel):
36
+ """Model for updating an existing integration"""
37
+ access_token: Optional[str] = None
38
+ refresh_token: Optional[str] = None
39
+ expires_in: Optional[int] = None
40
+
41
+ class Config:
42
+ extra = "ignore"
@@ -0,0 +1,3 @@
1
+ from .base_service import IntegrationBaseService
2
+
3
+ __all__ = ["IntegrationBaseService"]
@@ -0,0 +1,125 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Dict, Optional, List
3
+ from abs_integration_core.schema import TokenData, Integration
4
+ from abs_integration_core.repository import IntegrationRepository
5
+ from abs_exception_core.exceptions import NotFoundError
6
+ from abs_repository_core.services.base_service import BaseService
7
+ from abs_integration_core.utils.encryption import Encryption
8
+
9
+
10
+ class IntegrationBaseService(ABC, BaseService):
11
+ """
12
+ Base abstract class for all integration services.
13
+ Any integration service should inherit from this class and implement its methods.
14
+ """
15
+ def __init__(
16
+ self,
17
+ provider_name: str,
18
+ integration_repository: IntegrationRepository,
19
+ encryption: Encryption
20
+ ):
21
+ self.provider_name = provider_name
22
+ self.encryption = encryption
23
+ super().__init__(integration_repository)
24
+
25
+ @abstractmethod
26
+ def get_auth_url(self, state: Optional[Dict] = None) -> Dict[str, str]:
27
+ """
28
+ Generate an authentication URL for OAuth flow.
29
+
30
+ Args:
31
+ state: Optional state dictionary to include in the OAuth flow
32
+
33
+ Returns:
34
+ A dictionary containing the auth URL and other necessary information
35
+ """
36
+ pass
37
+
38
+ @abstractmethod
39
+ async def get_token_data(self, code: str) -> TokenData:
40
+ """
41
+ Exchange authorization code for token data.
42
+
43
+ Args:
44
+ code: The authorization code from OAuth callback
45
+
46
+ Returns:
47
+ TokenData object with access_token, refresh_token and expires_in
48
+ """
49
+ pass
50
+
51
+ @abstractmethod
52
+ async def handle_oauth_callback(self, code: str) -> TokenData:
53
+ """
54
+ Handle the OAuth callback and store tokens.
55
+
56
+ Args:
57
+ code: The authorization code from OAuth callback
58
+
59
+ Returns:
60
+ TokenData object
61
+ """
62
+ pass
63
+
64
+ @abstractmethod
65
+ async def refresh_token(self) -> Optional[TokenData]:
66
+ """
67
+ Refresh the access token using the refresh token.
68
+
69
+ Returns:
70
+ Updated TokenData if successful, None otherwise
71
+ """
72
+ pass
73
+
74
+ def get_query_by_provider(self):
75
+ return super().get_by_attr(
76
+ attr="provider_name",
77
+ value=self.provider_name
78
+ )
79
+
80
+ def get_integration(self) -> Optional[TokenData]:
81
+ """
82
+ Get integration data.
83
+
84
+ Returns:
85
+ TokenData if integration exists, None otherwise
86
+ """
87
+ try:
88
+ integration = self.get_query_by_provider()
89
+ return integration
90
+ except Exception:
91
+ return None
92
+
93
+ def get_all_integrations(self) -> List[Integration]:
94
+ """
95
+ Get all integrations.
96
+
97
+ Returns:
98
+ List of TokenData objects
99
+ """
100
+ try:
101
+ integrations = super().get_list()
102
+ return integrations
103
+ except Exception:
104
+ return []
105
+
106
+ def delete_integration(self) -> bool:
107
+ """
108
+ Delete an integration.
109
+
110
+ Returns:
111
+ True if deleted, False otherwise
112
+ """
113
+ try:
114
+ integration = self.get_query_by_provider()
115
+ super().remove_by_id(integration.id)
116
+
117
+ return True
118
+
119
+ except NotFoundError:
120
+ # If the integration doesn't exist, consider it "deleted"
121
+ return True
122
+
123
+ except Exception as e:
124
+ print(f"Error deleting integration: {str(e)}")
125
+ return False
@@ -0,0 +1,98 @@
1
+ from cryptography.fernet import Fernet
2
+ import base64
3
+ import hashlib
4
+ from typing import Optional
5
+
6
+
7
+ class Encryption:
8
+ def __init__(self, key: Optional[str] = None):
9
+ self.key = key
10
+
11
+ # Encryption key management
12
+ def get_encryption_key(self):
13
+ """
14
+ Get encryption key from environment variable or generate one if not exists
15
+
16
+ This should ideally be set in environment variables and consistent across restarts
17
+ Returns:
18
+ A valid Fernet key (32 url-safe base64-encoded bytes)
19
+ """
20
+
21
+ if not self.key:
22
+ # For demo only - in production, you should set a permanent key
23
+ # and store it securely in environment variables or a secret manager
24
+ self.key = Fernet.generate_key().decode()
25
+ print(f"WARNING: Generated temporary encryption key: {self.key}")
26
+ print("Set this in your environment as TOKEN_ENCRYPTION_KEY")
27
+ return self.key.encode() if isinstance(self.key, str) else self.key
28
+
29
+ # If key is not in valid Fernet format, convert it to a valid key
30
+ try:
31
+ # Try to use the key as is - if it's already a valid Fernet key
32
+ if isinstance(self.key, str):
33
+ self.key = self.key.encode()
34
+
35
+ # Test if it's a valid key
36
+ Fernet(self.key)
37
+ return self.key
38
+ except Exception:
39
+ # Not a valid Fernet key - convert to a valid one
40
+ # Use SHA-256 to get a consistent length, then encode in base64
41
+ if isinstance(self.key, str):
42
+ self.key = self.key.encode()
43
+
44
+ hashed_key = hashlib.sha256(self.key).digest()
45
+ encoded_key = base64.urlsafe_b64encode(hashed_key)
46
+
47
+ print("WARNING: Converted invalid encryption key to valid Fernet format")
48
+ return encoded_key
49
+
50
+ def encrypt_token(self, token: str) -> str:
51
+ """
52
+ Encrypt a token string using Fernet symmetric encryption
53
+
54
+ Args:
55
+ token: The raw token string to encrypt
56
+
57
+ Returns:
58
+ The encrypted token as a base64 string
59
+ """
60
+ if not token:
61
+ return token
62
+
63
+ # Get the encryption key
64
+ key = self.get_encryption_key()
65
+
66
+ # Create a Fernet cipher
67
+ cipher = Fernet(key)
68
+
69
+ # Encrypt the token (which must be bytes)
70
+ encrypted_token = cipher.encrypt(token.encode())
71
+
72
+ # Return as a string for storage
73
+ return encrypted_token.decode()
74
+
75
+ def decrypt_token(self, encrypted_token: str) -> str:
76
+ """
77
+ Decrypt a token string that was encrypted with Fernet
78
+
79
+ Args:
80
+ encrypted_token: The encrypted token as a base64 string
81
+
82
+ Returns:
83
+ The original decrypted token string
84
+ """
85
+ if not encrypted_token:
86
+ return encrypted_token
87
+
88
+ # Get the encryption key
89
+ key = self.get_encryption_key()
90
+
91
+ # Create a Fernet cipher
92
+ cipher = Fernet(key)
93
+
94
+ # Decrypt the token (convert string to bytes first)
95
+ decrypted_token = cipher.decrypt(encrypted_token.encode())
96
+
97
+ # Return as a string
98
+ return decrypted_token.decode()
@@ -0,0 +1,26 @@
1
+ [project]
2
+ name = "abs-integration-core"
3
+ version = "0.1.0"
4
+ description = "Core utilities for building OAuth-based integrations in FastAPI applications"
5
+ authors = [
6
+ {name = "AutoBridgeSystems", email = "info@autobridgesystems.com"}
7
+ ]
8
+ license = {text = "MIT"}
9
+ readme = "README.md"
10
+ requires-python = ">=3.13,<4.0"
11
+ dependencies = [
12
+ "fastapi >=0.95.0,<2.0.0",
13
+ "sqlalchemy >=2.0.0,<3.0.0",
14
+ "pydantic >=2.0.0,<3.0.0",
15
+ "abs-exception-core (>=0.1.2,<0.2.0)",
16
+ "abs-repository-core (>=0.2.2,<0.3.0)",
17
+ ]
18
+
19
+ [tool.poetry]
20
+ packages = [
21
+ { include = "abs_integration_core" }
22
+ ]
23
+
24
+ [build-system]
25
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
26
+ build-backend = "poetry.core.masonry.api"