Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Appconfig] consistency check #19281

Merged
merged 66 commits into from
Jun 30, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
5f23239
adding docstrings for displayname and description
seankane-msft Jun 16, 2021
184451f
changing key -> feature_id
seankane-msft Jun 16, 2021
4245462
fixing up secret reference for consistency
seankane-msft Jun 16, 2021
565be70
pylint fix
seankane-msft Jun 16, 2021
971ba34
updates to clients
seankane-msft Jun 21, 2021
89184a9
fixing tests
seankane-msft Jun 21, 2021
5679d8d
updates to clients
seankane-msft Jun 21, 2021
5125053
merge conflicts
seankane-msft Jun 21, 2021
8f60ae5
changing base_url -> endpoint
seankane-msft Jun 21, 2021
2d3cd14
undoing changes to generated
seankane-msft Jun 21, 2021
b9d01c8
adding changelog
seankane-msft Jun 21, 2021
4371f0f
updating changelog, enabled is kwarg
seankane-msft Jun 21, 2021
375481e
lint fixes
seankane-msft Jun 21, 2021
b791000
formatting
seankane-msft Jun 22, 2021
84939ed
using key instead of feature_id
seankane-msft Jun 22, 2021
8a01a16
feature_id strips key prefix
seankane-msft Jun 22, 2021
f8d277c
working for sync
seankane-msft Jun 22, 2021
c58a41f
removing if conditional on delete
seankane-msft Jun 22, 2021
bc2263d
doc and typehint
seankane-msft Jun 22, 2021
8fcede6
adding delete overload to async
seankane-msft Jun 22, 2021
b40fd10
fixing tests
seankane-msft Jun 22, 2021
37d0ece
adding back label to not be breaking
seankane-msft Jun 22, 2021
de563be
non breaking for delete
seankane-msft Jun 22, 2021
c8a4aa9
undoing breaking change
seankane-msft Jun 22, 2021
163decc
undoing rename of endpoint
seankane-msft Jun 22, 2021
f5154d0
undoing more breaking changes
seankane-msft Jun 22, 2021
d65d39f
fixing up a couple more
seankane-msft Jun 22, 2021
9d9b932
one more removal
seankane-msft Jun 22, 2021
b2c2450
changelog
seankane-msft Jun 22, 2021
f130b68
fixing breaking change
seankane-msft Jun 22, 2021
e53bda8
secret reference issue, breaking change fixed
seankane-msft Jun 22, 2021
6378d76
pylint and mypy fixes
seankane-msft Jun 22, 2021
cfb9642
more of xiangs feedback
seankane-msft Jun 22, 2021
4ac7be2
fixing key and feature_id
seankane-msft Jun 22, 2021
bdc7d76
working with xiang
seankane-msft Jun 22, 2021
5c93cb5
Merge branch 'main' of https://github.com/Azure/azure-sdk-for-python …
seankane-msft Jun 23, 2021
fe14053
fixed secref
seankane-msft Jun 23, 2021
c73406b
fixed feature flag
seankane-msft Jun 23, 2021
4c87a30
fixed feature filter tests
seankane-msft Jun 23, 2021
1ba6c4c
mypy and pylint fixes
seankane-msft Jun 23, 2021
813baad
updating tests
seankane-msft Jun 23, 2021
2242415
bringing in overload
seankane-msft Jun 23, 2021
8825de1
fixes
seankane-msft Jun 23, 2021
fd6e205
remove overload
seankane-msft Jun 23, 2021
1e844a7
fixing changelog
seankane-msft Jun 23, 2021
4242112
fixing docstrings
seankane-msft Jun 23, 2021
675a926
fixing attribute map
seankane-msft Jun 23, 2021
2cc0ed4
fixing for py2.7
seankane-msft Jun 24, 2021
d526af6
mypy, pylint, and py2.7 fixes
seankane-msft Jun 24, 2021
218aaa9
pylint fix
seankane-msft Jun 24, 2021
94114a7
renaming
seankane-msft Jun 24, 2021
a1d8216
testing typeerror
seankane-msft Jun 24, 2021
f94ebbb
value property instead of enabled and filters
seankane-msft Jun 25, 2021
8e63b29
fixing value configuring
seankane-msft Jun 25, 2021
576dbec
fixing tests for only value @property on featflag
seankane-msft Jun 25, 2021
6f39042
working for secref
seankane-msft Jun 25, 2021
5d1a5ce
pylint and mypy fixes
seankane-msft Jun 28, 2021
c725580
test fix
seankane-msft Jun 28, 2021
108816d
cleaning up
seankane-msft Jun 28, 2021
dd84819
xiangs comment
seankane-msft Jun 28, 2021
01e02d8
Merge branch 'main' of https://github.com/Azure/azure-sdk-for-python …
seankane-msft Jun 28, 2021
3330b66
patching storage stuff
seankane-msft Jun 28, 2021
ad4d592
fixing value property
seankane-msft Jun 29, 2021
1647c2e
Merge branch 'main' of https://github.com/Azure/azure-sdk-for-python …
seankane-msft Jun 29, 2021
8ddc1e3
to generated update
seankane-msft Jun 29, 2021
c9cc8f3
Merge branch 'main' into appconfig-consistency
seankane-msft Jun 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sdk/appconfiguration/azure-appconfiguration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ from azure.appconfiguration import AzureAppConfigurationClient

