1 /*
2 * SessionPanmirrorCrossref.cpp
3 *
4 * Copyright (C) 2009-16 by RStudio, Inc.
5 *
6 * Unless you have received this program directly from RStudio pursuant
7 * to the terms of a commercial license agreement with RStudio, then
8 * this program is licensed to you under the terms of version 3 of the
9 * GNU Affero General Public License. This program is distributed WITHOUT
10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13 *
14 */
15
16 #include "SessionPanmirrorCrossref.hpp"
17
18 #include <shared_core/Error.hpp>
19 #include <shared_core/json/Json.hpp>
20
21 #include <core/Exec.hpp>
22 #include <core/json/JsonRpc.hpp>
23 #include <core/http/Util.hpp>
24
25 #include <r/ROptions.hpp>
26
27 #include <session/SessionModuleContext.hpp>
28 #include <session/SessionAsyncDownloadFile.hpp>
29
30 using namespace rstudio::core;
31
32 namespace rstudio {
33 namespace session {
34 namespace modules {
35 namespace panmirror {
36 namespace crossref {
37
38 namespace {
39
crossrefContentRequestHandler(const core::json::Value & value,core::json::JsonRpcResponse * pResponse)40 void crossrefContentRequestHandler(const core::json::Value& value, core::json::JsonRpcResponse* pResponse)
41 {
42 pResponse->setResult(value);
43 }
44
crossrefApiRequestHandler(const core::json::Value & value,core::json::JsonRpcResponse * pResponse)45 void crossrefApiRequestHandler(const core::json::Value& value, core::json::JsonRpcResponse* pResponse)
46 {
47 if (json::isType<json::Object>(value))
48 {
49 json::Object responseJson = value.getObject();
50 std::string status;
51 json::Object message;
52 Error error = json::readObject(responseJson, "status", status,
53 "message", message);
54 if (error)
55 {
56 json::setErrorResponse(error, pResponse);
57 }
58 else if (status != "ok")
59 {
60 Error error = systemError(boost::system::errc::state_not_recoverable,
61 "Unexpected status from crossref api: " + status,
62 ERROR_LOCATION);
63 json::setErrorResponse(error, pResponse);
64 }
65 else
66 {
67 pResponse->setResult(message);
68 }
69 }
70 else
71 {
72 Error error = systemError(boost::system::errc::state_not_recoverable,
73 "Unexpected response from crossref api",
74 ERROR_LOCATION);
75 json::setErrorResponse(error, pResponse);
76 }
77 }
78
crossrefRequest(const std::string & resource,const http::Fields & params,const session::JsonRpcResponseHandler & handler,const json::JsonRpcFunctionContinuation & cont)79 void crossrefRequest(const std::string& resource,
80 const http::Fields& params,
81 const session::JsonRpcResponseHandler& handler,
82 const json::JsonRpcFunctionContinuation& cont)
83 {
84 // email address
85 std::string email = r::options::getOption<std::string>("rstudio.crossref_email",
86 "crossref@rstudio.com", false);
87
88 // build user agent
89 std::string userAgent = r::options::getOption<std::string>("HTTPUserAgent", "RStudio") +
90 "; RStudio Crossref Cite (mailto:" + email + ")";
91
92 // build query string
93 std::string queryString;
94 core::http::util::buildQueryString(params, &queryString);
95 if (queryString.length() > 0)
96 queryString = "?" + queryString;
97
98 // build the url and make the request
99 boost::format fmt("%s/%s%s");
100 const std::string url = boost::str(fmt % kCrossrefApiHost % resource % queryString);
101
102 http::Fields headers;
103 asyncJsonRpcRequest(url, userAgent, headers, handler, cont);
104 }
105
106
crossrefWorks(const json::JsonRpcRequest & request,const json::JsonRpcFunctionContinuation & cont)107 void crossrefWorks(const json::JsonRpcRequest& request,
108 const json::JsonRpcFunctionContinuation& cont)
109 {
110 // extract query
111 std::string query;
112 Error error = json::readParams(request.params, &query);
113 if (error)
114 {
115 json::JsonRpcResponse response;
116 setErrorResponse(error, &response);
117 cont(Success(), &response);
118 return;
119 }
120
121 // build params
122 core::http::Fields params;
123 params.push_back(std::make_pair("query", query));
124
125 // make the request
126 crossrefRequest(kCrossrefWorks, params, crossrefApiRequestHandler, cont);
127 }
128
crossrefDoi(const json::JsonRpcRequest & request,const json::JsonRpcFunctionContinuation & cont)129 void crossrefDoi(const json::JsonRpcRequest& request,
130 const json::JsonRpcFunctionContinuation& cont)
131 {
132 std::string doi;
133 Error error = json::readParams(request.params, &doi);
134 if (error)
135 {
136 json::JsonRpcResponse response;
137 setErrorResponse(error, &response);
138 cont(Success(), &response);
139 return;
140 }
141
142 // Path to DOI metadata works/{doi}/transform/{format} (see: https://citation.crosscite.org/docs.html#sec-5)
143 const char * const kCitationFormat = "application/vnd.citationstyles.csl+json";
144 boost::format fmt("%s/%s/transform/%s");
145 const std::string resourcePath = boost::str(fmt % kCrossrefWorks % doi % kCitationFormat);
146
147 // No parameters
148 core::http::Fields params;
149
150 // make the request
151 crossrefRequest(resourcePath, params, crossrefContentRequestHandler, cont);
152 }
153
154
155 } // end anonymous namespace
156
initialize()157 Error initialize()
158 {
159 ExecBlock initBlock;
160 initBlock.addFunctions()
161 (boost::bind(module_context::registerAsyncRpcMethod, "crossref_works", crossrefWorks))
162 (boost::bind(module_context::registerAsyncRpcMethod, "crossref_doi", crossrefDoi))
163 ;
164 return initBlock.execute();
165 }
166
167 } // end namespace crossref
168 } // end namespace panmirror
169 } // end namespace modules
170 } // end namespace session
171 } // end namespace rstudio
172