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
17 changes: 9 additions & 8 deletions api/apipatrols.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,13 @@ type DateRangeFilter struct {
// Client methods
// ----------------------------------------------

func (c *Client) Patrols(days int) (*PatrolsResponse, error) {
var endpoint string
func (c *Client) Patrols(days int, status string) (*PatrolsResponse, error) {
params := url.Values{}
params.Add("exclude_empty_patrols", "true")

if status != "" {
params.Add("status", status)
}

if days > 0 {
now := time.Now().UTC()
Expand All @@ -82,15 +87,11 @@ func (c *Client) Patrols(days int) (*PatrolsResponse, error) {
return nil, fmt.Errorf("failed to marshal date filter: %w", err)
}

params := url.Values{}
params.Add("filter", string(filterJSON))
params.Add("exclude_empty_patrols", "true")

endpoint = fmt.Sprintf("%s?%s", API_PATROLS, params.Encode())
} else {
endpoint = fmt.Sprintf("%s?exclude_empty_patrols=true", API_PATROLS)
}

endpoint := fmt.Sprintf("%s?%s", API_PATROLS, params.Encode())

req, err := c.newRequest("GET", endpoint, false)
if err != nil {
return nil, fmt.Errorf("failed to create patrols request: %w", err)
Expand Down
115 changes: 93 additions & 22 deletions api/patrols_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ import (

func TestPatrols(t *testing.T) {
tests := []struct {
name string
days int
mockResponse string
expectedError bool
validateResult func(*testing.T, *PatrolsResponse)
name string
days int
status string
mockResponse string
expectedError bool
validateResult func(*testing.T, *PatrolsResponse)
validateRequest func(*testing.T, *http.Request)
}{
{
name: "successful response without date filter",
days: 0,
name: "successful response without filters",
days: 0,
status: "",
mockResponse: `{
"data": {
"count": 1,
Expand Down Expand Up @@ -51,6 +54,17 @@ func TestPatrols(t *testing.T) {
}
}`,
expectedError: false,
validateRequest: func(t *testing.T, r *http.Request) {
if !strings.Contains(r.URL.String(), "exclude_empty_patrols=true") {
t.Error("Expected exclude_empty_patrols parameter")
}
if strings.Contains(r.URL.String(), "status=") {
t.Error("Unexpected status parameter")
}
if strings.Contains(r.URL.String(), "filter=") {
t.Error("Unexpected filter parameter")
}
},
validateResult: func(t *testing.T, response *PatrolsResponse) {
if response == nil {
t.Fatal("Expected non-nil response")
Expand All @@ -77,8 +91,9 @@ func TestPatrols(t *testing.T) {
},
},
{
name: "successful response with date filter",
days: 7,
name: "successful response with date filter",
days: 7,
status: "",
mockResponse: `{
"data": {
"count": 1,
Expand All @@ -96,6 +111,17 @@ func TestPatrols(t *testing.T) {
}
}`,
expectedError: false,
validateRequest: func(t *testing.T, r *http.Request) {
if !strings.Contains(r.URL.String(), "exclude_empty_patrols=true") {
t.Error("Expected exclude_empty_patrols parameter")
}
if !strings.Contains(r.URL.String(), "filter=") {
t.Error("Expected filter parameter")
}
if !strings.Contains(r.URL.String(), "patrols_overlap_daterange") {
t.Error("Expected patrols_overlap_daterange in filter")
}
},
validateResult: func(t *testing.T, response *PatrolsResponse) {
if response == nil {
t.Fatal("Expected non-nil response")
Expand All @@ -106,10 +132,60 @@ func TestPatrols(t *testing.T) {
},
},
{
name: "error response",
days: 0,
mockResponse: `{"status": {"code": 500, "message": "Internal Server Error"}}`,
expectedError: true,
name: "successful response with status filter",
days: 7,
status: "active",
mockResponse: `{
"data": {
"count": 1,
"results": [
{
"id": "test789",
"serial_number": 1003,
"state": "active"
}
]
},
"status": {
"code": 200,
"message": "OK"
}
}`,
expectedError: false,
validateRequest: func(t *testing.T, r *http.Request) {
if !strings.Contains(r.URL.String(), "exclude_empty_patrols=true") {
t.Error("Expected exclude_empty_patrols parameter")
}
if !strings.Contains(r.URL.String(), "status=active") {
t.Error("Expected status parameter")
}
if !strings.Contains(r.URL.String(), "filter=") {
t.Error("Expected filter parameter")
}
},
validateResult: func(t *testing.T, response *PatrolsResponse) {
if response == nil {
t.Fatal("Expected non-nil response")
}
if len(response.Data.Results) != 1 {
t.Errorf("Expected 1 result, got %d", len(response.Data.Results))
}
if response.Data.Results[0].State != "active" {
t.Errorf("Expected state 'active', got '%s'", response.Data.Results[0].State)
}
},
},
{
name: "error response",
days: 0,
status: "",
mockResponse: `{"status": {"code": 500, "message": "Internal Server Error"}}`,
expectedError: true,
validateRequest: func(t *testing.T, r *http.Request) {
if !strings.Contains(r.URL.String(), "exclude_empty_patrols=true") {
t.Error("Expected exclude_empty_patrols parameter")
}
},
validateResult: nil,
},
}
Expand All @@ -122,13 +198,8 @@ func TestPatrols(t *testing.T) {
t.Errorf("Expected GET request, got %s", r.Method)
}

if tt.days > 0 {
if !strings.Contains(r.URL.String(), "filter=") {
t.Error("Expected filter parameter in URL for date-filtered request")
}
if !strings.Contains(r.URL.String(), "patrols_overlap_daterange") {
t.Error("Expected patrols_overlap_daterange in filter")
}
if tt.validateRequest != nil {
tt.validateRequest(t, r)
}

// Return mock response
Expand All @@ -143,7 +214,7 @@ func TestPatrols(t *testing.T) {
defer server.Close()

client := ERClient("test", "test-token", server.URL)
response, err := client.Patrols(tt.days)
response, err := client.Patrols(tt.days, tt.status)

if tt.expectedError {
if err == nil {
Expand Down Expand Up @@ -203,7 +274,7 @@ func TestDateRangeFilter(t *testing.T) {
defer server.Close()

client := ERClient("test", "test-token", server.URL)
_, err := client.Patrols(7)
_, err := client.Patrols(7, "")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
Expand Down
24 changes: 21 additions & 3 deletions cmd/patrols.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,16 @@ import (
"github.com/spf13/cobra"
)

var days int
var (
days int
status string
)

var validStatuses = map[string]bool{
"active": true,
"done": true,
"cancelled": true,
}

// ----------------------------------------------
// patrols command
Expand All @@ -22,6 +31,14 @@ var patrolsCmd = &cobra.Command{
Use: "patrols",
Short: "Get patrols data",
Long: `Return patrol data including serial number, state, ID, location, and time information`,
PreRunE: func(cmd *cobra.Command, args []string) error {
if status != "" {
if !validStatuses[status] {
return fmt.Errorf("invalid status value: %s\nValid status values are: active, done, cancelled", status)
}
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
patrols()
},
Expand All @@ -37,7 +54,7 @@ func patrols() {
}

func handlePatrols(client *api.Client) {
patrolsResponse, err := client.Patrols(days)
patrolsResponse, err := client.Patrols(days, status)
if err != nil {
log.Fatalf("Error getting patrols: %v", err)
}
Expand Down Expand Up @@ -138,5 +155,6 @@ func configurePatrolsTable() *tablewriter.Table {

func init() {
rootCmd.AddCommand(patrolsCmd)
patrolsCmd.Flags().IntVarP(&days, "days", "d", 0, "Number of days to fetch patrols for (defaults to all)")
patrolsCmd.Flags().IntVarP(&days, "days", "d", 7, "Number of days to fetch patrols for")
patrolsCmd.Flags().StringVarP(&status, "status", "s", "", "Patrol status (active, done, or cancelled)")
}
Loading