VersionEntity.cpp (6362B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 4 #include "VersionEntity.h" 5 6 #include "../ErrorHandling.h" 7 #include "../ReportingHandler.h" 8 #include "ContentId.h" 9 10 #include <nlohmann/json.hpp> 11 12 #define THROW_INVALID_RESPONSE_IF_NOT(condition, message, handler) \ 13 THROW_CODE_IF_NOT_LOG(ServiceInvalidResponse, condition, handler, message) 14 15 using namespace SFS; 16 using namespace SFS::details; 17 using json = nlohmann::json; 18 19 namespace 20 { 21 void ValidateContentType(const VersionEntity& entity, ContentType expectedType, const ReportingHandler& handler) 22 { 23 THROW_CODE_IF_LOG(Result::ServiceUnexpectedContentType, 24 entity.GetContentType() != expectedType, 25 handler, 26 "The service returned entity \"" + entity.contentId.name + "\" with content type [" + 27 ToString(entity.GetContentType()) + "] while the expected type was [" + 28 ToString(expectedType) + "]"); 29 } 30 } // namespace 31 32 std::unique_ptr<VersionEntity> VersionEntity::FromJson(const nlohmann::json& data, const ReportingHandler& handler) 33 { 34 // Expected format for a generic version entity: 35 // { 36 // "ContentId": { 37 // "Namespace": <ns>, 38 // "Name": <name>, 39 // "Version": <version> 40 // } 41 // } 42 // 43 // Expected extra elements for an app version entity: 44 // { 45 // ... 46 // "UpdateId": "<id>", 47 // "Prerequisites": [ 48 // { 49 // "Namespace": "<ns>", 50 // "Name": "<name>", 51 // "Version": "<version>" 52 // } 53 // ] 54 // } 55 56 THROW_INVALID_RESPONSE_IF_NOT(data.is_object(), "Response is not a JSON object", handler); 57 58 std::unique_ptr<VersionEntity> tmp; 59 const bool isAppEntity = data.contains("UpdateId"); 60 if (isAppEntity) 61 { 62 tmp = std::make_unique<AppVersionEntity>(); 63 } 64 else 65 { 66 tmp = std::make_unique<GenericVersionEntity>(); 67 } 68 69 THROW_INVALID_RESPONSE_IF_NOT(data.contains("ContentId"), "Missing ContentId in response", handler); 70 71 const auto& contentId = data["ContentId"]; 72 THROW_INVALID_RESPONSE_IF_NOT(contentId.is_object(), "ContentId is not a JSON object", handler); 73 74 THROW_INVALID_RESPONSE_IF_NOT(contentId.contains("Namespace"), "Missing ContentId.Namespace in response", handler); 75 THROW_INVALID_RESPONSE_IF_NOT(contentId["Namespace"].is_string(), "ContentId.Namespace is not a string", handler); 76 tmp->contentId.nameSpace = contentId["Namespace"]; 77 78 THROW_INVALID_RESPONSE_IF_NOT(contentId.contains("Name"), "Missing ContentId.Name in response", handler); 79 THROW_INVALID_RESPONSE_IF_NOT(contentId["Name"].is_string(), "ContentId.Name is not a string", handler); 80 tmp->contentId.name = contentId["Name"]; 81 82 THROW_INVALID_RESPONSE_IF_NOT(contentId.contains("Version"), "Missing ContentId.Version in response", handler); 83 THROW_INVALID_RESPONSE_IF_NOT(contentId["Version"].is_string(), "ContentId.Version is not a string", handler); 84 tmp->contentId.version = contentId["Version"]; 85 86 if (isAppEntity) 87 { 88 auto appEntity = dynamic_cast<AppVersionEntity*>(tmp.get()); 89 90 THROW_INVALID_RESPONSE_IF_NOT(data["UpdateId"].is_string(), "UpdateId is not a string", handler); 91 appEntity->updateId = data["UpdateId"]; 92 93 THROW_INVALID_RESPONSE_IF_NOT(data.contains("Prerequisites"), "Missing Prerequisites in response", handler); 94 THROW_INVALID_RESPONSE_IF_NOT(data["Prerequisites"].is_array(), "Prerequisites is not an array", handler); 95 96 for (const auto& prereq : data["Prerequisites"]) 97 { 98 THROW_INVALID_RESPONSE_IF_NOT(prereq.is_object(), "Prerequisite element is not a JSON object", handler); 99 100 GenericVersionEntity prereqEntity; 101 THROW_INVALID_RESPONSE_IF_NOT(prereq.contains("Namespace"), 102 "Missing Prerequisite.Namespace in response", 103 handler); 104 THROW_INVALID_RESPONSE_IF_NOT(prereq["Namespace"].is_string(), 105 "Prerequisite.Namespace is not a string", 106 handler); 107 prereqEntity.contentId.nameSpace = prereq["Namespace"]; 108 109 THROW_INVALID_RESPONSE_IF_NOT(prereq.contains("Name"), "Missing Prerequisite.Name in response", handler); 110 THROW_INVALID_RESPONSE_IF_NOT(prereq["Name"].is_string(), "Prerequisite.Name is not a string", handler); 111 prereqEntity.contentId.name = prereq["Name"]; 112 113 THROW_INVALID_RESPONSE_IF_NOT(prereq.contains("Version"), 114 "Missing Prerequisite.Version in response", 115 handler); 116 THROW_INVALID_RESPONSE_IF_NOT(prereq["Version"].is_string(), 117 "Prerequisite.Version is not a string", 118 handler); 119 prereqEntity.contentId.version = prereq["Version"]; 120 121 appEntity->prerequisites.push_back(std::move(prereqEntity)); 122 } 123 } 124 return tmp; 125 } 126 127 std::unique_ptr<ContentId> VersionEntity::ToContentId(VersionEntity&& entity, const ReportingHandler& handler) 128 { 129 std::unique_ptr<ContentId> tmp; 130 THROW_IF_FAILED_LOG(ContentId::Make(std::move(entity.contentId.nameSpace), 131 std::move(entity.contentId.name), 132 std::move(entity.contentId.version), 133 tmp), 134 handler); 135 return tmp; 136 } 137 138 ContentType GenericVersionEntity::GetContentType() const 139 { 140 return ContentType::Generic; 141 } 142 143 ContentType AppVersionEntity::GetContentType() const 144 { 145 return ContentType::App; 146 } 147 148 AppVersionEntity* AppVersionEntity::GetAppVersionEntityPtr(std::unique_ptr<VersionEntity>& versionEntity, 149 const ReportingHandler& handler) 150 { 151 ValidateContentType(*versionEntity, ContentType::App, handler); 152 return dynamic_cast<AppVersionEntity*>(versionEntity.get()); 153 }