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
25 changes: 11 additions & 14 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
---
on: [pull_request] # yamllint disable-line rule:truthy
on: [pull_request] # yamllint disable-line rule:truthy
name: main
jobs:

commitlint:
runs-on: ubuntu-latest
steps:

- uses: actions/checkout@v3
with:
fetch-depth: 0
Expand All @@ -17,26 +15,26 @@ jobs:
- run: npm install --save-dev @commitlint/{config-conventional,cli}
- run: npx commitlint --from=${{ github.event.pull_request.base.sha }}

golangci-lint:
golangci:
strategy:
matrix:
go-version: [1.20.x]
os: [ubuntu-latest, macos-latest]
go: [1.23.x]
os: [ubuntu-latest]
name: lint
runs-on: ${{ matrix.os }}
steps:

- uses: actions/checkout@v3

- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
uses: golangci/golangci-lint-action@v6
with:
version: v1.51.1
version: v1.61

yamllint:
runs-on: ubuntu-latest
steps:

- uses: actions/checkout@v3
with:
fetch-depth: 0
Expand All @@ -47,11 +45,10 @@ jobs:
test:
strategy:
matrix:
go-version: [1.20.x]
go-version: [1.23.x]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:

- name: Install Go
uses: actions/setup-go@v2
with:
Expand Down
116 changes: 116 additions & 0 deletions cameras/cameras.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package cameras

import (
"encoding/json"
"fmt"
"net/http"
"strconv"

"alpineworks.io/wsdot"
)

const (
getCamerasAsJsonURL = "http://www.wsdot.wa.gov/Traffic/api/HighwayCameras/HighwayCamerasREST.svc/GetCamerasAsJson"
getCameraAsJsonURL = "http://www.wsdot.wa.gov/Traffic/api/HighwayCameras/HighwayCamerasREST.svc/GetCameraAsJson"

ParamCameraID = "CameraID"
)

type CamerasClient struct {
wsdot *wsdot.WSDOTClient
}

func NewCamerasClient(wsdotClient *wsdot.WSDOTClient) (*CamerasClient, error) {
if wsdotClient == nil {
return nil, wsdot.ErrNoClient
}

return &CamerasClient{
wsdot: wsdotClient,
}, nil
}

type CameraLocation struct {
Description any `json:"Description"`
Direction string `json:"Direction"`
Latitude float64 `json:"Latitude"`
Longitude float64 `json:"Longitude"`
MilePost int `json:"MilePost"`
RoadName string `json:"RoadName"`
}

type Camera struct {
CameraID int `json:"CameraID"`
CameraLocation CameraLocation `json:"CameraLocation"`
CameraOwner string `json:"CameraOwner"`
Description any `json:"Description"`
DisplayLatitude float64 `json:"DisplayLatitude"`
DisplayLongitude float64 `json:"DisplayLongitude"`
ImageHeight int `json:"ImageHeight"`
ImageURL string `json:"ImageURL"`
ImageWidth int `json:"ImageWidth"`
IsActive bool `json:"IsActive"`
OwnerURL string `json:"OwnerURL"`
Region string `json:"Region"`
SortOrder int `json:"SortOrder"`
Title string `json:"Title"`
}

func (c *CamerasClient) GetCameras() ([]Camera, error) {
req, err := http.NewRequest(http.MethodGet, getCamerasAsJsonURL, nil)
if err != nil {
return nil, fmt.Errorf("error creating request: %v", err)
}

q := req.URL.Query()
q.Add(wsdot.ParamAccessCode, c.wsdot.ApiKey)
req.URL.RawQuery = q.Encode()
req.Header.Set("Content-Type", "application/json")

resp, err := c.wsdot.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making request: %v", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}

var cameras []Camera
if err := json.NewDecoder(resp.Body).Decode(&cameras); err != nil {
return nil, fmt.Errorf("error decoding response: %v", err)
}

return cameras, nil
}

func (c *CamerasClient) GetCamera(cameraID int) (*Camera, error) {
req, err := http.NewRequest(http.MethodGet, getCameraAsJsonURL, nil)
if err != nil {
return nil, fmt.Errorf("error creating request: %v", err)
}

q := req.URL.Query()
q.Add(wsdot.ParamAccessCode, c.wsdot.ApiKey)
q.Add(ParamCameraID, strconv.Itoa(cameraID))
req.URL.RawQuery = q.Encode()
req.Header.Set("Content-Type", "application/json")

resp, err := c.wsdot.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making request: %v", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}

var camera *Camera
if err := json.NewDecoder(resp.Body).Decode(&camera); err != nil {
return nil, fmt.Errorf("error decoding response: %v", err)
}

return camera, nil
}
53 changes: 53 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package wsdot

import (
"errors"
"net/http"
)

type WSDOTClient struct {
Client *http.Client
ApiKey string
}

type WSDOTClientOption func(*WSDOTClient)

type WSDOTClientError error

var (
ErrInvalidAPIKey WSDOTClientError = errors.New("invalid api key")
ErrNoClient WSDOTClientError = errors.New("no client")
)

const (
ParamAccessCode = "AccessCode"
)

func NewWSDOTClient(options ...WSDOTClientOption) (*WSDOTClient, error) {
client := &http.Client{}
wsdotClient := &WSDOTClient{
Client: client,
}

for _, option := range options {
option(wsdotClient)
}

if wsdotClient.ApiKey == "" {
return nil, ErrInvalidAPIKey
}

return wsdotClient, nil
}

func WithHTTPClient(client *http.Client) WSDOTClientOption {
return func(w *WSDOTClient) {
w.Client = client
}
}

func WithAPIKey(apiKey string) WSDOTClientOption {
return func(w *WSDOTClient) {
w.ApiKey = apiKey
}
}
54 changes: 54 additions & 0 deletions example/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package main

import (
"fmt"
"os"

"alpineworks.io/wsdot"
"alpineworks.io/wsdot/cameras"
)

func main() {
apiKey := os.Getenv("API_KEY")
if apiKey == "" {
panic("API_KEY environment variable is required")
}

// Create a new WSDOT client
wsdotClient, err := wsdot.NewWSDOTClient(
wsdot.WithAPIKey(apiKey),
)

if err != nil {
panic(err)
}

// Create a new Cameras client
camerasClient, err := cameras.NewCamerasClient(wsdotClient)
if err != nil {
panic(err)
}

// Get the cameras
cameras, err := camerasClient.GetCameras()
if err != nil {
panic(err)
}

if len(cameras) > 0 {
fmt.Println(cameras[0].CameraID)
fmt.Println(cameras[0].Title)
fmt.Println(cameras[0].ImageURL)
}

// Get a specific camera
camera, err := camerasClient.GetCamera(cameras[0].CameraID)
if err != nil {
panic(err)
}

fmt.Println(camera.CameraID)
fmt.Println(camera.Title)
fmt.Println(camera.ImageURL)

}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/michaelpeterswa/go-lib
module alpineworks.io/wsdot

go 1.18
go 1.22
1 change: 0 additions & 1 deletion temp/temp.go

This file was deleted.

Loading