blob: b783f33d13575562cc46a4b9fe063fc68593a39c [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 <lib/fxl/logging.h>
#include <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"
#include "peridot/lib/module_manifest/module_facet_reader.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,
modular::ModuleFacetReader* const module_facet_reader,
fidl::VectorPtr<fidl::StringPtr> mod_name,
fuchsia::modular::Intent intent,
fuchsia::modular::SurfaceRelationPtr surface_relation,
fidl::VectorPtr<fidl::StringPtr> surface_parent_mod_name,
fuchsia::modular::ModuleSource module_source, ResultCall done)
: Operation("AddModCommandRunner::AddModCall", std::move(done)),
story_storage_(story_storage),
module_resolver_(module_resolver),
entity_resolver_(entity_resolver),
module_facet_reader_(module_facet_reader),
mod_name_(std::move(mod_name)),
intent_(std::move(intent)),
surface_relation_(std::move(surface_relation)),
surface_parent_mod_name_(std::move(surface_parent_mod_name)),
module_source_(module_source) {}
private:
void Run() override {
FlowToken flow{this, &out_result_, &out_module_data_};
if (!surface_parent_mod_name_) {
surface_parent_mod_name_.resize(0);
}
// 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 (!intent_.action.is_null()) {
AddFindModulesOperation(
&operation_queue_, story_storage_, module_resolver_, entity_resolver_,
CloneOptional(intent_), surface_parent_mod_name_.Clone(),
[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: {
FXL_LOG(INFO)
<< "Module Resolver does not know about module '"
<< intent_.handler << "' with action = '" << intent_.action
<< "'. Going to try using it anyway..";
candidate_module_.module_id = intent_.handler;
} break;
}
CreateLinks(flow);
});
} else {
// We arrive here if the Intent has a handler, but no action.
FXL_DCHECK(!intent_.handler.is_null())
<< "Cannot start a module without an action or a handler";
candidate_module_.module_id = 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 = surface_parent_mod_name_.Clone();
full_module_path->insert(full_module_path->end(), mod_name_->begin(),
mod_name_->end());
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,
std::function<void()> done) {
parameter_info_ = fuchsia::modular::CreateModuleParameterMapInfo::New();
std::vector<FuturePtr<fuchsia::modular::CreateModuleParameterMapEntry>>
did_get_entries;
did_get_entries.reserve(intent_.parameters->size());
for (auto& param : *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::kLinkName: {
auto did_get_lp = Future<fuchsia::modular::LinkPathPtr>::Create(
"AddModCommandRunner::AddModCall::did_get_link");
// TODO(miguelfrde): get rid of using surface_parent_mod_name this
// way. Maybe INVALID status should be returned here since using
// this parameter in a StoryCommand doesn't make much sense.
AddGetLinkPathForParameterNameOperation(
&operations_, story_storage_, surface_parent_mod_name_.Clone(),
param.data.link_name(), did_get_lp->Completer());
did_get_entries.emplace_back(
did_get_lp->Map([this, param_name = param.name,
done](fuchsia::modular::LinkPathPtr link_path) {
if (!GetWeakPtr()) {
return fuchsia::modular::CreateModuleParameterMapEntry{};
}
if (!link_path) {
// OH NO! bail on this entire function's flow.
out_result_.status =
fuchsia::modular::ExecuteStatus::INTERNAL_ERROR;
out_result_.error_message = fxl::StringPrintf(
"Link path does not exist for parameter name = %s",
param_name.get().c_str());
done();
return fuchsia::modular::CreateModuleParameterMapEntry{};
}
fuchsia::modular::CreateModuleParameterMapEntry entry;
entry.key = param_name;
entry.value.set_link_path(std::move(*link_path));
return entry;
}));
continue;
}
case fuchsia::modular::IntentParameterData::Tag::kLinkPath: {
fuchsia::modular::LinkPath lp;
param.data.link_path().Clone(&lp);
entry.value.set_link_path(std::move(lp));
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, 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 = surface_parent_mod_name_.Clone();
out_module_data_.module_path->insert(out_module_data_.module_path->end(),
mod_name_->begin(), mod_name_->end());
out_module_data_.module_source = module_source_;
out_module_data_.module_deleted = false;
fidl::Clone(surface_relation_, &out_module_data_.surface_relation);
out_module_data_.intent =
std::make_unique<fuchsia::modular::Intent>(std::move(intent_));
auto write_to_storage_cont = [this, flow]() {
// 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([this, flow] {});
};
// If the resolver gave us a module manifest (which is described in the
// module facet), we don't need to try and read it.
if (candidate_module_.manifest) {
fidl::Clone(candidate_module_.manifest,
&out_module_data_.module_manifest);
write_to_storage_cont();
return;
}
module_facet_reader_->GetModuleManifest(
out_module_data_.module_url,
[this, flow,
write_to_storage_cont](fuchsia::modular::ModuleManifestPtr manifest) {
out_module_data_.module_manifest = std::move(manifest);
write_to_storage_cont();
});
}
StoryStorage* const story_storage_; // Not owned.
fuchsia::modular::ModuleResolver* const module_resolver_; // Not owned.
fuchsia::modular::EntityResolver* const entity_resolver_; // Not owned.
modular::ModuleFacetReader* const module_facet_reader_; // Not owned.
fidl::VectorPtr<fidl::StringPtr> mod_name_;
fuchsia::modular::Intent intent_;
fuchsia::modular::SurfaceRelationPtr surface_relation_;
fidl::VectorPtr<fidl::StringPtr> surface_parent_mod_name_;
fuchsia::modular::ModuleSource module_source_;
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_;
FXL_DISALLOW_COPY_AND_ASSIGN(AddModCall);
}; // namespace
} // namespace
void AddAddModOperation(
OperationContainer* const container, StoryStorage* const story_storage,
fuchsia::modular::ModuleResolver* const module_resolver,
fuchsia::modular::EntityResolver* const entity_resolver,
modular::ModuleFacetReader* const module_facet_reader,
fidl::VectorPtr<fidl::StringPtr> mod_name, fuchsia::modular::Intent intent,
fuchsia::modular::SurfaceRelationPtr surface_relation,
fidl::VectorPtr<fidl::StringPtr> surface_parent_mod_name,
fuchsia::modular::ModuleSource module_source,
std::function<void(fuchsia::modular::ExecuteResult,
fuchsia::modular::ModuleData)>
done) {
container->Add(new AddModCall(
story_storage, module_resolver, entity_resolver, module_facet_reader,
std::move(mod_name), std::move(intent), std::move(surface_relation),
std::move(surface_parent_mod_name), module_source, std::move(done)));
}
} // namespace modular