Skip to content

Commit 0cfd03a

Browse files
authored
Merge pull request #283 from poissoncorp/RDBC-1017
RDBC-1017 Remote attachments, JSON Schema Validation & 7.2 Changes
2 parents 70d7c62 + 929a183 commit 0cfd03a

25 files changed

Lines changed: 2402 additions & 73 deletions

.github/workflows/RavenClient.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: tests/python
22

33
on:
44
push:
5-
branches: [v7.1]
5+
branches: [v7.2]
66
pull_request:
7-
branches: [v7.1]
7+
branches: [v7.2]
88
schedule:
99
- cron: '0 10 * * *'
1010
workflow_dispatch:
@@ -30,7 +30,7 @@ jobs:
3030
strategy:
3131
matrix:
3232
python-version: [ '3.10' ,'3.11', '3.12', '3.13', '3.14']
33-
serverVersion: [ '7.1', '7.2' ]
33+
serverVersion: [ '7.2' ]
3434
fail-fast: false
3535

3636
steps:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pip install ravendb
88
````
99

1010
## Introduction and changelog
11-
Python client API (v7.1) for [RavenDB](https://ravendb.net/), a NoSQL document database.
11+
Python client API (v7.2) for [RavenDB](https://ravendb.net/), a NoSQL document database.
1212

1313
**Type-hinted entire project and API results** - using the API is now much more comfortable with IntelliSense
1414

README_pypi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pip install ravendb
88
```
99

1010
## Introduction
11-
Python client API (v7.1) for [RavenDB](https://ravendb.net/) , a NoSQL document database.
11+
Python client API (v7.2) for [RavenDB](https://ravendb.net/) , a NoSQL document database.
1212

1313
**Type-hinted entire project and API results** - using the API is now much more comfortable with IntelliSense
1414

ravendb/documents/bulk_insert_operation.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from ravendb.json.metadata_as_dictionary import MetadataAsDictionary
3131
from ravendb.documents.commands.batches import CommandType
3232
from ravendb.documents.commands.bulkinsert import GetNextOperationIdCommand, KillOperationCommand
33+
from ravendb.documents.operations.attachments import StoreAttachmentParameters
3334
from ravendb.exceptions.documents.bulkinsert import BulkInsertAbortedException
3435
from ravendb.documents.identity.hilo import GenerateEntityIdOnTheClient
3536
from ravendb.tools.utils import Utils
@@ -615,13 +616,16 @@ def __init__(self, operation: BulkInsertOperation, key: str):
615616
self.key = key
616617

617618
def store(self, name: str, attachment_bytes: bytes, content_type: Optional[str] = None) -> None:
618-
self.operation._attachments_operation.store(self.key, name, attachment_bytes, content_type)
619+
self.store_with_parameters(StoreAttachmentParameters(name, attachment_bytes, content_type=content_type))
620+
621+
def store_with_parameters(self, parameters: StoreAttachmentParameters) -> None:
622+
self.operation._attachments_operation.store(self.key, parameters)
619623

620624
class AttachmentsBulkInsertOperation:
621625
def __init__(self, operation: BulkInsertOperation):
622626
self.operation = operation
623627

624-
def store(self, key: str, name: str, attachment_bytes: bytes, content_type: Optional[str] = None):
628+
def store(self, key: str, parameters: StoreAttachmentParameters):
625629
release_lock_callback = self.operation._concurrency_check()
626630
try:
627631
self.operation._end_previous_command_if_needed()
@@ -634,22 +638,30 @@ def store(self, key: str, name: str, attachment_bytes: bytes, content_type: Opti
634638
if not self.operation._first:
635639
self.operation._write_comma()
636640

641+
self.operation._first = False
642+
self.operation._in_progress_command = CommandType.NONE
643+
637644
self.operation._write_string_no_escape('{"Id":"')
638645
self.operation._write_string(key)
639646
self.operation._write_string_no_escape('","Type":"AttachmentPUT","Name":"')
640-
self.operation._write_string(name)
647+
self.operation._write_string(parameters.name)
641648

642-
if content_type:
643-
self.operation._write_string_no_escape('","ContentType:"')
644-
self.operation._write_string(content_type)
649+
if parameters.content_type:
650+
self.operation._write_string_no_escape('","ContentType":"')
651+
self.operation._write_string(parameters.content_type)
645652

646653
self.operation._write_string_no_escape('","ContentLength":')
647-
self.operation._write_string_no_escape(str(len(attachment_bytes)))
654+
self.operation._write_string_no_escape(str(len(parameters.stream)))
655+
656+
if parameters.remote_parameters is not None:
657+
self.operation._write_string_no_escape(',"RemoteParameters":')
658+
self.operation._write_string_no_escape(json.dumps(parameters.remote_parameters.to_json()))
659+
648660
self.operation._write_string_no_escape("}")
649661

650662
self.operation._flush_if_needed()
651663

652-
self.operation._current_data_buffer += bytearray(attachment_bytes)
664+
self.operation._current_data_buffer += bytearray(parameters.stream)
653665

654666
self.operation._flush_if_needed()
655667

ravendb/documents/commands/batches.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
if TYPE_CHECKING:
2525
from ravendb.documents.conventions import DocumentConventions
26+
from ravendb.documents.operations.attachments import RemoteAttachmentParameters
2627
from ravendb.documents.operations.patch import PatchRequest
2728
from ravendb.documents.session.document_session_operations.in_memory_document_session_operations import (
2829
InMemoryDocumentSessionOperations,
@@ -117,6 +118,8 @@ def __init__(
117118
if self.__attachment_streams is None:
118119
self.__attachment_streams = []
119120
stream = command.stream
121+
if stream is None:
122+
continue # remote-only attachment — no stream to track
120123
if stream in self.__attachment_streams:
121124
raise RuntimeError(
122125
"It is forbidden to re-use the same stream for more than one attachment. "
@@ -139,12 +142,13 @@ def create_request(self, node: ServerNode) -> requests.Request:
139142
for command in self.__commands:
140143
if command.command_type == CommandType.ATTACHMENT_PUT:
141144
command: PutAttachmentCommandData
142-
files[command.name] = (
143-
command.name,
144-
command.stream,
145-
command.content_type,
146-
{"Command-Type": "AttachmentStream"},
147-
)
145+
if command.stream is not None:
146+
files[command.name] = (
147+
command.name,
148+
command.stream,
149+
command.content_type,
150+
{"Command-Type": "AttachmentStream"},
151+
)
148152
if not request.data:
149153
request.data = {"Commands": []}
150154
request.data["Commands"].append(command.serialize(self.__conventions))
@@ -526,7 +530,17 @@ def serialize(self, conventions: DocumentConventions) -> Dict:
526530

527531

528532
class PutAttachmentCommandData(CommandData):
529-
def __init__(self, document_id: str, name: str, stream: bytes, content_type: str, change_vector: str):
533+
def __init__(
534+
self,
535+
document_id: str,
536+
name: str,
537+
stream: bytes,
538+
content_type: str,
539+
change_vector: str,
540+
remote_parameters: Optional["RemoteAttachmentParameters"] = None,
541+
hash: str = None,
542+
size_in_bytes: int = None,
543+
):
530544
if not document_id:
531545
raise ValueError(document_id)
532546
if not name:
@@ -535,6 +549,9 @@ def __init__(self, document_id: str, name: str, stream: bytes, content_type: str
535549
super(PutAttachmentCommandData, self).__init__(document_id, name, change_vector, CommandType.ATTACHMENT_PUT)
536550
self.__stream = stream
537551
self.__content_type = content_type
552+
self.__remote_parameters = remote_parameters
553+
self.__hash = hash
554+
self.__size_in_bytes = size_in_bytes
538555

539556
@property
540557
def stream(self):
@@ -544,14 +561,23 @@ def stream(self):
544561
def content_type(self):
545562
return self.__content_type
546563

564+
@property
565+
def remote_parameters(self) -> Optional["RemoteAttachmentParameters"]:
566+
return self.__remote_parameters
567+
547568
def serialize(self, conventions: DocumentConventions) -> dict:
548-
return {
569+
result = {
549570
"Id": self._key,
550571
"Name": self._name,
551572
"ContentType": self.__content_type,
552573
"ChangeVector": self._change_vector,
553574
"Type": str(self._command_type),
575+
"RemoteParameters": self.__remote_parameters.to_json() if self.__remote_parameters is not None else None,
576+
"Hash": self.__hash,
554577
}
578+
if self.__size_in_bytes is not None:
579+
result["SizeInBytes"] = self.__size_in_bytes
580+
return result
555581

556582

557583
class CopyAttachmentCommandData(CommandData):

ravendb/documents/indexes/definitions.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import datetime
33
import enum
44
import re
5-
from enum import Enum
5+
from enum import Enum, IntFlag
66
from abc import ABC
77
from typing import Union, Optional, List, Dict, Set, Iterable
88
from ravendb.documents.indexes.spatial.configuration import SpatialOptions, AutoSpatialOptions
@@ -140,6 +140,38 @@ def __str__(self):
140140
return self.value
141141

142142

143+
class IndexDefinitionCompareDifferences(IntFlag):
144+
NONE = 0
145+
MAPS = 1 << 0
146+
REDUCE = 1 << 1
147+
FIELDS = 1 << 2
148+
CONFIGURATION = 1 << 3
149+
LOCK_MODE = 1 << 4
150+
PRIORITY = 1 << 5
151+
STATE = 1 << 6
152+
ADDITIONAL_SOURCES = 1 << 7
153+
ADDITIONAL_ASSEMBLIES = 1 << 8
154+
DEPLOYMENT_MODE = 1 << 12
155+
COMPOUND_FIELDS = 1 << 13
156+
ARCHIVED_DATA_PROCESSING_BEHAVIOR = 1 << 14
157+
SCHEMA_VALIDATION_CONFIGURATION = 1 << 15
158+
ALL = (
159+
MAPS
160+
| REDUCE
161+
| FIELDS
162+
| CONFIGURATION
163+
| LOCK_MODE
164+
| PRIORITY
165+
| STATE
166+
| ADDITIONAL_SOURCES
167+
| ADDITIONAL_ASSEMBLIES
168+
| DEPLOYMENT_MODE
169+
| COMPOUND_FIELDS
170+
| ARCHIVED_DATA_PROCESSING_BEHAVIOR
171+
| SCHEMA_VALIDATION_CONFIGURATION
172+
)
173+
174+
143175
class GroupByArrayBehavior(Enum):
144176
NOT_APPLICABLE = "NotApplicable"
145177
BY_CONTENT = "ByContent"
@@ -205,6 +237,7 @@ def __init__(
205237
pattern_references_collection_name: Optional[str] = None,
206238
deployment_mode: Optional[IndexDeploymentMode] = None,
207239
search_engine_type: Optional[SearchEngineType] = None,
240+
schema_definitions: Optional[Dict[str, str]] = None,
208241
):
209242
super(IndexDefinition, self).__init__(name, priority, state)
210243
self.lock_mode = lock_mode
@@ -222,6 +255,7 @@ def __init__(
222255
self.pattern_references_collection_name = pattern_references_collection_name
223256
self.deployment_mode = deployment_mode
224257
self.search_engine_type = search_engine_type
258+
self.schema_definitions = schema_definitions
225259

226260
@classmethod
227261
def from_json(cls, json_dict: dict) -> IndexDefinition:
@@ -255,6 +289,7 @@ def from_json(cls, json_dict: dict) -> IndexDefinition:
255289
deploy = json_dict.get("DeploymentMode", None)
256290
if deploy is not None:
257291
result.deployment_mode = IndexDeploymentMode(deploy)
292+
result.schema_definitions = json_dict.get("SchemaDefinitions")
258293
return result
259294

260295
def to_json(self) -> dict:
@@ -278,6 +313,7 @@ def to_json(self) -> dict:
278313
"PatternForOutputReduceToCollectionReferences": self.pattern_for_output_reduce_to_collection_references,
279314
"PatternReferencesCollectionName": self.pattern_references_collection_name,
280315
"DeploymentMode": self.deployment_mode,
316+
"SchemaDefinitions": self.schema_definitions,
281317
}
282318

283319
@property

0 commit comments

Comments
 (0)