Skip to content

Commit 6e02acd

Browse files
committed
Add static analysis and coverage tasks to Rakefile; update README for API key changes and installation instruction.
Improve coverage
1 parent 8c959d1 commit 6e02acd

File tree

11 files changed

+139
-85
lines changed

11 files changed

+139
-85
lines changed

.github/workflows/c-cpp.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ jobs:
1818
- name: test
1919
run: make setup build test
2020
env:
21-
API_KEY: ${{secrets.API_KEY}}
21+
SERPAPI_KEY: ${{secrets.SERPAPI_KEY}}

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ require 'serpapi'
272272
using namespace rapidjson;
273273
using namespace std;
274274

275-
void info(string msg) {
275+
void info(const string& msg) {
276276
cout << "\nINFO: " << msg;
277277
}
278278

@@ -291,7 +291,11 @@ void info(const Document& document) {
291291
int main()
292292
{
293293
// initialize a client
294-
const char* env_p = std::getenv("API_KEY");
294+
const char* env_p = std::getenv("SERPAPI_KEY");
295+
if (env_p == nullptr) {
296+
cout << "SERPAPI_KEY not set, skipping OOBT" << endl;
297+
return 0;
298+
}
295299
std::string apiKey(env_p);
296300
std::map<string, string> default_parameter;
297301
default_parameter["api_key"] = apiKey;
@@ -322,7 +326,7 @@ int main()
322326
info(" id: " + id);
323327

324328
info("search archive with id: " + id);
325-
client.searchArchive(id);
329+
client.search_archive(id);
326330
assert(d["search_metadata"]["status"] == "Success");
327331
info(" search found in archive.");
328332
info(" test passed.\n");
@@ -423,7 +427,7 @@ The directory spec/ includes specification which serves the dual purposes of exa
423427

424428
Set your secret API key in your shell before running a test.
425429
```bash
426-
export API_KEY="your_secret_key"
430+
export SERPAPI_KEY="your_secret_key"
427431
```
428432
Install testing dependency
429433
```bash
@@ -447,30 +451,26 @@ To flush the flow.
447451
$ rake
448452
```
449453

450-
Open coverage report generated by `rake test`
451-
```sh
452-
open coverage/index.html
453-
```
454454

455455
Open ./Rakefile for more information.
456456

457457
Contributions are welcome. Feel to submit a pull request!
458458

459459
// TODO
460-
- [] Release version 1.0.0
460+
- [] Release version 1.1.0
461461

462462

463463
## Run with Docker
464464

465465
```bash
466-
env API_KEY=API_KEY docker run --rm -it -e API_KEY --workdir /tmp/serpapi -v $PWD:/tmp/serpapi conanio/gcc10 make install_linux reset all
466+
env SERPAPI_KEY=SERPAPI_KEY docker run --rm -it -e SERPAPI_KEY --workdir /tmp/serpapi -v $PWD:/tmp/serpapi conanio/gcc10 make install_linux reset all
467467
```
468468

469469
This will use https://hub.docker.com/layers/conanio/gcc10 image instead
470470
of creating our own image for compilation on Linux.
471471

472472
## Get started.
473-
- Set an environment API_KEY=<secret>
473+
- Set an environment SERPAPI_KEY=<secret>
474474
- Install dependency either apple or linux tested.
475475
- `rake install_apple`
476476
- `rake install_linux`

README.md.erb

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ The directory spec/ includes specification which serves the dual purposes of exa
361361

362362
Set your secret API key in your shell before running a test.
363363
```bash
364-
export API_KEY="your_secret_key"
364+
export SERPAPI_KEY="your_secret_key"
365365
```
366366
Install testing dependency
367367
```bash
@@ -385,30 +385,26 @@ To flush the flow.
385385
$ rake
386386
```
387387

388-
Open coverage report generated by `rake test`
389-
```sh
390-
open coverage/index.html
391-
```
392388

393389
Open ./Rakefile for more information.
394390

395391
Contributions are welcome. Feel to submit a pull request!
396392

397393
// TODO
398-
- [] Release version 1.0.0
394+
- [] Release version 1.1.0
399395

400396

401397
## Run with Docker
402398

403399
```bash
404-
env API_KEY=API_KEY docker run --rm -it -e API_KEY --workdir /tmp/serpapi -v $PWD:/tmp/serpapi conanio/gcc10 make install_linux reset all
400+
env SERPAPI_KEY=SERPAPI_KEY docker run --rm -it -e SERPAPI_KEY --workdir /tmp/serpapi -v $PWD:/tmp/serpapi conanio/gcc10 make install_linux reset all
405401
```
406402

407403
This will use https://hub.docker.com/layers/conanio/gcc10 image instead
408404
of creating our own image for compilation on Linux.
409405

410406
## Get started.
411-
- Set an environment API_KEY=<secret>
407+
- Set an environment SERPAPI_KEY=<secret>
412408
- Install dependency either apple or linux tested.
413409
- `rake install_apple`
414410
- `rake install_linux`

Rakefile

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ task :test do
2323
sh('ninja -C build test')
2424
end
2525

26+
desc('run static analysis')
27+
task :lint do
28+
rapidjson_inc = `pkg-config --variable=includedir rapidjson`.strip
29+
sh("cppcheck src/*.cpp test/*.cpp oobt/*.cpp example/*.cpp -I src -I #{rapidjson_inc} --enable=warning,style,performance,portability --suppress='*:*/rapidjson/*' --suppress=missingIncludeSystem --suppress=unusedFunction --error-exitcode=1")
30+
end
31+
32+
desc('generate coverage report')
33+
task :coverage do
34+
sh('meson setup build --wipe -Db_coverage=true')
35+
sh('ninja -C build')
36+
sh('ninja -C build test')
37+
# Use gcovr directly to focus on src/ and provide a cleaner report
38+
sh('gcovr -r . --filter src/')
39+
sh('gcovr -r . --filter src/ -s')
40+
end
41+
2642
task readme: ['README.md.erb'] do
2743
`erb -T '-' README.md.erb > README.md`
2844
end
@@ -50,11 +66,11 @@ end
5066
namespace :install do
5167
desc('install dependency on debian AARCH64 and x86 [tested]')
5268
task :linux do
53-
sh('sudo apt update -y && sudo apt install -f -y build-essential meson pkg-config curl cmake meson ninja-build libcurl4-openssl-dev rapidjson-dev googletest')
69+
sh('sudo apt update -y && sudo apt install -f -y build-essential meson pkg-config curl cmake meson ninja-build libcurl4-openssl-dev rapidjson-dev googletest cppcheck gcovr lcov')
5470
end
5571

5672
desc('install dependency on Apple M1 aarch64 [tested]')
5773
task :apple do
58-
sh('brew install meson pkg-config curl cmake meson ninja rapidjson googletest')
74+
sh('brew install meson pkg-config curl cmake meson ninja rapidjson googletest cppcheck gcovr lcov')
5975
end
6076
end

example/google_example.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ using namespace std;
1919
int main()
2020
{
2121
// initialize a client
22-
const char* env_p = std::getenv("API_KEY");
22+
const char* env_p = std::getenv("SERPAPI_KEY");
23+
if (env_p == nullptr) {
24+
cout << "SERPAPI_KEY not set, skipping example" << endl;
25+
return 0;
26+
}
2327
std::string apiKey(env_p);
2428
std::map<string, string> default_parameter;
2529
default_parameter["engine"] = "google";
@@ -41,7 +45,7 @@ int main()
4145
assert(d["search_metadata"]["id"].IsString());
4246

4347
string id = d["search_metadata"]["id"].GetString();
44-
client.searchArchive(id);
48+
client.search_archive(id);
4549
assert(d["search_metadata"]["status"] == "Success");
4650
info(d);
4751
}

example/helper.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
using namespace std;
1212
using namespace rapidjson;
1313

14-
void info(string msg) {
14+
void info(const string& msg) {
1515
cout << "\nINFO: " << msg << endl;
1616
}
1717

example/helper.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66

77
#include "rapidjson/document.h"
88

9-
void info(string msg);
9+
void info(const std::string& msg);
1010
void info(double msg);
1111
void info(const rapidjson::Document& document);

oobt/main.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
using namespace rapidjson;
1515
using namespace std;
1616

17-
void info(string msg) {
17+
void info(const string& msg) {
1818
cout << "\nINFO: " << msg;
1919
}
2020

@@ -33,7 +33,11 @@ void info(const Document& document) {
3333
int main()
3434
{
3535
// initialize a client
36-
const char* env_p = std::getenv("API_KEY");
36+
const char* env_p = std::getenv("SERPAPI_KEY");
37+
if (env_p == nullptr) {
38+
cout << "SERPAPI_KEY not set, skipping OOBT" << endl;
39+
return 0;
40+
}
3741
std::string apiKey(env_p);
3842
std::map<string, string> default_parameter;
3943
default_parameter["api_key"] = apiKey;
@@ -64,7 +68,7 @@ int main()
6468
info(" id: " + id);
6569

6670
info("search archive with id: " + id);
67-
client.searchArchive(id);
71+
client.search_archive(id);
6872
assert(d["search_metadata"]["status"] == "Success");
6973
info(" search found in archive.");
7074
info(" test passed.\n");

src/serpapi.cpp

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace serpapi
99

1010
const static string HOST = "https://serpapi.com";
1111
const static string NAME = "serpapi-cpp";
12-
const static string VERSION = "1.0.0";
12+
const static string VERSION = "0.1.0";
1313

1414
using namespace rapidjson;
1515
using namespace std;
@@ -19,103 +19,108 @@ struct GetResponse {
1919
string payload;
2020
};
2121

22-
Client::Client(map<string, string> parameter)
22+
Client::Client(const map<string, string>& parameter)
2323
{
2424
this->parameter = parameter;
2525
}
2626

27+
Client::~Client()
28+
{
29+
}
30+
2731
/***
2832
* Get HTML search results
2933
*/
30-
string Client::html(map<string, string> parameter)
34+
string Client::html(const map<string, string>& parameter)
3135
{
3236
GetResponse gr = Client::get("/search", "html", parameter);
3337
return gr.payload;
3438
}
3539

36-
Document Client::search(map<string, string> parameter)
40+
Document Client::search(const map<string, string>& parameter)
3741
{
3842
return Client::json("/search", parameter);
3943
}
4044

41-
Document Client::searchArchive(string id)
45+
Document Client::search_archive(const string& id)
4246
{
4347
return Client::json("/searches/" + id + ".json", map<string, string>());
4448
}
4549

46-
Document Client::account(map<string, string> parameter)
50+
Document Client::account(const map<string, string>& parameter)
4751
{
4852
return Client::json("/account.json", parameter);
4953
}
5054

51-
Document Client::location(map<string, string> parameter)
55+
Document Client::location(const map<string, string>& parameter)
5256
{
5357
return Client::json("/locations.json", parameter);
5458
}
5559

56-
Document Client::json(string uri, map<string, string> parameter)
60+
Document Client::json(const string& uri, const map<string, string>& parameter)
5761
{
5862
GetResponse gr = get(uri, "json", parameter);
59-
const char* json = gr.payload.c_str();
63+
const char* json_payload = gr.payload.c_str();
6064
Document d;
61-
d.Parse(json);
62-
// if(gr.httpCode != 200 && d.HasMember("error")) {
63-
// Value entry(d);
64-
// entry.AddMember("httpCode", string(gr.httpCode), d.GetAllocator());
65-
// entry.AddMember("error", "serpapi.com did not returned any error message", d.GetAllocator());
66-
// }
65+
d.Parse(json_payload);
6766
return d;
6867
}
6968

70-
string encodeUrl(map<string, string> parameter)
69+
string encodeUrl(CURL* curl, const map<string, string>& parameter)
7170
{
72-
map<string, string> param(parameter);
7371
string s = "";
74-
map<string,string>::iterator it;
75-
for (it = param.begin(); it != param.end(); ++it)
72+
map<string,string>::const_iterator it;
73+
for (it = parameter.begin(); it != parameter.end(); ++it)
7674
{
7775
if (s != "")
7876
{
7977
s += "&";
8078
}
81-
// encode each value in case of special character
82-
s += it->first + "=" + it->second;
79+
80+
char* escaped_key = curl_easy_escape(curl, it->first.c_str(), it->first.length());
81+
char* escaped_value = curl_easy_escape(curl, it->second.c_str(), it->second.length());
82+
83+
s += string(escaped_key) + "=" + string(escaped_value);
84+
85+
curl_free(escaped_key);
86+
curl_free(escaped_value);
8387
}
8488
return s;
8589
}
8690

87-
string Client::url(string output, map<string, string> parameter)
91+
string Client::url(CURL* curl, const string& output, const map<string, string>& parameter)
8892
{
8993
// encode parameter
90-
string url = encodeUrl(parameter);
91-
if(size(this->parameter) > 0) {
92-
url += "&";
93-
url += encodeUrl(this->parameter);
94+
string url_str = encodeUrl(curl, parameter);
95+
if(this->parameter.size() > 0) {
96+
if (!url_str.empty()) url_str += "&";
97+
url_str += encodeUrl(curl, this->parameter);
9498
}
9599
// append output format
96-
url += "&output=" + output;
100+
url_str += "&output=" + output;
97101
// append source language
98-
url += "&source="+NAME+":"+VERSION;
99-
// cout << url << endl;
100-
return url;
102+
url_str += "&source="+NAME+":"+VERSION;
103+
return url_str;
101104
}
102105

103-
GetResponse Client::get(string uri, string output, map<string, string> parameter)
106+
GetResponse Client::get(const string& uri, const string& output, const map<string, string>& parameter)
104107
{
105108
curl_global_init(CURL_GLOBAL_DEFAULT);
106-
curl = curl_easy_init();
107-
const string url = HOST + uri + "?" + this->url(output, parameter);
108-
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
109+
CURL* curl = curl_easy_init();
110+
const string url_params = this->url(curl, output, parameter);
111+
const string full_url = HOST + uri + "?" + url_params;
112+
113+
curl_easy_setopt(curl, CURLOPT_URL, full_url.c_str());
109114
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
110-
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
115+
curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)timeout);
111116
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
112-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
113-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
117+
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
118+
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
114119

115120
long httpCode(0);
116-
unique_ptr<string> httpData(new string());
121+
string httpData;
117122
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);
118-
curl_easy_setopt(curl, CURLOPT_WRITEDATA, httpData.get());
123+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &httpData);
119124

120125
// execute search
121126
curl_easy_perform(curl);
@@ -125,7 +130,7 @@ GetResponse Client::get(string uri, string output, map<string, string> parameter
125130
curl_global_cleanup();
126131
GetResponse gr;
127132
gr.httpCode = httpCode;
128-
gr.payload = *httpData.get();
133+
gr.payload = httpData;
129134
return gr;
130135
}
131136

0 commit comments

Comments
 (0)