@@ 2,7 2,6 @@ package main
import (
"strings"
- "strconv"
)
type Store struct {
@@ 21,81 20,15 @@ type Configuration struct {
}
const (
- WORKSPACES = "workspaces"
- COMPONENTS = "components"
WORKSPACE = "0"
)
-
-// - GET retrieves a component config
-//
-// /components
-// Retrieves a list of all components
-// Response: JSON - List of strings of component names
-//
-// /components/COMPONENT[/..][?rev=REV]
-// Retrieves a configuration for a single component, optionally by version.
-// Response: JSON - JSON serialization of component.
-//
-// /workspaces[?limit=COUNT]
-// Retrieves a list of all workspace revisions and tags. If limit is supplied, only
-// the last COUNT revisions are returned; all tags and branch names are always
-// included
-// Response: JSON - A hash containing "revisions" List, "tags" List,
-// and "branches" List
-//
-// /workspaces/ID
-// Retrieves an entire workspace (all components) by revision or tag
-// Response: JSON - JSON serialization of the workspace
-func (h Configuration) Get( path []string, m map[string]string ) (response string, code int) {
- var serial Serializer
- switch m["format"] {
- case "props" : serial = new(PropsSerializer)
- default : serial = new(JsonSerializer)
- }
-
- malformed := "Malformed request: "+to_string(path)+
- "; must be /components[/COMPONENT[/..][?rev=REV]] or /workspaces[/ID] or "+
- "/workspaces[?limit=COUNT]"
- path_len := len(path)
- if path_len < 1 {
- return malformed, 400
- } else if path_len == 1 {
- switch path[0] {
- case WORKSPACES:
- return getWorkspaces( h, path, m, malformed, serial )
- case COMPONENTS:
- component_names := keys(h.workspace)
- return serial.Serialize( component_names ), 200
- default:
- return malformed, 400
- }
- } else {
- switch path[0] {
- case WORKSPACES:
- revision := h.repo.Get( path[1] )
- return revision, 200
- case COMPONENTS:
- return getComponent( h, path, m, serial )
- default:
- return malformed, 400
- }
- }
- return "Internal server error", 500
-}
-
-func getWorkspaces( h Configuration, path []string, m map[string]string,
- malformed string, serial Serializer) (string, int) {
+func (h Configuration) GetWorkspaces ( limit int ) map[string]([]string) {
revs := h.repo.Revs()
- limit := m["limit"]
- if (limit != "") {
- lim,e := strconv.Atoi(limit)
- if e != nil {
- return malformed, 400
- }
+ if limit != 0 {
length := len(revs)-1
- revs = revs[length-lim:]
+ revs = revs[length-limit:]
}
tags := h.repo.Tags()
branches := h.repo.Branches()
@@ 103,82 36,57 @@ func getWorkspaces( h Configuration, pat
return_map["revisions"] = revs
return_map["tags"] = tags
return_map["branches"] = branches
- return serial.Serialize( return_map ), 200
+ return return_map
}
-func getComponent( h Configuration, path []string, m map[string]string,
- serial Serializer) (string, int) {
- json := new( JsonSerializer )
- component_name := to_string(path[1:])
- components := h.workspace // Get the workspace by default
- if m["rev"] != "" {
- var revision StoreMap
- json.Deserialize(h.repo.Get(m["rev"]), &revision)
- components = revision
- }
- component := components[ component_name ]
- attrset := expand( components, path[1:], component )
- return serial.Serialize(attrset), 200
+
+func (h Configuration) GetWorkspace ( rev string ) (json string) {
+ return h.repo.Get( rev )
+}
+
+
+func (h Configuration) GetComponents () []string {
+ return keys(h.workspace)
}
-// - PUT stores a unit
-//
-// /components/COMPONENT[/COMPONENT]
-// Stores a component in the workspace. This *replaces* the current component
-// definition. The inheritance hierarchy is defined in the payload, which
-// should be a JSON encoding of a hash of attributes and parents.
-// Response: ""
-//
-// /workspaces[?tag=TAGNAME]
-// Checks in the workspace
-// Response: revision number created
-func (h Configuration) Put( path []string, m map[string]string, body string ) (response string, code int) {
- malformed := "Malformed request: "+to_string(path)+
- "; must be /components/COMPONENT[/..] or /workspaces/NUM."
- if (len(path) < 2) {
- return malformed, 400
+func (h Configuration) GetComponent ( component_name string, rev string ) Store {
+ json := new( JsonSerializer )
+ components := h.workspace // Get the workspace by default
+ if rev != "" {
+ var revision StoreMap
+ json.Deserialize(h.repo.Get(rev), &revision)
+ components = revision
}
-
- json := new(JsonSerializer)
- switch path[0] {
- case WORKSPACES:
- tag := m[ "tag" ]
- rev := h.repo.Commit( "config.json", json.Serialize( h.workspace ), tag )
- return rev, 200
- case COMPONENTS:
- var config Store
- json.Deserialize( body, &config )
- reduce( h.workspace, path, &config )
- component_name := to_string( path[1:] )
- h.workspace[ component_name ] = config
- return "", 200
- default:
- return malformed, 400
- }
- return "Internal server error", 500
+ component := components[ component_name ]
+ path := strings.Split( component_name, "/", -1 )
+ attrset := expand( components, path, component )
+ return attrset
}
-// POST creates a new component or revision
-//
-// /workspaces[/PARENT_REVISION]
-// Creates a new workspace, optionally branching off of a specified version.
-// Response: ""
-func (h Configuration) Post( path []string ) (response string, code int) {
- malformed := "Malformed request: "+to_string(path)+
- "; must be /workspaces[/PARENT_REVISION]"
- if len(path) < 1 || len(path) > 2 || path[0] != WORKSPACES {
- return malformed, 400
- }
+
+func (h Configuration) PutWorkspace( body string, tag string ) (newRev string) {
json := new(JsonSerializer)
- if len(path) == 2 {
- parent_revision := path[1]
- h.repo.Branch( parent_revision )
- h.workspace = *new(StoreMap)
- json.Deserialize(h.repo.Get( parent_revision ), &h.workspace)
- }
- return "", 200
+ return h.repo.Commit( "config.json", json.Serialize( h.workspace ), tag )
+}
+
+
+func (h Configuration) PutComponent( component_name string, body string ) {
+ var config Store
+ json := new( JsonSerializer )
+ json.Deserialize( body, &config )
+ reduce( h.workspace, strings.Split(component_name,"/",-1), &config )
+ h.workspace[ component_name ] = config
+}
+
+
+
+func (h Configuration) Branch( parent_revision string ) {
+ json := new(JsonSerializer)
+ h.repo.Branch( parent_revision )
+ h.workspace = *new(StoreMap)
+ json.Deserialize(h.repo.Get( parent_revision ), &h.workspace)
}
@@ 206,8 114,9 @@ func keys( m StoreMap ) []string {
func reduce( workspace StoreMap, path []string, config *Store ) {
length := len(path)
if length == 1 { return }
- parent := workspace[ to_string(path[0:length-2]) ]
- parent = expand( workspace, path, parent )
+ parent_path := path[0:length-2]
+ parent := workspace[ to_string(parent_path) ]
+ parent = expand( workspace, parent_path, parent )
for k,v := range parent.attrs {
if v == config.attrs[k] {
config.attrs[k] = "", false
@@ 7,37 7,161 @@ import (
"log"
"flag"
"strconv"
+ "os"
)
///////////////////////////////////////////////////////////////////////////////
// REST server ////////////////////////////////////////////////////////////////
+
+const (
+ Workspaces = "workspaces"
+ Components = "components"
+ Usage = `<pre>
+ GET retrieves a component config
+
+ /components
+ Retrieves a list of all components
+ Response: JSON - List of strings of component names
+
+ /components/COMPONENT[/..][?rev=REV]
+ Retrieves a configuration for a single component, optionally by version.
+ Response: JSON - JSON serialization of component.
+
+ /workspaces[?limit=COUNT]
+ Retrieves a list of all workspace revisions and tags. If limit is supplied, only
+ the last COUNT revisions are returned; all tags and branch names are always
+ included
+ Response: JSON - A hash containing "revisions" List, "tags" List,
+ and "branches" List
+
+ /workspaces/REV
+ Retrieves an entire workspace (all components) by revision or tag. Use 0 for
+ the editable workspace.
+ Response: JSON - JSON serialization of the workspace
+
+ - PUT stores a unit
+
+ /components/COMPONENT[/COMPONENT]
+ Stores a component in the workspace. This *replaces* the current component
+ definition. The inheritance hierarchy is defined in the payload, which
+ should be a JSON encoding of a hash of attributes and parents.
+ Response: ""
+
+ /workspaces[?tag=TAGNAME]
+ Checks in the workspace
+ Response: revision number created
+
+ POST creates a new component or revision
+
+ /workspaces[/PARENT_REVISION]
+ Creates a new workspace, optionally branching off of a specified version.
+ Response: ""
+
+ </pre>`
+)
+
func (h Configuration) ServeHTTP( response http.ResponseWriter, request *http.Request ) {
+ // Get the domain (workspaces or components) and the path name
path := strings.Split( request.URL.Path, "/", -1 )
- if len(path) > 0 { path = path[1:] }
+ var domain, component string
+ if len(path) > 0 {
+ domain = path[1]
+ if len(path) > 1 {
+ component = strings.Join( path[2:], "/" )
+ } else {
+ component = ""
+ }
+ }
+ if domain != Workspaces && domain != Components {
+ malformed := "<html>"+
+ " <head>"+
+ " <title>Malformed request</title>"+
+ " </head>"+
+ " <body>"+
+ " <h1>Malformed request</h1>"+
+ " <p/>"+
+ " Request: <pre>"+request.URL.String()+"</pre>"+
+ " <p/>"+
+ " <h2>Usage</h2>"+
+ " <p/>"+
+ Usage +
+ " </body>"+
+ "</html>"
+ response.Header().Set("Content-Type", "text/html")
+ response.WriteHeader( http.StatusBadRequest )
+ response.Write([]byte(malformed))
+ return
+ }
+
values := request.URL.Query()
- params := make( map[string]string, len(values) )
- for k,_ := range values {
- params[k] = values.Get(k)
- }
- var payload string
- var code int
+
+ var payload interface{}
+ code := http.StatusOK
switch request.Method {
- case "GET": payload, code = h.Get( path, params )
+ case "GET":
+ var err os.Error
+ malformed := "Malformed request: "+to_string(path)+
+ "; must be /components[/COMPONENT[/..][?rev=REV]] or /workspaces[/REV] or "+
+ "/workspaces[?limit=COUNT]"
+ if component == "" {
+ if domain == Workspaces {
+ limitStr := values.Get("limit")
+ var limit int
+ limit,err = strconv.Atoi(limitStr)
+ payload = h.GetWorkspaces( limit )
+ } else {
+ payload = h.GetComponents( )
+ }
+ } else {
+ if domain == Workspaces {
+ payload = h.GetWorkspace( component )
+ } else {
+ rev := values.Get("rev")
+ payload = h.GetComponent( component, rev )
+ }
+ }
+ if err != nil {
+ payload = malformed
+ code = http.StatusBadRequest
+ }
+
case "PUT":
+ //malformed := "Malformed request: "+to_string(path)+
+ // "; must be /components/COMPONENT[/..] or /workspaces"
var buffer bytes.Buffer
buffer.ReadFrom( request.Body )
- payload, code = h.Put( path, params, buffer.String() )
- case "POST": payload, code = h.Post( path )
- case "DELETE": payload, code = h.Delete( path )
- default: payload, code = "Unhandled rquest type: "+request.Method, 400
+ body := buffer.String()
+ if domain == Workspaces {
+ tag := values.Get("tag")
+ payload = h.PutWorkspace( body, tag )
+ } else {
+ h.PutComponent( component, body )
+ }
+
+ case "POST":
+ //malformed := "Malformed request: "+to_string(path)+
+ // "; must be /workspaces[/PARENT_REVISION]"
+ payload = ""
+ h.Branch( component )
+
+ case "DELETE":
+ payload, code = "Not yet implemented", http.StatusNotImplemented
+
+ default:
+ payload, code = "Unhandled rquest type: "+request.Method, 400
+
}
- respond( response, payload, code )
-}
-
-func respond( response http.ResponseWriter, payload string, status int ) {
- response.Header().Set("Content-Type", "text/json")
- response.Write([]byte(payload))
+ var serial Serializer
+ var content_type string
+ switch values.Get("format") {
+ case "props" : serial, content_type = new(PropsSerializer), "text/json"
+ default : serial, content_type = new(JsonSerializer), "text/plain"
+ }
+ retVal := serial.Serialize( payload )
+ response.Header().Set("Content-Type", content_type)
+ response.WriteHeader(code)
+ response.Write([]byte(retVal))
}
func main() {