Skip to content

Commit 4dcfb5e

Browse files
Create CommandSchemaBuilder, move integration tests into a folder, create unit tests for ConSysAPI.
1 parent 3063c76 commit 4dcfb5e

16 files changed

Lines changed: 783 additions & 87 deletions

CSAPI-lib/CSAPI-lib.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
<ClInclude Include="ConnectedSystemsAPI.h" />
138138
<ClInclude Include="ControlStreamsAPI.h" />
139139
<ClInclude Include="DataModels\CommandSchema.h" />
140+
<ClInclude Include="DataModels\CommandSchemaBuilder.h" />
140141
<ClInclude Include="DataModels\Component\Boolean.h" />
141142
<ClInclude Include="DataModels\Component\BooleanBuilder.h" />
142143
<ClInclude Include="DataModels\Component\Category.h" />

CSAPI-lib/CSAPI-lib.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,5 +219,8 @@
219219
<ClInclude Include="DataModels\ControlStreamBuilder.h">
220220
<Filter>Header Files\DataModels</Filter>
221221
</ClInclude>
222+
<ClInclude Include="DataModels\CommandSchemaBuilder.h">
223+
<Filter>Header Files\DataModels</Filter>
224+
</ClInclude>
222225
</ItemGroup>
223226
</Project>

CSAPI-lib/DataModels/CommandSchema.h

Lines changed: 66 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,69 +6,79 @@
66
#include "Component/DataComponent.h"
77
#include "Component/DataComponentRegistry.h"
88

