frontend
This commit is contained in:
parent
9d4af37c22
commit
71cb14c0cd
|
@ -20,7 +20,15 @@ func Generate() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return generateCode(m)
|
if err := generateBackendCode(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := generateFrontendCode(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var allowedTyped = map[string]bool{
|
var allowedTyped = map[string]bool{
|
||||||
|
@ -130,6 +138,8 @@ func createMetadata(a *AnalyzeData) (*Metadata, error) {
|
||||||
field.Type = "object"
|
field.Type = "object"
|
||||||
el := typeNames[f]
|
el := typeNames[f]
|
||||||
field.ElementType = &el
|
field.ElementType = &el
|
||||||
|
} else if !a.Fields[f].NoBool {
|
||||||
|
field.Type = "bool"
|
||||||
} else if a.Fields[f].IsString {
|
} else if a.Fields[f].IsString {
|
||||||
field.Type = "string"
|
field.Type = "string"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,8 @@
|
||||||
package df
|
package df
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"go/format"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"sort"
|
"sort"
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/iancoleman/strcase"
|
|
||||||
"github.com/robertjanetzko/LegendsBrowser2/backend/util"
|
"github.com/robertjanetzko/LegendsBrowser2/backend/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,103 +32,6 @@ type Field struct {
|
||||||
Plus bool
|
Plus bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Field) TypeLine() string {
|
|
||||||
n := f.Name
|
|
||||||
|
|
||||||
if n == "Id" || n == "Name" {
|
|
||||||
n = n + "_"
|
|
||||||
}
|
|
||||||
|
|
||||||
m := ""
|
|
||||||
if f.Multiple {
|
|
||||||
m = "[]"
|
|
||||||
}
|
|
||||||
t := f.Type
|
|
||||||
if f.Type == "array" {
|
|
||||||
t = "[]*" + *f.ElementType
|
|
||||||
}
|
|
||||||
if f.Type == "map" {
|
|
||||||
t = "map[int]*" + *f.ElementType
|
|
||||||
}
|
|
||||||
if f.Type == "object" {
|
|
||||||
t = "*" + *f.ElementType
|
|
||||||
}
|
|
||||||
j := fmt.Sprintf("`json:\"%s\" legend:\"%s\"`", strcase.ToLowerCamel(f.Name), f.Legend)
|
|
||||||
return fmt.Sprintf("%s %s%s %s", n, m, t, j)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f Field) Init(plus bool) string {
|
|
||||||
if !plus && f.Type == "map" {
|
|
||||||
return fmt.Sprintf("obj.%s = make(map[int]*%s)", f.Name, *f.ElementType)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f Field) StartAction(plus bool) string {
|
|
||||||
n := f.Name
|
|
||||||
|
|
||||||
if n == "Id" || n == "Name" {
|
|
||||||
n = n + "_"
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Type == "object" {
|
|
||||||
p := fmt.Sprintf("v, _ := parse%s(d, &t)", *f.ElementType)
|
|
||||||
if !f.Multiple {
|
|
||||||
return fmt.Sprintf("%s\nobj.%s = v", p, n)
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf("%s\nobj.%s = append(obj.%s, v)", p, n, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Type == "array" || f.Type == "map" {
|
|
||||||
gen := fmt.Sprintf("parse%s", *f.ElementType)
|
|
||||||
|
|
||||||
if f.Type == "array" {
|
|
||||||
return fmt.Sprintf("parseArray(d, &obj.%s, %s)", f.Name, gen)
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Type == "map" {
|
|
||||||
if !plus {
|
|
||||||
return fmt.Sprintf("parseMap(d, &obj.%s, %s)", f.Name, gen)
|
|
||||||
} else {
|
|
||||||
gen = fmt.Sprintf("parse%sPlus", *f.ElementType)
|
|
||||||
return fmt.Sprintf("parseMapPlus(d, &obj.%s, %s)", f.Name, gen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Type == "int" || f.Type == "string" {
|
|
||||||
return "data = nil"
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f Field) EndAction() string {
|
|
||||||
n := f.Name
|
|
||||||
|
|
||||||
if n == "Id" || n == "Name" {
|
|
||||||
n = n + "_"
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.Multiple {
|
|
||||||
if f.Type == "int" {
|
|
||||||
return fmt.Sprintf("obj.%s = n(data)", n)
|
|
||||||
} else if f.Type == "string" {
|
|
||||||
return fmt.Sprintf("obj.%s = string(data)", n)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if f.Type == "int" {
|
|
||||||
return fmt.Sprintf("obj.%s = append(obj.%s, n(data))", n, n)
|
|
||||||
} else if f.Type == "string" {
|
|
||||||
return fmt.Sprintf("obj.%s = append(obj.%s, string(data))", n, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f Field) Active(plus bool) bool {
|
func (f Field) Active(plus bool) bool {
|
||||||
if plus && f.Plus {
|
if plus && f.Plus {
|
||||||
return true
|
return true
|
||||||
|
@ -187,164 +82,3 @@ func (f Object) ActiveSubTypes(plus bool) []*ActiveSubType {
|
||||||
|
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
var packageTemplate = template.Must(template.New("").Parse(`// Code generated by legendsbrowser; DO NOT EDIT.
|
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/xml"
|
|
||||||
"strconv"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
{{- range $name, $obj := $.Objects }}
|
|
||||||
type {{ $obj.Name }} struct {
|
|
||||||
{{- range $fname, $field := $obj.Fields }}
|
|
||||||
{{- if not (and (eq $fname "type") (not (not $obj.SubTypes))) }}
|
|
||||||
{{ $field.TypeLine }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if not (not $obj.SubTypes) }}
|
|
||||||
Details any
|
|
||||||
{{- end }}
|
|
||||||
}
|
|
||||||
|
|
||||||
{{- if $obj.Id }}
|
|
||||||
func (x *{{ $obj.Name }}) Id() int { return x.Id_ }
|
|
||||||
{{- end }}
|
|
||||||
{{- if $obj.Named }}
|
|
||||||
func (x *{{ $obj.Name }}) Name() string { return x.Name_ }
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
// Parser
|
|
||||||
|
|
||||||
func n(d []byte) int {
|
|
||||||
v, _ := strconv.Atoi(string(d))
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
{{- range $name, $obj := $.Objects }}
|
|
||||||
{{- range $plus := $.Modes }}
|
|
||||||
func parse{{ $obj.Name }}{{ if $plus }}Plus{{ end }}(d *xml.Decoder, start *xml.StartElement{{ if $plus }}, obj *{{ $obj.Name }}{{ end }}) (*{{ $obj.Name }}, error) {
|
|
||||||
var (
|
|
||||||
{{- if not $plus }}
|
|
||||||
obj = &{{ $obj.Name }}{}
|
|
||||||
{{- end }}
|
|
||||||
data []byte
|
|
||||||
)
|
|
||||||
{{- if $plus }}
|
|
||||||
if obj == nil {
|
|
||||||
obj = &{{ $obj.Name }}{}
|
|
||||||
}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{- range $fname, $field := $obj.Fields }}
|
|
||||||
{{ $field.Init $plus }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
for {
|
|
||||||
tok, err := d.Token()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch t := tok.(type) {
|
|
||||||
case xml.StartElement:
|
|
||||||
switch t.Name.Local {
|
|
||||||
{{- range $fname, $field := $obj.Fields }}
|
|
||||||
{{- if $field.Active $plus }}
|
|
||||||
case "{{ $fname }}":
|
|
||||||
{{ $field.StartAction $plus }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
default:
|
|
||||||
// fmt.Println("unknown field", t.Name.Local)
|
|
||||||
d.Skip()
|
|
||||||
}
|
|
||||||
|
|
||||||
case xml.CharData:
|
|
||||||
data = append(data, t...)
|
|
||||||
|
|
||||||
case xml.EndElement:
|
|
||||||
if t.Name.Local == start.Name.Local {
|
|
||||||
return obj, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t.Name.Local {
|
|
||||||
{{- range $fname, $field := $obj.Fields }}{{- if $field.Active $plus }}
|
|
||||||
case "{{ $fname }}":
|
|
||||||
{{- if and (eq $fname "type") (not (not $obj.SubTypes)) }}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
switch string(data) {
|
|
||||||
{{- range $sub := ($obj.ActiveSubTypes $plus) }}
|
|
||||||
case "{{ $sub.Case }}":
|
|
||||||
{{- if eq 1 (len $sub.Options) }}
|
|
||||||
{{- if not $plus }}
|
|
||||||
obj.Details, err = parse{{ $sub.Name }}(d, start)
|
|
||||||
{{- else }}
|
|
||||||
obj.Details, err = parse{{ $sub.Name }}Plus(d, start, obj.Details.(*{{ $sub.Name }}))
|
|
||||||
{{- end }}
|
|
||||||
{{- else }}
|
|
||||||
switch details := obj.Details.(type) {
|
|
||||||
{{- range $opt := $sub.Options }}
|
|
||||||
case *{{ $opt}}:
|
|
||||||
obj.Details, err = parse{{ $opt }}Plus(d, start, details)
|
|
||||||
{{- end }}
|
|
||||||
default:
|
|
||||||
fmt.Println("unknown subtype option", obj.Details)
|
|
||||||
d.Skip()
|
|
||||||
}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
default:
|
|
||||||
d.Skip()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return obj, nil
|
|
||||||
|
|
||||||
{{- else }}
|
|
||||||
{{ $field.EndAction }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}{{- end }}
|
|
||||||
default:
|
|
||||||
// fmt.Println("unknown field", t.Name.Local)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
`))
|
|
||||||
|
|
||||||
func generateCode(objects *Metadata) error {
|
|
||||||
file, _ := json.MarshalIndent(objects, "", " ")
|
|
||||||
_ = ioutil.WriteFile("model.json", file, 0644)
|
|
||||||
|
|
||||||
f, err := os.Create("../backend/model/model.go")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err = packageTemplate.Execute(&buf, struct {
|
|
||||||
Objects *Metadata
|
|
||||||
Modes []bool
|
|
||||||
}{
|
|
||||||
Objects: objects,
|
|
||||||
Modes: []bool{false, true},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
p, err := format.Source(buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("WARN: could not format source", err)
|
|
||||||
p = buf.Bytes()
|
|
||||||
}
|
|
||||||
_, err = f.Write(p)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,273 @@
|
||||||
|
package df
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"go/format"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/iancoleman/strcase"
|
||||||
|
)
|
||||||
|
|
||||||
|
var backendTemplate = template.Must(template.New("").Parse(`// Code generated by legendsbrowser; DO NOT EDIT.
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"strconv"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
{{- range $name, $obj := $.Objects }}
|
||||||
|
type {{ $obj.Name }} struct {
|
||||||
|
{{- range $fname, $field := $obj.Fields }}
|
||||||
|
{{- if not (and (eq $fname "type") (not (not $obj.SubTypes))) }}
|
||||||
|
{{ $field.TypeLine }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if not (not $obj.SubTypes) }}
|
||||||
|
Details any
|
||||||
|
{{- end }}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{- if $obj.Id }}
|
||||||
|
func (x *{{ $obj.Name }}) Id() int { return x.Id_ }
|
||||||
|
{{- end }}
|
||||||
|
{{- if $obj.Named }}
|
||||||
|
func (x *{{ $obj.Name }}) Name() string { return x.Name_ }
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
// Parser
|
||||||
|
|
||||||
|
func n(d []byte) int {
|
||||||
|
v, _ := strconv.Atoi(string(d))
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
{{- range $name, $obj := $.Objects }}
|
||||||
|
{{- range $plus := $.Modes }}
|
||||||
|
func parse{{ $obj.Name }}{{ if $plus }}Plus{{ end }}(d *xml.Decoder, start *xml.StartElement{{ if $plus }}, obj *{{ $obj.Name }}{{ end }}) (*{{ $obj.Name }}, error) {
|
||||||
|
var (
|
||||||
|
{{- if not $plus }}
|
||||||
|
obj = &{{ $obj.Name }}{}
|
||||||
|
{{- end }}
|
||||||
|
data []byte
|
||||||
|
)
|
||||||
|
{{- if $plus }}
|
||||||
|
if obj == nil {
|
||||||
|
obj = &{{ $obj.Name }}{}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- range $fname, $field := $obj.Fields }}
|
||||||
|
{{ $field.Init $plus }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
for {
|
||||||
|
tok, err := d.Token()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch t := tok.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
switch t.Name.Local {
|
||||||
|
{{- range $fname, $field := $obj.Fields }}
|
||||||
|
{{- if $field.Active $plus }}
|
||||||
|
case "{{ $fname }}":
|
||||||
|
{{ $field.StartAction $plus }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
default:
|
||||||
|
// fmt.Println("unknown field", t.Name.Local)
|
||||||
|
d.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
case xml.CharData:
|
||||||
|
data = append(data, t...)
|
||||||
|
|
||||||
|
case xml.EndElement:
|
||||||
|
if t.Name.Local == start.Name.Local {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t.Name.Local {
|
||||||
|
{{- range $fname, $field := $obj.Fields }}{{- if $field.Active $plus }}
|
||||||
|
case "{{ $fname }}":
|
||||||
|
{{- if and (eq $fname "type") (not (not $obj.SubTypes)) }}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
switch string(data) {
|
||||||
|
{{- range $sub := ($obj.ActiveSubTypes $plus) }}
|
||||||
|
case "{{ $sub.Case }}":
|
||||||
|
{{- if eq 1 (len $sub.Options) }}
|
||||||
|
{{- if not $plus }}
|
||||||
|
obj.Details, err = parse{{ $sub.Name }}(d, start)
|
||||||
|
{{- else }}
|
||||||
|
obj.Details, err = parse{{ $sub.Name }}Plus(d, start, obj.Details.(*{{ $sub.Name }}))
|
||||||
|
{{- end }}
|
||||||
|
{{- else }}
|
||||||
|
switch details := obj.Details.(type) {
|
||||||
|
{{- range $opt := $sub.Options }}
|
||||||
|
case *{{ $opt}}:
|
||||||
|
obj.Details, err = parse{{ $opt }}Plus(d, start, details)
|
||||||
|
{{- end }}
|
||||||
|
default:
|
||||||
|
fmt.Println("unknown subtype option", obj.Details)
|
||||||
|
d.Skip()
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
default:
|
||||||
|
d.Skip()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj, nil
|
||||||
|
|
||||||
|
{{- else }}
|
||||||
|
{{ $field.EndAction }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}{{- end }}
|
||||||
|
default:
|
||||||
|
// fmt.Println("unknown field", t.Name.Local)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
`))
|
||||||
|
|
||||||
|
func generateBackendCode(objects *Metadata) error {
|
||||||
|
file, _ := json.MarshalIndent(objects, "", " ")
|
||||||
|
_ = ioutil.WriteFile("model.json", file, 0644)
|
||||||
|
|
||||||
|
f, err := os.Create("../backend/model/model.go")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err = backendTemplate.Execute(&buf, struct {
|
||||||
|
Objects *Metadata
|
||||||
|
Modes []bool
|
||||||
|
}{
|
||||||
|
Objects: objects,
|
||||||
|
Modes: []bool{false, true},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p, err := format.Source(buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("WARN: could not format source", err)
|
||||||
|
p = buf.Bytes()
|
||||||
|
}
|
||||||
|
_, err = f.Write(p)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Field) TypeLine() string {
|
||||||
|
n := f.Name
|
||||||
|
|
||||||
|
if n == "Id" || n == "Name" {
|
||||||
|
n = n + "_"
|
||||||
|
}
|
||||||
|
|
||||||
|
m := ""
|
||||||
|
if f.Multiple {
|
||||||
|
m = "[]"
|
||||||
|
}
|
||||||
|
t := f.Type
|
||||||
|
if f.Type == "array" {
|
||||||
|
t = "[]*" + *f.ElementType
|
||||||
|
}
|
||||||
|
if f.Type == "map" {
|
||||||
|
t = "map[int]*" + *f.ElementType
|
||||||
|
}
|
||||||
|
if f.Type == "object" {
|
||||||
|
t = "*" + *f.ElementType
|
||||||
|
}
|
||||||
|
j := fmt.Sprintf("`json:\"%s\" legend:\"%s\"`", strcase.ToLowerCamel(f.Name), f.Legend)
|
||||||
|
return fmt.Sprintf("%s %s%s %s", n, m, t, j)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Field) Init(plus bool) string {
|
||||||
|
if !plus && f.Type == "map" {
|
||||||
|
return fmt.Sprintf("obj.%s = make(map[int]*%s)", f.Name, *f.ElementType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Field) StartAction(plus bool) string {
|
||||||
|
n := f.Name
|
||||||
|
|
||||||
|
if n == "Id" || n == "Name" {
|
||||||
|
n = n + "_"
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Type == "object" {
|
||||||
|
p := fmt.Sprintf("v, _ := parse%s(d, &t)", *f.ElementType)
|
||||||
|
if !f.Multiple {
|
||||||
|
return fmt.Sprintf("%s\nobj.%s = v", p, n)
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%s\nobj.%s = append(obj.%s, v)", p, n, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Type == "array" || f.Type == "map" {
|
||||||
|
gen := fmt.Sprintf("parse%s", *f.ElementType)
|
||||||
|
|
||||||
|
if f.Type == "array" {
|
||||||
|
return fmt.Sprintf("parseArray(d, &obj.%s, %s)", f.Name, gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Type == "map" {
|
||||||
|
if !plus {
|
||||||
|
return fmt.Sprintf("parseMap(d, &obj.%s, %s)", f.Name, gen)
|
||||||
|
} else {
|
||||||
|
gen = fmt.Sprintf("parse%sPlus", *f.ElementType)
|
||||||
|
return fmt.Sprintf("parseMapPlus(d, &obj.%s, %s)", f.Name, gen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Type == "int" || f.Type == "string" || f.Type == "bool" {
|
||||||
|
return "data = nil"
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Field) EndAction() string {
|
||||||
|
n := f.Name
|
||||||
|
|
||||||
|
if n == "Id" || n == "Name" {
|
||||||
|
n = n + "_"
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.Multiple {
|
||||||
|
if f.Type == "int" {
|
||||||
|
return fmt.Sprintf("obj.%s = n(data)", n)
|
||||||
|
} else if f.Type == "string" {
|
||||||
|
return fmt.Sprintf("obj.%s = string(data)", n)
|
||||||
|
} else if f.Type == "bool" {
|
||||||
|
return fmt.Sprintf("obj.%s = true", n)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if f.Type == "int" {
|
||||||
|
return fmt.Sprintf("obj.%s = append(obj.%s, n(data))", n, n)
|
||||||
|
} else if f.Type == "string" {
|
||||||
|
return fmt.Sprintf("obj.%s = append(obj.%s, string(data))", n, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package df
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/iancoleman/strcase"
|
||||||
|
)
|
||||||
|
|
||||||
|
var frontendTemplate = template.Must(template.New("").Parse(`// Code generated by legendsbrowser; DO NOT EDIT.
|
||||||
|
{{- range $name, $obj := $.Objects }}
|
||||||
|
export interface {{ $obj.Name }} {
|
||||||
|
{{- range $fname, $field := $obj.Fields }}
|
||||||
|
{{- if not (and (eq $fname "type") (not (not $obj.SubTypes))) }}
|
||||||
|
{{ $field.JsonTypeLine }};
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if not (not $obj.SubTypes) }}
|
||||||
|
Details: any;
|
||||||
|
{{- end }}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
`))
|
||||||
|
|
||||||
|
func generateFrontendCode(objects *Metadata) error {
|
||||||
|
file, _ := json.MarshalIndent(objects, "", " ")
|
||||||
|
_ = ioutil.WriteFile("model.json", file, 0644)
|
||||||
|
|
||||||
|
f, err := os.Create("../frontend/src/app/types.ts")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err = frontendTemplate.Execute(&buf, struct {
|
||||||
|
Objects *Metadata
|
||||||
|
Modes []bool
|
||||||
|
}{
|
||||||
|
Objects: objects,
|
||||||
|
Modes: []bool{false, true},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = f.Write(buf.Bytes())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Field) JsonTypeLine() string {
|
||||||
|
m := ""
|
||||||
|
if f.Multiple {
|
||||||
|
m = "[]"
|
||||||
|
}
|
||||||
|
t := f.Type
|
||||||
|
if f.Type == "int" {
|
||||||
|
t = "number"
|
||||||
|
}
|
||||||
|
if f.Type == "array" {
|
||||||
|
t = *f.ElementType + "[]"
|
||||||
|
}
|
||||||
|
if f.Type == "map" {
|
||||||
|
t = "{ [key:number]:" + *f.ElementType + "; }"
|
||||||
|
}
|
||||||
|
if f.Type == "object" {
|
||||||
|
t = *f.ElementType
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s: %s%s", strcase.ToLowerCamel(f.Name), t, m)
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ func AnalyzeStructure(filex string) error {
|
||||||
|
|
||||||
type FieldData struct {
|
type FieldData struct {
|
||||||
IsString bool
|
IsString bool
|
||||||
|
NoBool bool
|
||||||
Multiple bool
|
Multiple bool
|
||||||
Base bool
|
Base bool
|
||||||
Plus bool
|
Plus bool
|
||||||
|
@ -253,6 +254,9 @@ Loop:
|
||||||
if _, err := strconv.Atoi(s); err != nil {
|
if _, err := strconv.Atoi(s); err != nil {
|
||||||
a.GetField(strings.Join(path, PATH_SEPARATOR)).IsString = true
|
a.GetField(strings.Join(path, PATH_SEPARATOR)).IsString = true
|
||||||
}
|
}
|
||||||
|
if len(s) > 0 {
|
||||||
|
a.GetField(strings.Join(path, PATH_SEPARATOR)).NoBool = true
|
||||||
|
}
|
||||||
return &Value{Name: t.Name.Local, Value: s}, nil
|
return &Value{Name: t.Name.Local, Value: s}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,23 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html/template"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/pkg/profile"
|
"github.com/pkg/profile"
|
||||||
"github.com/robertjanetzko/LegendsBrowser2/backend/model"
|
"github.com/robertjanetzko/LegendsBrowser2/backend/model"
|
||||||
"github.com/robertjanetzko/LegendsBrowser2/backend/server"
|
"github.com/robertjanetzko/LegendsBrowser2/backend/server"
|
||||||
|
"github.com/robertjanetzko/LegendsBrowser2/backend/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
var world *model.DfWorld
|
var world *model.DfWorld
|
||||||
|
@ -27,6 +32,39 @@ func main() {
|
||||||
|
|
||||||
router := mux.NewRouter().StrictSlash(true)
|
router := mux.NewRouter().StrictSlash(true)
|
||||||
|
|
||||||
|
functions := template.FuncMap{
|
||||||
|
"json": func(obj any) string {
|
||||||
|
b, err := json.MarshalIndent(obj, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
},
|
||||||
|
"check": func(condition bool, v any) any {
|
||||||
|
if condition {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
"title": func(input string) string {
|
||||||
|
words := strings.Split(input, " ")
|
||||||
|
smallwords := " a an on the to of "
|
||||||
|
|
||||||
|
for index, word := range words {
|
||||||
|
if strings.Contains(smallwords, " "+word+" ") && index > 0 {
|
||||||
|
words[index] = word
|
||||||
|
} else {
|
||||||
|
words[index] = strings.Title(word)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(words, " ")
|
||||||
|
},
|
||||||
|
"getHf": func(id int) *model.HistoricalFigure { return world.HistoricalFigures[id] },
|
||||||
|
"getEntity": func(id int) *model.Entity { return world.Entities[id] },
|
||||||
|
}
|
||||||
|
t := templates.New(functions)
|
||||||
|
|
||||||
if len(*f) > 0 {
|
if len(*f) > 0 {
|
||||||
defer profile.Start(profile.MemProfile).Stop()
|
defer profile.Start(profile.MemProfile).Stop()
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -77,6 +115,9 @@ func main() {
|
||||||
server.RegisterResource(router, "musicalForm", world.MusicalForms)
|
server.RegisterResource(router, "musicalForm", world.MusicalForms)
|
||||||
server.RegisterResource(router, "poeticForm", world.PoeticForms)
|
server.RegisterResource(router, "poeticForm", world.PoeticForms)
|
||||||
// server.RegisterResource(router, "written", world.WrittenContents)
|
// server.RegisterResource(router, "written", world.WrittenContents)
|
||||||
|
|
||||||
|
RegisterPage(router, "/hf/{id}", t, "hf.html", func(id int) any { return world.HistoricalFigures[id] })
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spa := spaHandler{staticFS: frontend, staticPath: "resources/frontend", indexPath: "index.html"}
|
spa := spaHandler{staticFS: frontend, staticPath: "resources/frontend", indexPath: "index.html"}
|
||||||
|
@ -87,6 +128,23 @@ func main() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RegisterPage(router *mux.Router, path string, templates *templates.Template, template string, accessor func(int) any) {
|
||||||
|
get := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id, err := strconv.Atoi(mux.Vars(r)["id"])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(w, err)
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
err = templates.Render(w, template, accessor(id))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(w, err)
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
router.HandleFunc(path, get).Methods("GET")
|
||||||
|
}
|
||||||
|
|
||||||
type spaHandler struct {
|
type spaHandler struct {
|
||||||
staticFS embed.FS
|
staticFS embed.FS
|
||||||
staticPath string
|
staticPath string
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
||||||
|
{{template "layout.html" .}}
|
||||||
|
|
||||||
|
{{define "title"}}<h1>{{.Title}}</h1>{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<h1>HF</h1>
|
||||||
|
{{end}}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{block "title" .}}{{end}}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="main">{{block "content" .}}{{end}}</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1 @@
|
||||||
|
<a href="/entity/{{ .Id }}">{{ title .Name }}</a>
|
|
@ -0,0 +1,30 @@
|
||||||
|
{{template "layout.html" .}}
|
||||||
|
|
||||||
|
{{define "title"}}{{ title .Name }}{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<h1>{{ title .Name }}</h1>
|
||||||
|
{{ .Race }} (*{{ .BirthYear }}{{ if ge .DeathYear 0 }} †{{ .DeathYear }}{{ end }})
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{- if ne 0 (len .HfLink) }}
|
||||||
|
<h3>Related Figures</h3>
|
||||||
|
<ul>
|
||||||
|
{{- range $i := .HfLink }}
|
||||||
|
<li>
|
||||||
|
{{ template "hfLink.html" (getHf $i.Hfid) }} ({{ $i.LinkType }})
|
||||||
|
</li>
|
||||||
|
{{- end }}
|
||||||
|
</ul>
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- if ne 0 (len .EntityLink) }}
|
||||||
|
<h3>Related Entities</h3>
|
||||||
|
<ul>
|
||||||
|
{{- range $i := .EntityLink }}
|
||||||
|
<li>
|
||||||
|
{{ template "entityLink.html" (getEntity $i.EntityId) }} ({{ $i.LinkType }})
|
||||||
|
</li>
|
||||||
|
{{- end }}
|
||||||
|
</ul>
|
||||||
|
{{- end }}
|
|
@ -0,0 +1,5 @@
|
||||||
|
the
|
||||||
|
{{ .Race }}
|
||||||
|
{{ check .Deity "deity"}}
|
||||||
|
{{ check .Force "force"}}
|
||||||
|
<a href="/hf/{{ .Id }}">{{ title .Name }}</a>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{block "title" .}}{{end}}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="main">{{block "content" .}}{{end}}</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,40 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed *.html
|
||||||
|
var templateFS embed.FS
|
||||||
|
|
||||||
|
type Template struct {
|
||||||
|
funcMap template.FuncMap
|
||||||
|
templates *template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(funcMap template.FuncMap) *Template {
|
||||||
|
templates := template.Must(template.New("").Funcs(funcMap).ParseFS(templateFS, "*.html"))
|
||||||
|
return &Template{
|
||||||
|
funcMap: funcMap,
|
||||||
|
templates: templates,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDebug(funcMap template.FuncMap) *Template {
|
||||||
|
templates := template.Must(template.New("").Funcs(funcMap).ParseGlob("templates/*.html"))
|
||||||
|
return &Template{
|
||||||
|
funcMap: funcMap,
|
||||||
|
templates: templates,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var DebugTemplates = true
|
||||||
|
|
||||||
|
func (t *Template) Render(w io.Writer, name string, data interface{}) error {
|
||||||
|
if DebugTemplates {
|
||||||
|
return NewDebug(t.funcMap).templates.ExecuteTemplate(w, name, data)
|
||||||
|
}
|
||||||
|
return t.templates.ExecuteTemplate(w, name, data)
|
||||||
|
}
|
|
@ -3,10 +3,12 @@ import { RouterModule, Routes } from '@angular/router';
|
||||||
import { EntitiesResolver, EntityResolver } from './entity.service';
|
import { EntitiesResolver, EntityResolver } from './entity.service';
|
||||||
import { CivilizationsComponent } from './pages/civilizations/civilizations.component';
|
import { CivilizationsComponent } from './pages/civilizations/civilizations.component';
|
||||||
import { EntityComponent } from './pages/entity/entity.component';
|
import { EntityComponent } from './pages/entity/entity.component';
|
||||||
|
import { HfComponent } from './pages/hf/hf.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', component: CivilizationsComponent, resolve: { civilizations: EntitiesResolver } },
|
{ path: '', component: CivilizationsComponent, resolve: { civilizations: EntitiesResolver } },
|
||||||
{ path: 'entity/:id', component: EntityComponent, resolve: { entity: EntityResolver } },
|
{ path: 'entity/:id', component: EntityComponent, resolve: { entity: EntityResolver }, data: { resource: "entity" } },
|
||||||
|
{ path: 'hf/:id', component: HfComponent, resolve: { data: EntityResolver }, data: { resource: "hf" } },
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
|
|
@ -5,12 +5,20 @@ import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { CivilizationsComponent } from './pages/civilizations/civilizations.component';
|
import { CivilizationsComponent } from './pages/civilizations/civilizations.component';
|
||||||
import { EntityComponent } from './pages/entity/entity.component';
|
import { EntityComponent } from './pages/entity/entity.component';
|
||||||
|
import { HfComponent } from './pages/hf/hf.component';
|
||||||
|
import { EventListComponent } from './components/event-list/event-list.component';
|
||||||
|
import { InlineEntityComponent } from './components/inline-entity/inline-entity.component';
|
||||||
|
import { InlineHfComponent } from './components/inline-hf/inline-hf.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
CivilizationsComponent,
|
CivilizationsComponent,
|
||||||
EntityComponent
|
EntityComponent,
|
||||||
|
HfComponent,
|
||||||
|
EventListComponent,
|
||||||
|
InlineEntityComponent,
|
||||||
|
InlineHfComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<p>event-list works!</p>
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { EventListComponent } from './event-list.component';
|
||||||
|
|
||||||
|
describe('EventListComponent', () => {
|
||||||
|
let component: EventListComponent;
|
||||||
|
let fixture: ComponentFixture<EventListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ EventListComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(EventListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-event-list',
|
||||||
|
templateUrl: './event-list.component.html',
|
||||||
|
styleUrls: ['./event-list.component.scss']
|
||||||
|
})
|
||||||
|
export class EventListComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
<a [routerLink]="['/entity', data?.id]">{{data?.name}}</a>
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { InlineEntityComponent } from './inline-entity.component';
|
||||||
|
|
||||||
|
describe('InlineEntityComponent', () => {
|
||||||
|
let component: InlineEntityComponent;
|
||||||
|
let fixture: ComponentFixture<InlineEntityComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ InlineEntityComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(InlineEntityComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { EntityService } from 'src/app/entity.service';
|
||||||
|
import { Entity } from 'src/app/types';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-inline-entity',
|
||||||
|
templateUrl: './inline-entity.component.html',
|
||||||
|
styleUrls: ['./inline-entity.component.scss']
|
||||||
|
})
|
||||||
|
export class InlineEntityComponent implements OnInit {
|
||||||
|
|
||||||
|
_id?: number
|
||||||
|
data?: Entity
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
get id(): number | undefined {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
set id(val: number | undefined) {
|
||||||
|
this._id = val
|
||||||
|
if (val) {
|
||||||
|
this.service.getOne<Entity>("entity", val).then(data => this.data = data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private service: EntityService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
<a [routerLink]="['/hf', data?.id]">{{data?.name}}</a>
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { InlineHfComponent } from './inline-hf.component';
|
||||||
|
|
||||||
|
describe('InlineHfComponent', () => {
|
||||||
|
let component: InlineHfComponent;
|
||||||
|
let fixture: ComponentFixture<InlineHfComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ InlineHfComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(InlineHfComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { EntityService } from 'src/app/entity.service';
|
||||||
|
import { HistoricalFigure } from 'src/app/types';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-inline-hf',
|
||||||
|
templateUrl: './inline-hf.component.html',
|
||||||
|
styleUrls: ['./inline-hf.component.scss']
|
||||||
|
})
|
||||||
|
export class InlineHfComponent implements OnInit {
|
||||||
|
|
||||||
|
_id?: number
|
||||||
|
data?: HistoricalFigure
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
get id(): number | undefined {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
set id(val: number | undefined) {
|
||||||
|
this._id = val
|
||||||
|
if (val) {
|
||||||
|
this.service.getOne<HistoricalFigure>("hf", val).then(data => this.data = data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private service: EntityService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,8 +15,8 @@ export class EntityService {
|
||||||
return firstValueFrom(this.http.get<Entity[]>("./api/entity"));
|
return firstValueFrom(this.http.get<Entity[]>("./api/entity"));
|
||||||
}
|
}
|
||||||
|
|
||||||
getOne(id: string | number): Promise<Entity> {
|
getOne<T>(resource: string, id: string | number): Promise<T> {
|
||||||
return firstValueFrom(this.http.get<Entity>("./api/entity/" + id));
|
return firstValueFrom(this.http.get<T>(`./api/${resource}/${id}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ export class EntityResolver implements Resolve<Entity> {
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot
|
state: RouterStateSnapshot
|
||||||
): Observable<Entity> | Promise<Entity> | Entity {
|
): Observable<Entity> | Promise<Entity> | Entity {
|
||||||
return this.service.getOne(route.paramMap.get('id')!);
|
console.log("R", route.data)
|
||||||
|
return this.service.getOne(route.data['resource'], route.paramMap.get('id')!);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<h1>{{data.name | titlecase}}</h1>
|
||||||
|
<span>{{data.race}}</span> (*{{data.birthYear}}<span *ngIf="data.deathYear >= 0"> †{{data.deathYear}}</span>)
|
||||||
|
|
||||||
|
<h3>Related Figures</h3>
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let i of data.hfLink">
|
||||||
|
<app-inline-hf [id]="i.hfid"></app-inline-hf> ({{i.linkType}})
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Related Entities</h3>
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let i of data.entityLink">
|
||||||
|
<app-inline-entity [id]="i.entityId"></app-inline-entity> ({{i.linkType}})
|
||||||
|
</li>
|
||||||
|
</ul>
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { HfComponent } from './hf.component';
|
||||||
|
|
||||||
|
describe('HfComponent', () => {
|
||||||
|
let component: HfComponent;
|
||||||
|
let fixture: ComponentFixture<HfComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ HfComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(HfComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { EntityService } from 'src/app/entity.service';
|
||||||
|
import { HistoricalFigure } from 'src/app/types';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-hf',
|
||||||
|
templateUrl: './hf.component.html',
|
||||||
|
styleUrls: ['./hf.component.scss']
|
||||||
|
})
|
||||||
|
export class HfComponent implements OnInit {
|
||||||
|
|
||||||
|
data!: HistoricalFigure
|
||||||
|
|
||||||
|
constructor(private route: ActivatedRoute, private service: EntityService) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.data = this.route.snapshot.data['data'];
|
||||||
|
this.route.params.subscribe(p => this.service.getOne<HistoricalFigure>("hf", p['id']).then(data => this.data = data));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue