ooo

package module
v0.0.0-...-571808a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2024 License: MIT Imports: 38 Imported by: 4

README

ooo

Test

Zero configuration data persistence and communication layer.

Dynamic websocket and restful http service to quickly prototype realtime applications.

features

  • dynamic routing
  • glob pattern routes for lists
  • patch updates on lists subscriptions
  • version check on subscriptions (no message on version match)
  • restful CRUD service that reflects interactions to real-time subscriptions
  • filtering and audit middleware
  • auto managed timestamps (created, updated)

quickstart

client

There's a js client.

server

with go installed get the library

go get github.com/benitogf/ooo

create a file main.go

package main

import "github.com/benitogf/ooo"

func main() {
  app := ooo.Server{}
  app.Start("0.0.0.0:8800")
  app.WaitClose()
}

run the service:

go run main.go

routes

method description url
GET key list http://{host}:{port}
websocket clock ws://{host}:{port}
POST create/update http://{host}:{port}/{key}
GET read http://{host}:{port}/{key}
DELETE delete http://{host}:{port}/{key}
websocket subscribe ws://{host}:{port}/{key}

control

static routes

Activating this flag will limit the server to process requests defined in read and write filters

app := ooo.Server{}
app.Static = true
filters
  • Write filters will be called before processing a write operation
  • Read filters will be called before sending the results of a read operation
  • if the static flag is enabled only filtered routes will be available
app.WriteFilter("books/*", func(index string, data json.RawMessage) (json.RawMessage, error) {
  // returning an error will deny the write
  return data, nil
})
app.AfterWrite("books/*", func(index string) {
  // trigger after a write is done
  log.Println("wrote data on ", index)
})
app.ReadFilter("books/taup", func(index string, data json.RawMessage) (json.RawMessage, error) {
  // returning an error will deny the read
  return json.RawMessage(`{"intercepted":true}`), nil
})
app.DeleteFilter("books/taup", func(key string) (error) {
  // returning an error will prevent the delete
  return errors.New("can't delete")
})
audit
app.Audit = func(r *http.Request) bool {
  return false // condition to allow access to the resource
}
subscribe events capture
// new subscription event
server.OnSubscribe = func(key string) error {
  log.Println(key)
  // returning an error will deny the subscription
  return nil
}
// closing subscription event
server.OnUnsubscribe = func(key string) {
  log.Println(key)
}
extra routes
// Define custom endpoints
app.Router = mux.NewRouter()
app.Router.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("Content-Type", "application/json")
  fmt.Fprintf(w, "{}")
})
app.Start("0.0.0.0:8800")
write/read storage api
package main

import (
	"encoding/json"
	"log"
	"strconv"
	"time"

	"github.com/benitogf/ooo"
	"github.com/benitogf/ooo/meta"
)

type Game struct {
	Started int64 `json:"started"`
}

// not good practice, just for illustration purposes only
// handle errors responsably :)
func panicHandle(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	// create a static server
	server := ooo.Server{
		Static: true,
	}
	// define the path so it's available throug http/ws
	server.OpenFilter("game")

	// start the server/storage (default to memory storage)
	server.Start("0.0.0.0:8800")

	// write
	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
	index, err := server.Storage.Set("game", json.RawMessage(`{"started": `+timestamp+`}`))
	panicHandle(err)
	log.Println("stored in", index)

	// read
	data, err := server.Storage.Get("game")
	panicHandle(err)
	dataObject, err := meta.Decode(data)
	panicHandle(err)
	log.Println("created", dataObject.Created)
	log.Println("updated", dataObject.Updated)
	log.Println("data", string(dataObject.Data))

	// parse json to struct
	game := Game{}
	err = json.Unmarshal(dataObject.Data, &game)
	panicHandle(err)
	log.Println("started", game.Started)

	// close server handler
	server.WaitClose()
}

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidPath = errors.New("ooo: invalid path")
	ErrNotFound    = errors.New("ooo: not found")
	ErrNoop        = errors.New("ooo: noop")
)
View Source
var (
	ErrNotAuthorized = errors.New("ooo: pathKeyError key is not valid")
)
View Source
var TEST_DATA = json.RawMessage(`{
	"statuses": [
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Mon Sep 24 03:35:21 +0000 2012",
		"id_str": "250075927172759552",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "freebandnames",
			  "indices": [
				20,
				34
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "Aggressive Ponytail #freebandnames",
		"metadata": {
		  "iso_language_code": "en",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 250075927172759552,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "DDEEF6",
		  "profile_sidebar_border_color": "C0DEED",
		  "profile_background_tile": false,
		  "name": "Sean Cummings",
		  "profile_image_url": "http://a0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg",
		  "created_at": "Mon Apr 26 06:01:55 +0000 2010",
		  "location": "LA, CA",
		  "follow_request_sent": null,
		  "profile_link_color": "0084B4",
		  "is_translator": false,
		  "id_str": "137238150",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "",
				  "indices": [
					0,
					0
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": true,
		  "contributors_enabled": false,
		  "favourites_count": 0,
		  "url": null,
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg",
		  "utc_offset": -28800,
		  "id": 137238150,
		  "profile_use_background_image": true,
		  "listed_count": 2,
		  "profile_text_color": "333333",
		  "lang": "en",
		  "followers_count": 70,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme1/bg.png",
		  "profile_background_color": "C0DEED",
		  "verified": false,
		  "geo_enabled": true,
		  "time_zone": "Pacific Time (US & Canada)",
		  "description": "Born 330 Live 310",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/images/themes/theme1/bg.png",
		  "statuses_count": 579,
		  "friends_count": 110,
		  "following": null,
		  "show_all_inline_media": false,
		  "screen_name": "sean_cummings"
		},
		"in_reply_to_screen_name": null,
		"source": "<a href=\"//itunes.apple.com/us/app/twitter/id409789998?mt=12%5C%22\" rel=\"\\\"nofollow\\\"\">Twitter for Mac</a>",
		"in_reply_to_status_id": null
	  },
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Fri Sep 21 23:40:54 +0000 2012",
		"id_str": "249292149810667520",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "FreeBandNames",
			  "indices": [
				20,
				34
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "Thee Namaste Nerdz. #FreeBandNames",
		"metadata": {
		  "iso_language_code": "pl",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 249292149810667520,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "DDFFCC",
		  "profile_sidebar_border_color": "BDDCAD",
		  "profile_background_tile": true,
		  "name": "Chaz Martenstein",
		  "profile_image_url": "http://a0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg",
		  "created_at": "Tue Apr 07 19:05:07 +0000 2009",
		  "location": "Durham, NC",
		  "follow_request_sent": null,
		  "profile_link_color": "0084B4",
		  "is_translator": false,
		  "id_str": "29516238",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "http://bullcityrecords.com/wnng/",
				  "indices": [
					0,
					32
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": false,
		  "contributors_enabled": false,
		  "favourites_count": 8,
		  "url": "http://bullcityrecords.com/wnng/",
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg",
		  "utc_offset": -18000,
		  "id": 29516238,
		  "profile_use_background_image": true,
		  "listed_count": 118,
		  "profile_text_color": "333333",
		  "lang": "en",
		  "followers_count": 2052,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/profile_background_images/9423277/background_tile.bmp",
		  "profile_background_color": "9AE4E8",
		  "verified": false,
		  "geo_enabled": false,
		  "time_zone": "Eastern Time (US & Canada)",
		  "description": "You will come to Durham, North Carolina. I will sell you some records then, here in Durham, North Carolina. Fun will happen.",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/profile_background_images/9423277/background_tile.bmp",
		  "statuses_count": 7579,
		  "friends_count": 348,
		  "following": null,
		  "show_all_inline_media": true,
		  "screen_name": "bullcityrecords"
		},
		"in_reply_to_screen_name": null,
		"source": "web",
		"in_reply_to_status_id": null
	  },
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Fri Sep 21 23:30:20 +0000 2012",
		"id_str": "249289491129438208",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "freebandnames",
			  "indices": [
				29,
				43
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "Mexican Heaven, Mexican Hell #freebandnames",
		"metadata": {
		  "iso_language_code": "en",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 249289491129438208,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "99CC33",
		  "profile_sidebar_border_color": "829D5E",
		  "profile_background_tile": false,
		  "name": "Thomas John Wakeman",
		  "profile_image_url": "http://a0.twimg.com/profile_images/2219333930/Froggystyle_normal.png",
		  "created_at": "Tue Sep 01 21:21:35 +0000 2009",
		  "location": "Kingston New York",
		  "follow_request_sent": null,
		  "profile_link_color": "D02B55",
		  "is_translator": false,
		  "id_str": "70789458",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "",
				  "indices": [
					0,
					0
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": false,
		  "contributors_enabled": false,
		  "favourites_count": 19,
		  "url": null,
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/2219333930/Froggystyle_normal.png",
		  "utc_offset": -18000,
		  "id": 70789458,
		  "profile_use_background_image": true,
		  "listed_count": 1,
		  "profile_text_color": "3E4415",
		  "lang": "en",
		  "followers_count": 63,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme5/bg.gif",
		  "profile_background_color": "352726",
		  "verified": false,
		  "geo_enabled": false,
		  "time_zone": "Eastern Time (US & Canada)",
		  "description": "Science Fiction Writer, sort of. Likes Superheroes, Mole People, Alt. Timelines.",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/images/themes/theme5/bg.gif",
		  "statuses_count": 1048,
		  "friends_count": 63,
		  "following": null,
		  "show_all_inline_media": false,
		  "screen_name": "MonkiesFist"
		},
		"in_reply_to_screen_name": null,
		"source": "web",
		"in_reply_to_status_id": null
	  },
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Fri Sep 21 22:51:18 +0000 2012",
		"id_str": "249279667666817024",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "freebandnames",
			  "indices": [
				20,
				34
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "The Foolish Mortals #freebandnames",
		"metadata": {
		  "iso_language_code": "en",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 249279667666817024,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "BFAC83",
		  "profile_sidebar_border_color": "615A44",
		  "profile_background_tile": true,
		  "name": "Marty Elmer",
		  "profile_image_url": "http://a0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png",
		  "created_at": "Mon May 04 00:05:00 +0000 2009",
		  "location": "Wisconsin, USA",
		  "follow_request_sent": null,
		  "profile_link_color": "3B2A26",
		  "is_translator": false,
		  "id_str": "37539828",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "http://www.omnitarian.me",
				  "indices": [
					0,
					24
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": false,
		  "contributors_enabled": false,
		  "favourites_count": 647,
		  "url": "http://www.omnitarian.me",
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png",
		  "utc_offset": -21600,
		  "id": 37539828,
		  "profile_use_background_image": true,
		  "listed_count": 52,
		  "profile_text_color": "000000",
		  "lang": "en",
		  "followers_count": 608,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/profile_background_images/106455659/rect6056-9.png",
		  "profile_background_color": "EEE3C4",
		  "verified": false,
		  "geo_enabled": false,
		  "time_zone": "Central Time (US & Canada)",
		  "description": "Cartoonist, Illustrator, and T-Shirt connoisseur",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/profile_background_images/106455659/rect6056-9.png",
		  "statuses_count": 3575,
		  "friends_count": 249,
		  "following": null,
		  "show_all_inline_media": true,
		  "screen_name": "Omnitarian"
		},
		"in_reply_to_screen_name": null,
		"source": "<a href=\"//twitter.com/download/iphone%5C%22\" rel=\"\\\"nofollow\\\"\">Twitter for iPhone</a>",
		"in_reply_to_status_id": null
	  }
	],
	"search_metadata": {
	  "max_id": 250126199840518145,
	  "since_id": 24012619984051000,
	  "refresh_url": "?since_id=250126199840518145&q=%23freebandnames&result_type=mixed&include_entities=1",
	  "next_results": "?max_id=249279667666817023&q=%23freebandnames&count=4&include_entities=1&result_type=mixed",
	  "count": 4,
	  "completed_in": 0.035,
	  "since_id_str": "24012619984051000",
	  "query": "%23freebandnames",
	  "max_id_str": "250126199840518145",
	  "something": "something 🧰"
	}
  }`)

https://gist.github.com/slaise/9b9d63e0d59e8c8923bbd9d53f5beb61 https://medium.com/geekculture/my-golang-json-evaluation-20a9ca6ef79c

View Source
var TEST_DATA_UPDATE = json.RawMessage(`{
	"statuses": [
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Mon Sep 24 03:35:21 +0000 2012",
		"id_str": "250075927172759552",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "freebandnames",
			  "indices": [
				20,
				34
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "Aggressive Ponytail #freebandnames",
		"metadata": {
		  "iso_language_code": "en",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 250075927172759552,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "DDEEF6",
		  "profile_sidebar_border_color": "C0DEED",
		  "profile_background_tile": false,
		  "name": "Sean Cummings",
		  "profile_image_url": "http://a0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg",
		  "created_at": "Mon Apr 26 06:01:55 +0000 2010",
		  "location": "LA, CA",
		  "follow_request_sent": null,
		  "profile_link_color": "0084B4",
		  "is_translator": false,
		  "id_str": "137238150",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "",
				  "indices": [
					0,
					0
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": true,
		  "contributors_enabled": false,
		  "favourites_count": 0,
		  "url": null,
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/2359746665/1v6zfgqo8g0d3mk7ii5s_normal.jpeg",
		  "utc_offset": -28800,
		  "id": 137238150,
		  "profile_use_background_image": true,
		  "listed_count": 2,
		  "profile_text_color": "333333",
		  "lang": "en",
		  "followers_count": 70,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme1/bg.png",
		  "profile_background_color": "C0DEED",
		  "verified": false,
		  "geo_enabled": true,
		  "time_zone": "Pacific Time (US & Canada)",
		  "description": "Born 330 Live 310",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/images/themes/theme1/bg.png",
		  "statuses_count": 579,
		  "friends_count": 110,
		  "following": null,
		  "show_all_inline_media": false,
		  "screen_name": "sean_cummings"
		},
		"in_reply_to_screen_name": null,
		"source": "<a href=\"//itunes.apple.com/us/app/twitter/id409789998?mt=12%5C%22\" rel=\"\\\"nofollow\\\"\">Twitter for Mac</a>",
		"in_reply_to_status_id": null
	  },
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Fri Sep 21 23:40:54 +0000 2012",
		"id_str": "249292149810667520",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "FreeBandNames",
			  "indices": [
				20,
				34
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "Thee Namaste Nerdz. #FreeBandNames",
		"metadata": {
		  "iso_language_code": "pl",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 249292149810667520,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "DDFFCC",
		  "profile_sidebar_border_color": "BDDCAD",
		  "profile_background_tile": true,
		  "name": "Chaz Martenstein",
		  "profile_image_url": "http://a0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg",
		  "created_at": "Tue Apr 07 19:05:07 +0000 2009",
		  "location": "Durham, NC",
		  "follow_request_sent": null,
		  "profile_link_color": "0084B4",
		  "is_translator": false,
		  "id_str": "29516238",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "http://bullcityrecords.com/wnng/",
				  "indices": [
					0,
					32
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": false,
		  "contributors_enabled": false,
		  "favourites_count": 8,
		  "url": "http://bullcityrecords.com/wnng/",
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/447958234/Lichtenstein_normal.jpg",
		  "utc_offset": -18000,
		  "id": 29516238,
		  "profile_use_background_image": true,
		  "listed_count": 118,
		  "profile_text_color": "333333",
		  "lang": "en",
		  "followers_count": 2052,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/profile_background_images/9423277/background_tile.bmp",
		  "profile_background_color": "9AE4E8",
		  "verified": false,
		  "geo_enabled": false,
		  "time_zone": "Eastern Time (US & Canada)",
		  "description": "You will come to Durham, North Carolina. I will sell you some records then, here in Durham, North Carolina. Fun will happen.",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/profile_background_images/9423277/background_tile.bmp",
		  "statuses_count": 7579,
		  "friends_count": 348,
		  "following": null,
		  "show_all_inline_media": true,
		  "screen_name": "bullcityrecords"
		},
		"in_reply_to_screen_name": null,
		"source": "web",
		"in_reply_to_status_id": null
	  },
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Fri Sep 21 23:30:20 +0000 2012",
		"id_str": "249289491129438208",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "freebandnames",
			  "indices": [
				29,
				43
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "Mexican Heaven, Mexican Hell #freebandnames",
		"metadata": {
		  "iso_language_code": "en",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 249289491129438208,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "99CC33",
		  "profile_sidebar_border_color": "829D5E",
		  "profile_background_tile": false,
		  "name": "Thomas John Wakeman",
		  "profile_image_url": "http://a0.twimg.com/profile_images/2219333930/Froggystyle_normal.png",
		  "created_at": "Tue Sep 01 21:21:35 +0000 2009",
		  "location": "Kingston New York",
		  "follow_request_sent": null,
		  "profile_link_color": "D02B55",
		  "is_translator": false,
		  "id_str": "70789458",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "",
				  "indices": [
					0,
					0
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": false,
		  "contributors_enabled": false,
		  "favourites_count": 19,
		  "url": null,
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/2219333930/Froggystyle_normal.png",
		  "utc_offset": -18000,
		  "id": 70789458,
		  "profile_use_background_image": true,
		  "listed_count": 1,
		  "profile_text_color": "3E4415",
		  "lang": "en",
		  "followers_count": 63,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme5/bg.gif",
		  "profile_background_color": "352726",
		  "verified": false,
		  "geo_enabled": false,
		  "time_zone": "Eastern Time (US & Canada)",
		  "description": "Science Fiction Writer, sort of. Likes Superheroes, Mole People, Alt. Timelines.",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/images/themes/theme5/bg.gif",
		  "statuses_count": 1048,
		  "friends_count": 63,
		  "following": null,
		  "show_all_inline_media": false,
		  "screen_name": "MonkiesFist"
		},
		"in_reply_to_screen_name": null,
		"source": "web",
		"in_reply_to_status_id": null
	  },
	  {
		"coordinates": null,
		"favorited": false,
		"truncated": false,
		"created_at": "Fri Sep 21 22:51:18 +0000 2012",
		"id_str": "249279667666817024",
		"entities": {
		  "urls": [
  
		  ],
		  "hashtags": [
			{
			  "text": "freebandnames",
			  "indices": [
				20,
				34
			  ]
			}
		  ],
		  "user_mentions": [
  
		  ]
		},
		"in_reply_to_user_id_str": null,
		"contributors": null,
		"text": "The Foolish Mortals #freebandnames",
		"metadata": {
		  "iso_language_code": "en",
		  "result_type": "recent"
		},
		"retweet_count": 0,
		"in_reply_to_status_id_str": null,
		"id": 249279667666817024,
		"geo": null,
		"retweeted": false,
		"in_reply_to_user_id": null,
		"place": null,
		"user": {
		  "profile_sidebar_fill_color": "BFAC83",
		  "profile_sidebar_border_color": "615A44",
		  "profile_background_tile": true,
		  "name": "Marty Elmer",
		  "profile_image_url": "http://a0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png",
		  "created_at": "Mon May 04 00:05:00 +0000 2009",
		  "location": "Wisconsin, USA",
		  "follow_request_sent": null,
		  "profile_link_color": "3B2A26",
		  "is_translator": false,
		  "id_str": "37539828",
		  "entities": {
			"url": {
			  "urls": [
				{
				  "expanded_url": null,
				  "url": "http://www.omnitarian.me",
				  "indices": [
					0,
					24
				  ]
				}
			  ]
			},
			"description": {
			  "urls": [
  
			  ]
			}
		  },
		  "default_profile": false,
		  "contributors_enabled": false,
		  "favourites_count": 647,
		  "url": "http://www.omnitarian.me",
		  "profile_image_url_https": "https://si0.twimg.com/profile_images/1629790393/shrinker_2000_trans_normal.png",
		  "utc_offset": -21600,
		  "id": 37539828,
		  "profile_use_background_image": true,
		  "listed_count": 52,
		  "profile_text_color": "000000",
		  "lang": "en",
		  "followers_count": 608,
		  "protected": false,
		  "notifications": null,
		  "profile_background_image_url_https": "https://si0.twimg.com/profile_background_images/106455659/rect6056-9.png",
		  "profile_background_color": "EEE3C4",
		  "verified": false,
		  "geo_enabled": false,
		  "time_zone": "Central Time (US & Canada)",
		  "description": "Cartoonist, Illustrator, and T-Shirt connoisseur",
		  "default_profile_image": false,
		  "profile_background_image_url": "http://a0.twimg.com/profile_background_images/106455659/rect6056-9.png",
		  "statuses_count": 3575,
		  "friends_count": 249,
		  "following": null,
		  "show_all_inline_media": true,
		  "screen_name": "Omnitarian"
		},
		"in_reply_to_screen_name": null,
		"source": "<a href=\"//twitter.com/download/iphone%5C%22\" rel=\"\\\"nofollow\\\"\">Twitter for iPhone</a>",
		"in_reply_to_status_id": null
	  }
	],
	"search_metadata": {
	  "max_id": 250126199840518145,
	  "since_id": 24012619984051000,
	  "refresh_url": "?since_id=250126199840518145&q=%23freebandnames&result_type=mixed&include_entities=1",
	  "next_results": "?max_id=249279667666817023&q=%23freebandnames&count=4&include_entities=1&result_type=mixed",
	  "count": 4,
	  "completed_in": 0.035,
	  "since_id_str": "24012619984051000",
	  "query": "%23freebandnames",
	  "max_id_str": "250126199840518145",
	  "something": "something else 🧰"
	}
  }`)

Functions

func NoopFilter

func NoopFilter(index string, data json.RawMessage) (json.RawMessage, error)

NoopFilter open noop filter

func NoopHook

func NoopHook(index string) error

NoopHook open noop hook

func StorageBatchSetTest

func StorageBatchSetTest(app *Server, t *testing.T, n int)

func StorageGetNRangeTest

func StorageGetNRangeTest(app *Server, t *testing.T, n int)

StorageGetNRangeTest testing storage GetN function

func StorageGetNTest

func StorageGetNTest(app *Server, t *testing.T, n int)

StorageGetNTest testing storage GetN function

func StorageKeysRangeTest

func StorageKeysRangeTest(app *Server, t *testing.T, n int)

StorageKeysRangeTest testing storage GetN function

func StorageListTest

func StorageListTest(app *Server, t *testing.T)

StorageListTest testing storage function

func StorageObjectTest

func StorageObjectTest(app *Server, t *testing.T)

StorageObjectTest testing storage function

func StorageSetGetDelTest

func StorageSetGetDelTest(db Database, b *testing.B)

StorageSetGetDelTest testing storage function

func StreamBroadcastFilterTest

func StreamBroadcastFilterTest(t *testing.T, app *Server)

StreamBroadcastFilterTest testing stream function

func StreamBroadcastForcePatchTest

func StreamBroadcastForcePatchTest(t *testing.T, app *Server)

StreamBroadcastForcePatchTest testing stream function

func StreamBroadcastNoPatchTest

func StreamBroadcastNoPatchTest(t *testing.T, app *Server)

StreamBroadcastNoPatchTest testing stream function

func StreamBroadcastTest

func StreamBroadcastTest(t *testing.T, app *Server)

StreamBroadcastTest testing stream function

func StreamConcurrentTest

func StreamConcurrentTest(t *testing.T, app *Server, n int)

func StreamGlobBroadcastTest

func StreamGlobBroadcastTest(t *testing.T, app *Server, n int)

StreamGlobBroadcastTest testing stream function

func StreamItemGlobBroadcastTest

func StreamItemGlobBroadcastTest(t *testing.T, app *Server)

StreamItemGlobBroadcastTest testing stream function

func Time

func Time() string

Time returns a string timestamp

func WatchStorageNoop

func WatchStorageNoop(dataStore Database)

WatchStorageNoop a noop reader of the watch channel

Types

type Apply

type Apply func(key string, data json.RawMessage) (json.RawMessage, error)

Apply filter function type for functions will serve as filters key: the key to filter data: the data received or about to be sent returns data: to be stored or sent to the client error: will prevent data to pass the filter

type ApplyDelete

type ApplyDelete func(key string) error

ApplyDelete callback function

type Database

type Database interface {
	Active() bool
	Start(StorageOpt) error
	Close()
	Keys() ([]byte, error)
	KeysRange(path string, from, to int64) ([]string, error)
	Get(key string) ([]byte, error)
	GetDescending(key string) ([]byte, error)
	GetN(path string, limit int) ([]meta.Object, error)
	GetNAscending(path string, limit int) ([]meta.Object, error)
	GetNRange(path string, limit int, from, to int64) ([]meta.Object, error)
	Set(key string, data json.RawMessage) (string, error)
	Patch(key string, data json.RawMessage) (string, error)
	SetWithMeta(key string, data json.RawMessage, created, updated int64) (string, error)
	GetAndLock(key string) ([]byte, error)
	SetAndUnlock(key string, data json.RawMessage) (string, error)
	Unlock(key string) error
	Del(key string) error
	Clear()
	Watch() StorageChan
}

Database interface to be implemented by storages

Active: returns a boolean with the state of the storage

Start: will attempt to start a storage client

Close: closes the storage client

Keys: returns a list with existing keys in the storage

Get(key): retrieve a value or list of values, the key can include a glob pattern (ascending created time order)

GetDescending(key): retrieve a value or list of values, the key can include a glob pattern (descending created time order)

