jx/internal/resource/document.go

269 lines
6.5 KiB
Go
Raw Normal View History

2024-03-20 19:23:31 +00:00
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
2024-03-22 17:39:06 +00:00
2024-03-20 16:15:27 +00:00
package resource
import (
"encoding/json"
2024-05-14 19:42:28 +00:00
"fmt"
2024-03-25 20:31:06 +00:00
"gopkg.in/yaml.v3"
"io"
2024-04-09 19:30:05 +00:00
"log/slog"
_ "net/url"
2024-04-19 07:52:10 +00:00
"github.com/sters/yaml-diff/yamldiff"
"strings"
"decl/internal/codec"
"decl/internal/types"
"decl/internal/config"
2024-05-24 05:11:51 +00:00
"context"
2024-03-20 16:15:27 +00:00
)
type ResourceMap[Value any] map[string]Value
2024-07-17 08:34:57 +00:00
func (rm ResourceMap[Value]) Get(key string) (any, bool) {
v, ok := rm[key]
return v, ok
}
type ResourceMapper interface {
Get(key string) (any, bool)
}
2024-03-20 16:15:27 +00:00
type Document struct {
uris ResourceMap[*Declaration]
ResourceDecls []Declaration `json:"resources" yaml:"resources"`
config *config.Document
2024-03-20 16:15:27 +00:00
}
func NewDocument() *Document {
return &Document{ uris: make(ResourceMap[*Declaration]) }
}
func (d *Document) Types() *types.Types[Resource] {
return ResourceTypes
2024-03-20 16:15:27 +00:00
}
2024-04-23 22:35:08 +00:00
func (d *Document) Filter(filter ResourceSelector) []*Declaration {
resources := make([]*Declaration, 0, len(d.ResourceDecls))
for i := range d.ResourceDecls {
filterResource := &d.ResourceDecls[i]
2024-04-25 07:45:05 +00:00
if filter == nil || filter(filterResource) {
2024-04-23 22:35:08 +00:00
resources = append(resources, &d.ResourceDecls[i])
}
}
return resources
}
func (d *Document) GetResource(uri string) *Declaration {
if decl, ok := d.uris[uri]; ok {
return decl
}
return nil
}
2024-04-19 07:52:10 +00:00
func (d *Document) Clone() *Document {
clone := NewDocument()
clone.config = d.config
2024-04-19 07:52:10 +00:00
clone.ResourceDecls = make([]Declaration, len(d.ResourceDecls))
for i, res := range d.ResourceDecls {
clone.ResourceDecls[i] = *res.Clone()
clone.ResourceDecls[i].SetDocument(clone)
clone.ResourceDecls[i].SetConfig(d.config)
2024-04-19 07:52:10 +00:00
}
return clone
}
func (d *Document) Load(r io.Reader) (err error) {
c := codec.NewYAMLDecoder(r)
err = c.Decode(d)
2024-07-17 08:34:57 +00:00
slog.Info("Document.Load()", "error", err)
if err == nil {
for i := range d.ResourceDecls {
d.ResourceDecls[i].SetDocument(d)
}
}
return
}
func (d *Document) Validate() error {
jsonDocument, jsonErr := d.JSON()
2024-04-09 19:30:05 +00:00
slog.Info("document.Validate() json", "json", jsonDocument, "err", jsonErr)
if jsonErr == nil {
2024-04-09 19:30:05 +00:00
s := NewSchema("document")
err := s.Validate(string(jsonDocument))
if err != nil {
return err
}
2024-04-09 19:30:05 +00:00
/*
for i := range d.ResourceDecls {
if e := d.ResourceDecls[i].Resource().Validate(); e != nil {
return fmt.Errorf("failed to validate resource %s; %w", d.ResourceDecls[i].Resource().URI(), e)
}
2024-03-25 20:31:06 +00:00
}
2024-04-09 19:30:05 +00:00
*/
2024-03-25 20:31:06 +00:00
}
return nil
2024-03-20 16:15:27 +00:00
}
func (d *Document) SetConfig(config *config.Document) {
d.config = config
}
func (d *Document) ConfigDoc() *config.Document {
return d.config
}
2024-03-20 19:23:31 +00:00
func (d *Document) Resources() []Declaration {
2024-03-25 20:31:06 +00:00
return d.ResourceDecls
2024-03-20 16:15:27 +00:00
}
2024-03-20 19:23:31 +00:00
2024-05-24 05:11:51 +00:00
func (d *Document) ResolveIds(ctx context.Context) {
for i := range d.ResourceDecls {
d.ResourceDecls[i].ResolveId(ctx)
}
}
func (d *Document) Apply(state string) error {
2024-04-21 06:13:17 +00:00
if d == nil {
panic("Undefined Document")
}
2024-05-24 05:11:51 +00:00
slog.Info("Document.Apply()", "declarations", d, "override", state)
var start, i int = 0, 0
if state == "delete" {
start = len(d.ResourceDecls) - 1
}
for {
idx := i - start
if idx < 0 { idx = - idx }
slog.Info("Document.Apply() applying resource", "index", idx, "uri", d.ResourceDecls[idx].Resource().URI(), "resource", d.ResourceDecls[idx].Resource())
if state != "" {
d.ResourceDecls[idx].Transition = state
}
d.ResourceDecls[idx].SetConfig(d.config)
2024-05-24 05:11:51 +00:00
if e := d.ResourceDecls[idx].Apply(); e != nil {
slog.Error("Document.Apply() error applying resource", "index", idx, "uri", d.ResourceDecls[idx].Resource().URI(), "resource", d.ResourceDecls[idx].Resource(), "error", e)
2024-03-25 20:31:06 +00:00
return e
}
2024-05-24 05:11:51 +00:00
if i >= len(d.ResourceDecls) - 1 {
break
}
i++
2024-03-25 20:31:06 +00:00
}
return nil
2024-03-20 19:23:31 +00:00
}
2024-03-25 20:31:06 +00:00
func (d *Document) Generate(w io.Writer) error {
e := codec.NewYAMLEncoder(w)
2024-04-03 19:27:16 +00:00
err := e.Encode(d);
if err == nil {
return e.Close()
}
e.Close()
return err
2024-03-20 19:23:31 +00:00
}
func (d *Document) MapResourceURI(uri string, declaration *Declaration) {
d.uris[uri] = declaration
}
2024-03-20 19:23:31 +00:00
func (d *Document) AddResourceDeclaration(resourceType string, resourceDeclaration Resource) {
2024-07-17 08:34:57 +00:00
slog.Info("Document.AddResourceDeclaration()", "type", resourceType, "resource", resourceDeclaration)
decl := NewDeclarationFromDocument(d)
decl.Type = TypeName(resourceType)
decl.Attributes = resourceDeclaration
2024-03-25 20:31:06 +00:00
d.ResourceDecls = append(d.ResourceDecls, *decl)
d.MapResourceURI(decl.Attributes.URI(), decl)
2024-07-17 08:34:57 +00:00
decl.SetDocument(d)
2024-03-20 19:23:31 +00:00
}
func (d *Document) AddResource(uri string) error {
2024-07-17 08:34:57 +00:00
decl := NewDeclarationFromDocument(d)
2024-04-03 19:27:16 +00:00
if e := decl.SetURI(uri); e != nil {
return e
}
2024-03-25 20:31:06 +00:00
d.ResourceDecls = append(d.ResourceDecls, *decl)
d.MapResourceURI(decl.Attributes.URI(), decl)
2024-07-17 08:34:57 +00:00
decl.SetDocument(d)
2024-03-25 20:31:06 +00:00
return nil
2024-03-20 19:23:31 +00:00
}
func (d *Document) JSON() ([]byte, error) {
return json.Marshal(d)
}
func (d *Document) YAML() ([]byte, error) {
return yaml.Marshal(d)
}
2024-04-19 07:52:10 +00:00
2024-05-14 19:42:28 +00:00
func (d *Document) Diff(with *Document, output io.Writer) (returnOutput string, diffErr error) {
defer func() {
if r := recover(); r != nil {
2024-05-14 19:42:28 +00:00
returnOutput = ""
diffErr = fmt.Errorf("%s", r)
}
}()
2024-05-09 07:39:45 +00:00
slog.Info("Document.Diff()")
2024-04-19 07:52:10 +00:00
opts := []yamldiff.DoOptionFunc{}
if output == nil {
output = &strings.Builder{}
}
ydata, yerr := d.YAML()
if yerr != nil {
return "", yerr
}
yamlDiff,yamlDiffErr := yamldiff.Load(string(ydata))
if yamlDiffErr != nil {
return "", yamlDiffErr
}
wdata,werr := with.YAML()
if werr != nil {
return "", werr
}
withDiff,withDiffErr := yamldiff.Load(string(wdata))
if withDiffErr != nil {
return "", withDiffErr
}
2024-05-09 07:39:45 +00:00
for _,docDiffResults := range yamldiff.Do(yamlDiff, withDiff, opts...) {
slog.Info("Diff()", "diff", docDiffResults, "dump", docDiffResults.Dump())
_,e := output.Write([]byte(docDiffResults.Dump()))
2024-04-22 06:11:17 +00:00
if e != nil {
return "", e
}
2024-04-19 07:52:10 +00:00
}
slog.Info("Document.Diff() ", "document.yaml", ydata, "with.yaml", wdata)
if stringOutput, ok := output.(*strings.Builder); ok {
return stringOutput.String(), nil
}
return "", nil
}
2024-07-17 08:34:57 +00:00
func (d *Document) UnmarshalYAML(value *yaml.Node) error {
type decodeDocument Document
t := (*decodeDocument)(d)
if unmarshalDocumentErr := value.Decode(t); unmarshalDocumentErr != nil {
return unmarshalDocumentErr
}
for i := range d.ResourceDecls {
d.ResourceDecls[i].SetDocument(d)
d.MapResourceURI(d.ResourceDecls[i].Attributes.URI(), &d.ResourceDecls[i])
}
return nil
}
func (d *Document) UnmarshalJSON(data []byte) error {
type decodeDocument Document
t := (*decodeDocument)(d)
if unmarshalDocumentErr := json.Unmarshal(data, t); unmarshalDocumentErr != nil {
return unmarshalDocumentErr
}
for i := range d.ResourceDecls {
d.ResourceDecls[i].SetDocument(d)
d.MapResourceURI(d.ResourceDecls[i].Attributes.URI(), &d.ResourceDecls[i])
}
return nil
}