different approch for complex multidimensional structures

This commit is contained in:
Jan Halfar 2014-10-02 23:32:43 +02:00
parent 8a9c1f321f
commit c5f97d80c9
9 changed files with 160 additions and 330 deletions

View File

@ -1,4 +1,4 @@
Content2Go Server Content Server
=========== ===========
Serves content tree structures very quickly through a json socket api Serves content tree structures very quickly through a json socket api
@ -14,22 +14,19 @@ It's up to you how you use it and which data you want to export to the server. O
All you have to do is to provide a tree of content nodes as a JSON encoded RepoNode. All you have to do is to provide a tree of content nodes as a JSON encoded RepoNode.
| Attribute | Type | Usage | | Attribute | Type | Usage |
| ------------- |:-------------:| -----:| | ------------- |:----------------------:| -----:|
| Id | string | unique id to identify the node | | Id | string | unique id to identify the node |
| MimeType | string | mime-type of the node, e.g. text/html, image/png, ... | | MimeType | string | mime-type of the node, e.g. text/html, image/png, ... |
| LinkIds | map[string]map[string]string | (symbolic) link/alias to another node | | LinkId | string | (symbolic) link/alias to another node |
| Handler | string | define a handler to easily control the output rendering | | Groups | []string | access control |
| Regions | []string | define regions for multi-market websites | | URI | string | a map of unique URIs for each region and language to resolve and link to the node |
| Groups | []string | access control | | Name | string | a name for this node in every region and language |
| States | []string | server states | | Hidden | bool | hide in menu specific for region and language |
| URIs | map[string]map[string]string | a map of unique URIs for each region and language to resolve and link to the node | | DestinationId | string | alias or symlink handling |
| Names | map[string]map[string]string | a name for this node in every region and language | | Data | map[string]interface{} | payload data |
| Hidden | map[string]map[string]bool | hide in menu specific for region and language | | Nodes | map[string]*RepoNode | child nodes |
| DestinationIds | map[string]map[string]string | alias or symlink handling | | Index | []string | contains the order of ou nodes|
| Data | map[string]interface{} | payload data |
| Nodes | map[string]*RepoNode | child nodes |
| Index | []string | ??? |
### Tips ### Tips
@ -43,4 +40,3 @@ All you have to do is to provide a tree of content nodes as a JSON encoded RepoN
## Request Data ## Request Data
There is a PHP Proxy implementation for foomo in Foomo.ContentServer. Feel free to use it or to implement your own proxy in the language you love. The API should be easily to implement in every other framework and language, too. There is a PHP Proxy implementation for foomo in Foomo.ContentServer. Feel free to use it or to implement your own proxy in the language you love. The API should be easily to implement in every other framework and language, too.

View File

@ -1,71 +0,0 @@
package server
import (
"fmt"
"github.com/foomo/contentserver/server/log"
"github.com/foomo/contentserver/server/repo"
"github.com/foomo/contentserver/server/requests"
"github.com/foomo/contentserver/server/utils"
"net/http"
"strconv"
"strings"
)
func contentHandler(w http.ResponseWriter, r *http.Request) {
request := requests.NewContent()
utils.PopulateRequest(r, request)
utils.JsonResponse(w, contentRepo.GetContent(request))
}
func uriHandler(w http.ResponseWriter, r *http.Request) {
parts := strings.Split(r.RequestURI, "/")
if len(parts) == 5 {
utils.JsonResponse(w, contentRepo.GetURI(parts[2], parts[3], parts[4]))
} else {
wtfHandler(w, r)
}
}
func wtfHandler(w http.ResponseWriter, r *http.Request) {
msg := "unhandled request: " + r.RequestURI
fmt.Fprint(w, msg, "\n")
log.Error(msg)
}
func update() interface{} {
contentRepo.Update()
return contentRepo.Directory
}
func commandHandler(w http.ResponseWriter, r *http.Request) {
parts := strings.Split(r.RequestURI, "/")
switch true {
case true == (len(parts) > 1):
switch parts[2] {
case "update":
utils.JsonResponse(w, update())
return
default:
help := make(map[string]interface{})
help["input"] = parts[2]
help["commands"] = []string{"update", "help", "content"}
utils.JsonResponse(w, help)
}
default:
wtfHandler(w, r)
}
}
func Run(addr string, serverUrl string) {
log.Notice("starting content server")
log.Notice(" loading content from " + serverUrl)
contentRepo = repo.NewRepo(serverUrl)
contentRepo.Update()
log.Notice(" loaded " + strconv.Itoa(len(contentRepo.Directory)) + " items")
http.HandleFunc("/content", contentHandler)
http.HandleFunc("/uri/", uriHandler)
http.HandleFunc("/cmd/", commandHandler)
http.HandleFunc("/", wtfHandler)
log.Notice(" starting service on " + addr)
http.ListenAndServe(addr, nil)
}