GetN(path, N): retrieve N list of values matching a glob pattern (descending created time order)

GetNAscending(path, N): retrieve N list of values matching a glob pattern (ascending created time order)

GetNRange(path, N, from, to): retrieve N list of values matching a glob pattern path created in the time from-to time range (descending created time order)

Set(key, data): store data under the provided key, key cannot not include glob pattern

SetWithMeta(key, data, created, updated): store data by manually providing created/updated time values

GetAndLock(key): same as get but will lock the key mutex until SetAndUnlock is called for the same key (non glob key only)

SetAndUnlock(key, data): same as set but will unlock the key mutex (non glob key only)

Unlock(key): unlock key mutex

Del(key): Delete a key from the storage

Clear: will clear all data from the storage

Watch: returns a channel that will receive any set or del operation

type MemoryStorage

type MemoryStorage struct {
	// contains filtered or unexported fields
}

MemoryStorage composition of Database interface

func (*MemoryStorage) Active

func (db *MemoryStorage) Active() bool

Active provides access to the status of the storage client

func (*MemoryStorage) Clear

func (db *MemoryStorage) Clear()

Clear all keys in the storage

func (*MemoryStorage) Close

func (db *MemoryStorage) Close()

Close the storage client

func (*MemoryStorage) Del

func (db *MemoryStorage) Del(path string) error

Del a key/pattern value(s)

func (*MemoryStorage) Get

func (db *MemoryStorage) Get(path string) ([]byte, error)

Get a key/pattern related value(s)

func (*MemoryStorage) GetAndLock

func (db *MemoryStorage) GetAndLock(path string) ([]byte, error)

func (*MemoryStorage) GetDescending

func (db *MemoryStorage) GetDescending(path string) ([]byte, error)

Get a key/pattern related value(s)

func (*MemoryStorage) GetN

func (db *MemoryStorage) GetN(path string, limit int) ([]meta.Object, error)

GetN get last N elements of a path related value(s)

func (*MemoryStorage) GetNAscending

func (db *MemoryStorage) GetNAscending(path string, limit int) ([]meta.Object, error)

GetN get last N elements of a path related value(s)

func (*MemoryStorage) GetNRange

func (db *MemoryStorage) GetNRange(path string, limit int, from, to int64) ([]meta.Object, error)

GetNRange get last N elements of a path related value(s)

func (*MemoryStorage) Keys

func (db *MemoryStorage) Keys() ([]byte, error)

Keys list all the keys in the storage

func (*MemoryStorage) KeysRange

func (db *MemoryStorage) KeysRange(path string, from, to int64) ([]string, error)

KeysRange list keys in a path and time range

func (*MemoryStorage) Patch

func (db *MemoryStorage) Patch(path string, data json.RawMessage) (string, error)

Set a value to matching keys

func (*MemoryStorage) Peek

func (db *MemoryStorage) Peek(key string, now int64) (int64, int64)

Peek a value timestamps

func (*MemoryStorage) Set

func (db *MemoryStorage) Set(path string, data json.RawMessage) (string, error)

Set a value

func (*MemoryStorage) SetAndUnlock

func (db *MemoryStorage) SetAndUnlock(path string, data json.RawMessage) (string, error)

func (*MemoryStorage) SetWithMeta

func (db *MemoryStorage) SetWithMeta(path string, data json.RawMessage, created int64, updated int64) (string, error)

SetWithMeta set entries with metadata created/updated values

func (*MemoryStorage) Start

func (db *MemoryStorage) Start(storageOpt StorageOpt) error

