A WIP client to interact with codeberg.org/madds/madds-go-queue Written to learn Go
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

487 lines
9.8 KiB

package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"math/rand"
"net/http"
"os"
"strconv"
"time"
)
type Queue []Job
type Job struct {
ID int // Job ID
Type string // Job Type (TIME_CRITICAL or NOT_TIME_CRITICAL)
Status string // Job status (QUEUED, IN_PROGRESS, CONCLUDED)
UserID int // The consumer that requested the job
AdditionalData string // Any additional data appeneded to the job
}
type QueueRequest struct {
Type string // Request Type (E,D,C,L,M) Enqueue, Dequeue, Conclude, List, More Details
ID int // The ID to act on (Used to conclude)
Status string // The job status to set (Enqueue)
JobType string // Job Type (Enqueue)
UserID int // The originating UserID
AdditionalData string // Any additional data (Enqueue)
}
type QueueResponse struct {
Type string // Response Type (L, I) List, Info
ResponseStatus int // Response Status (Action status: 0, 1, 2)
List []int // List of jobs
Job Job // Requested Job
}
var uID = 0
var inputChannel = make(chan string)
var commandChannel = make(chan int)
func init() {
rand.Seed(time.Now().UnixNano())
uID = rand.Intn(int(time.Now().UnixNano() / 10000))
}
func main() {
// Prepare the console
fmt.Print("\033[H\033[2J")
// interact with the queue
queue()
// loop()
}
// Interface with the Queue
func queue() {
inputChannel = inputLoop()
commandChannel = command()
command := 0
fmt.Print("\033[4;1H")
// Display Loop
for {
drawHeader()
drawJobList()
// Get waiting command
command = <-commandChannel
// fmt.Println(command)
switch command {
case 0:
// fmt.Println("0")
case 1:
// Exit
fmt.Print("\033[H\033[2J")
fmt.Println("-------------Queue system cli interface-------------")
fmt.Println("----------------------------------------------------")
fmt.Println("----------------Exiting the program!----------------")
fmt.Println("----------------------------------------------------")
os.Exit(0)
case 2:
drawResetToHeader()
// Create a queue job
cmdCreateJob()
time.Sleep(time.Second * 2)
case 3:
drawResetToHeader()
// Get queue job info
cmdGetJob()
time.Sleep(time.Second * 2)
case 4:
drawResetToHeader()
// Conclude a queue job
cmdConcludeJob()
time.Sleep(time.Second * 2)
default:
drawResetToHeader()
fmt.Println("Invalid Command")
}
// Brief sleep before continuing
time.Sleep(time.Second / 20)
}
}
func drawHeader() {
// Save cursor pos
fmt.Print("\0337")
// Clear the console
fmt.Print("\033[3;0H\033[1J\033[H")
// Header
fmt.Println("-------------Queue system cli interface-------------")
fmt.Println("Available commands are:")
fmt.Println("| Q: Quit | z: Create | x: Get Info | c: Conclude |")
// Reset cursor pos
fmt.Print("\0338")
}
func drawJobList() {
jobs := getJobList()
for i := range jobs {
log.Println(jobs[i])
}
}
func drawResetToHeader() {
fmt.Print("\033[4;1H\033[0J")
}
// handle commands
func command() chan int {
command := "0"
go func() chan int {
for {
// Make sure there's always a 0 so the
// Display doesn't block
commandChannel <- 0
// TODO: Is this bad? I would assume so
// Having a goroutine inside a for statement
// inside another goroutine seems like a
// bad idea but maybe it's smart enough
// Not sure how else to do this
// Async wait for input so nothing is blocked
go func() string {
// read from the input channel
// This blocks
command = <-inputChannel
return command
}()
if command != "0" {
switch command {
case "Q":
commandChannel <- 1 // Quit
case "z":
commandChannel <- 2 // Create
case "x":
commandChannel <- 3 // Get Info
case "c":
commandChannel <- 4 // Conclude
default:
commandChannel <- 99
}
command = "0"
}
}
}()
return commandChannel
}
func cmdCreateJob() {
drawResetToHeader()
fmt.Println("| Select job type |")
fmt.Println("| TIME_CRITICAL | NOT_TIME_CRITICAL |")
status := getInput()
drawResetToHeader()
fmt.Println("| Add any additional data |")
data := getInput()
url := "http://localhost:8080/api/v1/enqueue"
message := makeRequest("E", 0, "", status, data)
response := sendRequest(url, message)
var jobResponse Job
json.Unmarshal(response, &jobResponse)
drawResetToHeader()
fmt.Println(jobResponse)
}
func cmdGetJob() {
var i int64
url := "http://localhost:8080/api/v1/enqueue"
for {
drawResetToHeader()
fmt.Println("| Select job id |")
jobID := getInput()
var err error
i, err = strconv.ParseInt(jobID, 10, 0)
if err != nil {
fmt.Println("Not a valid int")
} else {
break
}
}
message := makeRequest("M", int(i), "", "", "")
response := sendRequest(url, message)
var jobResponse Job
json.Unmarshal(response, &jobResponse)
drawResetToHeader()
fmt.Println(jobResponse)
}
func cmdConcludeJob() {
var i int64
url := "/api/v1/jobs/"
for {
drawResetToHeader()
fmt.Println("| Select job id to conclude |")
jobID := getInput()
var err error
i, err = strconv.ParseInt(jobID, 10, 0)
if err != nil {
fmt.Println("Not a valid int")
} else {
break
}
}
message := makeRequest("C", int(i), "/", "", "")
url = url + string(i) + "/conclude"
response := sendRequest(url, message)
drawResetToHeader()
fmt.Println(response)
}
func getInput() string {
i := <-inputChannel
return i
}
// async get input
func inputLoop() chan string {
scanner := bufio.NewScanner(os.Stdin)
channel := make(chan string, 1)
go func() {
for {
scanner.Scan()
input := scanner.Text()
if scanner.Err() != nil {
log.Fatal(scanner.Err().Error())
break
}
// pass input string into the channel
channel <- input
}
}()
return channel
}
// Create job in the Queue
//func createQueueJob() int {
//}
// Get current job list, returns a slice of ints
func getJobList() []int {
response := getFromQueue("/jobs")
queue := parseQueue(response)
// Set max to prevent stalling system by
// trying to use overly large lists
knownJobs := make([]int, 0, 9999)
for i := range queue {
knownJobs = append(knownJobs, queue[i].ID)
}
return knownJobs
}
// // Get info for a job in the Queue
// func getJobInfo(endpoint string, job int) Job {
// fmt.Println("Getting Job Information")
// url := "localhost" + endpoint
// message := makeRequest("M", job, "", "", "")
// response, err, putError := sendRequest(url, message)
// if err != nil || putError != nil {
// fmt.Println("Put Error")
// }
// // Defer closing the response until reading it
// defer response.Body.Close()
// body, readError := ioutil.ReadAll(response.Body)
// if readError == nil {
// fmt.Println("Invalid response")
// }
// var jobResponse Job
// json.Unmarshal(body, &jobResponse)
// return jobResponse
// }
func getFromQueue(endpoint string) []byte {
fmt.Println("Connecting to Queue")
url := "http://localhost:8080/api/v1" + endpoint
response, err := http.Get(url)
if err != nil {
fmt.Println(err)
time.Sleep(time.Second)
}
responseData, readErr := ioutil.ReadAll(response.Body)
if readErr != nil {
fmt.Println(err)
}
return responseData
}
func parseQueue(response []byte) Queue {
fmt.Println("Parsing Queue")
// declare interface
var responseObject Queue
// parse valid JSON response
if !json.Valid(response) {
fmt.Println("Invalid JSON!")
} else {
json.Unmarshal(response, &responseObject)
}
return responseObject
}
func makeRequest(reqType string, id int, jobStatus string, jobType string, data string) []byte {
request, err := json.Marshal(QueueRequest{reqType, id, jobStatus, jobType, uID, data})
if err != nil {
fmt.Println(err)
}
return request
}
func sendRequest(url string, message []byte) []byte {
request, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(message))
request.Header.Set("Content-Type", "application/json; charset=utf-8")
client := &http.Client{}
response, putError := client.Do(request)
if err != nil || putError != nil {
fmt.Println("Put Error")
}
// Defer closing the response until reading it
defer response.Body.Close()
body, readError := ioutil.ReadAll(response.Body)
if readError == nil {
fmt.Println("Invalid response")
}
return body
}
// cat fact test area
//
//
//
//
//
//
func loop() {
for {
// Clear command line
fmt.Print("\033[H\033[2J")
fmt.Printf("Press Control+C to quit\n")
// Print a cat fact
catFact()
// Wait for 5 seconds
// TODO: change to .1s when accessing the other REST api
time.Sleep(5 * time.Second)
}
}
type CatFact []struct {
Status CatStatus `json:"status"`
ID string `json:"_id"`
User string `json:"user"`
Text string `json:"text"`
Version int `json:"__v"`
Source string `json:"source"`
UpdatedAt time.Time `json:"updatedAt"`
Type string `json:"type"`
CreatedAt time.Time `json:"createdAt"`
Deleted bool `json:"deleted"`
Used bool `json:"used"`
}
type CatStatus struct {
Verified bool `json:"verified"`
SentCount int `json:"sentCount"`
}
func getCatFact(url string) CatFact {
response, err := http.Get(url)
if err != nil {
fmt.Print(err.Error())
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Println("Error!")
log.Fatal(err)
// TODO: Find a better way to do this
var noCatFact CatFact
return noCatFact
}
var responseObject CatFact
json.Unmarshal(responseData, &responseObject)
return responseObject
}
func catFact() {
url := "https:// cat-fact.herokuapp.com/facts"
resp := getCatFact(url)
if len(resp) != 0 {
r := rand.Intn(len(resp))
if resp[r].Type != "cat" {
return
}
fmt.Println("Your random cat fact:")
fmt.Println(resp[r].Text + "\nSource: " + resp[r].Source)
} else {
fmt.Println("Not a valid response:")
fmt.Println(resp)
}
}