Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions src/mtconnect/agent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,8 @@ namespace mtconnect {
else if (old || !added)
{
di = device->getAssetChanged();
/// If we have changed the asset that is currently recorded as added. Make added unavailable.
/// If we have changed the asset that is currently recorded as added. Make added
/// unavailable.
if (added)
{
auto last = getLatest(added);
Expand Down Expand Up @@ -782,10 +783,11 @@ namespace mtconnect {
auto last = getLatest(changed);
if (last && asset->getAssetId() == last->getValue<string>())
{
m_loopback->receive(changed, {{"assetType", asset->getName()}, {"VALUE", g_unavailable}});
m_loopback->receive(changed,
{{"assetType", asset->getName()}, {"VALUE", g_unavailable}});
}
}

auto added = dev->getAssetAdded();
if (added)
{
Expand All @@ -798,7 +800,7 @@ namespace mtconnect {
}
}
}

// ---------------------------------------
// Agent Device
// ---------------------------------------
Expand Down Expand Up @@ -934,8 +936,9 @@ namespace mtconnect {
// Create asset removed data item and add it to the device.
entity::ErrorList errors;
auto di = DataItem::make({{"type", "ASSET_ADDED"s},
{"id", device->getId() + "_asset_add"}, {"discrete", true},
{"category", "EVENT"s}},
{"id", device->getId() + "_asset_add"},
{"discrete", true},
{"category", "EVENT"s}},
errors);
device->addDataItem(di, errors);
}
Expand Down
3 changes: 2 additions & 1 deletion src/mtconnect/agent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,8 @@ namespace mtconnect {
asset::AssetList &list);
/// @brief Send asset changed and added observation when an asset is removed.
///
/// Also sets asset changed and added to `UNAVAILABLE` if the asset removed asset was the last changed.
/// Also sets asset changed and added to `UNAVAILABLE` if the asset removed asset was the last
/// changed.
///
/// @param device The device related to the asset
/// @param asset The asset
Expand Down
113 changes: 80 additions & 33 deletions src/mtconnect/sink/rest_sink/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,13 @@
namespace mtconnect::sink::rest_sink {
using status = boost::beast::http::status;

/// @brief An Error entity for error reporting
/// Builds an Error entity with errorCode, URI, Request, and ErrorMessage.
/// Subclasses can add additional information
class AGENT_LIB_API Error : public mtconnect::entity::Entity
{
public:
/// @brief Error codes for MTConnect REST API
enum class ErrorCode
{
ASSET_NOT_FOUND,
Expand Down Expand Up @@ -69,9 +73,17 @@ namespace mtconnect::sink::rest_sink {
return error;
}

/// @brief Get the name for an error code
/// @param code the error code
static const std::string nameForCode(ErrorCode code);
/// @brief Get the controlled vocabulary enumeration string for an error code
/// @param code the error code
static const std::string enumForCode(ErrorCode code);

/// @brief static error factory method
/// @param code the error code
/// @param errorMessage optional error message
/// @param request optional request string
static entity::EntityPtr make(const ErrorCode code,
std::optional<std::string> errorMessage = std::nullopt,
std::optional<std::string> request = std::nullopt)
Expand All @@ -97,6 +109,7 @@ namespace mtconnect::sink::rest_sink {

using ErrorPtr = std::shared_ptr<Error>;

/// @brief A QueryParameter entity for error reporting
class AGENT_LIB_API QueryParameter : public entity::Entity
{
public:
Expand All @@ -123,12 +136,16 @@ namespace mtconnect::sink::rest_sink {
return qp;
}

/// @brief static factory method
/// @param properties the properties for the QueryParameter
static entity::EntityPtr make(const entity::Properties &properties)
{
return std::make_shared<QueryParameter>(properties);
}
};

/// @brief An InvalidParameterValue error
/// Builds a QueryParameter with name, value, type, and format
class AGENT_LIB_API InvalidParameterValue : public Error
{
public:
Expand Down Expand Up @@ -158,6 +175,13 @@ namespace mtconnect::sink::rest_sink {
return factory;
}

/// @brief static factory method
/// @param name the name of the parameter
/// @param value the value of the parameter
/// @param type the type of the parameter
/// @param format the format of the parameter
/// @param errorMessage optional error message
/// @param request optional request string
static entity::EntityPtr make(const std::string &name, const std::string &value,
const std::string &type, const std::string &format,
std::optional<std::string> errorMessage = std::nullopt,
Expand All @@ -180,6 +204,8 @@ namespace mtconnect::sink::rest_sink {
}
};

/// @brief An OutOfRange error
/// Builds a QueryParameter with name, value, min, and max
class AGENT_LIB_API OutOfRange : public Error
{
public:
Expand All @@ -205,6 +231,13 @@ namespace mtconnect::sink::rest_sink {
return factory;
}

/// @brief static factory method
/// @param name the name of the parameter
/// @param value the value of the parameter
/// @param min the minimum value of the parameter
/// @param max the maximum value of the parameter
/// @param errorMessage optional error message
/// @param request optional request string
static entity::EntityPtr make(const std::string &name, int64_t value, int64_t min, int64_t max,
std::optional<std::string> errorMessage = std::nullopt,
std::optional<std::string> request = std::nullopt)
Expand All @@ -225,6 +258,7 @@ namespace mtconnect::sink::rest_sink {
}
};

/// @brief An AssetNotFound error
class AGENT_LIB_API AssetNotFound : public Error
{
public:
Expand All @@ -249,6 +283,10 @@ namespace mtconnect::sink::rest_sink {
return factory;
}

/// @brief static factory method
/// @param assetId the asset ID that was not found
/// @param errorMessage optional error message
/// @param request optional request string
static entity::EntityPtr make(const std::string &assetId,
std::optional<std::string> errorMessage = std::nullopt,
std::optional<std::string> request = std::nullopt)
Expand All @@ -266,6 +304,7 @@ namespace mtconnect::sink::rest_sink {
}
};

/// @brief An exception that gets thrown during REST processing with an error and a status
class AGENT_LIB_API RestError
{
public:
Expand All @@ -276,12 +315,13 @@ namespace mtconnect::sink::rest_sink {
/// @param format optional format for the error
RestError(entity::EntityPtr error, std::string accepts = "application/xml",
status st = status::bad_request, std::optional<std::string> format = std::nullopt,
std::optional<std::string> request = std::nullopt)
: m_errors({error}), m_accepts(accepts), m_status(st), m_format(format)
{
if (request)
setRequest(*request);
}
std::optional<std::string> requestId = std::nullopt)
: m_errors({error}),
m_accepts(accepts),
m_status(st),
m_format(format),
m_requestId(requestId)
{}

/// @brief An exception that gets thrown during REST processing with an error and a status
/// @param errors a list of errors
Expand All @@ -290,12 +330,9 @@ namespace mtconnect::sink::rest_sink {
/// @param format optional format for the error
RestError(entity::EntityList &errors, std::string accepts = "application/xml",
status st = status::bad_request, std::optional<std::string> format = std::nullopt,
std::optional<std::string> request = std::nullopt)
: m_errors(errors), m_accepts(accepts), m_status(st), m_format(format)
{
if (request)
setRequest(*request);
}
std::optional<std::string> requestId = std::nullopt)
: m_errors(errors), m_accepts(accepts), m_status(st), m_format(format), m_requestId(requestId)
{}

/// @brief An exception that gets thrown during REST processing with an error and a status
/// @param error the error entity
Expand All @@ -304,12 +341,14 @@ namespace mtconnect::sink::rest_sink {
/// @param format optional format for the error
RestError(entity::EntityPtr error, const printer::Printer *printer = nullptr,
status st = status::bad_request, std::optional<std::string> format = std::nullopt,
std::optional<std::string> request = std::nullopt)
: m_errors({error}), m_status(st), m_format(format), m_printer(printer)
{
if (request)
setRequest(*request);
}
std::optional<std::string> requestId = std::nullopt)
: m_errors({error}),
m_status(st),
m_format(format),
m_requestId(requestId),
m_printer(printer)

{}

/// @brief An exception that gets thrown during REST processing with an error and a status
/// @param errors a list of errors
Expand All @@ -318,12 +357,9 @@ namespace mtconnect::sink::rest_sink {
/// @param format optional format for the error
RestError(entity::EntityList &errors, const printer::Printer *printer = nullptr,
status st = status::bad_request, std::optional<std::string> format = std::nullopt,
std::optional<std::string> request = std::nullopt)
: m_errors(errors), m_status(st), m_format(format), m_printer(printer)
{
if (request)
setRequest(*request);
}
std::optional<std::string> requestId = std::nullopt)
: m_errors(errors), m_status(st), m_format(format), m_requestId(requestId), m_printer(printer)
{}

~RestError() = default;
RestError(const RestError &o) = default;
Expand All @@ -336,24 +372,33 @@ namespace mtconnect::sink::rest_sink {
e->setProperty("URI", uri);
}

/// @brief set the Request for all errors
/// @brief set the Request ID for the websocket responses
/// @param requestId the Request ID
void setRequestId(const std::string &requestId) { m_requestId = requestId; }
const auto &getRequestId() const { return m_requestId; }

/// @brief The response document type for the request (e.g. MTConnectDevices)
/// @param request the Request
void setRequest(const std::string &request)
{
m_request = request;
for (auto &e : m_errors)
e->setProperty("Request", request);
}

const auto &getErrors() const { return m_errors; }

void setStatus(status st) { m_status = st; }
const auto &getStatus() const { return m_status; }

void setFormat(const std::string &format) { m_format = format; }
const auto &getFormat() const { return m_format; }

const auto &getAccepts() const { return m_accepts; }
const auto &getRequest() const { return m_request; }

auto getPrinter() const { return m_printer; }

/// @brief Get a string representation of the error(s) concating the error messages.
/// @return a string representation of the error(s)
std::string what() const
{
std::stringstream ss;
Expand All @@ -371,12 +416,14 @@ namespace mtconnect::sink::rest_sink {
}

protected:
entity::EntityList m_errors;
std::string m_accepts {"application/xml"};
status m_status;
std::optional<std::string> m_format;
std::optional<std::string> m_request;
const printer::Printer *m_printer {nullptr};
entity::EntityList m_errors; ///< The list of errors to be reported
std::string m_accepts {"application/xml"}; ///< The accepted mime types for the response
status m_status; ///< The HTTP status code
std::optional<std::string> m_format; ///< The format for the error response overriding accepts
std::optional<std::string> m_requestId; ///< The request id for the response
const printer::Printer *m_printer {
nullptr}; ///< The printer to use for the response. If the printer is not specified it will
///< be inferred from the accepts or format.
};

} // namespace mtconnect::sink::rest_sink
13 changes: 13 additions & 0 deletions src/mtconnect/sink/rest_sink/parameter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,19 @@ namespace mtconnect::sink::rest_sink {
return "unknown";
}

/// @brief Helper to convert a ParameterValue to a string
static std::string toString(const ParameterValue &v)
{
using namespace std::string_literals;
return std::visit(overloaded {[](const std::monostate &) { return "none"s; },
[](const std::string &s) { return s; },
[](int32_t i) { return std::to_string(i); },
[](uint64_t i) { return std::to_string(i); },
[](double d) { return std::to_string(d); },
[](bool b) { return b ? "true"s : "false"s; }},
v);
}

std::string m_name;
ParameterType m_type {STRING};
/// @brief Default value if one is available
Expand Down
34 changes: 23 additions & 11 deletions src/mtconnect/sink/rest_sink/request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,17 @@ namespace mtconnect::sink::rest_sink {
Request() = default;
Request(const Request &request) = default;

boost::beast::http::verb m_verb; ///< GET, PUT, POST, or DELETE
std::string m_body; ///< The body of the request
std::string m_accepts; ///< The accepts header
std::string m_acceptsEncoding; ///< Encodings that can be returned
std::string m_contentType; ///< The content type for the body
std::string m_path; ///< The URI for the request
std::string m_foreignIp; ///< The requestors IP Address
uint16_t m_foreignPort; ///< The requestors Port
QueryMap m_query; ///< The parsed query parameters
ParameterMap m_parameters; ///< The parsed path parameters
boost::beast::http::verb m_verb; ///< GET, PUT, POST, or DELETE
std::string m_body; ///< The body of the request
std::string m_accepts; ///< The accepts header
std::string m_acceptsEncoding; ///< Encodings that can be returned
std::string m_contentType; ///< The content type for the body
std::string m_path; ///< The URI for the request
std::string m_foreignIp; ///< The requestors IP Address
uint16_t m_foreignPort; ///< The requestors Port
QueryMap m_query; ///< The parsed query parameters
ParameterMap m_parameters; ///< The parsed path parameters
std::optional<std::string> m_request; ///< The request type for error reporting

/// @name Websocket related properties
///@{
Expand Down Expand Up @@ -76,7 +77,18 @@ namespace mtconnect::sink::rest_sink {
std::string getUri() const
{
std::string s;
if (!m_query.empty())
if (m_command)
{
std::stringstream uri;
uri << m_path << '/' << *m_command << '?';
for (auto &p : m_parameters)
{
uri << p.first << '=' << Parameter::toString(p.second) << '&';
}
s = uri.str();
s.erase(s.length() - 1);
}
else if (!m_query.empty())
{
std::stringstream uri;
uri << m_path << '?';
Expand Down
Loading
Loading