Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Frameworks/WebService/Server/VariantValue.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
9#include "Stroika/Foundation/IO/Network/HTTP/ClientErrorException.h"
10
11#include "Stroika/Frameworks/WebService/Server/Basic.h"
12
13#include "VariantValue.h"
14
15using namespace Stroika::Foundation;
19using namespace Stroika::Foundation::Execution;
20
21using namespace Stroika::Frameworks;
22using namespace Stroika::Frameworks::WebService;
23using namespace Stroika::Frameworks::WebService::Server;
24
26
27/*
28 ********************************************************************************
29 ******** WebService::Server::VariantValue::PickoutParamValuesFromURL ***********
30 ********************************************************************************
31 */
32Mapping<String, DataExchange::VariantValue> Server::VariantValue::PickoutParamValuesFromURL (const URI& url)
33{
36 if (auto query = url.GetQuery ()) {
37 Mapping<String, String> unconverted = query->GetMap ();
38 unconverted.Apply ([&] (const KeyValuePair<String, String>& kvp) { result.Add (kvp.fKey, VariantValue{kvp.fValue}); });
39 }
40 return result;
41 });
42}
43
44/*
45 ********************************************************************************
46 ******* WebService::Server::VariantValue::PickoutParamValuesFromBody ***********
47 ********************************************************************************
48 */
49Mapping<String, DataExchange::VariantValue> Server::VariantValue::PickoutParamValuesFromBody (const BLOB& body, const optional<InternetMediaType>& bodyContentType)
50{
51 static const InternetMediaType kDefaultCT_ = DataExchange::InternetMediaTypes::kJSON;
52 if (bodyContentType.value_or (kDefaultCT_) == DataExchange::InternetMediaTypes::kJSON) {
55 });
56 }
57 Throw (ClientErrorException{"Unrecognized content-type"sv});
58}
59
60/*
61 ********************************************************************************
62 **** WebService::Server::VariantValue::CombineWebServiceArgsAsVariantValue *****
63 ********************************************************************************
64 */
65DataExchange::VariantValue Server::VariantValue::CombineWebServiceArgsAsVariantValue (Request& request)
66{
69 {
70 Memory::BLOB inData = request.GetBody ();
71 if (not inData.empty ()) {
72 DataExchange::VariantValue bodyObj = Variant::JSON::Reader{}.Read (inData);
73 switch (bodyObj.GetType ()) {
74 case DataExchange::VariantValue::eMap:
76 break;
77 case DataExchange::VariantValue::eNull:
78 break;
79 default:
80 // Other types cannot be merged with URL data, so just return what we had in the body
81 return bodyObj;
82 }
83 }
84 }
85 result.AddAll (PickoutParamValuesFromURL (request));
86 return result.empty () ? DataExchange::VariantValue{} : DataExchange::VariantValue{result};
87 });
88}
89
90/*
91 ********************************************************************************
92 ************* WebService::Server::VariantValue::PickoutParamValues *************
93 ********************************************************************************
94 */
95Mapping<String, DataExchange::VariantValue> Server::VariantValue::PickoutParamValues (Request& request)
96{
97 Mapping<String, DataExchange::VariantValue> result = PickoutParamValuesFromURL (request);
98 // body params take precedence, if they overlap
99 PickoutParamValuesFromBody (request).Apply ([&] (auto i) { result.Add (i.fKey, i.fValue); });
100 return result;
101}
102
103/*
104 ********************************************************************************
105 *************** Server::VariantValue::PickOutNamedArguments ********************
106 ********************************************************************************
107 */
108Iterable<DataExchange::VariantValue> Server::VariantValue::PickOutNamedArguments (const Iterable<String>& argNames,
109 const Mapping<String, VariantValue>& argumentValueMap)
110{
111 return argNames.Map<Iterable<VariantValue>> ([=] (const String& i) -> VariantValue { return argumentValueMap.LookupValue (i); });
112}
113
114Iterable<DataExchange::VariantValue> Server::VariantValue::PickOutNamedArguments (const Iterable<String>& argNames, const VariantValue& argumentValueMap)
115{
116 return PickOutNamedArguments (argNames, argumentValueMap.As<Mapping<String, VariantValue>> ());
117}
118
119/*
120 ********************************************************************************
121 ********* Server::VariantValue::ExtractArgumentsAsVariantValue *****************
122 ********************************************************************************
123 */
125{
126 return ClientErrorException::TreatExceptionsAsClientError ([&] () { return request.GetBodyVariantValue (); });
127}
128
130{
131 return ClientErrorException::TreatExceptionsAsClientError ([&] () {
133 if (auto query = request.url ().GetQuery ()) {
134 Mapping<String, String> unconverted = query->GetMap ();
135 unconverted.Apply ([&] (const KeyValuePair<String, String>& kvp) { result.Add (kvp.fKey, VariantValue{kvp.fValue}); });
136 }
137 if (result.empty ()) {
138 return VariantValue{};
139 }
140 return VariantValue{result};
141 });
142}
143
145{
146 return ClientErrorException::TreatExceptionsAsClientError ([&] () {
147 VariantValue requestBody = FromRequestBody (request);
148 VariantValue urlBody = FromRequestURL (request);
149 if (requestBody == VariantValue{}) {
150 return urlBody;
151 }
152 if (urlBody == VariantValue{}) {
153 return requestBody;
154 }
155 Assert (requestBody != VariantValue{} and urlBody != VariantValue{});
156 if (requestBody.GetType () != VariantValue::eMap or urlBody.GetType () != VariantValue::eMap) {
157 Throw (ClientErrorException{"Expected url and body to both be structured VariantValue type"sv});
158 }
160 // merge - with url values taking precedence
161 rr.AddAll (urlBody.As<Mapping<String, VariantValue>> ());
162 return VariantValue{rr};
163 });
164}
165
166/*
167 ********************************************************************************
168 ************ WebService::Server::VariantValue::OrderParamValues ****************
169 ********************************************************************************
170 */
171Sequence<DataExchange::VariantValue> Server::VariantValue::OrderParamValues (const Iterable<String>& paramNames,
173{
175 paramNames.Apply ([&] (const String& name) {
176 if (auto o = paramValues.Lookup (name)) {
177 result += DataExchange::VariantValue{*o};
178 }
179 else {
180 result += DataExchange::VariantValue{};
181 }
182 });
183 return result;
184}
185
186/*
187 ********************************************************************************
188 ***************** WebService::Server::VariantValue::WriteResponse **************
189 ********************************************************************************
190 */
191void Server::VariantValue::WriteResponse (Response& response, const WebServiceMethodDescription& webServiceDescription, const Memory::BLOB& responseValue)
192{
193 if (webServiceDescription.fResponseType) {
194 response.contentType = *webServiceDescription.fResponseType;
195 response.write (responseValue);
196 }
197 else {
198 WeakAssert (responseValue.empty ()); // if you returned a value you probably meant to have it written!
199 }
200}
201
202void Server::VariantValue::WriteResponse (Response& response, const WebServiceMethodDescription& webServiceDescription, const VariantValue& responseValue)
203{
205 Require (not webServiceDescription.fResponseType.has_value () or
206 (webServiceDescription.fResponseType == DataExchange::InternetMediaTypes::kJSON or
207 webServiceDescription.fResponseType == DataExchange::InternetMediaTypes::kText_PLAIN)); // all we support for now
208 if (webServiceDescription.fResponseType) {
209 if (registry.IsA (InternetMediaTypes::kJSON, *webServiceDescription.fResponseType)) {
210 response.contentType = *webServiceDescription.fResponseType;
211 response.write (Variant::JSON::Writer{}.WriteAsBLOB (responseValue));
212 }
213 else if (registry.IsA (InternetMediaTypes::Wildcards::kText, *webServiceDescription.fResponseType)) {
214 response.contentType = *webServiceDescription.fResponseType;
215 response.write (Variant::JSON::Writer{}.WriteAsBLOB (responseValue));
216 }
217 else {
219 }
220 }
221 else {
222 WeakAssert (responseValue == nullptr); // if you returned a value you probably meant to have it written!
223 }
224}
#define RequireNotReached()
Definition Assertions.h:385
#define WeakAssert(c)
A WeakAssert() is for things that aren't guaranteed to be true, but are overwhelmingly likely to be t...
Definition Assertions.h:438
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual CONTAINER_OF_Key_T As() const
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
Definition Mapping.inl:190
nonvirtual optional< mapped_type > Lookup(ArgByValueType< key_type > key) const
Definition Mapping.inl:144
nonvirtual unsigned int AddAll(ITERABLE_OF_ADDABLE &&items, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
nonvirtual mapped_type LookupValue(ArgByValueType< key_type > key, ArgByValueType< mapped_type > defaultValue=mapped_type{}) const
Definition Mapping.inl:168
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Definition Sequence.h:187
static Execution::Synchronized< InternetMediaTypeRegistry > sThe
nonvirtual bool IsA(const InternetMediaType &moreGeneralType, const InternetMediaType &moreSpecificType) const
return true if moreSpecificType 'isa' moreGeneralType
nonvirtual Memory::BLOB WriteAsBLOB(const VariantValue &v) const
Definition Writer.cpp:48
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in ...
ClientErrorException is to capture exceptions caused by a bad (e.g ill-formed) request.
nonvirtual bool empty() const
Definition BLOB.inl:246
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
nonvirtual void Apply(const function< void(ArgByValueType< T > item)> &doToElement, Execution::SequencePolicy seq=Execution::SequencePolicy::eDEFAULT) const
Run the argument function (or lambda) on each element of the container.
nonvirtual RESULT_CONTAINER Map(ELEMENT_MAPPER &&elementMapper) const
functional API which iterates over all members of an Iterable, applies a map function to each element...
nonvirtual bool empty() const
Returns true iff size() == 0.
Definition Iterable.inl:306
this represents a HTTP request object for the WebServer module
nonvirtual DataExchange::VariantValue GetBodyVariantValue()
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
static VariantValue FromRequestURL(Request &request)
extracts Query args from request url into a Mapping<String,String> (converted to VariantValue),...