///|
/// HTTP operation used by an nsqd/nsqlookupd administration client.
pub enum AdminOperation {
CreateTopic(String)
DeleteTopic(String)
CreateChannel(String, String)
DeleteChannel(String, String)
Tombstone(String)
Pause(String)
Unpause(String)
} derive(Debug, Eq)
///|
pub extend AdminOperation with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend AdminOperation with Eq::{equal, not_equal}
///|
/// Constructors kept explicit so callers can build operations without matching internals.
pub fn create_topic_operation(topic : String) -> AdminOperation {
CreateTopic(topic)
}
///|
pub fn delete_topic_operation(topic : String) -> AdminOperation {
DeleteTopic(topic)
}
///|
pub fn create_channel_operation(
topic : String,
channel : String,
) -> AdminOperation {
CreateChannel(topic, channel)
}
///|
pub fn delete_channel_operation(
topic : String,
channel : String,
) -> AdminOperation {
DeleteChannel(topic, channel)
}
///|
pub fn tombstone_operation(topic : String) -> AdminOperation {
Tombstone(topic)
}
///|
pub fn pause_operation(topic : String) -> AdminOperation {
Pause(topic)
}
///|
pub fn unpause_operation(topic : String) -> AdminOperation {
Unpause(topic)
}
///|
/// Validates an admin operation before it becomes an HTTP path.
pub fn AdminOperation::validate(
self : AdminOperation,
) -> Unit raise ProtocolError {
match self {
CreateTopic(topic)
| DeleteTopic(topic)
| Tombstone(topic)
| Pause(topic)
| Unpause(topic) => validate_name(topic)
CreateChannel(topic, channel) | DeleteChannel(topic, channel) => {
validate_name(topic)
validate_name(channel)
}
}
}
///|
/// Produces the relative endpoint path used by the NSQ HTTP API.
pub fn AdminOperation::path(
self : AdminOperation,
) -> String raise ProtocolError {
self.validate()
match self {
CreateTopic(topic) => "/topic/create?topic=\{topic}"
DeleteTopic(topic) => "/topic/delete?topic=\{topic}"
CreateChannel(topic, channel) =>
"/channel/create?topic=\{topic}&channel=\{channel}"
DeleteChannel(topic, channel) =>
"/channel/delete?topic=\{topic}&channel=\{channel}"
Tombstone(topic) => "/topic/tombstone?topic=\{topic}"
Pause(topic) => "/topic/pause?topic=\{topic}"
Unpause(topic) => "/topic/unpause?topic=\{topic}"
}
}
///|
/// Returns the HTTP method used for an administrative operation.
/// NSQ v1 administration endpoints require POST.
pub fn AdminOperation::http_method(self : AdminOperation) -> String {
match self {
CreateTopic(_)
| DeleteTopic(_)
| CreateChannel(_, _)
| DeleteChannel(_, _)
| Tombstone(_)
| Pause(_)
| Unpause(_) => "POST"
}
}
///|
/// Returns whether the operation changes broker state.
pub fn AdminOperation::is_mutating(self : AdminOperation) -> Bool {
self.http_method() == "POST"
}
///|
/// Returns a concise operation label for logs and metrics.
pub fn AdminOperation::label(self : AdminOperation) -> String {
match self {
CreateTopic(_) => "topic.create"
DeleteTopic(_) => "topic.delete"
CreateChannel(_, _) => "channel.create"
DeleteChannel(_, _) => "channel.delete"
Tombstone(_) => "topic.tombstone"
Pause(_) => "topic.pause"
Unpause(_) => "topic.unpause"
}
}
///|
/// Simple URL builder for an nsqd administration endpoint.
pub struct AdminClient {
host : String
port : Int
base_path : String
} derive(Debug)
///|
pub extend AdminClient with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub fn AdminClient::new(
host : String,
port? : Int = 4151,
base_path? : String = "",
) -> AdminClient raise ProtocolError {
if host.length() == 0 || host.contains("/") || host.contains(" ") {
raise InvalidOption("admin host must be a hostname or IP address")
}
if port < 1 || port > 65_535 {
raise InvalidOption("admin port must be a valid TCP port")
}
if base_path.contains("?") || base_path.contains(" ") {
raise InvalidOption("admin base path contains invalid characters")
}
{ host, port, base_path, }
}
///|
pub fn AdminClient::host(self : AdminClient) -> String {
self.host
}
///|
pub fn AdminClient::port(self : AdminClient) -> Int {
self.port
}
///|
/// Builds an absolute HTTP URL without performing network I/O.
pub fn AdminClient::url(
self : AdminClient,
operation : AdminOperation,
) -> String raise ProtocolError {
"http://\{self.host}:\{self.port}\{self.base_path}\{operation.path()}"
}
///|
/// Builds a health endpoint URL.
pub fn AdminClient::health_url(self : AdminClient) -> String {
"http://\{self.host}:\{self.port}\{self.base_path}/info"
}
///|
/// Builds a stats endpoint URL with the requested format.
pub fn AdminClient::stats_url(
self : AdminClient,
format? : String = "json",
) -> String raise ProtocolError {
if !(format == "json" || format == "text") {
raise InvalidOption("stats format must be json or text")
}
"http://\{self.host}:\{self.port}\{self.base_path}/stats?format=\{format}"
}