Skip to content

Commit 55ff823

Browse files
authored
Merge pull request #56 from ks6088ts-labs/feature/issue-48_support-cosmosdb
support cosmosdb
2 parents 3b8223b + dc3e8e9 commit 55ff823

File tree

12 files changed

+390
-2
lines changed

12 files changed

+390
-2
lines changed

azure_cosmos_db.env.sample

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
AZURE_COSMOS_DB_ENDPOINT = "<cosmosdb-endpoint>"
2+
AZURE_COSMOS_DB_KEY = "<cosmosdb-key>"
3+
AZURE_COSMOS_DB_CONNECTION_STRING = "<cosmosdb-connection-string>"

backend/fastapi.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from backend.routers import azure_ai_document_intelligence as azure_ai_document_intelligence_router
55
from backend.routers import azure_ai_vision as azure_ai_vision_router
6+
from backend.routers import azure_cosmos_db as azure_cosmos_db_router
67
from backend.routers import azure_event_grid as azure_event_grid_router
78
from backend.routers import azure_openai as azure_openai_router
89
from backend.routers import azure_storage_blob as azure_storage_blob_router
@@ -19,6 +20,7 @@
1920
azure_openai_router.router,
2021
azure_storage_blob_router.router,
2122
azure_storage_queue_router.router,
23+
azure_cosmos_db_router.router,
2224
]:
2325
app.include_router(router)
2426

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
from logging import getLogger
2+
3+
from azure.core.exceptions import AzureError
4+
from azure.cosmos import CosmosClient, PartitionKey
5+
from azure.cosmos.container import ContainerProxy
6+
from azure.cosmos.database import DatabaseProxy
7+
8+
from backend.settings.azure_cosmos_db import Settings
9+
10+
logger = getLogger(__name__)
11+
12+
13+
class Client:
14+
def __init__(self, settings: Settings) -> None:
15+
self.settings = settings
16+
17+
def get_client(self) -> CosmosClient:
18+
return CosmosClient.from_connection_string(
19+
conn_str=self.settings.azure_cosmos_db_connection_string,
20+
)
21+
22+
def get_database(self, database_id: str) -> DatabaseProxy:
23+
return self.get_client().get_database_client(database_id)
24+
25+
def get_container(
26+
self,
27+
database_id: str,
28+
container_id: str,
29+
) -> ContainerProxy:
30+
return self.get_database(database_id=database_id).get_container_client(container=container_id)
31+
32+
def create_database(
33+
self,
34+
database_id: str,
35+
) -> str:
36+
try:
37+
self.get_client().create_database_if_not_exists(
38+
id=database_id,
39+
)
40+
except AzureError as e:
41+
logger.error(e)
42+
return database_id
43+
44+
def create_container(
45+
self,
46+
database_id: str,
47+
container_id: str,
48+
partition_key_path="/id",
49+
) -> str:
50+
try:
51+
self.get_database(database_id=database_id).create_container_if_not_exists(
52+
id=container_id,
53+
partition_key=PartitionKey(
54+
path=partition_key_path,
55+
),
56+
)
57+
except AzureError as e:
58+
logger.error(e)
59+
return container_id
60+
61+
def create_item(
62+
self,
63+
container: ContainerProxy,
64+
item: dict,
65+
) -> dict:
66+
return container.create_item(
67+
body=item,
68+
)
69+
70+
def read_item(
71+
self,
72+
container: ContainerProxy,
73+
item_id: str,
74+
) -> dict:
75+
return container.read_item(
76+
item=item_id,
77+
partition_key=item_id,
78+
)
79+
80+
def query_items(
81+
self,
82+
container: ContainerProxy,
83+
query: str,
84+
parameters: list | None = None,
85+
enable_cross_partition_query: bool = True,
86+
) -> list:
87+
return container.query_items(
88+
query=query,
89+
parameters=parameters,
90+
enable_cross_partition_query=enable_cross_partition_query,
91+
)

