Skip to content

Commit 7e68863

Browse files
Reorganization, add get, create, update, delete systems, comments
1 parent 9552645 commit 7e68863

16 files changed

Lines changed: 1085 additions & 151 deletions

CSAPI-lib/APIRequest.h

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88
#include "APIResponse.h"
99

1010
namespace ConnectedSystemsAPI {
11+
struct RawHttpResponse {
12+
int responseCode = 0;
13+
std::string responseMessage;
14+
std::string responseBody;
15+
std::map<std::string, std::vector<std::string>> headers;
16+
};
17+
1118
class APIRequest {
1219
private:
1320
std::string apiRoot;
@@ -23,13 +30,13 @@ namespace ConnectedSystemsAPI {
2330
public:
2431
template<typename T>
2532
APIResponse<T> execute() {
26-
std::string rawResponse = execute();
27-
return APIResponse<T>(0, "", rawResponse, {});
33+
RawHttpResponse rawResponse = execute();
34+
return APIResponse<T>(rawResponse.responseCode, rawResponse.responseMessage, rawResponse.responseBody, rawResponse.headers);
2835
}
2936

30-
std::string execute() const {
37+
RawHttpResponse execute() const {
3138
CURL* curl = curl_easy_init();
32-
std::string response;
39+
RawHttpResponse response;
3340
if (curl) {
3441
std::string url = apiRoot + endpoint;
3542
struct curl_slist* header_list = nullptr;
@@ -40,7 +47,10 @@ namespace ConnectedSystemsAPI {
4047
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
4148
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
4249
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
43-
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
50+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response.responseBody);
51+
52+
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
53+
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response);
4454

4555
if (requestMethod == "POST") {
4656
curl_easy_setopt(curl, CURLOPT_POST, 1L);
@@ -58,6 +68,8 @@ namespace ConnectedSystemsAPI {
5868
if (res != CURLE_OK)
5969
std::cerr << "cURL error: " << curl_easy_strerror(res) << std::endl;
6070

71+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response.responseCode);
72+
6173
curl_slist_free_all(header_list);
6274
curl_easy_cleanup(curl);
6375
}
@@ -71,6 +83,42 @@ namespace ConnectedSystemsAPI {
7183
return size * nmemb;
7284
}
7385

86+
static size_t HeaderCallback(char* buffer, size_t size, size_t nitems, void* userdata) {
87+
size_t totalSize = size * nitems;
88+
auto* response = static_cast<RawHttpResponse*>(userdata);
89+
std::string headerLine(buffer, totalSize);
90+
91+
// Check for the status line (e.g., "HTTP/1.1 200 OK\r\n")
92+
if (headerLine.find("HTTP/") == 0) {
93+
// Remove trailing \r\n
94+
headerLine.erase(headerLine.find_last_not_of("\r\n") + 1);
95+
// Remove "HTTP/1.1 " prefix to get the status message
96+
size_t firstSpace = headerLine.find(' ');
97+
if (firstSpace != std::string::npos) {
98+
size_t secondSpace = headerLine.find(' ', firstSpace + 1);
99+
if (secondSpace != std::string::npos) {
100+
response->responseMessage = headerLine.substr(secondSpace + 1);
101+
}
102+
}
103+
else
104+
response->responseMessage = headerLine;
105+
}
106+
else {
107+
// Parse header: "Key: Value"
108+
auto colonPos = headerLine.find(':');
109+
if (colonPos != std::string::npos) {
110+
std::string key = headerLine.substr(0, colonPos);
111+
std::string value = headerLine.substr(colonPos + 1);
112+
// Trim whitespace and trailing \r\n
113+
key.erase(key.find_last_not_of(" \t\r\n") + 1);
114+
value.erase(0, value.find_first_not_of(" \t"));
115+
value.erase(value.find_last_not_of("\r\n") + 1);
116+
response->headers[key].push_back(value);
117+
}
118+
}
119+
return totalSize;
120+
}
121+
74122
public:
75123
// Builder inner class
76124
class Builder {

CSAPI-lib/APIResponse.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,27 @@ namespace ConnectedSystemsAPI {
6161
return items;
6262
}
6363
};
64+
65+
template <>
66+
class APIResponse<void> {
67+
private:
68+
int responseCode;
69+
std::string responseMessage;
70+
std::string responseBody;
71+
std::map<std::string, std::vector<std::string>> headers;
72+
73+
public:
74+
APIResponse(int responseCode, const std::string& responseMessage, const std::string& responseBody, const std::map<std::string, std::vector<std::string>>& headers)
75+
: responseCode(responseCode), responseMessage(responseMessage), responseBody(responseBody), headers(headers) {
76+
}
77+
78+
bool isSuccessful() const {
79+
return responseCode >= 200 && responseCode < 400;
80+
}
81+
82+
int getResponseCode() const { return responseCode; }
83+
const std::string& getResponseMessage() const { return responseMessage; }
84+
const std::string& getResponseBody() const { return responseBody; }
85+
const std::map<std::string, std::vector<std::string>>& getHeaders() const { return headers; }
86+
};
6487
}

CSAPI-lib/CSAPI-lib.vcxproj

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,17 @@
132132
<ClInclude Include="APIRequest.h" />
133133
<ClInclude Include="APIResponse.h" />
134134
<ClInclude Include="ConnectedSystemsAPI.h" />
135+
<ClInclude Include="DataModels\Link.h" />
136+
<ClInclude Include="DataModels\Properties.h" />
137+
<ClInclude Include="DataModels\PropertiesBuilder.h" />
138+
<ClInclude Include="DataModels\System.h" />
139+
<ClInclude Include="DataModels\SystemBuilder.h" />
140+
<ClInclude Include="DataModels\TimeExtent.h" />
135141
<ClInclude Include="framework.h" />
136142
<ClInclude Include="OptionalTimePoint.h" />
137-
<ClInclude Include="Properties.h" />
138-
<ClInclude Include="System.h" />
143+
<ClInclude Include="Query\QueryParameters.h" />
144+
<ClInclude Include="Query\SystemsQuery.h" />
139145
<ClInclude Include="SystemsAPI.h" />
140-
<ClInclude Include="TimeExtent.h" />
141146
<ClInclude Include="TimeUtils.h" />
142147
</ItemGroup>
143148
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

CSAPI-lib/CSAPI-lib.vcxproj.filters

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
<Filter Include="Header Files\Util">
2020
<UniqueIdentifier>{5f2184e7-00be-4a1b-b6ca-531629b94bb6}</UniqueIdentifier>
2121
</Filter>
22+
<Filter Include="Header Files\Query">
23+
<UniqueIdentifier>{86d8700a-e917-4bd9-96e8-189e1b1a7030}</UniqueIdentifier>
24+
</Filter>
2225
</ItemGroup>
2326
<ItemGroup>
2427
<ClInclude Include="framework.h">
@@ -39,17 +42,32 @@
3942
<ClInclude Include="SystemsAPI.h">
4043
<Filter>Header Files</Filter>
4144
</ClInclude>
42-
<ClInclude Include="Properties.h">
45+
<ClInclude Include="TimeUtils.h">
46+
<Filter>Header Files\Util</Filter>
47+
</ClInclude>
48+
<ClInclude Include="DataModels\System.h">
4349
<Filter>Header Files\DataModels</Filter>
4450
</ClInclude>
45-
<ClInclude Include="System.h">
51+
<ClInclude Include="DataModels\SystemBuilder.h">
4652
<Filter>Header Files\DataModels</Filter>
4753
</ClInclude>
48-
<ClInclude Include="TimeExtent.h">
54+
<ClInclude Include="DataModels\Link.h">
4955
<Filter>Header Files\DataModels</Filter>
5056
</ClInclude>
51-
<ClInclude Include="TimeUtils.h">
52-
<Filter>Header Files\Util</Filter>
57+
<ClInclude Include="DataModels\Properties.h">
58+
<Filter>Header Files\DataModels</Filter>
59+
</ClInclude>
60+
<ClInclude Include="DataModels\TimeExtent.h">
61+
<Filter>Header Files\DataModels</Filter>
62+
</ClInclude>
63+
<ClInclude Include="DataModels\PropertiesBuilder.h">
64+
<Filter>Header Files\DataModels</Filter>
65+
</ClInclude>
66+
<ClInclude Include="Query\QueryParameters.h">
67+
<Filter>Header Files\Query</Filter>
68+
</ClInclude>
69+
<ClInclude Include="Query\SystemsQuery.h">
70+
<Filter>Header Files\Query</Filter>
5371
</ClInclude>
5472
</ItemGroup>
5573
</Project>

CSAPI-lib/DataModels/Link.h

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <nlohmann/json.hpp>
5+
6+
namespace ConnectedSystemsAPI {
7+
namespace DataModels {
8+
class Link {
9+
private:
10+
std::string href;
11+
std::string relationType;
12+
std::string mediaType;
13+
std::string hrefLanguage;
14+
std::string title;
15+
std::string uid;
16+
std::string resourceType;
17+
std::string interfaceUri;
18+
19+
public:
20+
Link() = default;
21+
Link(const std::string& href,
22+
const std::string& relationType,
23+
const std::string& mediaType,
24+
const std::string& hrefLanguage,
25+
const std::string& title,
26+
const std::string& uid,
27+
const std::string& resourceType,
28+
const std::string& interfaceUri)
29+
: href(href), relationType(relationType), mediaType(mediaType), hrefLanguage(hrefLanguage),
30+
title(title), uid(uid), resourceType(resourceType), interfaceUri(interfaceUri) {
31+
}
32+
33+
/// <returns>URL of the target resource.</returns>
34+
const std::string& getHref() const { return href; }
35+
/// <returns>Link relation type.</returns>
36+
const std::string& getRelationType() const { return relationType; }
37+
/// <returns>Media type of the target resource.</returns>
38+
const std::string& getMediaType() const { return mediaType; }
39+
/// <returns>Language tag of the target resource (2-letter language code, followed by optional 2-letter region code).</returns>
40+
const std::string& getHrefLanguage() const { return hrefLanguage; }
41+
/// <returns>Title of the target resource.</returns>
42+
const std::string& getTitle() const { return title; }
43+
/// <returns>Unique identifier of the target resource</returns>
44+
const std::string& getUid() const { return uid; }
45+
/// <returns>Semantic type of the target resource (RFC 6690).</returns>
46+
const std::string& getResourceType() const { return resourceType; }
47+
/// <returns>Interface used to access the target resource (RFC 6690).</returns>
48+
const std::string& getInterfaceUri() const { return interfaceUri; }
49+
};
50+
51+
inline void from_json(const nlohmann::json& j, Link& l) {
52+
l = Link(
53+
j.at("href").get<std::string>(),
54+
j.value("rel", ""),
55+
j.value("type", ""),
56+
j.value("hreflang", ""),
57+
j.value("title", ""),
58+
j.value("uid", ""),
59+
j.value("rt", ""),
60+
j.value("if", "")
61+
);
62+
}
63+
64+
inline void to_json(nlohmann::ordered_json& j, const Link& l) {
65+
j = nlohmann::ordered_json::object();
66+
j["href"] = l.getHref();
67+
68+
if (!l.getRelationType().empty()) {
69+
j["rel"] = l.getRelationType();
70+
}
71+
if (!l.getMediaType().empty()) {
72+
j["type"] = l.getMediaType();
73+
}
74+
if (!l.getHrefLanguage().empty()) {
75+
j["hreflang"] = l.getHrefLanguage();
76+
}
77+
if (!l.getTitle().empty()) {
78+
j["title"] = l.getTitle();
79+
}
80+
if (!l.getUid().empty()) {
81+
j["uid"] = l.getUid();
82+
}
83+
if (!l.getResourceType().empty()) {
84+
j["rt"] = l.getResourceType();
85+
}
86+
if (!l.getInterfaceUri().empty()) {
87+
j["if"] = l.getInterfaceUri();
88+
}
89+
}
90+
91+
inline std::ostream& operator<<(std::ostream& os, const Link& l) {
92+
nlohmann::ordered_json j;
93+
ConnectedSystemsAPI::DataModels::to_json(j, l);
94+
return os << j.dump(4);
95+
}
96+
}
97+
}
Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#pragma once
22

3-
#include <iostream>
43
#include <string>
54
#include <nlohmann/json.hpp>
65
#include "TimeExtent.h"
@@ -15,6 +14,7 @@ namespace ConnectedSystemsAPI {
1514
std::string description;
1615
std::string assetType;
1716
std::optional<TimeExtent> validTime;
17+
1818
public:
1919
Properties() = default;
2020
Properties(const std::string& featureType,
@@ -26,11 +26,17 @@ namespace ConnectedSystemsAPI {
2626
: featureType(featureType), uid(uid), name(name), description(description), assetType(assetType), validTime(validTime) {
2727
}
2828

29+
/// <returns>Identifier of the feature, either a URI, a CURIE, or a simple token.</returns>
2930
const std::string& getFeatureType() const { return featureType; }
31+
/// <returns>Globally unique identifier of the feature.</returns>
3032
const std::string& getUid() const { return uid; }
33+
/// <returns>Human-readable name of the feature.</returns>
3134
const std::string& getName() const { return name; }
35+
/// <returns>Human-readable description of the feature.</returns>
3236
const std::string& getDescription() const { return description; }
37+
/// <returns>Type of asset represented by this system.</returns>
3338
const std::string& getAssetType() const { return assetType; }
39+
/// <returns>Time period during which the system description is valid.</returns>
3440
const std::optional<TimeExtent>& getValidTime() const { return validTime; }
3541
};
3642

@@ -46,11 +52,10 @@ namespace ConnectedSystemsAPI {
4652
}
4753

4854
inline void to_json(nlohmann::ordered_json& j, const Properties& p) {
49-
j = nlohmann::ordered_json{
50-
{"featureType", p.getFeatureType()},
51-
{"uid", p.getUid()},
52-
{"name", p.getName()}
53-
};
55+
j = nlohmann::ordered_json::object();
56+
j["featureType"] = p.getFeatureType();
57+
j["uid"] = p.getUid();
58+
j["name"] = p.getName();
5459

5560
if (!p.getDescription().empty()) {
5661
j["description"] = p.getDescription();
@@ -68,6 +73,5 @@ namespace ConnectedSystemsAPI {
6873
ConnectedSystemsAPI::DataModels::to_json(j, p);
6974
return os << j.dump(4);
7075
}
71-
7276
}
7377
}

0 commit comments

Comments
 (0)