Skip to content

Commit 732c1fc

Browse files
committed
Swap dynamo client to use a smithy base class
1 parent 4cad430 commit 732c1fc

35 files changed

+1637
-2150
lines changed

generated/src/aws-cpp-sdk-dynamodb/include/aws/dynamodb/DynamoDBClient.h

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@
66
#pragma once
77
#include <aws/dynamodb/DynamoDB_EXPORTS.h>
88
#include <aws/core/client/ClientConfiguration.h>
9-
#include <aws/core/client/AWSClient.h>
109
#include <aws/core/client/AWSClientAsyncCRTP.h>
11-
#include <aws/core/utils/json/JsonSerializer.h>
1210
#include <aws/dynamodb/DynamoDBServiceClientModel.h>
11+
#include <smithy/client/AwsSmithyClient.h>
12+
#include <smithy/identity/auth/built-in/SigV4AuthSchemeResolver.h>
13+
#include <smithy/identity/auth/built-in/SigV4AuthScheme.h>
14+
#include <smithy/client/serializer/JsonOutcomeSerializer.h>
1315

1416
namespace Aws
1517
{
1618
namespace DynamoDB
1719
{
20+
AWS_DYNAMODB_API extern const char SERVICE_NAME[];
1821
/**
1922
* <fullname>Amazon DynamoDB</fullname> <p>Amazon DynamoDB is a fully managed NoSQL
2023
* database service that provides fast and predictable performance with seamless
@@ -33,10 +36,16 @@ namespace DynamoDB
3336
* Zones in an Amazon Web Services Region, providing built-in high availability and
3437
* data durability.</p>
3538
*/
36-
class AWS_DYNAMODB_API DynamoDBClient : public Aws::Client::AWSJsonClient, public Aws::Client::ClientWithAsyncTemplateMethods<DynamoDBClient>
39+
class AWS_DYNAMODB_API DynamoDBClient : smithy::client::AwsSmithyClientT<Aws::DynamoDB::SERVICE_NAME,
40+
Aws::DynamoDB::DynamoDBClientConfiguration,
41+
smithy::SigV4AuthSchemeResolver<>,
42+
Aws::Crt::Variant<smithy::SigV4AuthScheme>,
43+
DynamoDBEndpointProviderBase,
44+
smithy::client::JsonOutcomeSerializer,
45+
smithy::client::JsonOutcome>,
46+
Aws::Client::ClientWithAsyncTemplateMethods<DynamoDBClient>
3747
{
3848
public:
39-
typedef Aws::Client::AWSJsonClient BASECLASS;
4049
static const char* GetServiceName();
4150
static const char* GetAllocationTag();
4251

@@ -2275,9 +2284,12 @@ namespace DynamoDB
22752284
friend class Aws::Client::ClientWithAsyncTemplateMethods<DynamoDBClient>;
22762285
void init(const DynamoDBClientConfiguration& clientConfiguration);
22772286

2287+
void OptionallyUpdateDescribeEndpointsCache(Aws::Endpoint::AWSEndpoint& resolvedEndpoint,
2288+
const Aws::String& operationName,
2289+
const Aws::String& endpointKey,
2290+
const Aws::DynamoDB::Model::DescribeEndpointsRequest& endpointRequest,
2291+
bool enforceDiscovery) const;
22782292
mutable Aws::Utils::ConcurrentCache<Aws::String, Aws::String> m_endpointsCache;
2279-
DynamoDBClientConfiguration m_clientConfiguration;
2280-
std::shared_ptr<DynamoDBEndpointProviderBase> m_endpointProvider;
22812293
};
22822294

22832295
} // namespace DynamoDB

generated/src/aws-cpp-sdk-dynamodb/source/DynamoDBClient.cpp

Lines changed: 540 additions & 2105 deletions
Large diffs are not rendered by default.

src/aws-cpp-sdk-core/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ file(GLOB SMITHY_CLIENT_HEADERS "include/smithy/client/*.h")
7777
file(GLOB SMITHY_CLIENT_IMPL_HEADERS "include/smithy/client/impl/*.h")
7878
file(GLOB SMITHY_CLIENT_COMMON_HEADERS "include/smithy/client/common/*.h")
7979
file(GLOB SMITHY_CLIENT_FEATURES_HEADERS "include/smithy/client/features/*.h")
80+
file(GLOB SMITHY_CLIENT_SERIALIZER_HEADERS "include/smithy/client/serializer/*.h")
8081
file(GLOB SMITHY_TRACING_HEADERS "include/smithy/tracing/*.h")
8182
file(GLOB SMITHY_IDENTITY_HEADERS "include/smithy/identity/*.h")
8283
file(GLOB SMITHY_IDENTITY_AUTH_HEADERS "include/smithy/identity/auth/*.h")
@@ -346,6 +347,7 @@ file(GLOB AWS_NATIVE_SDK_COMMON_HEADERS
346347
${SMITHY_CLIENT_IMPL_HEADERS}
347348
${SMITHY_CLIENT_COMMON_HEADERS}
348349
${SMITHY_CLIENT_FEATURES_HEADERS}
350+
${SMITHY_CLIENT_SERIALIZER_HEADERS}
349351
${SMITHY_TRACING_HEADERS}
350352
${SMITHY_IDENTITY_HEADERS}
351353
${SMITHY_IDENTITY_AUTH_HEADERS}
@@ -478,6 +480,7 @@ if(MSVC)
478480
source_group("Header Files\\smithy\\client" FILES ${SMITHY_CLIENT_IMPL_HEADERS})
479481
source_group("Header Files\\smithy\\client" FILES ${SMITHY_CLIENT_COMMON_HEADERS})
480482
source_group("Header Files\\smithy\\client" FILES ${SMITHY_CLIENT_FEATURES_HEADERS})
483+
source_group("Header Files\\smithy\\client" FILES ${SMITHY_CLIENT_SERIALIZER_HEADERS})
481484
source_group("Header Files\\smithy\\tracing" FILES ${SMITHY_TRACING_HEADERS})
482485
source_group("Header Files\\smithy\\identity\\auth" FILES ${SMITHY_IDENTITY_AUTH_HEADERS})
483486
source_group("Header Files\\smithy\\identity\\auth\\impl" FILES ${SMITHY_IDENTITY_AUTH_IMPL_HEADERS})
@@ -751,13 +754,18 @@ install (FILES ${CJSON_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/aws/core/extern
751754
install (FILES ${TINYXML2_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/aws/core/external/tinyxml2)
752755
install (FILES ${SMITHY_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy)
753756
install (FILES ${SMITHY_CLIENT_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/client)
757+
install (FILES ${SMITHY_CLIENT_IMPL_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/client/impl)
758+
install (FILES ${SMITHY_CLIENT_COMMON_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/client/common)
759+
install (FILES ${SMITHY_CLIENT_FEATURES_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/client/features)
760+
install (FILES ${SMITHY_CLIENT_SERIALIZER_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/client/serializer)
754761
install (FILES ${SMITHY_TRACING_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/tracing)
755762
install (FILES ${SMITHY_IDENTITY_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity)
756763
install (FILES ${SMITHY_IDENTITY_AUTH_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/auth)
757764
install (FILES ${SMITHY_IDENTITY_AUTH_IMPL_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/auth/impl)
758765
install (FILES ${SMITHY_IDENTITY_AUTH_BUILTIN_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/auth/built-in)
759766
install (FILES ${SMITHY_IDENTITY_IDENTITY_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/identity)
760767
install (FILES ${SMITHY_IDENTITY_RESOLVER_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/resolver)
768+
install (FILES ${SMITHY_IDENTITY_RESOLVER_BUILTIN_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/resolver/built-in)
761769
install (FILES ${SMITHY_IDENTITY_IDENTITY_IMPL_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/identity/impl)
762770
install (FILES ${SMITHY_IDENTITY_RESOLVER_IMPL_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/resolver/impl)
763771
install (FILES ${SMITHY_IDENTITY_SIGNER_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/signer)

src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ namespace client
2727
typename ServiceClientConfigurationT,
2828
typename ServiceAuthSchemeResolverT,
2929
typename AuthSchemesVariantT,
30-
typename EndpointProviderT>
30+
typename EndpointProviderT,
31+
typename SerializerT,
32+
typename ResponseT>
3133
class AwsSmithyClientT : public AwsSmithyClientBase
3234
{
3335
public:
@@ -38,10 +40,11 @@ namespace client
3840
const std::shared_ptr<ServiceAuthSchemeResolverT>& authSchemeResolver,
3941
const Aws::UnorderedMap<Aws::String, AuthSchemesVariantT>& authSchemes)
4042
: AwsSmithyClientBase(Aws::MakeUnique<ServiceClientConfigurationT>(ServiceNameT, clientConfig), serviceName, httpClient, errorMarshaller),
41-
m_clientConfig(*AwsSmithyClientBase::m_clientConfig.get()),
43+
m_clientConfiguration(*static_cast<ServiceClientConfigurationT*>(AwsSmithyClientBase::m_clientConfig.get())),
4244
m_endpointProvider(endpointProvider),
4345
m_authSchemeResolver(authSchemeResolver),
44-
m_authSchemes(authSchemes)
46+
m_authSchemes(authSchemes),
47+
m_serializer(Aws::MakeUnique<SerializerT>(ServiceNameT, m_clientConfiguration.telemetryProvider))
4548
{
4649
m_serviceName = ServiceNameT;
4750
}
@@ -69,7 +72,7 @@ namespace client
6972

7073
identityParams.serviceName = m_serviceName;
7174
identityParams.operation = ctx.m_requestName;
72-
identityParams.region = m_clientConfig.region;
75+
identityParams.region = m_clientConfiguration.region;
7376

7477
if (ctx.m_pRequest) {
7578
// refactor once auth scheme resolver will use it's own rule set
@@ -118,11 +121,21 @@ namespace client
118121
return AwsClientRequestSigning<AuthSchemesVariantT>::AdjustClockSkew(outcome, authSchemeOption, m_authSchemes);
119122
}
120123

124+
ResponseT MakeRequestDeserialize(Aws::AmazonWebServiceRequest const * const request,
125+
const char* requestName,
126+
Aws::Http::HttpMethod method,
127+
EndpointUpdateCallback&& endpointCallback) const
128+
{
129+
auto httpResponseOutcome = MakeRequestSync(request, requestName, method, std::move(endpointCallback));
130+
return m_serializer->Deserialize(std::move(httpResponseOutcome), GetServiceClientName(), requestName);
131+
}
132+
121133
protected:
122-
ServiceClientConfigurationT& m_clientConfig;
134+
ServiceClientConfigurationT& m_clientConfiguration;
123135
std::shared_ptr<EndpointProviderT> m_endpointProvider{};
124136
std::shared_ptr<ServiceAuthSchemeResolverT> m_authSchemeResolver{};
125137
Aws::UnorderedMap<Aws::String, AuthSchemesVariantT> m_authSchemes{};
138+
Aws::UniquePtr<SerializerT> m_serializer{};
126139
};
127140

128141
} // namespace client

src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientBase.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ namespace client
148148
std::shared_ptr<Aws::Http::HttpResponse> httpResponse) const;
149149

150150
inline virtual const char* GetServiceClientName() const { return m_serviceName.c_str(); }
151+
inline virtual const std::shared_ptr<Aws::Http::HttpClient>& GetHttpClient() { return m_httpClient; }
152+
virtual void DisableRequestProcessing();
151153

152154
virtual ResolveEndpointOutcome ResolveEndpoint(const Aws::Endpoint::EndpointParameters& endpointParameters, EndpointUpdateCallback&& epCallback) const = 0;
153155
virtual SelectAuthSchemeOptionOutcome SelectAuthSchemeOption(const AwsSmithyClientAsyncRequestContext& ctx) const = 0;

src/aws-cpp-sdk-core/include/smithy/client/common/AwsSmithyRequestSigning.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ namespace smithy
114114
return;
115115
}
116116

117-
auto identityResult = identityResolver->getIdentity(m_targetAuthSchemeOption.identityProperties, m_targetAuthSchemeOption.identityProperties);
117+
auto identityResult = identityResolver->getIdentity(m_targetAuthSchemeOption.identityProperties(), m_targetAuthSchemeOption.identityProperties());
118118

119119
if (!identityResult.IsSuccess())
120120
{
@@ -133,7 +133,7 @@ namespace smithy
133133
return;
134134
}
135135

136-
result.emplace(signer->sign(m_httpRequest, *identity, m_targetAuthSchemeOption.signerProperties));
136+
result.emplace(signer->sign(m_httpRequest, *identity, m_targetAuthSchemeOption.signerProperties()));
137137
}
138138
};
139139

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
#pragma once
6+
#include <aws/core/AmazonWebServiceResult.h>
7+
#include <aws/core/client/AWSError.h>
8+
#include <aws/core/utils/Outcome.h>
9+
#include <aws/core/utils/json/JsonSerializer.h>
10+
#include <smithy/tracing/TracingUtils.h>
11+
#include <smithy/tracing/TelemetryProvider.h>
12+
13+
namespace smithy
14+
{
15+
namespace client
16+
{
17+
using TracingUtils = components::tracing::TracingUtils;
18+
using CoreErrors = Aws::Client::CoreErrors;
19+
using AWSError = Aws::Client::AWSError<CoreErrors>;
20+
using JsonValue = Aws::Utils::Json::JsonValue;
21+
using HttpResponseOutcome = Aws::Utils::Outcome<std::shared_ptr<Aws::Http::HttpResponse>, AWSError>;
22+
using JsonOutcome = Aws::Utils::Outcome<Aws::AmazonWebServiceResult<JsonValue>, AWSError>;
23+
using TelemetryProvider = components::tracing::TelemetryProvider;
24+
25+
class JsonOutcomeSerializer
26+
{
27+
public:
28+
explicit JsonOutcomeSerializer(const std::shared_ptr<TelemetryProvider>& telemetryProvider)
29+
: m_telemetryProvider(telemetryProvider)
30+
{
31+
}
32+
33+
JsonOutcomeSerializer(const JsonOutcomeSerializer& other) = delete;
34+
JsonOutcomeSerializer(JsonOutcomeSerializer&& other) noexcept = default;
35+
JsonOutcomeSerializer& operator=(const JsonOutcomeSerializer& other) = delete;
36+
JsonOutcomeSerializer& operator=(JsonOutcomeSerializer&& other) noexcept = default;
37+
virtual ~JsonOutcomeSerializer() = default;
38+
39+
JsonOutcome Deserialize(HttpResponseOutcome&& httpOutcome,
40+
const Aws::String& serviceName,
41+
const Aws::String& requestName) const
42+
{
43+
if (!httpOutcome.IsSuccess())
44+
{
45+
return TracingUtils::MakeCallWithTiming<JsonOutcome>(
46+
[&]() -> JsonOutcome {
47+
return JsonOutcome{std::move(httpOutcome)};
48+
},
49+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
50+
*m_telemetryProvider->getMeter(serviceName, {}),
51+
{{TracingUtils::SMITHY_METHOD_DIMENSION, requestName},
52+
{TracingUtils::SMITHY_SERVICE_DIMENSION, serviceName}});
53+
}
54+
55+
if (httpOutcome.GetResult()->GetResponseBody().good() &&
56+
httpOutcome.GetResult()->GetResponseBody().tellp() > 0)
57+
{
58+
JsonValue jsonValue(httpOutcome.GetResult()->GetResponseBody());
59+
if (!jsonValue.WasParseSuccessful()) {
60+
return TracingUtils::MakeCallWithTiming<JsonOutcome>(
61+
[&]() -> JsonOutcome {
62+
return JsonOutcome{AWSError(CoreErrors::UNKNOWN,
63+
"Json Parser Error",
64+
jsonValue.GetErrorMessage(),
65+
false)};
66+
},
67+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
68+
*m_telemetryProvider->getMeter(serviceName, {}),
69+
{{TracingUtils::SMITHY_METHOD_DIMENSION, requestName},
70+
{TracingUtils::SMITHY_SERVICE_DIMENSION, serviceName}});
71+
}
72+
73+
return TracingUtils::MakeCallWithTiming<JsonOutcome>(
74+
[&]() -> JsonOutcome {
75+
return JsonOutcome{Aws::AmazonWebServiceResult<JsonValue>(std::move(jsonValue),
76+
httpOutcome.GetResult()->GetHeaders(),
77+
httpOutcome.GetResult()->GetResponseCode())};
78+
},
79+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
80+
*m_telemetryProvider->getMeter(serviceName, {}),
81+
{{TracingUtils::SMITHY_METHOD_DIMENSION, requestName},
82+
{TracingUtils::SMITHY_SERVICE_DIMENSION, serviceName}});
83+
}
84+
85+
return TracingUtils::MakeCallWithTiming<JsonOutcome>(
86+
[&]() -> JsonOutcome {
87+
return JsonOutcome{Aws::AmazonWebServiceResult<JsonValue>(JsonValue(),
88+
httpOutcome.GetResult()->GetHeaders())};
89+
},
90+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
91+
*m_telemetryProvider->getMeter(serviceName, {}),
92+
{{TracingUtils::SMITHY_METHOD_DIMENSION, requestName},
93+
{TracingUtils::SMITHY_SERVICE_DIMENSION, serviceName}});
94+
}
95+
96+
private:
97+
std::shared_ptr<TelemetryProvider> m_telemetryProvider;
98+
};
99+
} // namespace client
100+
} // namespace smithy
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
#pragma once
6+
#include <aws/core/AmazonWebServiceResult.h>
7+
#include <aws/core/client/AWSError.h>
8+
#include <aws/core/utils/Outcome.h>
9+
#include <aws/core/utils/xml/XmlSerializer.h>
10+
#include <smithy/tracing/TracingUtils.h>
11+
#include <smithy/tracing/TelemetryProvider.h>
12+
13+
namespace smithy
14+
{
15+
namespace client
16+
{
17+
using TracingUtils = components::tracing::TracingUtils;
18+
using CoreErrors = Aws::Client::CoreErrors;
19+
using AWSError = Aws::Client::AWSError<CoreErrors>;
20+
using XmlDocument = Aws::Utils::Xml::XmlDocument;
21+
using HttpResponseOutcome = Aws::Utils::Outcome<std::shared_ptr<Aws::Http::HttpResponse>, AWSError>;
22+
using XmlServiceResult = Aws::AmazonWebServiceResult<XmlDocument>;
23+
using XmlOutcome = Aws::Utils::Outcome<XmlServiceResult, AWSError>;
24+
using TelemetryProvider = components::tracing::TelemetryProvider;
25+
26+
class XmlOutcomeSerializer
27+
{
28+
public:
29+
explicit XmlOutcomeSerializer(const std::shared_ptr<TelemetryProvider>& telemetryProvider)
30+
: m_telemetryProvider(telemetryProvider)
31+
{
32+
}
33+
34+
XmlOutcomeSerializer(const XmlOutcomeSerializer& other) = delete;
35+
XmlOutcomeSerializer(XmlOutcomeSerializer&& other) noexcept = default;
36+
XmlOutcomeSerializer& operator=(const XmlOutcomeSerializer& other) = delete;
37+
XmlOutcomeSerializer& operator=(XmlOutcomeSerializer&& other) noexcept = default;
38+
virtual ~XmlOutcomeSerializer() = default;
39+
40+
41+
XmlOutcome Deserialize(HttpResponseOutcome&& httpOutcome,
42+
const Aws::String& serviceName,
43+
const Aws::String& requestName) const
44+
{
45+
if (!httpOutcome.IsSuccess())
46+
{
47+
return TracingUtils::MakeCallWithTiming<XmlOutcome>(
48+
[&]() -> XmlOutcome {
49+
return {std::move(httpOutcome)};
50+
},
51+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
52+
*m_telemetryProvider->getMeter(serviceName, {}),
53+
{{TracingUtils::SMITHY_METHOD_DIMENSION, requestName}, {TracingUtils::SMITHY_SERVICE_DIMENSION, serviceName}});
54+
}
55+
56+
if (httpOutcome.GetResult()->GetResponseBody().good() &&
57+
httpOutcome.GetResult()->GetResponseBody().tellp() > 0)
58+
{
59+
return TracingUtils::MakeCallWithTiming<XmlOutcome>(
60+
[&]() -> XmlOutcome {
61+
XmlDocument xmlDoc = XmlDocument::CreateFromXmlStream(httpOutcome.GetResult()->GetResponseBody());
62+
63+
if (!xmlDoc.WasParseSuccessful())
64+
{
65+
AWS_LOGSTREAM_ERROR("XmlOutcomeSerializer", "Xml parsing for error failed with message " << xmlDoc.GetErrorMessage().c_str());
66+
return AWSError(CoreErrors::UNKNOWN,
67+
"Xml Parse Error",
68+
xmlDoc.GetErrorMessage(),
69+
false);
70+
}
71+
72+
return {XmlServiceResult(std::move(xmlDoc),
73+
httpOutcome.GetResult()->GetHeaders(),
74+
httpOutcome.GetResult()->GetResponseCode())};
75+
},
76+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
77+
*m_telemetryProvider->getMeter(serviceName, {}),
78+
{{TracingUtils::SMITHY_METHOD_DIMENSION, requestName}, {TracingUtils::SMITHY_SERVICE_DIMENSION, serviceName}});
79+
}
80+
81+
return {XmlServiceResult(XmlDocument(), httpOutcome.GetResult()->GetHeaders())};
82+
}
83+
private:
84+
std::shared_ptr<TelemetryProvider> m_telemetryProvider;
85+
};
86+
87+
} // namespace client
88+
} // namespace smithy

0 commit comments

Comments
 (0)