Skip to content

Commit

Permalink
Add support for ADLS Gen2 Filesystem API (#67)
Browse files Browse the repository at this point in the history
* Initial go at ADLS Gen2 Filesystem API support (it compiles, at least)

* Corrected support for List Filesystems using IncompleteVector
Fix HTTP verbs for requests
Fix expected status codes for responses

* Format json

* Add E2E tests and fix silly mistakes uncovered by them
  • Loading branch information
sawlody authored Nov 2, 2020
1 parent 0b84bda commit 9907c22
Show file tree
Hide file tree
Showing 35 changed files with 1,890 additions and 71 deletions.
4 changes: 3 additions & 1 deletion sdk/core/src/headers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ pub const APPEND_POSITION: &str = "x-ms-blob-condition-appendpos";
pub const CACHE_CONTROL: &str = "x-ms-blob-cache-control";
pub const CONTENT_DISPOSITION: &str = "x-ms-blob-content-disposition";
pub const ACTIVITY_ID: &str = "x-ms-activity-id";
pub const HEADER_CONTINUATION: &str = "x-ms-continuation";
pub const CONTINUATION: &str = "x-ms-continuation";
pub const SESSION_TOKEN: &str = "x-ms-session-token";
pub const REQUIRES_SYNC: &str = "x-ms-requires-sync";
pub const VERSION: &str = "x-ms-version";
pub const PROPERTIES: &str = "x-ms-properties"; //=> [String] }
pub const NAMESPACE_ENABLED: &str = "x-ms-namespace-enabled"; //=> [String] }
12 changes: 11 additions & 1 deletion sdk/core/src/headers/utilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ pub fn last_modified_from_headers(headers: &HeaderMap) -> Result<DateTime<Utc>,
pub fn continuation_token_from_headers_optional(
headers: &HeaderMap,
) -> Result<Option<String>, AzureError> {
if let Some(hc) = headers.get(HEADER_CONTINUATION) {
if let Some(hc) = headers.get(CONTINUATION) {
Ok(Some(hc.to_str()?.to_owned()))
} else {
Ok(None)
Expand Down Expand Up @@ -293,6 +293,16 @@ pub fn request_server_encrypted_from_headers(headers: &HeaderMap) -> Result<bool
Ok(request_server_encrypted)
}

pub fn content_type_from_headers(headers: &HeaderMap) -> Result<&str, AzureError> {
Ok(headers
.get(http::header::CONTENT_TYPE)
.ok_or_else(|| {
let header = http::header::CONTENT_TYPE;
AzureError::HeaderNotFound(header.as_str().to_owned())
})?
.to_str()?)
}

pub async fn perform_http_request(
client: &Client<hyper_rustls::HttpsConnector<hyper::client::HttpConnector>>,
req: Request<Body>,
Expand Down
18 changes: 18 additions & 0 deletions sdk/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,24 @@ pub trait LeaseBreakPeriodOption {
}
}

pub trait ContinuationSupport<'a> {
type O;
fn with_continuation(self, continuation: &'a str) -> Self::O;
}

pub trait ContinuationOption<'a> {
fn continuation(&self) -> Option<&'a str>;

#[must_use]
fn add_header(&self, builder: Builder) -> Builder {
if let Some(continuation) = self.continuation() {
builder.header(CONTINUATION, continuation)
} else {
builder
}
}
}

pub trait ContainerNameSupport<'a> {
type O;
fn with_container_name(self, container_name: &'a str) -> Self::O;
Expand Down
29 changes: 15 additions & 14 deletions sdk/core/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@ pub use crate::{
ContentDispositionOption, ContentDispositionSupport, ContentEncodingOption,
ContentEncodingSupport, ContentLanguageOption, ContentLanguageSupport, ContentLengthOption,
ContentLengthRequired, ContentLengthSupport, ContentMD5Option, ContentMD5Support,
ContentTypeOption, ContentTypeRequired, ContentTypeSupport, DeleteSnapshotsMethod,
DeleteSnapshotsMethodSupport, DelimiterOption, DelimiterSupport, IfMatchConditionOption,
IfMatchConditionSupport, IfModifiedSinceOption, IfModifiedSinceSupport, IfSinceConditionOption,
IfSinceConditionSupport, IncludeCopyOption, IncludeCopySupport, IncludeDeletedOption,
IncludeDeletedSupport, IncludeListOptions, IncludeMetadataOption, IncludeMetadataSupport,
IncludeSnapshotsOption, IncludeSnapshotsSupport, IncludeUncommittedBlobsOption,
IncludeUncommittedBlobsSupport, IsSynchronousOption, IsSynchronousSupport,
LeaseBreakPeriodOption, LeaseBreakPeriodRequired, LeaseBreakPeriodSupport,
LeaseDurationRequired, LeaseDurationSupport, LeaseIdOption, LeaseIdRequired, LeaseIdSupport,
MaxResultsOption, MaxResultsSupport, MetadataOption, MetadataSupport, NextMarkerOption,
NextMarkerSupport, PageBlobLengthRequired, PageBlobLengthSupport, PrefixOption, PrefixSupport,
ProposedLeaseIdOption, ProposedLeaseIdRequired, ProposedLeaseIdSupport, RangeOption,
RangeRequired, RangeSupport, SequenceNumberConditionOption, SequenceNumberConditionSupport,
SequenceNumberOption, SequenceNumberSupport, SnapshotOption, SnapshotRequired, SnapshotSupport,
ContentTypeOption, ContentTypeRequired, ContentTypeSupport, ContinuationOption,
ContinuationSupport, DeleteSnapshotsMethod, DeleteSnapshotsMethodSupport, DelimiterOption,
DelimiterSupport, IfMatchConditionOption, IfMatchConditionSupport, IfModifiedSinceOption,
IfModifiedSinceSupport, IfSinceConditionOption, IfSinceConditionSupport, IncludeCopyOption,
IncludeCopySupport, IncludeDeletedOption, IncludeDeletedSupport, IncludeListOptions,
IncludeMetadataOption, IncludeMetadataSupport, IncludeSnapshotsOption, IncludeSnapshotsSupport,
IncludeUncommittedBlobsOption, IncludeUncommittedBlobsSupport, IsSynchronousOption,
IsSynchronousSupport, LeaseBreakPeriodOption, LeaseBreakPeriodRequired,
LeaseBreakPeriodSupport, LeaseDurationRequired, LeaseDurationSupport, LeaseIdOption,
LeaseIdRequired, LeaseIdSupport, MaxResultsOption, MaxResultsSupport, MetadataOption,
MetadataSupport, NextMarkerOption, NextMarkerSupport, PageBlobLengthRequired,
PageBlobLengthSupport, PrefixOption, PrefixSupport, ProposedLeaseIdOption,
ProposedLeaseIdRequired, ProposedLeaseIdSupport, RangeOption, RangeRequired, RangeSupport,
SequenceNumberConditionOption, SequenceNumberConditionSupport, SequenceNumberOption,
SequenceNumberSupport, SnapshotOption, SnapshotRequired, SnapshotSupport,
SourceContentMD5Option, SourceContentMD5Support, SourceUrlRequired, SourceUrlSupport,
StoredAccessPolicy, StoredAccessPolicyList, TimeoutOption, TimeoutSupport, UserAgentOption,
UserAgentSupport,
Expand Down
10 changes: 0 additions & 10 deletions sdk/cosmos/src/from_headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,16 +277,6 @@ pub(crate) fn content_location_from_headers(headers: &HeaderMap) -> Result<&str,
.to_str()?)
}

pub(crate) fn content_type_from_headers(headers: &HeaderMap) -> Result<&str, AzureError> {
Ok(headers
.get(http::header::CONTENT_TYPE)
.ok_or_else(|| {
let header = http::header::CONTENT_TYPE;
AzureError::HeaderNotFound(header.as_str().to_owned())
})?
.to_str()?)
}

pub(crate) fn gateway_version_from_headers(headers: &HeaderMap) -> Result<&str, AzureError> {
Ok(headers
.get(HEADER_GATEWAY_VERSION)
Expand Down
1 change: 0 additions & 1 deletion sdk/cosmos/src/headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pub(crate) const HEADER_DOCUMENTDB_IS_UPSERT: &str = "x-ms-documentdb-is-upsert"
pub(crate) const HEADER_INDEXING_DIRECTIVE: &str = "x-ms-indexing-directive"; // [IndexingDirective]
pub(crate) const HEADER_MAX_ITEM_COUNT: &str = "x-ms-max-item-count"; // [u64]
pub(crate) const HEADER_ITEM_COUNT: &str = "x-ms-item-count"; // [u64]
pub(crate) const HEADER_CONTINUATION: &str = "x-ms-continuation"; // [ContinuationToken]
pub(crate) const HEADER_CONSISTENCY_LEVEL: &str = "x-ms-consistency-level"; // [ConsistencyLevel]
pub(crate) const HEADER_SESSION_TOKEN: &str = "x-ms-session-token"; // [ContinuationToken]
pub(crate) const HEADER_ALLOW_MULTIPLE_WRITES: &str = "x-ms-cosmos-allow-tentative-writes"; // [bool]
Expand Down
18 changes: 0 additions & 18 deletions sdk/cosmos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,24 +242,6 @@ pub trait PartitionRangeIdOption<'a> {
}
}

pub trait ContinuationSupport<'a> {
type O;
fn with_continuation(self, continuation: &'a str) -> Self::O;
}

pub trait ContinuationOption<'a> {
fn continuation(&self) -> Option<&'a str>;

#[must_use]
fn add_header(&self, builder: Builder) -> Builder {
if let Some(continuation) = self.continuation() {
builder.header(HEADER_CONTINUATION, continuation)
} else {
builder
}
}
}

pub trait IndexingDirectiveSupport {
type O;
fn with_indexing_directive(self, indexing_directive: IndexingDirective) -> Self::O;
Expand Down
44 changes: 22 additions & 22 deletions sdk/cosmos/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,26 @@ pub use crate::{
AttachmentClient, AttachmentClientRequired, AuthorizationToken, CollectionClient,
CollectionClientRequired, CollectionNameRequired, CollectionNameSupport, CollectionRequired,
CollectionSupport, ConsistencyLevel, ConsistencyLevelOption, ConsistencyLevelSupport,
ContinuationOption, ContinuationSupport, CosmosClient, CosmosClientRequired, DatabaseClient,
DatabaseClientRequired, DatabaseNameRequired, DatabaseNameSupport, DocumentClient,
DocumentIdRequired, DocumentIdSupport, ExpirySecondsOption, ExpirySecondsSupport,
HasStoredProcedureClient, IndexingDirective, IndexingDirectiveOption, IndexingDirectiveSupport,
IndexingPolicyRequired, IndexingPolicySupport, IntoAttachmentClient, IntoCollectionClient,
IntoDatabaseClient, IntoDocumentClient, IntoPermissionClient, IntoStoredProcedureClient,
IntoUserClient, IsUpsertOption, IsUpsertSupport, MaxItemCountOption, MaxItemCountSupport,
MediaRequired, MediaSupport, Offer, OfferRequired, OfferSupport,
ParallelizeCrossPartitionQueryOption, ParallelizeCrossPartitionQuerySupport, ParametersOption,
ParametersSupport, PartitionKeyOption, PartitionKeyRequired, PartitionKeySupport,
PartitionKeys, PartitionKeysOption, PartitionKeysRequired, PartitionKeysSupport,
PartitionRangeIdOption, PartitionRangeIdSupport, PermissionClient, PermissionClientRequired,
QueryCrossPartitionOption, QueryCrossPartitionSupport, QueryRequired, QuerySupport,
StoredProcedureBodyRequired, StoredProcedureBodySupport, StoredProcedureClient,
StoredProcedureClientRequired, StoredProcedureNameRequired, StoredProcedureNameSupport,
TriggerBodyRequired, TriggerBodySupport, TriggerClient, TriggerClientRequired,
TriggerOperationRequired, TriggerOperationSupport, TriggerTypeRequired, TriggerTypeSupport,
UserClient, UserClientRequired, UserDefinedFunctionBodyRequired,
UserDefinedFunctionBodySupport, UserDefinedFunctionClient, UserDefinedFunctionClientRequired,
UserName, UserNameRequired, UserNameSupport, WithAttachmentClient, WithCollectionClient,
WithDatabaseClient, WithDocumentClient, WithPermissionClient, WithStoredProcedureClient,
WithTriggerClient, WithUserClient, WithUserDefinedFunctionClient,
CosmosClient, CosmosClientRequired, DatabaseClient, DatabaseClientRequired,
DatabaseNameRequired, DatabaseNameSupport, DocumentClient, DocumentIdRequired,
DocumentIdSupport, ExpirySecondsOption, ExpirySecondsSupport, HasStoredProcedureClient,
IndexingDirective, IndexingDirectiveOption, IndexingDirectiveSupport, IndexingPolicyRequired,
IndexingPolicySupport, IntoAttachmentClient, IntoCollectionClient, IntoDatabaseClient,
IntoDocumentClient, IntoPermissionClient, IntoStoredProcedureClient, IntoUserClient,
IsUpsertOption, IsUpsertSupport, MaxItemCountOption, MaxItemCountSupport, MediaRequired,
MediaSupport, Offer, OfferRequired, OfferSupport, ParallelizeCrossPartitionQueryOption,
ParallelizeCrossPartitionQuerySupport, ParametersOption, ParametersSupport, PartitionKeyOption,
PartitionKeyRequired, PartitionKeySupport, PartitionKeys, PartitionKeysOption,
PartitionKeysRequired, PartitionKeysSupport, PartitionRangeIdOption, PartitionRangeIdSupport,
PermissionClient, PermissionClientRequired, QueryCrossPartitionOption,
QueryCrossPartitionSupport, QueryRequired, QuerySupport, StoredProcedureBodyRequired,
StoredProcedureBodySupport, StoredProcedureClient, StoredProcedureClientRequired,
StoredProcedureNameRequired, StoredProcedureNameSupport, TriggerBodyRequired,
TriggerBodySupport, TriggerClient, TriggerClientRequired, TriggerOperationRequired,
TriggerOperationSupport, TriggerTypeRequired, TriggerTypeSupport, UserClient,
UserClientRequired, UserDefinedFunctionBodyRequired, UserDefinedFunctionBodySupport,
UserDefinedFunctionClient, UserDefinedFunctionClientRequired, UserName, UserNameRequired,
UserNameSupport, WithAttachmentClient, WithCollectionClient, WithDatabaseClient,
WithDocumentClient, WithPermissionClient, WithStoredProcedureClient, WithTriggerClient,
WithUserClient, WithUserDefinedFunctionClient,
};
4 changes: 3 additions & 1 deletion sdk/cosmos/src/responses/get_attachment_response.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::from_headers::*;
use crate::{Attachment, IndexingDirective, ResourceQuota};
use azure_core::errors::AzureError;
use azure_core::headers::{etag_from_headers, session_token_from_headers};
use azure_core::headers::{
content_type_from_headers, etag_from_headers, session_token_from_headers,
};
use azure_core::SessionToken;
use chrono::{DateTime, Utc};
use hyper::header::HeaderMap;
Expand Down
1 change: 1 addition & 0 deletions sdk/storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ account = []
blob = []
queue = []
table = []
adls_gen2 = []
90 changes: 90 additions & 0 deletions sdk/storage/src/adls_gen2/filesystem/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
pub mod requests;
pub mod responses;

use azure_core::errors::AzureError;
use azure_core::headers::{CONTINUATION, PROPERTIES};
use azure_core::incompletevector::IncompleteVector;
use azure_core::util::HeaderMapExt;
use http::{request::Builder, HeaderMap};
use serde::Deserialize;

pub trait FilesystemRequired<'a> {
fn filesystem(&self) -> &'a str;
}

pub trait FilesystemSupport<'a> {
type O;
fn with_filesystem(self, filesystem: &'a str) -> Self::O;
}

pub trait PropertiesOption<'a> {
fn properties(&self) -> Option<&'a str>;

#[must_use]
fn add_header(&self, mut builder: Builder) -> Builder {
if let Some(properties) = self.properties() {
builder = builder.header(PROPERTIES, properties);
}
builder
}
}

pub trait PropertiesSupport<'a> {
type O;
fn with_properties(self, properties: &'a str) -> Self::O;
}

pub(crate) fn properties_from_headers(headers: &HeaderMap) -> Result<String, AzureError> {
let properties = headers
.get_as_str(azure_core::headers::PROPERTIES)
.ok_or_else(|| AzureError::HeaderNotFound(azure_core::headers::PROPERTIES.to_owned()))?;
Ok(properties.to_owned())
}

pub(crate) fn namespace_enabled_from_headers(headers: &HeaderMap) -> Result<bool, AzureError> {
let namespace_enabled = headers
.get(azure_core::headers::NAMESPACE_ENABLED)
.ok_or_else(|| {
AzureError::HeaderNotFound(azure_core::headers::NAMESPACE_ENABLED.to_owned())
})?
.to_str()?;

let namespace_enabled = namespace_enabled.parse::<bool>()?;
Ok(namespace_enabled)
}

#[derive(Debug, Clone, Deserialize)]
pub struct Filesystem {
pub etag: String,
#[serde(rename = "lastModified")]
pub last_modified: String,
pub name: String,
}

#[inline]
pub(crate) fn incomplete_vector_from_response(
headers: &HeaderMap,
body: &str,
) -> Result<IncompleteVector<Filesystem>, AzureError> {
trace!("body = {}", body);

let continuation = match headers.get_as_string(CONTINUATION) {
Some(ref ct) if ct == "" => None,
Some(ct) => Some(ct),
None => None,
};

debug!("continuation == {:?}", continuation);

#[derive(Deserialize)]
struct Filesystems {
filesystems: Vec<Filesystem>,
}

let Filesystems { filesystems } = serde_json::from_str(&body)?;

Ok(IncompleteVector::<Filesystem>::new(
continuation,
filesystems,
))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"name": "CreateFilesystemBuilder",
"derive": "Debug, Clone",
"uses": [
"crate::core::prelude::*",
"crate::filesystem::responses::CreateFilesystemResponse",
"crate::filesystem::{FilesystemRequired, FilesystemSupport, PropertiesOption, PropertiesSupport}",
"azure_core::errors::{check_status_extract_headers_and_body, AzureError}",
"azure_core::{ClientRequestIdOption, ClientRequestIdSupport, TimeoutOption, TimeoutSupport}",
"azure_core::{No, ToAssign, Yes}",
"hyper::{Method, StatusCode}",
"std::marker::PhantomData"
],
"inline": true,
"extra_types": [
"'a",
"C"
],
"extra_wheres": [
"C: Client"
],
"constructor_fields": [
{
"name": "client",
"field_type": "&'a C",
"trait_get": "ClientRequired<'a, C>"
}
],
"fields": [
{
"name": "filesystem",
"field_type": "&'a str",
"builder_type": "FilesystemSet",
"optional": false,
"trait_get": "FilesystemRequired<'a>",
"trait_set": "FilesystemSupport<'a>"
},
{
"name": "timeout",
"field_type": "u64",
"optional": true,
"trait_get": "TimeoutOption",
"trait_set": "TimeoutSupport"
},
{
"name": "properties",
"field_type": "&'a str",
"optional": true,
"trait_get": "PropertiesOption",
"trait_set": "PropertiesSupport"
},
{
"name": "client_request_id",
"field_type": "&'a str",
"optional": true,
"trait_get": "ClientRequestIdOption<'a>",
"trait_set": "ClientRequestIdSupport<'a>"
}
]
}
Loading

0 comments on commit 9907c22

Please sign in to comment.