backend/routers/azure_cosmos_db.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from logging import getLogger
2+
3+
from fastapi import APIRouter
4+
5+
from backend.internals.azure_cosmos_db import Client
6+
from backend.schemas import azure_cosmos_db as azure_cosmos_db_schemas
7+
from backend.settings.azure_cosmos_db import Settings
8+
9+
logger = getLogger(__name__)
10+
11+
client = Client(
12+
settings=Settings(),
13+
)
14+
15+
router = APIRouter(
16+
prefix="/azure_cosmos_db",
17+
tags=["azure_cosmos_db"],
18+
responses={404: {"description": "Not found"}},
19+
)
20+
21+
22+
@router.post(
23+
"/database",
24+
response_model=azure_cosmos_db_schemas.CreateDatabaseResponse,
25+
status_code=200,
26+
)
27+
async def create_database(body: azure_cosmos_db_schemas.CreateDatabaseRequest):
28+
database_id = client.create_database(
29+
database_id=body.database_id,
30+
)
31+
return azure_cosmos_db_schemas.CreateDatabaseResponse(
32+
database_id=database_id,
33+
)
34+
35+
36+
@router.post(
37+
"/container",
38+
response_model=azure_cosmos_db_schemas.CreateContainerResponse,
39+
status_code=200,
40+
)
41+
async def create_container(body: azure_cosmos_db_schemas.CreateContainerRequest):
42+
container_id = client.create_container(
43+
container_id=body.container_id,
44+
database_id=body.database_id,
45+
)
46+
return azure_cosmos_db_schemas.CreateContainerResponse(
47+
container_id=container_id,
48+
)
49+
50+
51+
@router.post(
52+
"/item",
53+
response_model=azure_cosmos_db_schemas.CreateItemResponse,
54+
status_code=200,
55+
)
56+
async def create_item(body: azure_cosmos_db_schemas.CreateItemRequest):
57+
container = client.get_container(
58+
container_id=body.container_id,
59+
database_id=body.database_id,
60+
)
61+
created_item = client.create_item(
62+
container=container,
63+
item=body.item,
64+
)
65+
return azure_cosmos_db_schemas.CreateItemResponse(
66+
container_id=body.container_id,
67+
database_id=body.database_id,
68+
item=created_item,
69+
)

backend/schemas/azure_cosmos_db.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from logging import getLogger
2+
3+
from pydantic import BaseModel
4+
5+
logger = getLogger(__name__)
6+
7+
8+
class CreateDatabaseRequest(BaseModel):
9+
database_id: str
10+
11+
12+
class CreateDatabaseResponse(BaseModel):
13+
database_id: str
14+
15+
16+
class CreateContainerRequest(BaseModel):
17+
container_id: str
18+
database_id: str
19+
20+
21+
class CreateContainerResponse(BaseModel):
22+
container_id: str
23+
24+
25+
class CreateItemRequest(BaseModel):
26+
container_id: str
27+
database_id: str
28+
item: dict
29+
30+
31+
class CreateItemResponse(BaseModel):
32+
container_id: str
33+
database_id: str
34+
item: dict
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from pydantic_settings import BaseSettings, SettingsConfigDict
2+
3+
4+
class Settings(BaseSettings):
5+
azure_cosmos_db_endpoint: str = "https://<your-cosmos-db-name>.documents.azure.com:443/"
6+
azure_cosmos_db_key: str = "<your-cosmos-db-key>"
7+
azure_cosmos_db_connection_string: str = "<your-cosmos-db-connection-string>"
8+
9+
model_config = SettingsConfigDict(
10+
env_file="azure_cosmos_db.env",
11+
env_file_encoding="utf-8",
12+
extra="ignore",
13+
)

docs/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,11 @@
6060
### Azure AI Document Intelligence
6161

