Collection resource not notifying its updated model when child resource is added...
[platform/upstream/iotivity.git] / service / simulator / src / server / simulator_resource_factory.cpp
1 /******************************************************************\r
2  *\r
3  * Copyright 2015 Samsung Electronics All Rights Reserved.\r
4  *\r
5  *\r
6  *\r
7  * Licensed under the Apache License, Version 2.0 (the "License");\r
8  * you may not use this file except in compliance with the License.\r
9  * You may obtain a copy of the License at\r
10  *\r
11  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  *\r
13  * Unless required by applicable law or agreed to in writing, software\r
14  * distributed under the License is distributed on an "AS IS" BASIS,\r
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
16  * See the License for the specific language governing permissions and\r
17  * limitations under the License.\r
18  *\r
19  ******************************************************************/\r
20 \r
21 #include "simulator_resource_factory.h"\r
22 #include "simulator_single_resource_impl.h"\r
23 #include "simulator_collection_resource_impl.h"\r
24 #include "simulator_logger.h"\r
25 #include "logger.h"\r
26 \r
27 #define TAG "SIM_RESOURCE_FACTORY"\r
28 \r
29 SimulatorResourceFactory *SimulatorResourceFactory::getInstance()\r
30 {\r
31     static SimulatorResourceFactory s_instance;\r
32     return &s_instance;\r
33 }\r
34 \r
35 std::shared_ptr<SimulatorResource> SimulatorResourceFactory::createResource(\r
36     const std::string &configPath)\r
37 {\r
38     // Parse the RAML file\r
39     std::shared_ptr<RAML::RamlParser> ramlParser = std::make_shared<RAML::RamlParser>(configPath);\r
40     RAML::RamlPtr raml = ramlParser->getRamlPtr();\r
41 \r
42     // Get the first resource model from RAML\r
43     RAML::RamlResourcePtr ramlResource;\r
44     if (0 == raml->getResources().size()\r
45         || nullptr == (ramlResource = raml->getResources().begin()->second))\r
46     {\r
47         OC_LOG(ERROR, TAG, "Zero resources detected from RAML!");\r
48         return nullptr;\r
49     }\r
50 \r
51     return buildResource(ramlResource);\r
52 }\r
53 \r
54 std::vector<std::shared_ptr<SimulatorResource> > SimulatorResourceFactory::createResource(\r
55     const std::string &configPath, unsigned int count)\r
56 {\r
57     std::vector<std::shared_ptr<SimulatorResource>> resources;\r
58 \r
59     // Parse the RAML file\r
60     std::shared_ptr<RAML::RamlParser> ramlParser = std::make_shared<RAML::RamlParser>(configPath);\r
61     RAML::RamlPtr raml = ramlParser->getRamlPtr();\r
62 \r
63     // Get the first resource model from RAML\r
64     RAML::RamlResourcePtr ramlResource;\r
65     if (0 == raml->getResources().size()\r
66         || nullptr == (ramlResource = raml->getResources().begin()->second))\r
67     {\r
68         OC_LOG(ERROR, TAG, "Zero resources detected from RAML!");\r
69         return resources;\r
70     }\r
71 \r
72     while (count--)\r
73     {\r
74         std::shared_ptr<SimulatorResource> resource = buildResource(ramlResource);\r
75         if (!resource)\r
76         {\r
77             OC_LOG(ERROR, TAG, "Failed to create resource!");\r
78             return resources;\r
79         }\r
80 \r
81         resources.push_back(resource);\r
82     }\r
83 \r
84     return resources;\r
85 }\r
86 \r
87 std::shared_ptr<SimulatorSingleResource> SimulatorResourceFactory::createSingleResource(\r
88     const std::string &name, const std::string &uri, const std::string &resourceType)\r
89 {\r
90     SimulatorSingleResourceImpl *simpleResource = new SimulatorSingleResourceImpl();\r
91     simpleResource->setName(name);\r
92     simpleResource->setURI(uri);\r
93     simpleResource->setResourceType(resourceType);\r
94     return std::shared_ptr<SimulatorSingleResource>(simpleResource);\r
95 }\r
96 \r
97 std::shared_ptr<SimulatorCollectionResource> SimulatorResourceFactory::createCollectionResource(\r
98     const std::string &name, const std::string &uri, const std::string &resourceType)\r
99 {\r
100     SimulatorCollectionResourceImpl *collectionResource = new SimulatorCollectionResourceImpl();\r
101     collectionResource->setName(name);\r
102     collectionResource->setURI(uri);\r
103     collectionResource->setResourceType(resourceType);\r
104     return std::shared_ptr<SimulatorCollectionResource>(collectionResource);\r
105 }\r
106 \r
107 SimulatorResourceModel::Attribute SimulatorResourceFactory::buildAttribute(\r
108     std::shared_ptr<RAML::Properties> propertyElement)\r
109 {\r
110     std::string propName = propertyElement->getName();\r
111 \r
112     // Build representation attribute\r
113     SimulatorResourceModel::Attribute attribute(propName);\r
114     switch (propertyElement->getVariantType())\r
115     {\r
116         case RAML::VariantType::INT:\r
117             {\r
118                 attribute.setValue(propertyElement->getValue<int>());\r
119 \r
120                 // Convert suppoted values\r
121                 std::vector<int> allowedValues = propertyElement->getAllowedValuesInt();\r
122                 if (allowedValues.size() > 0)\r
123                 {\r
124                     SimulatorResourceModel::AttributeProperty attrProp(allowedValues);\r
125                     attribute.setProperty(attrProp);\r
126                 }\r
127             }\r
128             break;\r
129 \r
130         case RAML::VariantType::DOUBLE:\r
131             {\r
132                 attribute.setValue(propertyElement->getValue<double>());\r
133 \r
134                 // Convert suppoted values\r
135                 std::vector<double> allowedValues = propertyElement->getAllowedValuesDouble();\r
136                 if (allowedValues.size() > 0)\r
137                 {\r
138                     SimulatorResourceModel::AttributeProperty attrProp(allowedValues);\r
139                     attribute.setProperty(attrProp);\r
140                 }\r
141             }\r
142             break;\r
143 \r
144         case RAML::VariantType::BOOL:\r
145             {\r
146                 attribute.setValue(propertyElement->getValue<bool>());\r
147 \r
148                 std::vector<bool> allowedValues = {true, false};\r
149                 SimulatorResourceModel::AttributeProperty attrProp(allowedValues);\r
150                 attribute.setProperty(attrProp);\r
151             }\r
152             break;\r
153 \r
154         case RAML::VariantType::STRING:\r
155             {\r
156                 attribute.setValue(propertyElement->getValue<std::string>());\r
157 \r
158                 // Convert suppoted values\r
159                 std::vector<std::string> allowedValues = propertyElement->getAllowedValuesString();\r
160                 if (allowedValues.size() > 0)\r
161                 {\r
162                     SimulatorResourceModel::AttributeProperty attrProp(allowedValues);\r
163                     attribute.setProperty(attrProp);\r
164                 }\r
165             }\r
166             break;\r
167     }\r
168 \r
169     // Set the range property if its present\r
170     double min, max;\r
171     int multipleof;\r
172     propertyElement->getRange(min, max, multipleof);\r
173     if (min != INT_MIN && max != INT_MAX)\r
174     {\r
175         SimulatorResourceModel::AttributeProperty attrProp(min, max);\r
176         attribute.setProperty(attrProp);\r
177     }\r
178     return attribute;\r
179 }\r
180 \r
181 SimulatorResourceModel SimulatorResourceFactory::buildResourceModel(\r
182     std::shared_ptr<RAML::Items> item)\r
183 {\r
184     SimulatorResourceModel itemModel;\r
185     for ( auto &propElement : item->getProperties())\r
186     {\r
187         if (!propElement.second)\r
188             continue;\r
189 \r
190         std::string propName = propElement.second->getName();\r
191         if ("p" == propName || "n" == propName || "id" == propName)\r
192         {\r
193             continue;\r
194         }\r
195 \r
196         if ("array" == propElement.second->getType())\r
197         {\r
198             std::vector<SimulatorResourceModel> arrayResModel;\r
199             for ( auto &propertyItem : propElement.second->getItems())\r
200             {\r
201                 arrayResModel.push_back(buildResourceModel(propertyItem));\r
202             }\r
203             itemModel.add(propName, arrayResModel);\r
204         }\r
205         else\r
206         {\r
207             itemModel.add(buildAttribute(propElement.second));\r
208         }\r
209     }\r
210     return itemModel;\r
211 }\r
212 \r
213 RAML::RequestResponseBodyPtr SimulatorResourceFactory::getRAMLResponseBody(\r
214     std::shared_ptr<RAML::RamlResource> ramlResource, RAML::ActionType type, std::string responseCode)\r
215 {\r
216     // Get the resource representation schema from response body\r
217     RAML::ActionPtr action = ramlResource->getAction(type);\r
218     if (!action)\r
219     {\r
220         OC_LOG(ERROR, TAG, "Resource does not possess the request!");\r
221         return nullptr;\r
222     }\r
223 \r
224     RAML::ResponsePtr response = action->getResponse(responseCode);\r
225     if (!response)\r
226     {\r
227         OC_LOG(ERROR, TAG, "Resource does not provide valid GET response!");\r
228         return nullptr;\r
229     }\r
230 \r
231     RAML::RequestResponseBodyPtr responseBody = response->getResponseBody("application/json");\r
232     if (!responseBody)\r
233     {\r
234         OC_LOG(ERROR, TAG, "GET response is not of type \"application/json\" ");\r
235         return nullptr;\r
236     }\r
237 \r
238     return responseBody;\r
239 }\r
240 \r
241 SimulatorResourceModel SimulatorResourceFactory::buildModelFromResponseBody(\r
242     RAML::RequestResponseBodyPtr responseBody, std::string &resourceType, std::vector<std::string> &interfaceType)\r
243 {\r
244     SimulatorResourceModel resModel;\r
245 \r
246     if (!responseBody)\r
247         return resModel;\r
248 \r
249     // Iterate throgh all resource property and extract information needed for simulating resource.\r
250     RAML::JsonSchemaPtr resourceProperties = responseBody->getSchema()->getProperties();\r
251 \r
252 \r
253     for ( auto &propertyElement : resourceProperties->getProperties())\r
254     {\r
255         if (!propertyElement.second)\r
256             continue;\r
257 \r
258         std::string propName = propertyElement.second->getName();\r
259 \r
260         // Resource type\r
261         if ("rt" == propName || "resourceType" == propName)\r
262         {\r
263             resourceType = propertyElement.second->getValueString();\r
264             continue;\r
265         }\r
266 \r
267         // Interface type\r
268         if ("if" == propName)\r
269         {\r
270             if ("string" == propertyElement.second->getType())\r
271             {\r
272                 interfaceType.push_back(propertyElement.second->getValueString());\r
273             }\r
274             else if ("array" == propertyElement.second->getType())\r
275             {\r
276                 for (auto &item : propertyElement.second->getItems())\r
277                 {\r
278                     if ("string" == item->getType())\r
279                     {\r
280                         interfaceType = item->getAllowedValuesString();\r
281                         break;\r
282                     }\r
283                 }\r
284             }\r
285             continue;\r
286         }\r
287 \r
288         // Other Standard properties which should not be part of resource model\r
289         if ("p" == propName || "n" == propName || "id" == propName)\r
290         {\r
291             continue;\r
292         }\r
293 \r
294         // Add the attribute to resource model\r
295         if ("array" == propertyElement.second->getType())\r
296         {\r
297             std::vector<SimulatorResourceModel> arrayResModel;\r
298             for ( auto &propertyItem : propertyElement.second->getItems())\r
299             {\r
300                 arrayResModel.push_back(buildResourceModel(propertyItem));\r
301             }\r
302             resModel.add(propName, arrayResModel);\r
303         }\r
304         else\r
305         {\r
306             resModel.add(buildAttribute(propertyElement.second));\r
307         }\r
308     }\r
309 \r
310     if ("array" == resourceProperties->getType())\r
311     {\r
312         std::vector<SimulatorResourceModel> arrayResModel;\r
313         for ( auto &propertyItem : resourceProperties->getItems())\r
314         {\r
315             arrayResModel.push_back(buildResourceModel(propertyItem));\r
316         }\r
317         resModel.add("links", arrayResModel);\r
318     }\r
319 \r
320     return resModel;\r
321 }\r
322 \r
323 std::shared_ptr<SimulatorResource> SimulatorResourceFactory::buildResource(\r
324     std::shared_ptr<RAML::RamlResource> ramlResource)\r
325 {\r
326     std::string name;\r
327     std::string uri;\r
328     std::string resourceType, rt;\r
329     std::vector<std::string> interfaceType, ifType;\r
330 \r
331     name = ramlResource->getDisplayName();\r
332     uri = ramlResource->getResourceUri();\r
333 \r
334     RAML::RequestResponseBodyPtr successResponseBody = getRAMLResponseBody(\r
335         ramlResource, RAML::ActionType::GET, "200");\r
336     RAML::RequestResponseBodyPtr putErrorResponseBody = getRAMLResponseBody(\r
337         ramlResource, RAML::ActionType::PUT, "403");\r
338     RAML::RequestResponseBodyPtr postErrorResponseBody = getRAMLResponseBody(\r
339         ramlResource, RAML::ActionType::POST, "403");\r
340 \r
341     SimulatorResourceModel successResponseModel = buildModelFromResponseBody(\r
342         successResponseBody, resourceType, interfaceType);\r
343     SimulatorResourceModel putErrorResponseModel = buildModelFromResponseBody(\r
344         putErrorResponseBody, rt, ifType);\r
345     SimulatorResourceModel postErrorResponseModel = buildModelFromResponseBody(\r
346         postErrorResponseBody, rt, ifType);\r
347 \r
348     // Create simple/collection resource\r
349     std::shared_ptr<SimulatorResource> simResource;\r
350     if (successResponseModel.containsAttribute("links"))\r
351     {\r
352         try\r
353         {\r
354             std::shared_ptr<SimulatorCollectionResourceImpl> collectionRes(\r
355                 new SimulatorCollectionResourceImpl());\r
356 \r
357             collectionRes->setName(name);\r
358             collectionRes->setResourceType(resourceType);\r
359             collectionRes->setInterface(interfaceType);\r
360             collectionRes->setURI(ResourceURIFactory::getInstance()->constructURI(uri));\r
361 \r
362             collectionRes->setResourceModel(successResponseModel);\r
363             simResource = std::dynamic_pointer_cast<SimulatorResource>(collectionRes);\r
364         }\r
365         catch (InvalidArgsException &e) {}\r
366     }\r
367     else\r
368     {\r
369         try\r
370         {\r
371             std::shared_ptr<SimulatorSingleResourceImpl> singleRes(\r
372                 new SimulatorSingleResourceImpl());\r
373 \r
374             singleRes->setName(name);\r
375             singleRes->setResourceType(resourceType);\r
376             singleRes->setInterface(interfaceType);\r
377             singleRes->setURI(ResourceURIFactory::getInstance()->constructURI(uri));\r
378 \r
379             singleRes->setResourceModel(successResponseModel);\r
380             singleRes->setPutErrorResponseModel(putErrorResponseModel);\r
381             singleRes->setPostErrorResponseModel(postErrorResponseModel);\r
382 \r
383             simResource = std::dynamic_pointer_cast<SimulatorResource>(singleRes);\r
384         }\r
385         catch (InvalidArgsException &e) {}\r
386     }\r
387 \r
388     return simResource;\r
389 }\r
390 \r
391 ResourceURIFactory *ResourceURIFactory::getInstance()\r
392 {\r
393     static ResourceURIFactory s_instance;\r
394     return &s_instance;\r
395 }\r
396 \r
397 ResourceURIFactory::ResourceURIFactory()\r
398     : m_id(0) {}\r
399 \r
400 std::string ResourceURIFactory::constructURI(const std::string &uri)\r
401 {\r
402     std::lock_guard<std::mutex> lock(m_lock);\r
403     if (isUnique(uri))\r
404     {\r
405         updateUri(uri);\r
406         return uri;\r
407     }\r
408     std::ostringstream os;\r
409     os << uri;\r
410     if (!uri.empty() && '/' != uri[uri.length() - 1])\r
411         os << '/';\r
412     os << m_id++;\r
413     updateUri(os.str());\r
414     return os.str();\r
415 }\r
416 \r
417 void ResourceURIFactory::updateUri(const std::string &uri)\r
418 {\r
419     m_uriList.insert(std::pair<std::string, bool>(uri, true));\r
420 }\r
421 \r
422 bool ResourceURIFactory::isUnique(const std::string &uri)\r
423 {\r
424     if (m_uriList.end() == m_uriList.find(uri))\r
425         return true;\r
426     else\r
427         return false;\r
428 }\r
429 \r