Optimized search and json deserialization#800
Conversation
| if (!info.has_value() || info.value().ServerSupportedVersions.size() == 0) | ||
| { | ||
| AICLI_LOG(Repo, Verbose, << "Incomplete information returned by rest source"); | ||
| THROW_HR_MSG(E_UNEXPECTED, "Missing supported versions from rest source information"); |
There was a problem hiding this comment.
Given that this is coming from an external party, we should have a real error code. Also, it makes the log line above unnecessary. #Resolved
|
|
||
| static utility::string_t GetInformationEndpoint(const std::string& restApiUri); | ||
|
|
||
| static std::optional<std::string> GetSupportedRestClientVersion(const std::string& restApi); |
There was a problem hiding this comment.
I'm not a fan of repeating the type name in the methods, can these three drop RestClient from the name? #Resolved
| AICLI_LOG(Repo, Verbose, << "Downloading manifest"); | ||
| AICLI_LOG(Repo, Verbose, << "Getting manifest"); | ||
|
|
||
| if (!m_versionInfo.Manifest.Id.empty()) |
There was a problem hiding this comment.
Make Manifest a std::optional instead of guessing at it's validity based on a member. #Resolved
| } | ||
|
|
||
| Interface::Interface(const std::string& restApi) | ||
| Interface::Interface(const std::string& restApi, const std::string& restApiVersion) |
There was a problem hiding this comment.
Shouldn't this version value be implicit in the version of the interface? Seems strange to define it as 1.0 in the filesystem but pass in the value that it uses. #Resolved
There was a problem hiding this comment.
This was supposed to be the rest API Version, but now that we have removed it from our schema, I will remove this.
In reply to: 596376550 [](ancestors = 596376550)
| if (!data.has_value()) | ||
| { | ||
| AICLI_LOG(Repo, Verbose, << "Missing data"); | ||
| return {}; |
There was a problem hiding this comment.
General error handling strategy for these deserialization methods:
- Log as Error
- Returning an empty optional is fine as long as the caller treats that as an error and throws an appropriate error understandable to our stack.
- Wrap in a function level try/catch that also returns an empty optional.
- I would suggest combining 2 and 3 by making the public method non-optional and throw and a private one that does the work. #Resolved
There was a problem hiding this comment.
Basically, don't ignore error conditions by just skipping them and also don't propagate the REST specific exception types out of the source.
If a value is truly optional, then you wouldn't return early. #Resolved
There was a problem hiding this comment.
Have added the errors and made public methods throw when it gets an optional value.
In reply to: 596389291 [](ancestors = 596389291)
| constexpr std::string_view SourceIdentifier = "SourceIdentifier"sv; | ||
| constexpr std::string_view ServerSupportedVersions = "ServerSupportedVersions"sv; | ||
| constexpr std::string_view APIVersion = "APIVersion"sv; | ||
| constexpr std::string_view ObjectVersions = "ObjectVersions"sv; |
There was a problem hiding this comment.
I don't understand what all of these versions are for. #Resolved
There was a problem hiding this comment.
|
|
||
| if (!JsonHelper::IsValidNonEmptyStringValue(packageId) || !JsonHelper::IsValidNonEmptyStringValue(packageName) || !JsonHelper::IsValidNonEmptyStringValue(packageName)) | ||
| { | ||
| AICLI_LOG(Repo, Verbose, << "Skipping manifest item because of missing required package fields."); |
There was a problem hiding this comment.
We should fail if required values are missing. #Resolved
| } | ||
| catch (...) | ||
| { | ||
| AICLI_LOG(Repo, Verbose, << "Error encountered while deserializing search result..."); |
There was a problem hiding this comment.
TODO: Catch known types and log error information from them #Resolved
| namespace | ||
| { | ||
| // Manifest response constants specific to this deserializer | ||
| constexpr std::string_view PackageIdentifier = "PackageIdentifier"sv; |
There was a problem hiding this comment.
Proof that having the tree walking interface and single definition of the manifest parser would be much less code. This would literally not need to know anything about the manifest structure (assuming that we actually defined the same tree for the REST response... please tell me we did that).
There was a problem hiding this comment.
Not heavily reviewed because I think we should do that work sooner rather than later.
Misspellings found, please review:
To accept these changes, run the following commands from this repository on this branch |
| case APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE: | ||
| return "The configured rest source is not supported"; | ||
| case APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA: | ||
| return "Invalid data returned by rest source"; |
There was a problem hiding this comment.
Let me know if you want to change the wording/name of these errors #Resolved
|
|
||
| pplx::task<web::http::http_response> HttpClientHelper::Post(const web::json::value& body) | ||
| pplx::task<web::http::http_response> HttpClientHelper::Post( | ||
| const web::json::value& body, std::optional<std::vector<std::pair<utility::string_t, utility::string_t>>> headers) |
There was a problem hiding this comment.
While an empty optional might be more efficient to construct than an empty vector, {} works equally well for both. I don't think a small runtime efficiency is worth the programmer overhead of this being an optional. #Resolved
| // Call information endpoint | ||
| HttpClientHelper httpClientHelper{ GetInformationEndpoint(restApi) }; | ||
| web::json::value response = httpClientHelper.HandleGet(); | ||
| web::json::value response = httpClientHelper.HandleGet({}); |
There was a problem hiding this comment.
Default to {} in the header for these methods so that the caller need to explicitly specify it. #Resolved
| } | ||
|
|
||
|
|
||
| m_versionInfo.Manifest = manifest.value(); |
There was a problem hiding this comment.
I don't know the rules around copy elision well enough to know if this will only produce one copy. But you could be sure of that by moving the value in the the version info and returning a copy of that. #Resolved
| private: | ||
| IRestClient::PackageInfo m_packageInfo; | ||
| IRestClient::VersionInfo m_versionInfo; | ||
| mutable IRestClient::VersionInfo m_versionInfo; |
There was a problem hiding this comment.
I think a specific const cast would be better here, or simply removal of the const from GetManifest as I was wrong to have it. #Resolved
| { | ||
| std::vector<AppInstaller::Manifest::ValidationError> validationErrors = | ||
| AppInstaller::Manifest::ValidateManifest(manifest.value()); | ||
| THROW_HR_IF(APPINSTALLER_CLI_ERROR_MULTIPLE_APPLICATIONS_FOUND, manifests.size() > 1); |
There was a problem hiding this comment.
That is not the appropriate error to return in this case, nor do I think this case is an error. I'm fine with a TODO for now though. #Resolved
|
Addressed the comments.. In reply to: 616735978 [](ancestors = 616735978) |
This PR contains the following updates:
Todo:
Microsoft Reviewers: Open in CodeFlow