6262
- [Azure AI Document Intelligence client library for Python](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/documentintelligence/azure-ai-documentintelligence/README.md)
63+
64+
### Azure Cosmos DB
65+
66+
- [Manage Azure Cosmos DB for NoSQL resources with Bicep > Azure Cosmos DB account with autoscale throughput](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/manage-with-bicep#azure-cosmos-db-account-with-autoscale-throughput)
67+
- [Get started with Azure Cosmos DB for NoSQL using Python](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-python-get-started?tabs=env-virtual%2Cazure-cli%2Clinux)
68+
- [Create a database in Azure Cosmos DB for NoSQL using Python](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-python-create-database)
69+
- [Create a container in Azure Cosmos DB for NoSQL using Python](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-python-create-container)
70+
- [Sample - demonstrates the basic CRUD operations on a Item resource for Azure Cosmos](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/cosmos/azure-cosmos/samples/document_management.py)

infra/main.bicep

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ param location string = resourceGroup().location
88
@description('Specifies the resource tags.')
99
param tags object = {}
1010

11-
// OpenAI
1211
@description('Specifies the name of the Azure OpenAI resource.')
1312
param openAiName string = '${prefix}openai'
1413

@@ -24,6 +23,9 @@ param storageAccountName string = '${prefix}sa'
2423
@description('Specifies the name of the Azure Event Grid resource.')
2524
param eventGridName string = '${prefix}eg'
2625

26+
@description('Specifies the name of the Azure Cosmos DB resource.')
27+
param cosmosDbName string = '${prefix}cosmosdb'
28+
2729
module openAi './modules/openAi.bicep' = {
2830
name: 'openAi'
2931
params: {
@@ -73,6 +75,17 @@ module eventGrid './modules/eventGrid.bicep' = {
7375
}
7476
}
7577

78+
module cosmosDb './modules/cosmosDb.bicep' = {
79+
name: 'cosmosDb'
80+
params: {
81+
name: cosmosDbName
82+
location: location
83+
tags: tags
84+
primaryRegion: location
85+
secondaryRegion: 'japanwest'
86+
}
87+
}
88+
7689
// Output
7790
output cognitiveServicesName string = cognitiveServices.outputs.name
7891
output cognitiveServicesEndpoint string = cognitiveServices.outputs.endpoint

infra/modules/cosmosDb.bicep

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Parameters
2+
@description('Specifies the name of the Azure Cosmos DB resource')
3+
param name string
4+
5+
@description('Specifies the primary location of Azure resources.')
6+
param location string = resourceGroup().location
7+
8+
@description('Specifies the resource tags.')
9+
param tags object = {}
10+
11+
@description('Specifies the name of the Azure Cosmos DB account name, max length 44 characters, lowercase.')
12+
param accountName string = toLower('${name}account')
13+
14+
@description('The primary region for the Cosmos DB account.')
15+
param primaryRegion string
16+
17+
@description('The secondary region for the Cosmos DB account.')
18+
param secondaryRegion string
19+
20+
@description('The default consistency level of the Cosmos DB account.')
21+
@allowed([
22+
'Eventual'
23+
'ConsistentPrefix'
24+
'Session'
25+
'BoundedStaleness'
26+
'Strong'
27+
])
28+
param defaultConsistencyLevel string = 'Session'
29+
30+
@description('Max stale requests. Required for BoundedStaleness. Valid ranges, Single Region: 10 to 2147483647. Multi Region: 100000 to 2147483647.')
31+
@minValue(10)
32+
@maxValue(2147483647)
33+
param maxStalenessPrefix int = 100000
34+
35+
@description('Max lag time (minutes). Required for BoundedStaleness. Valid ranges, Single Region: 5 to 84600. Multi Region: 300 to 86400.')
36+
@minValue(5)
37+
@maxValue(86400)
38+
param maxIntervalInSeconds int = 300
39+
40+
@description('Enable system managed failover for regions')
41+
param systemManagedFailover bool = true
42+
43+
var consistencyPolicy = {
44+
Eventual: {
45+
defaultConsistencyLevel: 'Eventual'
46+
}
47+
ConsistentPrefix: {
48+
defaultConsistencyLevel: 'ConsistentPrefix'
49+
}
50+
Session: {
51+
defaultConsistencyLevel: 'Session'
52+
}
53+
BoundedStaleness: {
54+
defaultConsistencyLevel: 'BoundedStaleness'
55+
maxStalenessPrefix: maxStalenessPrefix
56+
maxIntervalInSeconds: maxIntervalInSeconds
57+
}
58+
Strong: {
59+
defaultConsistencyLevel: 'Strong'
60+
}
61+
}
62+
var locations = [
63+
{
64+
locationName: primaryRegion
65+
failoverPriority: 0
66+
isZoneRedundant: false
67+
}
68+
{
69+
locationName: secondaryRegion
70+
failoverPriority: 1
71+
isZoneRedundant: false
72+
}
73+
]
74+
75+
resource account 'Microsoft.DocumentDB/databaseAccounts@2024-02-15-preview' = {
76+
name: accountName
77+
location: location
78+
tags: tags
79+
kind: 'GlobalDocumentDB'
80+
properties: {
81+
consistencyPolicy: consistencyPolicy[defaultConsistencyLevel]
82+
locations: locations
83+
databaseAccountOfferType: 'Standard'
84+
enableAutomaticFailover: systemManagedFailover
85+
}
86+
}
87+
88+
// Outputs
89+
output accountId string = account.id
90+
output accountName string = account.name
91+
output accountEndpoint string = account.properties.documentEndpoint

poetry.lock

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)