View File

@ -6,39 +6,21 @@ import (
) )
type RepoNode struct { type RepoNode struct {
Id string `json:"id"` // unique identifier - it is your responsibility, that they are unique Id string `json:"id"` // unique identifier - it is your responsibility, that they are unique
MimeType string `json:"mimeType"` // well a mime type http://www.ietf.org/rfc/rfc2046.txt MimeType string `json:"mimeType"` // well a mime type http://www.ietf.org/rfc/rfc2046.txt
LinkIds map[string]map[string]string `json:"linkIds"` // (symbolic) link/alias to another node LinkId string `json:"linkIds"` // (symbolic) link/alias to another node
Handler string `json:"handler"` // that information is for you Groups []string `json:"groups"` // which groups have access to the node, if empty everybody has access to it
Regions []string `json:"regions"` // in what regions is this node available, if empty it will be accessible everywhere URI string `json:"URI"`
Groups []string `json:"groups"` // which groups have access to the node, if empty everybody has access to it Name string `json:"names"`
States []string `json:"states"` // in which states is this node valid, if empty => in all of them Hidden bool `json:"hidden"` // hidden in content.nodes, but can still be resolved when being directly addressed
URIs map[string]map[string]string `json:"URIs"` DestinationId string `json:"destinationId"` // if a node does not have any content like a folder the destinationIds can point to nodes that do aka. the first displayable child node
Names map[string]map[string]string `json:"names"` Data map[string]interface{} `json:"data"` // what ever you want to stuff into it - the payload you want to attach to a node
Hidden map[string]map[string]bool `json:"hidden"` // hidden in content.nodes, but can still be resolved when being directly addressed Nodes map[string]*RepoNode `json:"nodes"` // child nodes
DestinationIds map[string]map[string]string `json:"destinationIds"` // if a node does not have any content like a folder the destinationIds can point to nodes that do aka. the first displayable child node Index []string `json:"index"` // defines the order of the child nodes
Data map[string]interface{} `json:"data"` // what ever you want to stuff into it - the payload you want to attach to a node parent *RepoNode // parent node - helps to resolve a path / bread crumb
Nodes map[string]*RepoNode `json:"nodes"` // child nodes
Index []string `json:"index"` // defines the order of the child nodes
parent *RepoNode // parent node - helps to resolve a path / bread crumb
// published from - to is going to be an array of fromTos // published from - to is going to be an array of fromTos
} }
func (node *RepoNode) GetLanguageAndRegionForURI(URI string) (resolved bool, region string, language string) {
for possibleRegion, URIs := range node.URIs {
for possibleLanguage, regionLangURI := range URIs {
if regionLangURI == URI {
resolved = true
region = possibleRegion
language = possibleLanguage
return
}
}
}
resolved = false
return
}
func (node *RepoNode) WireParents() { func (node *RepoNode) WireParents() {
for _, childNode := range node.Nodes { for _, childNode := range node.Nodes {
childNode.parent = node childNode.parent = node
@ -56,29 +38,7 @@ func (node *RepoNode) InPath(path []*Item) bool {
return false return false
} }
func (node *RepoNode) InState(state string) bool { func (node *RepoNode) GetPath() []*Item {
if len(node.States) == 0 {
return true
} else {
for _, nodeState := range node.States {
if state == nodeState {
return true
}
}
return false
}
}
func (node *RepoNode) InRegion(region string) bool {
for _, nodeRegion := range node.Regions {
if nodeRegion == region {
return true
}
}
return false
}
func (node *RepoNode) GetPath(region string, language string) []*Item {
parentNode := node.parent parentNode := node.parent
pathLength := 0 pathLength := 0
for parentNode != nil { for parentNode != nil {
@ -89,18 +49,18 @@ func (node *RepoNode) GetPath(region string, language string) []*Item {
i := 0 i := 0
path := make([]*Item, pathLength) path := make([]*Item, pathLength)
for parentNode != nil { for parentNode != nil {
path[i] = parentNode.ToItem(region, language, []string{}) path[i] = parentNode.ToItem([]string{})
parentNode = parentNode.parent parentNode = parentNode.parent
i++ i++
} }
return path return path
} }
func (node *RepoNode) ToItem(region string, language string, dataFields []string) *Item { func (node *RepoNode) ToItem(dataFields []string) *Item {
item := NewItem() item := NewItem()
item.Id = node.Id item.Id = node.Id
item.Name = node.GetName(region, language) item.Name = node.Name
item.URI = node.URIs[region][language] //uri //repo.GetURI(region, language, node.Id) item.URI = node.URI
for _, dataField := range dataFields { for _, dataField := range dataFields {
if data, ok := node.Data[dataField]; ok { if data, ok := node.Data[dataField]; ok {
item.Data[dataField] = data item.Data[dataField] = data
@ -118,21 +78,6 @@ func (node *RepoNode) AddNode(name string, childNode *RepoNode) *RepoNode {
return node return node
} }
func (node *RepoNode) IsHidden(region string, language string) bool {
if regionMap, ok := node.Hidden[region]; ok {
if languageHidden, ok := regionMap[language]; ok {
return languageHidden
} else {
return false
}
}
return false
}
func (node *RepoNode) GetName(region string, language string) string {
return node.Names[region][language]
}
func (node *RepoNode) IsOneOfTheseMimeTypes(mimeTypes []string) bool { func (node *RepoNode) IsOneOfTheseMimeTypes(mimeTypes []string) bool {
if len(mimeTypes) == 0 { if len(mimeTypes) == 0 {
return true return true
@ -164,10 +109,7 @@ func (node *RepoNode) CanBeAccessedByGroups(groups []string) bool {
func (node *RepoNode) PrintNode(id string, level int) { func (node *RepoNode) PrintNode(id string, level int) {
prefix := strings.Repeat(INDENT, level) prefix := strings.Repeat(INDENT, level)
fmt.Printf("%s %s:\n", prefix, id) fmt.Printf("%s %s %s:\n", prefix, id, node.Name)
for lang, name := range node.Names {
fmt.Printf("%s %s: %s\n", prefix+INDENT, lang, name)
}
for key, childNode := range node.Nodes { for key, childNode := range node.Nodes {
childNode.PrintNode(key, level+1) childNode.PrintNode(key, level+1)
} }

View File

@ -7,16 +7,15 @@ const (
) )
type SiteContent struct { type SiteContent struct {
Status int `json:"status"` Status int `json:"status"`
URI string `json:"URI"` URI string `json:"URI"`
Region string `json:"region"` Dimension string `json:"dimension"`
Language string `json:"language"` MimeType string `json:"mimeType"`
Handler string `json:"handler"` Item *Item `json:"item"`
Item *Item `json:"item"` Data interface{} `json:"data"`
Data interface{} `json:"data"` Path []*Item `json:"path"`
Path []*Item `json:"path"` URIs map[string]string `json:"URIs"`
URIs map[string]string `json:"URIs"` Nodes map[string]*Node `json:"nodes"`
Nodes map[string]*Node `json:"nodes"`
} }
func NewSiteContent() *SiteContent { func NewSiteContent() *SiteContent {

View File

@ -12,38 +12,49 @@ import (
"time" "time"
) )
type Dimension struct {
Directory map[string]*content.RepoNode
URIDirectory map[string]*content.RepoNode
Node *content.RepoNode
}
type RepoDimension struct {
Dimension string
Node *content.RepoNode
}
type Repo struct { type Repo struct {
server string server string
Regions []string Directory map[string]*Dimension
Languages []string updateChannel chan *RepoDimension
Directory map[string]*content.RepoNode
URIDirectory map[string]*content.RepoNode
Node *content.RepoNode
updateChannel chan *content.RepoNode
updateDoneChannel chan error updateDoneChannel chan error
} }
func NewRepo(server string) *Repo { func NewRepo(server string) *Repo {
log.Notice("creating new repo for " + server) log.Notice("creating new repo for " + server)
repo := new(Repo) repo := new(Repo)
repo.Directory = make(map[string]*Dimension)
repo.server = server repo.server = server
repo.updateChannel = make(chan *content.RepoNode) repo.updateChannel = make(chan *RepoDimension)
repo.updateDoneChannel = make(chan error) repo.updateDoneChannel = make(chan error)
go func() { go func() {
select { select {
case newNode := <-repo.updateChannel: case newDimension := <-repo.updateChannel:
repo.updateDoneChannel <- repo.load(newNode) repo.updateDoneChannel <- repo.load(newDimension.Dimension, newDimension.Node)
} }
}() }()
return repo return repo
} }
func (repo *Repo) Load(newNode *content.RepoNode) error { func (repo *Repo) Load(dimension string, node *content.RepoNode) error {
repo.updateChannel <- newNode repo.updateChannel <- &RepoDimension{
Dimension: dimension,
Node: node,
}
return <-repo.updateDoneChannel return <-repo.updateDoneChannel
} }
func (repo *Repo) load(newNode *content.RepoNode) error { func (repo *Repo) load(dimension string, newNode *content.RepoNode) error {
newNode.WireParents() newNode.WireParents()
newDirectory := make(map[string]*content.RepoNode) newDirectory := make(map[string]*content.RepoNode)
newURIDirectory := make(map[string]*content.RepoNode) newURIDirectory := make(map[string]*content.RepoNode)
@ -56,104 +67,81 @@ func (repo *Repo) load(newNode *content.RepoNode) error {
if err != nil { if err != nil {
return err return err
} }
repo.Node = newNode repo.Directory[dimension] = &Dimension{
repo.Directory = newDirectory Node: newNode,
repo.URIDirectory = newURIDirectory Directory: newDirectory,
URIDirectory: newURIDirectory,
}
return nil return nil
} }
func (repo *Repo) ResolveContent(state string, URI string) (resolved bool, resolvedURI string, region string, language string, repoNode *content.RepoNode) { func (repo *Repo) ResolveContent(dimensions []string, URI string) (resolved bool, resolvedURI string, resolvedDimension string, repoNode *content.RepoNode) {
parts := strings.Split(URI, content.PATH_SEPARATOR) parts := strings.Split(URI, content.PATH_SEPARATOR)
log.Debug("repo.ResolveContent: " + URI) log.Debug("repo.ResolveContent: " + URI)
for i := len(parts); i > -1; i-- { for _, dimension := range dimensions {
testURI := strings.Join(parts[0:i], content.PATH_SEPARATOR) if d, ok := repo.Directory[dimension]; ok {
testURIKey := uriKeyForState(state, testURI) for i := len(parts); i > -1; i-- {
log.Debug(" testing" + testURIKey) testURI := strings.Join(parts[0:i], content.PATH_SEPARATOR)
if repoNode, ok := repo.URIDirectory[testURIKey]; ok { log.Debug(" testing" + testURI)
resolved = true if repoNode, ok := d.URIDirectory[testURI]; ok {
_, region, language := repoNode.GetLanguageAndRegionForURI(testURI) resolved = true
log.Debug(" => " + testURIKey) log.Debug(" => " + testURI)
log.Debug(" destination " + fmt.Sprint(repoNode.DestinationIds)) log.Debug(" destination " + fmt.Sprint(repoNode.DestinationId))
// check this one if len(repoNode.DestinationId) > 0 {
// repoNode = repo.Directory[repoNode.DestinationIds] if destionationNode, destinationNodeOk := d.Directory[repoNode.DestinationId]; destinationNodeOk {
repoNode = destionationNode
if languageDestinations, regionOk := repoNode.DestinationIds[region]; regionOk { }
// this check should happen, when updating the repo
log.Debug(" there is a destionation map for this one " + fmt.Sprint(languageDestinations))
if languageDestination, destinationOk := languageDestinations[language]; destinationOk {
if destinationNode, destinationNodeOk := repo.Directory[languageDestination]; destinationNodeOk {
repoNode = destinationNode
} else {
log.Debug(" could not resolve this destinationId : " + languageDestination)
} }
return true, testURI, dimension, repoNode
} else {
log.Debug(" => !" + testURI)
resolved = false
} }
} }
return true, testURI, region, language, repoNode
} else {
log.Debug(" => !" + testURI)
resolved = false
} }
} }
return return
} }
func (repo *Repo) GetItemMap(id string, dataFields []string) map[string]map[string]*content.Item { func (repo *Repo) GetURIs(dimension string, ids []string) map[string]string {
itemMap := make(map[string]map[string]*content.Item)
if repoNode, ok := repo.Directory[id]; ok {
for region, languageURIs := range repoNode.URIs {
itemMap[region] = make(map[string]*content.Item)
for language, URI := range languageURIs {
log.Debug(fmt.Sprintf("region :%s language :%s URI: %s", region, language, URI))
itemMap[region][language] = repoNode.ToItem(region, language, dataFields)
}
}
} else {
log.Warning("GetItemMapForAllRegionsAndLanguages invalid id " + id)
}
return itemMap
}
func (repo *Repo) GetURIs(region string, language string, ids []string) map[string]string {
uris := make(map[string]string) uris := make(map[string]string)
for _, id := range ids { for _, id := range ids {
uris[id] = repo.GetURI(region, language, id) uris[id] = repo.GetURI(dimension, id)
} }
return uris return uris
} }
func (repo *Repo) GetURIForNode(region string, language string, repoNode *content.RepoNode) string { func (repo *Repo) GetURIForNode(dimension string, repoNode *content.RepoNode) string {
if len(repoNode.LinkIds) == 0 { if len(repoNode.LinkId) == 0 {
languageURIs, regionExists := repoNode.URIs[region] return repoNode.URI
if regionExists {
languageURI, languageURIExists := languageURIs[language]
if languageURIExists {
return languageURI
}
}
return ""
} else { } else {
return repo.GetURI(region, language, repoNode.LinkIds[region][language]) linkedNode, ok := repo.Directory[dimension].Directory[repoNode.LinkId]
if ok {
return repo.GetURIForNode(dimension, linkedNode)
} else {
return ""
}
} }
} }
func (repo *Repo) GetURI(region string, language string, id string) string { func (repo *Repo) GetURI(dimension string, id string) string {
repoNode, ok := repo.Directory[id] repoNode, ok := repo.Directory[dimension].Directory[id]
if ok { if ok {
return repo.GetURIForNode(region, language, repoNode) return repo.GetURIForNode(dimension, repoNode)
} }
return "" return ""
} }
func (repo *Repo) GetNode(repoNode *content.RepoNode, expanded bool, mimeTypes []string, path []*content.Item, level int, state string, groups []string, region string, language string, dataFields []string) *content.Node { func (repo *Repo) GetNode(repoNode *content.RepoNode, expanded bool, mimeTypes []string, path []*content.Item, level int, groups []string, dataFields []string) *content.Node {
node := content.NewNode() node := content.NewNode()
node.Item = repoNode.ToItem(region, language, dataFields) node.Item = repoNode.ToItem(dataFields)
log.Debug("repo.GetNode: " + repoNode.Id) log.Debug("repo.GetNode: " + repoNode.Id)
for _, childId := range repoNode.Index { for _, childId := range repoNode.Index {
childNode := repoNode.Nodes[childId] childNode := repoNode.Nodes[childId]
if (level == 0 || expanded || !expanded && childNode.InPath(path)) && childNode.InState(state) && !childNode.IsHidden(region, language) && childNode.CanBeAccessedByGroups(groups) && childNode.IsOneOfTheseMimeTypes(mimeTypes) && childNode.InRegion(region) { if (level == 0 || expanded || !expanded && childNode.InPath(path)) && !childNode.Hidden && childNode.CanBeAccessedByGroups(groups) && childNode.IsOneOfTheseMimeTypes(mimeTypes) {
node.Nodes[childId] = repo.GetNode(childNode, expanded, mimeTypes, path, level+1, state, groups, region, language, dataFields) node.Nodes[childId] = repo.GetNode(childNode, expanded, mimeTypes, path, level+1, groups, dataFields)
node.Index = append(node.Index, childId) node.Index = append(node.Index, childId)
} }
} }
@ -165,8 +153,8 @@ func (repo *Repo) GetNodes(r *requests.Nodes) map[string]*content.Node {
path := make([]*content.Item, 0) path := make([]*content.Item, 0)
for nodeName, nodeRequest := range r.Nodes { for nodeName, nodeRequest := range r.Nodes {
log.Debug(" adding node " + nodeName + " " + nodeRequest.Id) log.Debug(" adding node " + nodeName + " " + nodeRequest.Id)
if treeNode, ok := repo.Directory[nodeRequest.Id]; ok { if treeNode, ok := repo.Directory[nodeRequest.Dimension].Directory[nodeRequest.Id]; ok {
nodes[nodeName] = repo.GetNode(treeNode, nodeRequest.Expand, nodeRequest.MimeTypes, path, 0, r.Env.State, r.Env.Groups, r.Env.Defaults.Region, r.Env.Defaults.Language, nodeRequest.DataFields) nodes[nodeName] = repo.GetNode(treeNode, nodeRequest.Expand, nodeRequest.MimeTypes, path, 0, r.Env.Groups, nodeRequest.DataFields)
} else { } else {
log.Warning("you are requesting an invalid tree node for " + nodeName + " : " + nodeRequest.Id) log.Warning("you are requesting an invalid tree node for " + nodeName + " : " + nodeRequest.Id)
} }
@ -177,28 +165,26 @@ func (repo *Repo) GetNodes(r *requests.Nodes) map[string]*content.Node {
func (repo *Repo) GetContent(r *requests.Content) *content.SiteContent { func (repo *Repo) GetContent(r *requests.Content) *content.SiteContent {
log.Debug("repo.GetContent: " + r.URI) log.Debug("repo.GetContent: " + r.URI)
c := content.NewSiteContent() c := content.NewSiteContent()
resolved, resolvedURI, region, language, node := repo.ResolveContent(r.Env.State, r.URI) resolved, resolvedURI, resolvedDimension, node := repo.ResolveContent(r.Env.Dimensions, r.URI)
if resolved { if resolved {
log.Notice("200 for " + r.URI) log.Notice("200 for " + r.URI)
// forbidden ?! // forbidden ?!
c.Region = region
c.Language = language
c.Status = content.STATUS_OK c.Status = content.STATUS_OK
c.Handler = node.Handler c.MimeType = node.MimeType
c.Dimension = resolvedDimension
c.URI = resolvedURI c.URI = resolvedURI
c.Item = node.ToItem(region, language, []string{}) c.Item = node.ToItem([]string{})
c.Path = node.GetPath(region, language) c.Path = node.GetPath()
c.Data = node.Data c.Data = node.Data
} else { } else {
log.Notice("404 for " + r.URI) log.Notice("404 for " + r.URI)
c.Status = content.STATUS_NOT_FOUND c.Status = content.STATUS_NOT_FOUND
region = r.Env.Defaults.Region c.Dimension = r.Env.Dimensions[0]
language = r.Env.Defaults.Language
} }
for treeName, treeRequest := range r.Nodes { for treeName, treeRequest := range r.Nodes {
log.Debug(" adding tree " + treeName + " " + treeRequest.Id) log.Debug(" adding tree " + treeName + " " + treeRequest.Id)
if treeNode, ok := repo.Directory[treeRequest.Id]; ok { if treeNode, ok := repo.Directory[resolvedDimension].Directory[treeRequest.Id]; ok {
c.Nodes[treeName] = repo.GetNode(treeNode, treeRequest.Expand, treeRequest.MimeTypes, c.Path, 0, r.Env.State, r.Env.Groups, region, language, treeRequest.DataFields) c.Nodes[treeName] = repo.GetNode(treeNode, treeRequest.Expand, treeRequest.MimeTypes, c.Path, 0, r.Env.Groups, treeRequest.DataFields)
} else { } else {
log.Warning("you are requesting an invalid tree node for " + treeName + " : " + treeRequest.Id) log.Warning("you are requesting an invalid tree node for " + treeName + " : " + treeRequest.Id)
} }
@ -206,10 +192,11 @@ func (repo *Repo) GetContent(r *requests.Content) *content.SiteContent {
return c return c
} }
/*
func (repo *Repo) GetRepo() *content.RepoNode { func (repo *Repo) GetRepo() *content.RepoNode {
return repo.Node return repo.Node
} }
*/
func uriKeyForState(state string, uri string) string { func uriKeyForState(state string, uri string) string {
return state + "-" + uri return state + "-" + uri
} }
@ -222,30 +209,10 @@ func builDirectory(dirNode *content.RepoNode, directory map[string]*content.Repo
} }
directory[dirNode.Id] = dirNode directory[dirNode.Id] = dirNode
//todo handle duplicate uris //todo handle duplicate uris
for _, languageURIs := range dirNode.URIs { if _, thereIsAnExistingUriNode := uRIDirectory[dirNode.URI]; thereIsAnExistingUriNode {
for _, uri := range languageURIs { return errors.New("duplicate node with uri: " + dirNode.URI)
log.Debug(" uri: " + uri + " => Id: " + dirNode.Id)
if len(dirNode.States) == 0 {
key := uriKeyForState("", uri)
existingNode, ok = uRIDirectory[key]
if ok {
return errors.New("duplicate node with uri: " + uri)
}
uRIDirectory[key] = dirNode
} else {
for _, state := range dirNode.States {
key := uriKeyForState(state, uri)
existingNode, ok = uRIDirectory[key]
if ok {
return errors.New("dupicate node with uri: " + uri + " for state " + state)
}
uRIDirectory[key] = dirNode
}
}
}
} }
uRIDirectory[dirNode.URI] = dirNode
for _, childNode := range dirNode.Nodes { for _, childNode := range dirNode.Nodes {
err := builDirectory(childNode, directory, uRIDirectory) err := builDirectory(childNode, directory, uRIDirectory)
if err != nil { if err != nil {
@ -256,15 +223,12 @@ func builDirectory(dirNode *content.RepoNode, directory map[string]*content.Repo
} }
func wireAliases(directory map[string]*content.RepoNode) error { func wireAliases(directory map[string]*content.RepoNode) error {
// validation ?!
for _, repoNode := range directory { for _, repoNode := range directory {
if len(repoNode.LinkIds) > 0 { if len(repoNode.LinkId) > 0 {
for region, languages := range repoNode.LinkIds { if destinationNode, ok := directory[repoNode.LinkId]; ok {
for language, linkId := range languages { repoNode.URI = destinationNode.URI
if destinationNode, ok := directory[linkId]; ok { } else {
repoNode.URIs[region][language] = destinationNode.URIs[region][language] return errors.New("that link id point nowhere " + repoNode.LinkId + " from " + repoNode.Id)
}
}
} }
} }
} }
@ -273,16 +237,18 @@ func wireAliases(directory map[string]*content.RepoNode) error {
func (repo *Repo) Update() *responses.Update { func (repo *Repo) Update() *responses.Update {
updateResponse := responses.NewUpdate() updateResponse := responses.NewUpdate()
newNode := content.NewRepoNode() newNodes := make(map[string]*content.RepoNode) //content.NewRepoNode()
startTimeRepo := time.Now() startTimeRepo := time.Now()
ok, err := utils.Get(repo.server, newNode) ok, err := utils.Get(repo.server, newNodes)
updateResponse.Stats.RepoRuntime = time.Now().Sub(startTimeRepo).Seconds() updateResponse.Stats.RepoRuntime = time.Now().Sub(startTimeRepo).Seconds()
startTimeOwn := time.Now() startTimeOwn := time.Now()
updateResponse.Success = ok updateResponse.Success = ok
if ok { if ok {
repo.Load(newNode) for dimension, newNode := range newNodes {
updateResponse.Stats.NumberOfNodes = len(repo.Directory) repo.Load(dimension, newNode)
updateResponse.Stats.NumberOfURIs = len(repo.URIDirectory) updateResponse.Stats.NumberOfNodes = len(repo.Directory[dimension].Directory)
updateResponse.Stats.NumberOfURIs = len(repo.Directory[dimension].URIDirectory)
}
} else { } else {
log.Error(fmt.Sprintf("update error: %", err)) log.Error(fmt.Sprintf("update error: %", err))
updateResponse.ErrorMessage = fmt.Sprintf("%", err) updateResponse.ErrorMessage = fmt.Sprintf("%", err)

View File

@ -1,8 +1,8 @@
package requests package requests
type Content struct { type Content struct {
Env *Env `json:"env"` Env *Env `json:"env"`
URI string URI string `json:"URI"`
Nodes map[string]*Node `json:"nodes"` Nodes map[string]*Node `json:"nodes"`
} }

View File

@ -1,19 +1,14 @@
package requests package requests
type Defaults struct {
Region string `json:"region"`
Language string `json:"language"`
}
type Env struct { type Env struct {
Defaults *Defaults `json:"defaults"` Dimensions []string `json:"dimensions"`
Groups []string `json:"groups"` Groups []string `json:"groups"`
State string Data interface{} `json:"data"`
Data interface{} `json:"data"`
} }
type Node struct { type Node struct {
Id string `json:"id"` Id string `json:"id"`
Dimension string `json:"id"`
MimeTypes []string `json:"mimeTypes"` MimeTypes []string `json:"mimeTypes"`
Expand bool `json:"expand"` Expand bool `json:"expand"`
DataFields []string `json:"dataFields"` DataFields []string `json:"dataFields"`

View File

@ -1,9 +1,8 @@
package requests package requests
type URIs struct { type URIs struct {
Ids []string `json:"ids"` Ids []string `json:"ids"`
Region string `json:"region"` Dimension string `json:"dimension"`
Language string `json:"language"`
} }
func NewURIs() *URIs { func NewURIs() *URIs {

View File

@ -32,7 +32,7 @@ func handleSocketRequest(handler string, jsonBuffer []byte) (replyBytes []byte,
getURIRequest := requests.NewURIs() getURIRequest := requests.NewURIs()
jsonErr = json.Unmarshal(jsonBuffer, &getURIRequest) jsonErr = json.Unmarshal(jsonBuffer, &getURIRequest)
log.Debug(" getURIRequest: " + fmt.Sprint(getURIRequest)) log.Debug(" getURIRequest: " + fmt.Sprint(getURIRequest))
uris := contentRepo.GetURIs(getURIRequest.Region, getURIRequest.Language, getURIRequest.Ids) uris := contentRepo.GetURIs(getURIRequest.Dimension, getURIRequest.Ids)
log.Debug(" resolved: " + fmt.Sprint(uris)) log.Debug(" resolved: " + fmt.Sprint(uris))
reply = uris reply = uris
break break
@ -43,13 +43,15 @@ func handleSocketRequest(handler string, jsonBuffer []byte) (replyBytes []byte,
content := contentRepo.GetContent(contentRequest) content := contentRepo.GetContent(contentRequest)
reply = content reply = content
break break
case "getItemMap": /*
itemMapRequest := requests.NewItemMap() case "getItemMap":
jsonErr = json.Unmarshal(jsonBuffer, &itemMapRequest) itemMapRequest := requests.NewItemMap()
log.Debug(" itemMapRequest: " + fmt.Sprint(itemMapRequest)) jsonErr = json.Unmarshal(jsonBuffer, &itemMapRequest)
itemMap := contentRepo.GetItemMap(itemMapRequest.Id, itemMapRequest.DataFields) log.Debug(" itemMapRequest: " + fmt.Sprint(itemMapRequest))
reply = itemMap itemMap := contentRepo.GetItemMap(itemMapRequest.Id, itemMapRequest.DataFields)
break reply = itemMap
break
*/
case "getNodes": case "getNodes":
nodesRequest := requests.NewNodes() nodesRequest := requests.NewNodes()
jsonErr = json.Unmarshal(jsonBuffer, &nodesRequest) jsonErr = json.Unmarshal(jsonBuffer, &nodesRequest)
@ -64,13 +66,15 @@ func handleSocketRequest(handler string, jsonBuffer []byte) (replyBytes []byte,
updateResponse := contentRepo.Update() updateResponse := contentRepo.Update()
reply = updateResponse reply = updateResponse
break break
case "getRepo": /*
repoRequest := requests.NewRepo() case "getRepo":
jsonErr = json.Unmarshal(jsonBuffer, &repoRequest) repoRequest := requests.NewRepo()
log.Debug(" getRepoRequest: " + fmt.Sprint(repoRequest)) jsonErr = json.Unmarshal(jsonBuffer, &repoRequest)
repoResponse := contentRepo.GetRepo() log.Debug(" getRepoRequest: " + fmt.Sprint(repoRequest))
reply = repoResponse repoResponse := contentRepo.GetRepo()
break reply = repoResponse
break
*/
default: default:
err = errors.New(log.Error(" can not handle this one " + handler)) err = errors.New(log.Error(" can not handle this one " + handler))
errorResponse := responses.NewError(1, "unkown handler") errorResponse := responses.NewError(1, "unkown handler")