9-
namespace ConnectedSystemsAPI {
10-
namespace DataModels {
11-
class CommandSchema {
12-
private:
13-
std::string commandFormat;
14-
std::unique_ptr<Component::DataComponent> parametersSchema;
15-
std::unique_ptr<Component::DataComponent> resultSchema;
16-
//todo: feasibilityResultSchema
9+
namespace ConnectedSystemsAPI::DataModels {
10+
class CommandSchema {
11+
private:
12+
std::string commandFormat;
13+
std::unique_ptr<Component::DataComponent> parametersSchema;
14+
std::unique_ptr<Component::DataComponent> resultSchema;
15+
std::unique_ptr<Component::DataComponent> feasibilityResultSchema;
1716

18-
public:
19-
CommandSchema() = default;
20-
CommandSchema(const std::string& commandFormat,
21-
std::unique_ptr<Component::DataComponent> parametersSchema,
22-
std::unique_ptr<Component::DataComponent> resultSchema)
23-
: commandFormat(commandFormat), parametersSchema(std::move(parametersSchema)), resultSchema(std::move(resultSchema)) {
24-
}
17+
public:
18+
CommandSchema() = default;
19+
CommandSchema(const std::string& commandFormat,
20+
std::unique_ptr<Component::DataComponent> parametersSchema,
21+
std::unique_ptr<Component::DataComponent> resultSchema,
22+
std::unique_ptr<Component::DataComponent> feasibilityResultSchema = nullptr)
23+
: commandFormat(commandFormat),
24+
parametersSchema(std::move(parametersSchema)),
25+
resultSchema(std::move(resultSchema)),
26+
feasibilityResultSchema(std::move(feasibilityResultSchema)) {
27+
}
2528

26-
CommandSchema(const CommandSchema&) = delete;
27-
CommandSchema& operator=(const CommandSchema&) = delete;
28-
CommandSchema(CommandSchema&&) noexcept = default;
29-
CommandSchema& operator=(CommandSchema&&) noexcept = default;
29+
CommandSchema(const CommandSchema&) = delete;
30+
CommandSchema& operator=(const CommandSchema&) = delete;
31+
CommandSchema(CommandSchema&&) noexcept = default;
32+
CommandSchema& operator=(CommandSchema&&) noexcept = default;
3033

31-
/// <summary>
32-
/// Encoding format of the command.
33-
/// </summary>
34-
const std::string& getCommandFormat() const { return commandFormat; }
35-
/// <summary>
36-
/// Record schema for the command parameters property. If omitted, parameters are not included in the datastream.
37-
/// </summary>
38-
const Component::DataComponent* getParametersSchema() const { return parametersSchema.get(); }
39-
/// <summary>
40-
/// Schema for the command result property.
41-
/// this describes the observed properties included in the result
42-
/// and how they are structured if the result is a record, a vector quantity or a coverage.
43-
/// </summary>
44-
const Component::DataComponent* getResultSchema() const { return resultSchema.get(); }
34+
/// <summary>
35+
/// Encoding format of the command.
36+
/// </summary>
37+
const std::string& getCommandFormat() const { return commandFormat; }
38+
/// <summary>
39+
/// Record schema for the command parameters property. If omitted, parameters are not included in the datastream.
40+
/// </summary>
41+
const Component::DataComponent* getParametersSchema() const { return parametersSchema.get(); }
42+
/// <summary>
43+
/// Schema for the command result property.
44+
/// this describes the observed properties included in the result
45+
/// and how they are structured if the result is a record, a vector quantity or a coverage.
46+
/// </summary>
47+
const Component::DataComponent* getResultSchema() const { return resultSchema.get(); }
48+
/// <summary>
49+
/// Schema for the feasibility result property.
50+
/// This describes the structure of the feasibility assessment result.
51+
/// </summary>
52+
const Component::DataComponent* getFeasibilityResultSchema() const { return feasibilityResultSchema.get(); }
4553

46-
friend void from_json(const nlohmann::json& j, CommandSchema& v);
47-
friend void to_json(nlohmann::ordered_json& j, const CommandSchema& v);
48-
friend std::ostream& operator<<(std::ostream& os, const CommandSchema& v);
49-
};
54+
friend void from_json(const nlohmann::json& j, CommandSchema& v);
55+
friend void to_json(nlohmann::ordered_json& j, const CommandSchema& v);
56+
friend std::ostream& operator<<(std::ostream& os, const CommandSchema& v);
57+
};
5058

51-
inline void from_json(const nlohmann::json& j, CommandSchema& v) {
52-
// Print the json for debugging
53-
std::cout << "Deserializing CommandSchema from JSON: " << j.dump(2) << std::endl;
54-
v.commandFormat = j.at("commandFormat").get<std::string>();
55-
v.parametersSchema = Component::DataComponentRegistry::createDataComponent(j.at("paramsSchema"));
56-
if (j.contains("resultSchema"))
57-
v.resultSchema = Component::DataComponentRegistry::createDataComponent(j.at("resultSchema"));
58-
}
59+
inline void from_json(const nlohmann::json& j, CommandSchema& v) {
60+
// Print the json for debugging
61+
std::cout << "Deserializing CommandSchema from JSON: " << j.dump(2) << std::endl;
62+
v.commandFormat = j.at("commandFormat").get<std::string>();
63+
v.parametersSchema = Component::DataComponentRegistry::createDataComponent(j.at("paramsSchema"));
64+
if (j.contains("resultSchema"))
65+
v.resultSchema = Component::DataComponentRegistry::createDataComponent(j.at("resultSchema"));
66+
if (j.contains("feasibilityResultSchema"))
67+
v.feasibilityResultSchema = Component::DataComponentRegistry::createDataComponent(j.at("feasibilityResultSchema"));
68+
}
5969

60-
inline void to_json(nlohmann::ordered_json& j, const CommandSchema& v) {
61-
j = nlohmann::ordered_json::object();
70+
inline void to_json(nlohmann::ordered_json& j, const CommandSchema& v) {
71+
j = nlohmann::ordered_json::object();
6272

63-
j["commandFormat"] = v.commandFormat;
64-
if (v.parametersSchema) j["paramsSchema"] = v.getParametersSchema()->toJson();
65-
if (v.resultSchema) j["resultSchema"] = v.getResultSchema()->toJson();
66-
}
73+
j["commandFormat"] = v.commandFormat;
74+
if (v.parametersSchema) j["paramsSchema"] = v.getParametersSchema()->toJson();
75+
if (v.resultSchema) j["resultSchema"] = v.getResultSchema()->toJson();
76+
if (v.feasibilityResultSchema) j["feasibilityResultSchema"] = v.getFeasibilityResultSchema()->toJson();
77+
}
6778

68-
inline std::ostream& operator<<(std::ostream& os, const CommandSchema& v) {
69-
nlohmann::ordered_json j;
70-
to_json(j, v);
71-
return os << j.dump(2);
72-
}
79+
inline std::ostream& operator<<(std::ostream& os, const CommandSchema& v) {
80+
nlohmann::ordered_json j;
81+
to_json(j, v);
82+
return os << j.dump(2);
7383
}
7484
}
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <string_view>
5+
#include <optional>
6+
#include <memory>
7+
#include <utility>
8+
9+
#include "CommandSchema.h"
10+
#include "Component/DataComponent.h"
11+
#include "Component/DataComponentRegistry.h"
12+
#include "ValidationException.h"
13+
14+
namespace ConnectedSystemsAPI::DataModels {
15+
/// <summary>
16+
/// Builder for CommandSchema instances following the fluent builder pattern.
17+
/// This class is NOT thread-safe. Each thread should use its own builder instance.
18+
/// </summary>
19+
class CommandSchemaBuilder {
20+
private:
21+
std::optional<std::string> commandFormat;
22+
std::unique_ptr<Component::DataComponent> parametersSchema;
23+
std::unique_ptr<Component::DataComponent> resultSchema;
24+
std::unique_ptr<Component::DataComponent> feasibilityResultSchema;
25+
26+
static std::unique_ptr<Component::DataComponent> cloneSchema(const Component::DataComponent* source) {
27+
if (!source) return nullptr;
28+
nlohmann::ordered_json j = source->toJson();
29+
return Component::DataComponentRegistry::createDataComponent(j);
30+
}
31+
32+
public:
33+
CommandSchemaBuilder() = default;
34+
35+
/// <summary>
36+
/// Create a builder initialized with values from an existing CommandSchema.
37+
/// </summary>
38+
explicit CommandSchemaBuilder(const CommandSchema& cs) { withCommandSchema(cs); }
39+
40+
CommandSchemaBuilder(const CommandSchemaBuilder& other)
41+
: commandFormat(other.commandFormat),
42+
parametersSchema(cloneSchema(other.parametersSchema.get())),
43+
resultSchema(cloneSchema(other.resultSchema.get())),
44+
feasibilityResultSchema(cloneSchema(other.feasibilityResultSchema.get()))
45+
{
46+
}
47+
48+
CommandSchemaBuilder& operator=(const CommandSchemaBuilder& other) {
49+
if (this != &other) {
50+
commandFormat = other.commandFormat;
51+
parametersSchema = cloneSchema(other.parametersSchema.get());
52+
resultSchema = cloneSchema(other.resultSchema.get());
53+
feasibilityResultSchema = cloneSchema(other.feasibilityResultSchema.get());
54+
}
55+
return *this;
56+
}
57+
58+
CommandSchemaBuilder(CommandSchemaBuilder&&) noexcept = default;
59+
CommandSchemaBuilder& operator=(CommandSchemaBuilder&&) noexcept = default;
60+
~CommandSchemaBuilder() = default;
61+
62+
/// <summary>
63+
/// Static factory method to create a builder from an existing CommandSchema.
64+
/// </summary>
65+
static CommandSchemaBuilder from(const CommandSchema& cs) {
66+
return CommandSchemaBuilder(cs);
67+
}
68+
69+
/// <summary>
70+
/// Initialize the builder with values from an existing CommandSchema instance.
71+
/// Note: This will perform a deep copy of all fields; use before modifying any fields or they will be overwritten.
72+
/// </summary>
73+
CommandSchemaBuilder& withCommandSchema(const CommandSchema& cs) {
74+
commandFormat = cs.getCommandFormat();
75+
parametersSchema = cloneSchema(cs.getParametersSchema());
76+
resultSchema = cloneSchema(cs.getResultSchema());
77+
feasibilityResultSchema = cloneSchema(cs.getFeasibilityResultSchema());
78+
return *this;
79+
}
80+
81+
/// <summary>
82+
/// Reset the builder to its default state, clearing all fields.
83+
/// </summary>
84+
CommandSchemaBuilder& reset() noexcept {
85+
commandFormat.reset();
86+
parametersSchema.reset();
87+
resultSchema.reset();
88+
feasibilityResultSchema.reset();
89+
return *this;
90+
}
91+
92+
/// <summary>
93+
/// Encoding format of the command.
94+
/// </summary>
95+
CommandSchemaBuilder& withCommandFormat(std::string_view v) noexcept {
96+
commandFormat = std::string(v);
97+
return *this;
98+
}
99+
100+
/// <summary>
101+
/// Set the parameters schema by transferring ownership of a unique_ptr.
102+
/// Schema for the command parameters property. If omitted, parameters are not included in the control stream.
103+
/// </summary>
104+
CommandSchemaBuilder& withParametersSchema(std::unique_ptr<Component::DataComponent> v) noexcept {
105+
parametersSchema = std::move(v);
106+
return *this;
107+
}
108+
109+
/// <summary>
110+
/// Set the parameters schema by copying from an existing DataComponent instance.
111+
/// Schema for the command parameters property. If omitted, parameters are not included in the control stream.
112+
/// Note: This performs a deep copy via JSON serialization.
113+
/// </summary>
114+
CommandSchemaBuilder& withParametersSchema(const Component::DataComponent& v) {
115+
parametersSchema = cloneSchema(&v);
116+
return *this;
117+
}
118+
119+
/// <summary>
120+
/// Set the result schema by transferring ownership of a unique_ptr.
121+
/// Schema for the command result property describing the observed properties and their structure.
122+
/// </summary>
123+
CommandSchemaBuilder& withResultSchema(std::unique_ptr<Component::DataComponent> v) noexcept {
124+
resultSchema = std::move(v);
125+
return *this;
126+
}
127+
128+
/// <summary>
129+
/// Set the result schema by copying from an existing DataComponent instance.
130+
/// Schema for the command result property describing the observed properties and their structure.
131+
/// Note: This performs a deep copy via JSON serialization.
132+
/// </summary>
133+
CommandSchemaBuilder& withResultSchema(const Component::DataComponent& v) {
134+
resultSchema = cloneSchema(&v);
135+
return *this;
136+
}
137+
138+
/// <summary>
139+
/// Set the feasibility result schema by transferring ownership of a unique_ptr.
140+
/// Schema for the feasibility result property describing the structure of the feasibility assessment result.
141+
/// </summary>
142+
CommandSchemaBuilder& withFeasibilityResultSchema(std::unique_ptr<Component::DataComponent> v) noexcept {
143+
feasibilityResultSchema = std::move(v);
144+
return *this;
145+
}
146+
147+
/// <summary>
148+
/// Set the feasibility result schema by copying from an existing DataComponent instance.
149+
/// Schema for the feasibility result property describing the structure of the feasibility assessment result.
150+
/// Note: This performs a deep copy via JSON serialization.
151+
/// </summary>
152+
CommandSchemaBuilder& withFeasibilityResultSchema(const Component::DataComponent& v) {
153+
feasibilityResultSchema = cloneSchema(&v);
154+
return *this;
155+
}
156+
157+
/// <summary>
158+
/// Clear the parameters schema.
159+
/// </summary>
160+
CommandSchemaBuilder& clearParametersSchema() noexcept {
161+
parametersSchema.reset();
162+
return *this;
163+
}
164+
165+
/// <summary>
166+
/// Clear the result schema.
167+
/// </summary>
168+
CommandSchemaBuilder& clearResultSchema() noexcept {
169+
resultSchema.reset();
170+
return *this;
171+
}
172+
173+
/// <summary>
174+
/// Clear the feasibility result schema.
175+
/// </summary>
176+
CommandSchemaBuilder& clearFeasibilityResultSchema() noexcept {
177+
feasibilityResultSchema.reset();
178+
return *this;
179+
}
180+
181+
const std::optional<std::string>& getCommandFormat() const noexcept { return commandFormat; }
182+
const Component::DataComponent* getParametersSchema() const noexcept { return parametersSchema.get(); }
183+
const Component::DataComponent* getResultSchema() const noexcept { return resultSchema.get(); }
184+
const Component::DataComponent* getFeasibilityResultSchema() const noexcept { return feasibilityResultSchema.get(); }
185+
186+
/// <summary>
187+
/// Check if the current builder state is valid. Optionally populate a vector with validation errors.
188+
/// </summary>
189+
/// <param name="outErrors">Optional pointer to a vector to populate with validation errors. If nullptr, only validation result is returned.</param>
190+
/// <returns>True if validation passes, false otherwise.</returns>
191+
bool isValid(std::vector<ConnectedSystemsAPI::ValidationException::ValidationError>* outErrors = nullptr) const {
192+
std::vector<ConnectedSystemsAPI::ValidationException::ValidationError> errors;
193+
194+
if (!commandFormat || commandFormat->empty())
195+
errors.emplace_back("commandFormat", "Field is required and cannot be empty");
196+
197+
if (!parametersSchema)
198+
errors.emplace_back("parametersSchema", "Field is required");
199+
200+
bool valid = errors.empty();
201+
202+
if (outErrors)
203+
*outErrors = std::move(errors);
204+
205+
return valid;
206+
}
207+
208+
/// <summary>
209+
/// Build a CommandSchema instance from the current builder state.
210+
/// Note: This method clones the schemas, so the builder can be reused for multiple builds.
211+
/// </summary>
212+
/// <returns>A new CommandSchema instance.</returns>
213+
/// <exception cref="ValidationException">Thrown if validation fails.</exception>
214+
CommandSchema build() const {
215+
if (std::vector<ConnectedSystemsAPI::ValidationException::ValidationError> errors; !isValid(&errors)) {
216+
throw ConnectedSystemsAPI::ValidationException("CommandSchemaBuilder", errors);
217+
}
218+
219+
return CommandSchema(
220+
commandFormat.value(),
221+
cloneSchema(parametersSchema.get()),
222+
cloneSchema(resultSchema.get()),
223+
cloneSchema(feasibilityResultSchema.get())
224+
);
225+
}
226+
};
227+
}

0 commit comments

Comments
 (0)