Start the storage client

func (*MemoryStorage) Unlock

func (db *MemoryStorage) Unlock(path string) error

func (*MemoryStorage) Watch

func (db *MemoryStorage) Watch() StorageChan

Watch the storage set/del events

type Notify

type Notify func(key string)

Notify after a write is done

type Server

type Server struct {
	Router *mux.Router
	Stream stream.Stream

	Pivot           string
	NoBroadcastKeys []string
	DbOpt           interface{}
	Audit           audit
	Workers         int
	ForcePatch      bool
	NoPatch         bool
	OnSubscribe     stream.Subscribe
	OnUnsubscribe   stream.Unsubscribe
	OnClose         func()
	Deadline        time.Duration
	AllowedOrigins  []string
	AllowedMethods  []string
	AllowedHeaders  []string
	ExposedHeaders  []string
	Storage         Database
	Address         string

	Silence           bool
	Static            bool
	Tick              time.Duration
	Console           *coat.Console
	Signal            chan os.Signal
	Client            *http.Client
	ReadTimeout       time.Duration
	WriteTimeout      time.Duration
	ReadHeaderTimeout time.Duration
	IdleTimeout       time.Duration
	// contains filtered or unexported fields
}

Server application

Router: can be predefined with routes and passed to be extended

NoBroadcastKeys: array of keys that should not broadcast on changes

DbOpt: options for storage

Audit: function to audit requests

Workers: number of workers to use as readers of the storage->broadcast channel

ForcePatch: flag to force patch operations even if the patch is bigger than the snapshot

OnSubscribe: function to monitor subscribe events

OnUnsubscribe: function to monitor unsubscribe events

OnClose: function that triggers before closing the application

Deadline: time duration of a request before timing out

AllowedOrigins: list of allowed origins for cross domain access, defaults to ["*"]

AllowedMethods: list of allowed methods for cross domain access, defaults to ["GET", "POST", "DELETE", "PUT"]

AllowedHeaders: list of allowed headers for cross domain access, defaults to ["Authorization", "Content-Type"]

ExposedHeaders: list of exposed headers for cross domain access, defaults to nil

Storage: database interdace implementation

Silence: output silence flag

Static: static routing flag

Tick: time interval between ticks on the clock subscription

Signal: os signal channel

Client: http client to make requests

Example
package main

import (
	"github.com/benitogf/ooo"
)

func main() {
	app := ooo.Server{}
	app.Start("localhost:8800")
	app.WaitClose()
}
Output:

func (*Server) Active

func (app *Server) Active() bool

Active check if the server is active

func (*Server) AfterWrite

func (app *Server) AfterWrite(path string, apply Notify)

AfterWrite add a filter that triggers after a successful write

func (*Server) Close

func (app *Server) Close(sig os.Signal)

Close : shutdown the http server and database connection

func (*Server) DeleteFilter

func (app *Server) DeleteFilter(path string, apply ApplyDelete)

DeleteFilter add a filter that runs before sending a read result

func (*Server) OpenFilter

func (app *Server) OpenFilter(name string)

OpenFilter open noop read and write filters

func (*Server) ReadFilter

func (app *Server) ReadFilter(path string, apply Apply)

ReadFilter add a filter that runs before sending a read result

func (*Server) Start

func (app *Server) Start(address string)

Start : initialize and start the http server and database connection

func (*Server) WaitClose

func (app *Server) WaitClose()

WaitClose : Blocks waiting for SIGINT, SIGTERM, SIGKILL, SIGHUP

func (*Server) WriteFilter

func (app *Server) WriteFilter(path string, apply Apply)

WriteFilter add a filter that triggers on write

type Stats

type Stats struct {
	Keys []string `json:"keys"`
}

Stats data structure of global keys

type Storage

type Storage struct {
	Active bool
	Db     Database
}

Storage abstraction of persistent data layer

type StorageChan

type StorageChan chan StorageEvent

StorageChan an operation events channel

type StorageEvent

type StorageEvent struct {
	Key       string
	Operation string
}

StorageEvent an operation event

type StorageOpt

type StorageOpt struct {
	NoBroadcastKeys []string
	DbOpt           interface{}
}

StorageOpt options of the storage instance

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL