1# -------------------------------------------------------------------------
2# Copyright (c) Microsoft Corporation. All rights reserved.
3# Licensed under the MIT License. See License.txt in the project root for
4# license information.
5# --------------------------------------------------------------------------
6from ._shared.base_client import parse_connection_str
7from ._data_lake_file_client import DataLakeFileClient
8from ._models import DirectoryProperties
9from ._path_client import PathClient
10
11
12class DataLakeDirectoryClient(PathClient):
13    """A client to interact with the DataLake directory, even if the directory may not yet exist.
14
15    For operations relating to a specific subdirectory or file under the directory, a directory client or file client
16    can be retrieved using the :func:`~get_sub_directory_client` or :func:`~get_file_client` functions.
17
18    :ivar str url:
19        The full endpoint URL to the file system, including SAS token if used.
20    :ivar str primary_endpoint:
21        The full primary endpoint URL.
22    :ivar str primary_hostname:
23        The hostname of the primary endpoint.
24    :param str account_url:
25        The URI to the storage account.
26    :param file_system_name:
27        The file system for the directory or files.
28    :type file_system_name: str
29    :param directory_name:
30        The whole path of the directory. eg. {directory under file system}/{directory to interact with}
31    :type directory_name: str
32    :param credential:
33        The credentials with which to authenticate. This is optional if the
34        account URL already has a SAS token. The value can be a SAS token string, and account
35        shared access key, or an instance of a TokenCredentials class from azure.identity.
36        If the URL already has a SAS token, specifying an explicit credential will take priority.
37
38    .. admonition:: Example:
39
40        .. literalinclude:: ../samples/datalake_samples_instantiate_client.py
41            :start-after: [START instantiate_directory_client_from_conn_str]
42            :end-before: [END instantiate_directory_client_from_conn_str]
43            :language: python
44            :dedent: 4
45            :caption: Creating the DataLakeServiceClient from connection string.
46    """
47    def __init__(
48        self, account_url,  # type: str
49        file_system_name,  # type: str
50        directory_name,  # type: str
51        credential=None,  # type: Optional[Any]
52        **kwargs  # type: Any
53    ):
54        # type: (...) -> None
55        super(DataLakeDirectoryClient, self).__init__(account_url, file_system_name, path_name=directory_name,
56                                                      credential=credential, **kwargs)
57
58    @classmethod
59    def from_connection_string(
60            cls, conn_str,  # type: str
61            file_system_name,  # type: str
62            directory_name,  # type: str
63            credential=None,  # type: Optional[Any]
64            **kwargs  # type: Any
65        ):  # type: (...) -> DataLakeDirectoryClient
66        """
67        Create DataLakeDirectoryClient from a Connection String.
68
69        :param str conn_str:
70            A connection string to an Azure Storage account.
71        :param file_system_name:
72            The name of file system to interact with.
73        :type file_system_name: str
74        :param directory_name:
75            The name of directory to interact with. The directory is under file system.
76        :type directory_name: str
77        :param credential:
78            The credentials with which to authenticate. This is optional if the
79            account URL already has a SAS token, or the connection string already has shared
80            access key values. The value can be a SAS token string, and account shared access
81            key, or an instance of a TokenCredentials class from azure.identity.
82            Credentials provided here will take precedence over those in the connection string.
83        :return a DataLakeDirectoryClient
84        :rtype ~azure.storage.filedatalake.DataLakeDirectoryClient
85        """
86        account_url, _, credential = parse_connection_str(conn_str, credential, 'dfs')
87        return cls(
88            account_url, file_system_name=file_system_name, directory_name=directory_name,
89            credential=credential, **kwargs)
90
91    def create_directory(self, metadata=None,  # type: Optional[Dict[str, str]]
92                         **kwargs):
93        # type: (...) -> Dict[str, Union[str, datetime]]
94        """
95        Create a new directory.
96
97        :param metadata:
98            Name-value pairs associated with the file as metadata.
99        :type metadata: dict(str, str)
100        :keyword ~azure.storage.filedatalake.ContentSettings content_settings:
101            ContentSettings object used to set path properties.
102        :keyword lease:
103            Required if the file has an active lease. Value can be a DataLakeLeaseClient object
104            or the lease ID as a string.
105        :paramtype lease: ~azure.storage.filedatalake.DataLakeLeaseClient or str
106        :keyword str umask:
107            Optional and only valid if Hierarchical Namespace is enabled for the account.
108            When creating a file or directory and the parent folder does not have a default ACL,
109            the umask restricts the permissions of the file or directory to be created.
110            The resulting permission is given by p & ^u, where p is the permission and u is the umask.
111            For example, if p is 0777 and u is 0057, then the resulting permission is 0720.
112            The default permission is 0777 for a directory and 0666 for a file. The default umask is 0027.
113            The umask must be specified in 4-digit octal notation (e.g. 0766).
114        :keyword str permissions:
115            Optional and only valid if Hierarchical Namespace
116            is enabled for the account. Sets POSIX access permissions for the file
117            owner, the file owning group, and others. Each class may be granted
118            read, write, or execute permission.  The sticky bit is also supported.
119            Both symbolic (rwxrw-rw-) and 4-digit octal notation (e.g. 0766) are
120            supported.
121        :keyword ~datetime.datetime if_modified_since:
122            A DateTime value. Azure expects the date value passed in to be UTC.
123            If timezone is included, any non-UTC datetimes will be converted to UTC.
124            If a date is passed in without timezone info, it is assumed to be UTC.
125            Specify this header to perform the operation only
126            if the resource has been modified since the specified time.
127        :keyword ~datetime.datetime if_unmodified_since:
128            A DateTime value. Azure expects the date value passed in to be UTC.
129            If timezone is included, any non-UTC datetimes will be converted to UTC.
130            If a date is passed in without timezone info, it is assumed to be UTC.
131            Specify this header to perform the operation only if
132            the resource has not been modified since the specified date/time.
133        :keyword str etag:
134            An ETag value, or the wildcard character (*). Used to check if the resource has changed,
135            and act according to the condition specified by the `match_condition` parameter.
136        :keyword ~azure.core.MatchConditions match_condition:
137            The match condition to use upon the etag.
138        :keyword int timeout:
139            The timeout parameter is expressed in seconds.
140        :return: response dict (Etag and last modified).
141
142        .. admonition:: Example:
143
144            .. literalinclude:: ../samples/datalake_samples_directory.py
145                :start-after: [START create_directory]
146                :end-before: [END create_directory]
147                :language: python
148                :dedent: 8
149                :caption: Create directory.
150        """
151        return self._create('directory', metadata=metadata, **kwargs)
152
153    def delete_directory(self, **kwargs):
154        # type: (...) -> None
155        """
156        Marks the specified directory for deletion.
157
158        :keyword lease:
159            Required if the file has an active lease. Value can be a LeaseClient object
160            or the lease ID as a string.
161        :paramtype lease: ~azure.storage.filedatalake.DataLakeLeaseClient or str
162        :keyword ~datetime.datetime if_modified_since:
163            A DateTime value. Azure expects the date value passed in to be UTC.
164            If timezone is included, any non-UTC datetimes will be converted to UTC.
165            If a date is passed in without timezone info, it is assumed to be UTC.
166            Specify this header to perform the operation only
167            if the resource has been modified since the specified time.
168        :keyword ~datetime.datetime if_unmodified_since:
169            A DateTime value. Azure expects the date value passed in to be UTC.
170            If timezone is included, any non-UTC datetimes will be converted to UTC.
171            If a date is passed in without timezone info, it is assumed to be UTC.
172            Specify this header to perform the operation only if
173            the resource has not been modified since the specified date/time.
174        :keyword str etag:
175            An ETag value, or the wildcard character (*). Used to check if the resource has changed,
176            and act according to the condition specified by the `match_condition` parameter.
177        :keyword ~azure.core.MatchConditions match_condition:
178            The match condition to use upon the etag.
179        :keyword int timeout:
180            The timeout parameter is expressed in seconds.
181        :return: None
182
183        .. admonition:: Example:
184
185            .. literalinclude:: ../samples/datalake_samples_directory.py
186                :start-after: [START delete_directory]
187                :end-before: [END delete_directory]
188                :language: python
189                :dedent: 4
190                :caption: Delete directory.
191        """
192        return self._delete(**kwargs)
193
194    def get_directory_properties(self, **kwargs):
195        # type: (**Any) -> DirectoryProperties
196        """Returns all user-defined metadata, standard HTTP properties, and
197        system properties for the directory. It does not return the content of the directory.
198
199        :keyword lease:
200            Required if the directory or file has an active lease. Value can be a DataLakeLeaseClient object
201            or the lease ID as a string.
202        :paramtype lease: ~azure.storage.filedatalake.DataLakeLeaseClient or str
203        :keyword ~datetime.datetime if_modified_since:
204            A DateTime value. Azure expects the date value passed in to be UTC.
205            If timezone is included, any non-UTC datetimes will be converted to UTC.
206            If a date is passed in without timezone info, it is assumed to be UTC.
207            Specify this header to perform the operation only
208            if the resource has been modified since the specified time.
209        :keyword ~datetime.datetime if_unmodified_since:
210            A DateTime value. Azure expects the date value passed in to be UTC.
211            If timezone is included, any non-UTC datetimes will be converted to UTC.
212            If a date is passed in without timezone info, it is assumed to be UTC.
213            Specify this header to perform the operation only if
214            the resource has not been modified since the specified date/time.
215        :keyword str etag:
216            An ETag value, or the wildcard character (*). Used to check if the resource has changed,
217            and act according to the condition specified by the `match_condition` parameter.
218        :keyword ~azure.core.MatchConditions match_condition:
219            The match condition to use upon the etag.
220        :keyword int timeout:
221            The timeout parameter is expressed in seconds.
222        :rtype: DirectoryProperties
223
224        .. admonition:: Example:
225
226            .. literalinclude:: ../samples/datalake_samples_directory.py
227                :start-after: [START get_directory_properties]
228                :end-before: [END get_directory_properties]
229                :language: python
230                :dedent: 4
231                :caption: Getting the properties for a file/directory.
232        """
233        blob_properties = self._get_path_properties(**kwargs)
234        return DirectoryProperties._from_blob_properties(blob_properties)  # pylint: disable=protected-access
235
236    def rename_directory(self, new_name,  # type: str
237                         **kwargs):
238        # type: (**Any) -> DataLakeDirectoryClient
239        """
240        Rename the source directory.
241
242        :param str new_name:
243            the new directory name the user want to rename to.
244            The value must have the following format: "{filesystem}/{directory}/{subdirectory}".
245        :keyword source_lease:
246            A lease ID for the source path. If specified,
247            the source path must have an active lease and the leaase ID must
248            match.
249        :paramtype source_lease: ~azure.storage.filedatalake.DataLakeLeaseClient or str
250        :keyword lease:
251            Required if the file/directory has an active lease. Value can be a LeaseClient object
252            or the lease ID as a string.
253        :paramtype lease: ~azure.storage.filedatalake.DataLakeLeaseClient or str
254        :keyword ~datetime.datetime if_modified_since:
255            A DateTime value. Azure expects the date value passed in to be UTC.
256            If timezone is included, any non-UTC datetimes will be converted to UTC.
257            If a date is passed in without timezone info, it is assumed to be UTC.
258            Specify this header to perform the operation only
259            if the resource has been modified since the specified time.
260        :keyword ~datetime.datetime if_unmodified_since:
261            A DateTime value. Azure expects the date value passed in to be UTC.
262            If timezone is included, any non-UTC datetimes will be converted to UTC.
263            If a date is passed in without timezone info, it is assumed to be UTC.
264            Specify this header to perform the operation only if
265            the resource has not been modified since the specified date/time.
266        :keyword str etag:
267            An ETag value, or the wildcard character (*). Used to check if the resource has changed,
268            and act according to the condition specified by the `match_condition` parameter.
269        :keyword ~azure.core.MatchConditions match_condition:
270            The match condition to use upon the etag.
271        :keyword ~datetime.datetime source_if_modified_since:
272            A DateTime value. Azure expects the date value passed in to be UTC.
273            If timezone is included, any non-UTC datetimes will be converted to UTC.
274            If a date is passed in without timezone info, it is assumed to be UTC.
275            Specify this header to perform the operation only
276            if the resource has been modified since the specified time.
277        :keyword ~datetime.datetime source_if_unmodified_since:
278            A DateTime value. Azure expects the date value passed in to be UTC.
279            If timezone is included, any non-UTC datetimes will be converted to UTC.
280            If a date is passed in without timezone info, it is assumed to be UTC.
281            Specify this header to perform the operation only if
282            the resource has not been modified since the specified date/time.
283        :keyword str source_etag:
284            The source ETag value, or the wildcard character (*). Used to check if the resource has changed,
285            and act according to the condition specified by the `match_condition` parameter.
286        :keyword ~azure.core.MatchConditions source_match_condition:
287            The source match condition to use upon the etag.
288        :keyword int timeout:
289            The timeout parameter is expressed in seconds.
290        :return: DataLakeDirectoryClient
291
292        .. admonition:: Example:
293
294            .. literalinclude:: ../samples/datalake_samples_directory.py
295                :start-after: [START rename_directory]
296                :end-before: [END rename_directory]
297                :language: python
298                :dedent: 4
299                :caption: Rename the source directory.
300        """
301        new_name = new_name.strip('/')
302        new_file_system = new_name.split('/')[0]
303        path = new_name[len(new_file_system):]
304
305        new_directory_client = DataLakeDirectoryClient(
306            self.url, new_file_system, directory_name=path, credential=self._raw_credential,
307            _hosts=self._hosts, _configuration=self._config, _pipeline=self._pipeline,
308            require_encryption=self.require_encryption,
309            key_encryption_key=self.key_encryption_key,
310            key_resolver_function=self.key_resolver_function)
311        new_directory_client._rename_path('/'+self.file_system_name+'/'+self.path_name,  # pylint: disable=protected-access
312                                          **kwargs)
313        return new_directory_client
314
315    def create_sub_directory(self, sub_directory,  # type: Union[DirectoryProperties, str]
316                             metadata=None,  # type: Optional[Dict[str, str]]
317                             **kwargs):
318        # type: (...) -> DataLakeDirectoryClient
319        """
320        Create a subdirectory and return the subdirectory client to be interacted with.
321
322        :param sub_directory:
323            The directory with which to interact. This can either be the name of the directory,
324            or an instance of DirectoryProperties.
325        :type sub_directory: str or ~azure.storage.filedatalake.DirectoryProperties
326        :param metadata:
327            Name-value pairs associated with the file as metadata.
328        :type metadata: dict(str, str)
329        :keyword ~azure.storage.filedatalake.ContentSettings content_settings:
330            ContentSettings object used to set path properties.
331        :keyword lease:
332            Required if the file has an active lease. Value can be a DataLakeLeaseClient object
333            or the lease ID as a string.
334        :paramtype lease: ~azure.storage.filedatalake.DataLakeLeaseClient or str
335        :keyword str umask:
336            Optional and only valid if Hierarchical Namespace is enabled for the account.
337            When creating a file or directory and the parent folder does not have a default ACL,
338            the umask restricts the permissions of the file or directory to be created.
339            The resulting permission is given by p & ^u, where p is the permission and u is the umask.
340            For example, if p is 0777 and u is 0057, then the resulting permission is 0720.
341            The default permission is 0777 for a directory and 0666 for a file. The default umask is 0027.
342            The umask must be specified in 4-digit octal notation (e.g. 0766).
343        :keyword str permissions:
344            Optional and only valid if Hierarchical Namespace
345            is enabled for the account. Sets POSIX access permissions for the file
346            owner, the file owning group, and others. Each class may be granted
347            read, write, or execute permission.  The sticky bit is also supported.
348            Both symbolic (rwxrw-rw-) and 4-digit octal notation (e.g. 0766) are
349            supported.
350        :keyword ~datetime.datetime if_modified_since:
351            A DateTime value. Azure expects the date value passed in to be UTC.
352            If timezone is included, any non-UTC datetimes will be converted to UTC.
353            If a date is passed in without timezone info, it is assumed to be UTC.
354            Specify this header to perform the operation only
355            if the resource has been modified since the specified time.
356        :keyword ~datetime.datetime if_unmodified_since:
357            A DateTime value. Azure expects the date value passed in to be UTC.
358            If timezone is included, any non-UTC datetimes will be converted to UTC.
359            If a date is passed in without timezone info, it is assumed to be UTC.
360            Specify this header to perform the operation only if
361            the resource has not been modified since the specified date/time.
362        :keyword str etag:
363            An ETag value, or the wildcard character (*). Used to check if the resource has changed,
364            and act according to the condition specified by the `match_condition` parameter.
365        :keyword ~azure.core.MatchConditions match_condition:
366            The match condition to use upon the etag.
367        :keyword int timeout:
368            The timeout parameter is expressed in seconds.
369        :return: DataLakeDirectoryClient for the subdirectory.
370        """
371        subdir = self.get_sub_directory_client(sub_directory)
372        subdir.create_directory(metadata=metadata, **kwargs)
373        return subdir
374
375    def delete_sub_directory(self, sub_directory,  # type: Union[DirectoryProperties, str]
376                             **kwargs):
377        # type: (...) -> DataLakeDirectoryClient
378        """
379        Marks the specified subdirectory for deletion.
380
381        :param sub_directory:
382            The directory with which to interact. This can either be the name of the directory,
383            or an instance of DirectoryProperties.
384        :type sub_directory: str or ~azure.storage.filedatalake.DirectoryProperties
385        :keyword lease:
386            Required if the file has an active lease. Value can be a LeaseClient object
387            or the lease ID as a string.
388        :paramtype lease: ~azure.storage.filedatalake.DataLakeLeaseClient or str
389        :keyword ~datetime.datetime if_modified_since:
390            A DateTime value. Azure expects the date value passed in to be UTC.
391            If timezone is included, any non-UTC datetimes will be converted to UTC.
392            If a date is passed in without timezone info, it is assumed to be UTC.
393            Specify this header to perform the operation only
394            if the resource has been modified since the specified time.
395        :keyword ~datetime.datetime if_unmodified_since:
396            A DateTime value. Azure expects the date value passed in to be UTC.
397            If timezone is included, any non-UTC datetimes will be converted to UTC.
398            If a date is passed in without timezone info, it is assumed to be UTC.
399            Specify this header to perform the operation only if
400            the resource has not been modified since the specified date/time.
401        :keyword str etag:
402            An ETag value, or the wildcard character (*). Used to check if the resource has changed,
403            and act according to the condition specified by the `match_condition` parameter.
404        :keyword ~azure.core.MatchConditions match_condition:
405            The match condition to use upon the etag.
406        :keyword int timeout:
407            The timeout parameter is expressed in seconds.
408        :return: DataLakeDirectoryClient for the subdirectory
409        """
410        subdir = self.get_sub_directory_client(sub_directory)
411        subdir.delete_directory(**kwargs)
412        return subdir
413
414    def create_file(self, file,  # type: Union[FileProperties, str]
415                    **kwargs):
416        # type: (...) -> DataLakeFileClient
417        """
418        Create a new file and return the file client to be interacted with.
419
420        :param file:
421            The file with which to interact. This can either be the name of the file,
422            or an instance of FileProperties.
423        :type file: str or ~azure.storage.filedatalake.FileProperties
424        :keyword ~azure.storage.filedatalake.ContentSettings content_settings:
425            ContentSettings object used to set path properties.
426        :keyword metadata:
427            Name-value pairs associated with the file as metadata.
428        :type metadata: dict(str, str)
429        :keyword lease:
430            Required if the file has an active lease. Value can be a DataLakeLeaseClient object
431            or the lease ID as a string.
432        :paramtype lease: ~azure.storage.filedatalake.DataLakeLeaseClient or str
433        :keyword str umask:
434            Optional and only valid if Hierarchical Namespace is enabled for the account.
435            When creating a file or directory and the parent folder does not have a default ACL,
436            the umask restricts the permissions of the file or directory to be created.
437            The resulting permission is given by p & ^u, where p is the permission and u is the umask.
438            For example, if p is 0777 and u is 0057, then the resulting permission is 0720.
439            The default permission is 0777 for a directory and 0666 for a file. The default umask is 0027.
440            The umask must be specified in 4-digit octal notation (e.g. 0766).
441        :keyword str permissions:
442            Optional and only valid if Hierarchical Namespace
443            is enabled for the account. Sets POSIX access permissions for the file
444            owner, the file owning group, and others. Each class may be granted
445            read, write, or execute permission.  The sticky bit is also supported.
446            Both symbolic (rwxrw-rw-) and 4-digit octal notation (e.g. 0766) are
447            supported.
448        :keyword ~datetime.datetime if_modified_since:
449            A DateTime value. Azure expects the date value passed in to be UTC.
450            If timezone is included, any non-UTC datetimes will be converted to UTC.
451            If a date is passed in without timezone info, it is assumed to be UTC.
452            Specify this header to perform the operation only
453            if the resource has been modified since the specified time.
454        :keyword ~datetime.datetime if_unmodified_since:
455            A DateTime value. Azure expects the date value passed in to be UTC.
456            If timezone is included, any non-UTC datetimes will be converted to UTC.
457            If a date is passed in without timezone info, it is assumed to be UTC.
458            Specify this header to perform the operation only if
459            the resource has not been modified since the specified date/time.
460        :keyword str etag:
461            An ETag value, or the wildcard character (*). Used to check if the resource has changed,
462            and act according to the condition specified by the `match_condition` parameter.
463        :keyword ~azure.core.MatchConditions match_condition:
464            The match condition to use upon the etag.
465        :keyword int timeout:
466            The timeout parameter is expressed in seconds.
467        :return: DataLakeFileClient
468        """
469        file_client = self.get_file_client(file)
470        file_client.create_file(**kwargs)
471        return file_client
472
473    def get_file_client(self, file  # type: Union[FileProperties, str]
474                        ):
475        # type: (...) -> DataLakeFileClient
476        """Get a client to interact with the specified file.
477
478        The file need not already exist.
479
480        :param file:
481            The file with which to interact. This can either be the name of the file,
482            or an instance of FileProperties. eg. directory/subdirectory/file
483        :type file: str or ~azure.storage.filedatalake.FileProperties
484        :returns: A DataLakeFileClient.
485        :rtype: ~azure.storage.filedatalake..DataLakeFileClient
486        """
487        try:
488            file_path = file.name
489        except AttributeError:
490            file_path = self.path_name + '/' + file
491
492        return DataLakeFileClient(
493            self.url, self.file_system_name, file_path=file_path, credential=self._raw_credential,
494            _hosts=self._hosts, _configuration=self._config, _pipeline=self._pipeline,
495            require_encryption=self.require_encryption,
496            key_encryption_key=self.key_encryption_key,
497            key_resolver_function=self.key_resolver_function)
498
499    def get_sub_directory_client(self, sub_directory  # type: Union[DirectoryProperties, str]
500                                 ):
501        # type: (...) -> DataLakeDirectoryClient
502        """Get a client to interact with the specified subdirectory of the current directory.
503
504        The sub subdirectory need not already exist.
505
506        :param sub_directory:
507            The directory with which to interact. This can either be the name of the directory,
508            or an instance of DirectoryProperties.
509        :type sub_directory: str or ~azure.storage.filedatalake.DirectoryProperties
510        :returns: A DataLakeDirectoryClient.
511        :rtype: ~azure.storage.filedatalake.DataLakeDirectoryClient
512        """
513        try:
514            subdir_path = sub_directory.name
515        except AttributeError:
516            subdir_path = self.path_name + '/' + sub_directory
517
518        return DataLakeDirectoryClient(
519            self.url, self.file_system_name, directory_name=subdir_path, credential=self._raw_credential,
520            _hosts=self._hosts, _configuration=self._config, _pipeline=self._pipeline,
521            require_encryption=self.require_encryption,
522            key_encryption_key=self.key_encryption_key,
523            key_resolver_function=self.key_resolver_function)
524