credential = DefaultAzureCredential()

client = AzureAppConfigurationClient(base_url="your_endpoint_url", credential=credential)
client = AzureAppConfigurationClient(endpoint="your_endpoint_url", credential=credential)
```

## Key concepts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# license information.
# -------------------------------------------------------------------------
import binascii
from typing import Optional, Any, Mapping, Union
from typing import Optional, Any, Union, TYPE_CHECKING
from requests.structures import CaseInsensitiveDict
from azure.core import MatchConditions
from azure.core.pipeline import Pipeline
Expand Down Expand Up @@ -39,44 +39,37 @@
from ._sync_token import SyncTokenPolicy
from ._user_agent import USER_AGENT

try:
from typing import TYPE_CHECKING
except ImportError:
TYPE_CHECKING = False

if TYPE_CHECKING:
from azure.core.paging import ItemPaged
from azure.core.credentials import TokenCredential


class AzureAppConfigurationClient:
"""Represents an client that calls restful API of Azure App Configuration service.

:param str base_url: base url of the service
:param str endpoint: base url of the service
:param credential: An object which can provide secrets for the app configuration service
:type credential: :class:`~azure.appconfiguration.AppConfigConnectionStringCredential`
:keyword Pipeline pipeline: If omitted, the standard pipeline is used.
:keyword HttpTransport transport: If omitted, the standard pipeline is used.
:keyword list[HTTPPolicy] policies: If omitted, the standard pipeline is used.
:type credential: :class:`~azure.appconfiguration.AppConfigConnectionStringCredential` or :class:`~azure.core.credentials.TokenCredential`

"""

# pylint:disable=protected-access

def __init__(self, base_url, credential, **kwargs):
# type: (str, Any, **Any) -> None
def __init__(self, endpoint, credential, **kwargs):
seankane-msft marked this conversation as resolved.
Show resolved Hide resolved
# type: (str, Union[AppConfigConnectionStringCredential, TokenCredential], **Any) -> None
try:
if not base_url.lower().startswith("http"):
base_url = "https://" + base_url
if not endpoint.lower().startswith("http"):
endpoint = "https://" + endpoint
except AttributeError:
raise ValueError("Base URL must be a string.")

if not credential:
raise ValueError("Missing credential")

self._credential_scopes = base_url.strip("/") + "/.default"
self._credential_scopes = endpoint.strip("/") + "/.default"

self._config = AzureAppConfigurationConfiguration(
credential, base_url, credential_scopes=self._credential_scopes, **kwargs
credential, endpoint, credential_scopes=self._credential_scopes, **kwargs
)
self._config.user_agent_policy = UserAgentPolicy(
base_user_agent=USER_AGENT, **kwargs
Expand All @@ -89,11 +82,11 @@ def __init__(self, base_url, credential, **kwargs):
self._sync_token_policy = SyncTokenPolicy()
aad_mode = not isinstance(credential, AppConfigConnectionStringCredential)
pipeline = self._create_appconfig_pipeline(
credential=credential, aad_mode=aad_mode, base_url=base_url, **kwargs
credential=credential, aad_mode=aad_mode, endpoint=endpoint, **kwargs
)

self._impl = AzureAppConfiguration(
credential, base_url, pipeline=pipeline, credential_scopes=self._credential_scopes
credential, endpoint, pipeline=pipeline, credential_scopes=self._credential_scopes
)

@classmethod
Expand All @@ -115,22 +108,22 @@ def from_connection_string(cls, connection_string, **kwargs):
connection_str = "<my connection string>"
client = AzureAppConfigurationClient.from_connection_string(connection_str)
"""
base_url = "https://" + get_endpoint_from_connection_string(connection_string)
endpoint = "https://" + get_endpoint_from_connection_string(connection_string)
return cls(
credential=AppConfigConnectionStringCredential(connection_string),
base_url=base_url,
endpoint=endpoint,
**kwargs
)

def _create_appconfig_pipeline(
self, credential, base_url=None, aad_mode=False, **kwargs
self, credential, endpoint=None, aad_mode=False, **kwargs
):
transport = kwargs.get("transport")
policies = kwargs.get("policies")

if policies is None: # [] is a valid policy list
if aad_mode:
scope = base_url.strip("/") + "/.default"
scope = endpoint.strip("/") + "/.default"
if hasattr(credential, "get_token"):
credential_policy = BearerTokenCredentialPolicy(credential, scope)
else:
Expand Down Expand Up @@ -158,9 +151,8 @@ def _create_appconfig_pipeline(
return Pipeline(transport, policies)

@distributed_trace
def list_configuration_settings(
self, key_filter=None, label_filter=None, **kwargs
): # type: (Optional[str], Optional[str], **Any) -> ItemPaged[ConfigurationSetting]
def list_configuration_settings(self, **kwargs):
seankane-msft marked this conversation as resolved.
Show resolved Hide resolved
# type: (**Any) -> ItemPaged[ConfigurationSetting]

"""List the configuration settings stored in the configuration service, optionally filtered by
label and accept_datetime
Expand Down Expand Up @@ -197,6 +189,8 @@ def list_configuration_settings(
pass # do something
"""
select = kwargs.pop("fields", None)
key_filter = kwargs.pop("key_filter", None)
label_filter = kwargs.pop("label_filter", None)
if select:
select = ["locked" if x == "read_only" else x for x in select]
error_map = {401: ClientAuthenticationError}
Expand All @@ -219,25 +213,17 @@ def list_configuration_settings(
raise binascii.Error("Connection string secret has incorrect padding")

@distributed_trace
def get_configuration_setting(
self,
key, # type: str
label=None, # type: Optional[str]
etag="*", # type: Optional[str]
match_condition=MatchConditions.Unconditionally, # type: Optional[MatchConditions]
**kwargs # type: Any
): # type: (...) -> Union[None, ConfigurationSetting]
def get_configuration_setting(self, key, **kwargs):
seankane-msft marked this conversation as resolved.
Show resolved Hide resolved
# type: (...) -> Union[None, ConfigurationSetting]

"""Get the matched ConfigurationSetting from Azure App Configuration service

:param key: key of the ConfigurationSetting
:type key: str
:param label: label of the ConfigurationSetting
:type label: str
:param etag: check if the ConfigurationSetting is changed. Set None to skip checking etag
:type etag: str or None
:param match_condition: The match condition to use upon the etag
:type match_condition: :class:`~azure.core.MatchConditions`
:keyword str label: label of the ConfigurationSetting
:keyword str etag: check if the ConfigurationSetting is changed. Set None to skip checking etag
:keyword match_condition: The match condition to use upon the etag
:paramtype match_condition: :class:`~azure.core.MatchConditions`
:keyword datetime accept_datetime: the retrieved ConfigurationSetting that created no later than this datetime
:keyword dict headers: if "headers" exists, its value (a dict) will be added to the http request header
:return: The matched ConfigurationSetting object
Expand All @@ -253,6 +239,9 @@ def get_configuration_setting(
key="MyKey", label="MyLabel"
)
"""
label = kwargs.pop("label", None)
etag = kwargs.pop("etag", "*")
match_condition = kwargs.pop("match_condition", MatchConditions.Unconditionally)
error_map = {401: ClientAuthenticationError, 404: ResourceNotFoundError}
if match_condition == MatchConditions.IfNotModified:
error_map[412] = ResourceModifiedError
Expand Down Expand Up @@ -344,6 +333,7 @@ def set_configuration_setting(
:param match_condition: The match condition to use upon the etag
:type match_condition: :class:`~azure.core.MatchConditions`
:keyword dict headers: if "headers" exists, its value (a dict) will be added to the http request header
:keyword str etag: check if the ConfigurationSetting is changed. Set None to skip checking etag
:return: The ConfigurationSetting returned from the service
:rtype: :class:`~azure.appconfiguration.ConfigurationSetting`
:raises: :class:`HttpResponseError`, :class:`ClientAuthenticationError`, \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# ------------------------------------
import json
from typing import Dict, Optional, Any, List, Union
import six
from msrest.serialization import Model
from ._generated.models import KeyValue

Expand Down Expand Up @@ -118,8 +119,8 @@ class FeatureFlagConfigurationSetting(

:ivar etag: Entity tag (etag) of the object
:vartype etag: str
:ivar key:
:vartype key: str
:ivar feature_id:
:vartype feature_id: str
:ivar value: The value of the configuration setting
:vartype value: str
:ivar enabled:
Expand All @@ -128,6 +129,10 @@ class FeatureFlagConfigurationSetting(
:type filters: list[dict[str, Any]]
seankane-msft marked this conversation as resolved.
Show resolved Hide resolved
:param label:
:type label: str
:param display_name:
:type display_name: str
:param description:
:type description: str
:param content_type:
seankane-msft marked this conversation as resolved.
Show resolved Hide resolved
:type content_type: str
:ivar last_modified:
Expand All @@ -140,7 +145,7 @@ class FeatureFlagConfigurationSetting(

_attribute_map = {
"etag": {"key": "etag", "type": "str"},
"key": {"key": "key", "type": "str"},
"feature_id": {"key": "key", "type": "str"},
seankane-msft marked this conversation as resolved.
Show resolved Hide resolved
"label": {"key": "label", "type": "str"},
"content_type": {"key": "content_type", "type": "str"},
"value": {"key": "value", "type": "str"},
Expand All @@ -159,7 +164,7 @@ def __init__(self, feature_id, enabled, filters=[], **kwargs): # pylint: disabl
super(FeatureFlagConfigurationSetting, self).__init__(**kwargs)
if not feature_id.startswith(self.key_prefix):
seankane-msft marked this conversation as resolved.
Show resolved Hide resolved
feature_id = self.key_prefix + feature_id
self.key = feature_id
self.feature_id = feature_id
self.label = kwargs.get("label", None)
self.content_type = kwargs.get("content_type", self._feature_flag_content_type)
self.last_modified = kwargs.get("last_modified", None)
Expand All @@ -172,7 +177,7 @@ def __init__(self, feature_id, enabled, filters=[], **kwargs): # pylint: disabl

def _validate(self):
# type: () -> None
if not self.key.startswith(self.key_prefix):
if not self.feature_id.startswith(self.key_prefix):
seankane-msft marked this conversation as resolved.
Show resolved Hide resolved
raise ValueError("All FeatureFlagConfigurationSettings should be prefixed with {}.".format(self.key_prefix))
if not (self.value is None or isinstance(self.value, dict)):
raise ValueError("Expect 'value' to be a dictionary.")
Expand Down Expand Up @@ -251,7 +256,7 @@ def _to_generated(self):
# type: () -> KeyValue

return KeyValue(
key=self.key,
key=self.feature_id,
seankane-msft marked this conversation as resolved.
Show resolved Hide resolved
label=self.label,
value=json.dumps(self.value), # NOTE: This has to be added for valid json
content_type=self.content_type,
Expand All @@ -271,8 +276,8 @@ class SecretReferenceConfigurationSetting(ConfigurationSetting):
:vartype etag: str
:ivar key:
:vartype key: str
:ivar secret_uri:
:vartype secret_uri: str
:ivar secret_id:
:vartype secret_id: str
:param label:
:type label: str
:param content_type:
Expand Down Expand Up @@ -302,9 +307,8 @@ class SecretReferenceConfigurationSetting(ConfigurationSetting):
)
kind = "SecretReference"

def __init__(self, key, secret_uri, label=None, **kwargs):
def __init__(self, key, secret_id, label=None, **kwargs):
# type: (str, str, Optional[str], **Any) -> None
self._secret_uri = secret_uri
super(SecretReferenceConfigurationSetting, self).__init__(**kwargs)
self.key = key
self.label = label
Expand All @@ -315,16 +319,18 @@ def __init__(self, key, secret_uri, label=None, **kwargs):
self.last_modified = kwargs.get("last_modified", None)
self.read_only = kwargs.get("read_only", None)
self.tags = kwargs.get("tags", {})
self.value = {"secret_uri": self._secret_uri}
self.value = secret_id
if not isinstance(secret_id, dict) and isinstance(secret_id, six.string_types):
seankane-msft marked this conversation as resolved.
Show resolved Hide resolved
self.value = {"secret_uri": secret_id}

@property
def secret_uri(self):
def secret_id(self):
# type: () -> str
self._validate()
return self.value['secret_uri']

@secret_uri.setter
def secret_uri(self, value):
@secret_id.setter
def secret_id(self, value):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why value needs to be a dict?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how it is represented in the portal

if self.value is None or isinstance(self.value, dict):
seankane-msft marked this conversation as resolved.
Show resolved Hide resolved
if self.value is None:
self.value = {}
Expand Down
Loading