blob: bdd5076bf1d353703fe613e64680bc13a65f1591 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "peridot/bin/sessionmgr/puppet_master/command_runners/operation_calls/add_mod_call.h"
#include <lib/entity/cpp/json.h>
#include <lib/fidl/cpp/clone.h>
#include <lib/fsl/vmo/strings.h>
#include <src/lib/fxl/logging.h>
#include <src/lib/fxl/strings/string_printf.h>
#include "peridot/bin/sessionmgr/puppet_master/command_runners/operation_calls/find_modules_call.h"
#include "peridot/bin/sessionmgr/puppet_master/command_runners/operation_calls/get_link_path_for_parameter_name_call.h"
#include "peridot/bin/sessionmgr/puppet_master/command_runners/operation_calls/get_types_from_entity_call.h"
#include "peridot/bin/sessionmgr/puppet_master/command_runners/operation_calls/initialize_chain_call.h"
#include "peridot/lib/fidl/clone.h"
namespace modular {
namespace {
class AddModCall : public Operation<fuchsia::modular::ExecuteResult,
fuchsia::modular::ModuleData> {
public:
AddModCall(StoryStorage* const story_storage,
fuchsia::modular::ModuleResolver* const module_resolver,
fuchsia::modular::EntityResolver* const entity_resolver,
AddModParams add_mod_params, ResultCall done)
: Operation("AddModCommandRunner::AddModCall", std::move(done)),
story_storage_(story_storage),
module_resolver_(module_resolver),
entity_resolver_(entity_resolver),
add_mod_params_(std::move(add_mod_params)) {}
private:
void Run() override {
FlowToken flow{this, &out_result_, &out_module_data_};
// Success status by default, it will be update it if an error state is
// found.
out_result_.status = fuchsia::modular::ExecuteStatus::OK;
// If we have an action, we use the module resolver to type-check and
// resolve the (action, parameter) and the supplied optional handler to a
// module. If the module resolver doesn't recognize a supplied handler, we
// forgivingly execute the handler anyway.
if (!add_mod_params_.intent.action.is_null()) {
AddFindModulesOperation(
&operation_queue_, module_resolver_, entity_resolver_,
CloneOptional(add_mod_params_.intent),
add_mod_params_.parent_mod_path,
[this, flow](fuchsia::modular::ExecuteResult result,
fuchsia::modular::FindModulesResponse response) {
if (result.status != fuchsia::modular::ExecuteStatus::OK) {
out_result_ = std::move(result);
return;
// Operation finishes since |flow| goes out of scope.
}
// NOTE: leave this as a switch case and omit the default case; the
// compiler will make sure we're handling all error cases.
switch (response.status) {
case fuchsia::modular::FindModulesStatus::SUCCESS: {
if (response.results.empty()) {
out_result_.status =
fuchsia::modular::ExecuteStatus::NO_MODULES_FOUND;
out_result_.error_message =
"Resolution of intent gave zero results.";
return;
// Operation finishes since |flow| goes out of scope.
}
candidate_module_ = std::move(response.results.at(0));
} break;
case fuchsia::modular::FindModulesStatus::UNKNOWN_HANDLER: {
candidate_module_.module_id = add_mod_params_.intent.handler;
} break;
}
CreateLinks(flow);
});
} else {
// We arrive here if the Intent has a handler, but no action.
FXL_DCHECK(!add_mod_params_.intent.handler.is_null())
<< "Cannot start a module without an action or a handler";
candidate_module_.module_id = add_mod_params_.intent.handler;
CreateLinks(flow);
}
}
// Create module parameters info and create links.
void CreateLinks(FlowToken flow) {
CreateModuleParameterMapInfo(flow, [this, flow] {
if (out_result_.status != fuchsia::modular::ExecuteStatus::OK) {
return;
// Operation finishes since |flow| goes out of scope.
}
auto full_module_path = add_mod_params_.parent_mod_path;
full_module_path.push_back(add_mod_params_.mod_name);
AddInitializeChainOperation(
&operation_queue_, story_storage_, std::move(full_module_path),
std::move(parameter_info_),
[this, flow](fuchsia::modular::ExecuteResult result,
fuchsia::modular::ModuleParameterMapPtr map) {
if (result.status != fuchsia::modular::ExecuteStatus::OK) {
out_result_ = std::move(result);
return;
// Operation finishes since |flow| goes out of scope.
}
WriteModuleData(flow, std::move(map));
});
});
}
// On success, populates |parameter_info_|. On failure, |out_result_| contains
// error reason. Calls |done()| on completion in either case.
void CreateModuleParameterMapInfo(FlowToken flow,
fit::function<void()> done) {
parameter_info_ = fuchsia::modular::CreateModuleParameterMapInfo::New();
std::vector<FuturePtr<fuchsia::modular::CreateModuleParameterMapEntry>>
did_get_entries;
did_get_entries.reserve(add_mod_params_.intent.parameters->size());
for (auto& param : *add_mod_params_.intent.parameters) {
fuchsia::modular::CreateModuleParameterMapEntry entry;
entry.key = param.name;
switch (param.data.Which()) {
case fuchsia::modular::IntentParameterData::Tag::kEntityReference: {
fuchsia::modular::CreateLinkInfo create_link;
fsl::SizedVmo vmo;
FXL_CHECK(fsl::VmoFromString(
EntityReferenceToJson(param.data.entity_reference()), &vmo));
create_link.initial_data = std::move(vmo).ToTransport();
entry.value.set_create_link(std::move(create_link));
break;
}
case fuchsia::modular::IntentParameterData::Tag::kEntityType: {
// Create a link, but don't populate it. This is useful in the event
// that the link is used as an 'output' link. Setting a valid JSON
// value for null in the vmo.
fsl::SizedVmo vmo;
FXL_CHECK(fsl::VmoFromString("null", &vmo));
fuchsia::modular::CreateLinkInfo create_link;
create_link.initial_data = std::move(vmo).ToTransport();
entry.value.set_create_link(std::move(create_link));
break;
}
case fuchsia::modular::IntentParameterData::Tag::kJson: {
fuchsia::modular::CreateLinkInfo create_link;
param.data.json().Clone(&create_link.initial_data);
entry.value.set_create_link(std::move(create_link));
break;
}
case fuchsia::modular::IntentParameterData::Tag::Invalid: {
out_result_.status = fuchsia::modular::ExecuteStatus::INVALID_COMMAND;
out_result_.error_message =
fxl::StringPrintf("Invalid data for parameter with name: %s",
param.name.get().c_str());
done();
return;
}
}
auto did_create_entry =
Future<fuchsia::modular::CreateModuleParameterMapEntry>::
CreateCompleted(
"AddModCommandRunner::FindModulesCall.did_create_entry",
std::move(entry));
did_get_entries.emplace_back(std::move(did_create_entry));
}
Wait("AddModCommandRunner::AddModCall::Wait", did_get_entries)
->Then([this, done = std::move(done), flow](
std::vector<fuchsia::modular::CreateModuleParameterMapEntry>
entries) {
parameter_info_->property_info.reset(std::move(entries));
done();
});
}
// Write module data
void WriteModuleData(FlowToken flow,
fuchsia::modular::ModuleParameterMapPtr map) {
fidl::Clone(*map, &out_module_data_.parameter_map);
out_module_data_.module_url = candidate_module_.module_id;
out_module_data_.module_path = add_mod_params_.parent_mod_path;
out_module_data_.module_path.push_back(add_mod_params_.mod_name);
out_module_data_.module_source = add_mod_params_.module_source;
out_module_data_.module_deleted = false;
fidl::Clone(add_mod_params_.surface_relation,
&out_module_data_.surface_relation);
out_module_data_.is_embedded = add_mod_params_.is_embedded;
out_module_data_.intent = std::make_unique<fuchsia::modular::Intent>(
std::move(add_mod_params_.intent));
// Operation stays alive until flow goes out of scope.
fuchsia::modular::ModuleData module_data;
out_module_data_.Clone(&module_data);
story_storage_->WriteModuleData(std::move(module_data))->Then([flow] {
});
}
StoryStorage* const story_storage_; // Not owned.
fuchsia::modular::ModuleResolver* const module_resolver_; // Not owned.
fuchsia::modular::EntityResolver* const entity_resolver_; // Not owned.
modular::AddModParams add_mod_params_;
fuchsia::modular::FindModulesResult candidate_module_;
fuchsia::modular::CreateModuleParameterMapInfoPtr parameter_info_;
fuchsia::modular::ModuleData out_module_data_;
fuchsia::modular::ExecuteResult out_result_;
// Used when creating the map info to execute an operation as soon as it
// arrives.
OperationCollection operations_;
// Used to enqueue sub-operations that should be executed sequentially.
OperationQueue operation_queue_;
};
} // namespace
void AddAddModOperation(OperationContainer* const container,
StoryStorage* const story_storage,
fuchsia::modular::ModuleResolver* const module_resolver,
fuchsia::modular::EntityResolver* const entity_resolver,
AddModParams add_mod_params,
fit::function<void(fuchsia::modular::ExecuteResult,
fuchsia::modular::ModuleData)>
done) {
container->Add(std::make_unique<AddModCall>(
story_storage, module_resolver, entity_resolver,
std::move(add_mod_params), std::move(done)));
}
} // namespace modular