Commit bdd78271 authored by Paweł Wegner's avatar Paweł Wegner

Refactored getItemData to use EitherError.

parent 7525b245
......@@ -70,7 +70,7 @@ class MockProvider : public ICloudProvider {
MockGetItemRequest(const std::string& path, GetItemCallback);
void finish() override {}
void cancel() override {}
IItem::Pointer result() override { return result_; }
EitherError<IItem> result() override { return result_; }
private:
IItem::Pointer result_;
......@@ -100,7 +100,7 @@ class MockProvider : public ICloudProvider {
MockGetItemDataRequest(const std::string& id, GetItemDataCallback);
void finish() override {}
void cancel() override {}
IItem::Pointer result() override { return result_; }
EitherError<IItem> result() override { return result_; }
private:
IItem::Pointer result_;
......
......@@ -339,8 +339,8 @@ void Window::play(int item_id) {
contentItem()->setFocus(false);
last_played_ = item_id;
item_data_request_ = cloud_provider_->getItemDataAsync(
item->item()->id(), [this](IItem::Pointer i) {
if (i) emit runPlayerFromUrl(i->url().c_str());
item->item()->id(), [this](EitherError<IItem> i) {
if (i.right()) emit runPlayerFromUrl(i.right()->url().c_str());
});
}
......
......@@ -134,7 +134,8 @@ int main(int, char**) {
std::getline(line, file);
auto item = getChild(current_provider, current_directory, file);
if (item) {
auto data = current_provider->getItemDataAsync(item->id())->result();
auto data =
current_provider->getItemDataAsync(item->id())->result().right();
if (data) std::cout << "Url: " << data->url() << "\n";
}
}
......
......@@ -69,9 +69,12 @@ bool rename(Request<bool>* r, std::string dest_id, std::string source_id,
output);
if (!IHttpRequest::isSuccess(code)) return false;
} else {
auto directory = r->provider()
->getItemDataAsync(source_id, [](IItem::Pointer) {})
->result();
auto directory =
r->provider()
->getItemDataAsync(source_id, [](EitherError<IItem>) {})
->result()
.right();
if (!directory) return false;
Request<bool>::Semaphore semaphore(r);
auto children_request = r->provider()->listDirectoryAsync(
directory, [&semaphore](const std::vector<IItem::Pointer>&) {
......@@ -269,8 +272,8 @@ ICloudProvider::DeleteItemRequest::Pointer AmazonS3::deleteItemAsync(
ICloudProvider::GetItemDataRequest::Pointer AmazonS3::getItemDataAsync(
const std::string& id, GetItemCallback callback) {
auto r = util::make_unique<Request<IItem::Pointer>>(shared_from_this());
r->set_resolver([=](Request<IItem::Pointer>* r) -> IItem::Pointer {
auto r = util::make_unique<Request<EitherError<IItem>>>(shared_from_this());
r->set_resolver([=](Request<EitherError<IItem>>* r) -> EitherError<IItem> {
if (access_id().empty() || secret().empty()) r->reauthorize();
auto data = split(id);
auto item = std::make_shared<Item>(
......@@ -280,8 +283,8 @@ ICloudProvider::GetItemDataRequest::Pointer AmazonS3::getItemDataAsync(
: IItem::FileType::Unknown);
if (item->type() != IItem::FileType::Directory)
item->set_url(getUrl(*item));
callback(item);
return item;
callback(EitherError<IItem>(item));
return EitherError<IItem>(item);
});
return std::move(r);
}
......
......@@ -49,49 +49,50 @@ bool Box::reauthorize(int code) const {
ICloudProvider::GetItemDataRequest::Pointer Box::getItemDataAsync(
const std::string& id, GetItemDataCallback callback) {
auto r = util::make_unique<Request<IItem::Pointer>>(shared_from_this());
r->set_resolver(
[id, callback, this](Request<IItem::Pointer>* r) -> IItem::Pointer {
std::stringstream output;
int code = r->sendRequest(
[this, id](std::ostream&) {
return http()->create(endpoint() + "/2.0/files/" + id, "GET");
},
output);
if (!IHttpRequest::isSuccess(code)) {
int code = r->sendRequest(
[this, id](std::ostream&) {
return http()->create(endpoint() + "/2.0/folders/" + id, "GET");
},
output);
if (IHttpRequest::isSuccess(code)) {
Json::Value response;
output >> response;
auto item = toItem(response);
callback(item);
return item;
}
callback(nullptr);
return nullptr;
}
auto r = util::make_unique<Request<EitherError<IItem>>>(shared_from_this());
r->set_resolver([id, callback,
this](Request<EitherError<IItem>>* r) -> EitherError<IItem> {
std::stringstream output;
Error error;
int code = r->sendRequest(
[this, id](std::ostream&) {
return http()->create(endpoint() + "/2.0/files/" + id, "GET");
},
output, &error);
if (!IHttpRequest::isSuccess(code)) {
int code = r->sendRequest(
[this, id](std::ostream&) {
return http()->create(endpoint() + "/2.0/folders/" + id, "GET");
},
output, &error);
if (IHttpRequest::isSuccess(code)) {
Json::Value response;
output >> response;
auto item = toItem(response);
code = r->sendRequest(
[this, id](std::ostream&) {
auto request = http()->create(
endpoint() + "/2.0/files/" + id + "/content", "GET", false);
return request;
},
output);
if (IHttpRequest::isRedirect(code)) {
std::string redirect_url;
output >> redirect_url;
static_cast<Item*>(item.get())->set_url(redirect_url);
}
callback(item);
return item;
});
}
callback(error);
return error;
}
Json::Value response;
output >> response;
auto item = toItem(response);
code = r->sendRequest(
[this, id](std::ostream&) {
auto request = http()->create(
endpoint() + "/2.0/files/" + id + "/content", "GET", false);
return request;
},
output);
if (IHttpRequest::isRedirect(code)) {
std::string redirect_url;
output >> redirect_url;
static_cast<Item*>(item.get())->set_url(redirect_url);
}
callback(item);
return item;
});
return std::move(r);
}
......
......@@ -49,9 +49,9 @@ bool Dropbox::reauthorize(int code) const { return code == 400 || code == 401; }
ICloudProvider::GetItemDataRequest::Pointer Dropbox::getItemDataAsync(
const std::string& id, GetItemDataCallback callback) {
auto f = util::make_unique<Request<IItem::Pointer>>(shared_from_this());
f->set_resolver([this, id,
callback](Request<IItem::Pointer>* r) -> IItem::Pointer {
auto f = util::make_unique<Request<EitherError<IItem>>>(shared_from_this());
f->set_resolver([this, id, callback](
Request<EitherError<IItem>>* r) -> EitherError<IItem> {
auto item_data = [this, r, id](std::ostream& input) {
auto request =
http()->create(endpoint() + "/2/files/get_metadata", "POST");
......@@ -63,9 +63,10 @@ ICloudProvider::GetItemDataRequest::Pointer Dropbox::getItemDataAsync(
return request;
};
std::stringstream output;
if (!IHttpRequest::isSuccess(r->sendRequest(item_data, output))) {
callback(nullptr);
return nullptr;
Error error;
if (!IHttpRequest::isSuccess(r->sendRequest(item_data, output, &error))) {
callback(error);
return error;
}
Json::Value response;
output >> response;
......
......@@ -322,22 +322,24 @@ AuthorizeRequest::Pointer MegaNz::authorizeAsync() {
ICloudProvider::GetItemDataRequest::Pointer MegaNz::getItemDataAsync(
const std::string& id, GetItemDataCallback callback) {
auto r = util::make_unique<Request<IItem::Pointer>>(shared_from_this());
r->set_resolver(
[id, callback, this](Request<IItem::Pointer>* r) -> IItem::Pointer {
if (!ensureAuthorized(r)) {
callback(nullptr);
return nullptr;
}
std::unique_ptr<mega::MegaNode> node(mega_->getNodeByPath(id.c_str()));
if (!node) {
callback(nullptr);
return nullptr;
}
auto item = toItem(node.get());
callback(item);
return item;
});
auto r = util::make_unique<Request<EitherError<IItem>>>(shared_from_this());
r->set_resolver([id, callback,
this](Request<EitherError<IItem>>* r) -> EitherError<IItem> {
if (!ensureAuthorized(r)) {
Error e{401, "unauthorized"};
callback(e);
return e;
}
std::unique_ptr<mega::MegaNode> node(mega_->getNodeByPath(id.c_str()));
if (!node) {
Error e{404, "not found"};
callback(e);
return e;
}
auto item = toItem(node.get());
callback(item);
return item;
});
return std::move(r);
}
......
......@@ -49,42 +49,44 @@ IItem::Pointer YandexDisk::rootDirectory() const {
ICloudProvider::GetItemDataRequest::Pointer YandexDisk::getItemDataAsync(
const std::string& id, GetItemDataCallback callback) {
auto r = util::make_unique<Request<IItem::Pointer>>(shared_from_this());
r->set_resolver(
[this, id, callback](Request<IItem::Pointer>* r) -> IItem::Pointer {
std::stringstream output;
int code = r->sendRequest(
[this, id](std::ostream&) {
auto request =
http()->create(endpoint() + "/v1/disk/resources", "GET");
request->setParameter("path", id);
return request;
},
output);
if (!IHttpRequest::isSuccess(code)) {
callback(nullptr);
return nullptr;
}
Json::Value json;
auto r = util::make_unique<Request<EitherError<IItem>>>(
shared_from_this());
r->set_resolver([this, id, callback](Request<EitherError<IItem>>* r)
-> EitherError<IItem> {
std::stringstream output;
Error error;
int code = r->sendRequest(
[this, id](std::ostream&) {
auto request =
http()->create(endpoint() + "/v1/disk/resources", "GET");
request->setParameter("path", id);
return request;
},
output, &error);
if (!IHttpRequest::isSuccess(code)) {
callback(error);
return error;
}
Json::Value json;
output >> json;
auto item = toItem(json);
if (item->type() != IItem::FileType::Directory) {
code = r->sendRequest(
[this, id](std::ostream&) {
auto request = http()->create(
endpoint() + "/v1/disk/resources/download", "GET");
request->setParameter("path", id);
return request;
},
output);
if (IHttpRequest::isSuccess(code)) {
output >> json;
auto item = toItem(json);
if (item->type() != IItem::FileType::Directory) {
code = r->sendRequest(
[this, id](std::ostream&) {
auto request = http()->create(
endpoint() + "/v1/disk/resources/download", "GET");
request->setParameter("path", id);
return request;
},
output);
if (IHttpRequest::isSuccess(code)) {
output >> json;
static_cast<Item*>(item.get())->set_url(json["href"].asString());
}
}
callback(item);
return item;
});
static_cast<Item*>(item.get())->set_url(json["href"].asString());
}
}
callback(item);
return item;
});
return std::move(r);
}
......@@ -116,7 +118,7 @@ ICloudProvider::DownloadFileRequest::Pointer YandexDisk::downloadFileAsync(
std::string url = json["href"].asString();
code = r->sendRequest(
[this, url](std::ostream&) { return http()->create(url, "GET"); },
stream,
stream, nullptr,
std::bind(&IDownloadFileCallback::progress, callback.get(), _1, _2));
if (IHttpRequest::isSuccess(code)) callback->done();
}
......
......@@ -105,56 +105,57 @@ ICloudProvider::ListDirectoryRequest::Pointer YouTube::listDirectoryAsync(
ICloudProvider::GetItemDataRequest::Pointer YouTube::getItemDataAsync(
const std::string& id, GetItemDataCallback callback) {
auto r = util::make_unique<Request<IItem::Pointer>>(shared_from_this());
r->set_resolver(
[this, id, callback](Request<IItem::Pointer>* r) -> IItem::Pointer {
if (id == AUDIO_DIRECTORY) {
IItem::Pointer r = util::make_unique<Item>(
AUDIO_DIRECTORY, AUDIO_DIRECTORY, IItem::FileType::Directory);
callback(r);
return r;
}
std::stringstream response_stream;
int code = r->sendRequest(
[this, id](std::ostream& input) {
return getItemDataRequest(id, input);
auto r = util::make_unique<Request<EitherError<IItem>>>(shared_from_this());
r->set_resolver([this, id, callback](
Request<EitherError<IItem>>* r) -> EitherError<IItem> {
if (id == AUDIO_DIRECTORY) {
IItem::Pointer r = util::make_unique<Item>(
AUDIO_DIRECTORY, AUDIO_DIRECTORY, IItem::FileType::Directory);
callback(r);
return r;
}
std::stringstream response_stream;
Error error;
int code = r->sendRequest(
[this, id](std::ostream& input) {
return getItemDataRequest(id, input);
},
response_stream, &error);
if (IHttpRequest::isSuccess(code)) {
auto i = getItemDataResponse(
response_stream, id.find(AUDIO_ID_PREFIX) != std::string::npos);
if (i->type() == IItem::FileType::Audio) {
code = r->sendRequest(
[this, id](std::ostream&) {
auto request =
http()->create(youtube_dl_url_ + "/api/info", "GET");
request->setParameter("format", "bestaudio");
request->setParameter(
"url",
"http://youtube.com/watch?v=" +
extractId(id).substr(VIDEO_ID_PREFIX.length()));
return request;
},
response_stream);
response_stream, &error);
if (IHttpRequest::isSuccess(code)) {
auto i = getItemDataResponse(
response_stream, id.find(AUDIO_ID_PREFIX) != std::string::npos);
if (i->type() == IItem::FileType::Audio) {
code = r->sendRequest(
[this, id](std::ostream&) {
auto request =
http()->create(youtube_dl_url_ + "/api/info", "GET");
request->setParameter("format", "bestaudio");
request->setParameter(
"url",
"http://youtube.com/watch?v=" +
extractId(id).substr(VIDEO_ID_PREFIX.length()));
return request;
},
response_stream);
if (IHttpRequest::isSuccess(code)) {
Json::Value response;
response_stream >> response;
for (const Json::Value& v : response["info"]["formats"])
if (v["format_id"] == response["info"]["format_id"]) {
auto item = util::make_unique<Item>(
i->filename() + "." + v["ext"].asString(), i->id(),
i->type());
item->set_url(v["url"].asString());
i = std::move(item);
}
Json::Value response;
response_stream >> response;
for (const Json::Value& v : response["info"]["formats"])
if (v["format_id"] == response["info"]["format_id"]) {
auto item = util::make_unique<Item>(
i->filename() + "." + v["ext"].asString(), i->id(),
i->type());
item->set_url(v["url"].asString());
i = std::move(item);
}
}
callback(i);
return i;
}
callback(nullptr);
return nullptr;
});
}
callback(i);
return i;
}
callback(error);
return error;
});
return std::move(r);
}
......@@ -170,19 +171,19 @@ ICloudProvider::DownloadFileRequest::Pointer YouTube::downloadFileAsync(
if (item->type() == IItem::FileType::Audio) {
Request<void>::Semaphore semaphore(r);
auto t = getItemDataAsync(
item->id(), [&semaphore](IItem::Pointer) { semaphore.notify(); });
item->id(), [&semaphore](EitherError<IItem>) { semaphore.notify(); });
semaphore.wait();
if (r->is_cancelled())
t->cancel();
else
url = t->result()->url();
else if (t->result().right())
url = t->result().right()->url();
}
DownloadStreamWrapper wrapper(std::bind(
&IDownloadFileCallback::receivedData, callback.get(), _1, _2));
std::ostream stream(&wrapper);
int code = r->sendRequest(
[this, url](std::ostream&) { return http()->create(url, "GET"); },
stream,
stream, nullptr,
std::bind(&IDownloadFileCallback::progress, callback.get(), _1, _2));
if (IHttpRequest::isSuccess(code)) callback->done();
});
......
......@@ -45,10 +45,10 @@ class ICloudProvider {
using ExchangeCodeRequest = IRequest<EitherError<std::string>>;
using ListDirectoryRequest = IRequest<std::vector<IItem::Pointer>>;
using GetItemRequest = IRequest<IItem::Pointer>;
using GetItemRequest = IRequest<EitherError<IItem>>;
using DownloadFileRequest = IRequest<void>;
using UploadFileRequest = IRequest<void>;
using GetItemDataRequest = IRequest<IItem::Pointer>;
using GetItemDataRequest = IRequest<EitherError<IItem>>;
using DeleteItemRequest = IRequest<bool>;
using CreateDirectoryRequest = IRequest<IItem::Pointer>;
using MoveItemRequest = IRequest<bool>;
......@@ -236,9 +236,9 @@ class ICloudProvider {
*
* @return object representing the pending request
*/
virtual GetItemRequest::Pointer getItemAsync(const std::string& absolute_path,
GetItemCallback callback =
[](IItem::Pointer) {}) = 0;
virtual GetItemRequest::Pointer getItemAsync(
const std::string& absolute_path,
GetItemCallback callback = [](EitherError<IItem>) {}) = 0;
/**
* Downloads the item, the file is provided by callback.
......@@ -278,7 +278,7 @@ class ICloudProvider {
*/
virtual GetItemDataRequest::Pointer getItemDataAsync(
const std::string& id,
GetItemDataCallback callback = [](IItem::Pointer) {}) = 0;
GetItemDataCallback callback = [](EitherError<IItem>) {}) = 0;
/**
* Downloads thumbnail image, before calling the function, make sure provided
......
......@@ -183,6 +183,8 @@ class Either {
public:
Either(const Left& left) : left_(std::make_shared<Left>(left)) {}
Either(const Right& right) : right_(std::make_shared<Right>(right)) {}
Either(std::shared_ptr<Left> left) : left_(left) {}
Either(std::shared_ptr<Right> right) : right_(right) {}
std::shared_ptr<Left> left() const { return left_; }
std::shared_ptr<Right> right() const { return right_; }
......@@ -196,8 +198,8 @@ template <class T>
using EitherError = Either<Error, T>;
using ExchangeCodeCallback = std::function<void(EitherError<std::string>)>;
using GetItemCallback = std::function<void(IItem::Pointer)>;
using GetItemDataCallback = std::function<void(IItem::Pointer)>;
using GetItemCallback = std::function<void(EitherError<IItem>)>;
using GetItemDataCallback = std::function<void(EitherError<IItem>)>;
using DeleteItemCallback = std::function<void(bool)>;
using CreateDirectoryCallback = std::function<void(IItem::Pointer)>;
using MoveItemCallback = std::function<void(bool)>;
......
......@@ -45,8 +45,9 @@ DownloadFileRequest::DownloadFileRequest(std::shared_ptr<CloudProvider> p,
std::ostream response_stream(&stream_wrapper_);
int code = sendRequest(
[this](std::ostream& input) { return request_factory_(*file_, input); },
response_stream, std::bind(&DownloadFileRequest::ICallback::progress,
callback_.get(), _1, _2));
response_stream, nullptr,
std::bind(&DownloadFileRequest::ICallback::progress, callback_.get(),
_1, _2));
if (IHttpRequest::isSuccess(code))
callback_->done();
else if (fallback_thumbnail_)
......@@ -62,12 +63,13 @@ void DownloadFileRequest::generateThumbnail(
if (!r->provider()->thumbnailer()) return;
Request<void>::Semaphore semaphore(r);
auto item_data = r->provider()->getItemDataAsync(
item->id(), [&semaphore](IItem::Pointer) { semaphore.notify(); });
item->id(), [&semaphore](EitherError<IItem>) { semaphore.notify(); });
semaphore.wait();
if (r->is_cancelled()) return item_data->cancel();
if (!item_data->result()) return callback->error("Couldn't get item url.");
if (!item_data->result().right())
return callback->error("Couldn't get item url.");
auto thumbnail_data = r->provider()->thumbnailer()->generateThumbnail(
r->provider(), item_data->result(),
r->provider(), item_data->result().right(),
[&semaphore, callback](const std::vector<char>& data) {
if (!data.empty()) {
callback->receivedData(data.data(), data.size());
......
......@@ -31,20 +31,21 @@ namespace cloudstorage {
GetItemDataRequest::GetItemDataRequest(std::shared_ptr<CloudProvider> p,
const std::string& id, Callback callback)
: Request(p), id_(id), callback_(callback) {
set_resolver([this](Request*) -> IItem::Pointer {
set_resolver([this](Request*) -> EitherError<IItem> {
std::stringstream response_stream;
Error error;
int code = sendRequest(
[this](std::ostream& input) {
return provider()->getItemDataRequest(id_, input);
},
response_stream);
response_stream, &error);
if (IHttpRequest::isSuccess(code)) {
auto i = provider()->getItemDataResponse(response_stream);
callback_(i);
return i;
}
callback_(nullptr);
return nullptr;
callback_(error);
return error;
});
}
......
......@@ -30,7 +30,7 @@
namespace cloudstorage {
class GetItemDataRequest : public Request<IItem::Pointer> {
class GetItemDataRequest : public Request<EitherError<IItem>> {
public:
using Callback = GetItemDataCallback;
......
......@@ -30,10 +30,11 @@ namespace cloudstorage {
GetItemRequest::GetItemRequest(std::shared_ptr<CloudProvider> p,
const std::string& path, Callback callback)
: Request(p), path_(path), callback_(callback) {
set_resolver([this](Request*) -> IItem::Pointer {
set_resolver([this](Request*) -> EitherError<IItem> {
if (path_.empty() || path_.front() != '/') {
callback_(nullptr);
return nullptr;
Error e{403, "invalid path"};
callback_(e);
return e;
}
IItem::Pointer node = provider()->rootDirectory();
std::stringstream stream(path_.substr(1));
......@@ -45,7 +46,10 @@ GetItemRequest::GetItemRequest(std::shared_ptr<CloudProvider> p,
}
{
std::lock_guard<std::mutex> lock(mutex_);
if (is_cancelled()) return nullptr;
if (is_cancelled()) {
Error e{IHttpRequest::Aborted, ""};
return e;
}
current_request_ = provider()->listDirectoryAsync(
std::move(node), [](const std::vector<IItem::Pointer>&) {});
}
......
......@@ -28,7 +28,7 @@
namespace cloudstorage {
class GetItemRequest : public Request<IItem::Pointer> {
class GetItemRequest : public Request<EitherError<IItem>> {
public:
using Callback = GetItemCallback;
......
......@@ -153,7 +153,8 @@ std::string Request<T>::error_string(int code, const std::string& desc) const {
template <class T>
int Request<T>::sendRequest(
std::function<IHttpRequest::Pointer(std::ostream&)> factory,
std::ostream& output, ProgressFunction download, ProgressFunction upload) {
std::ostream& output, Error* error, ProgressFunction download,
ProgressFunction upload) {
auto p = provider();
if (!p) return IHttpRequest::Unknown;
std::stringstream input, error_stream;
......@@ -178,6 +179,7 @@ int Request<T>::sendRequest(
if (!is_cancelled() && code != IHttpRequest::Aborted)
this->error(code, error_stream.str());
}
if (error) *error = {code, error_stream.str()};
return code;
}
......@@ -221,5 +223,6 @@ template class Request<std::vector<std::shared_ptr<cloudstorage::IItem>>>;
template class Request<std::vector<char>>;
template class Request<std::string>;
template class Request<EitherError<std::string>>;
template class Request<EitherError<IItem>>;
} // namespace cloudstorage
......@@ -74,12 +74,14 @@ class Request : public IRequest<ReturnValue> {
*
* @param factory function which should create request to perform
* @param output output stream
* @param error_stream error stream
* @param download download progress callback
* @param upload upload progress callback
* @return http code or curl error code
*/
int sendRequest(std::function<IHttpRequest::Pointer(std::ostream&)> factory,
std::ostream& output, ProgressFunction download = nullptr,
std::ostream& output, Error* error_stream = nullptr,
ProgressFunction download = nullptr,
ProgressFunction upload = nullptr);
int send(IHttpRequest*, std::istream& input, std::ostream& output,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment