Remove custom template logic

This commit removes custom template logic in the entire distillery
codebase.
This commit is contained in:
Tom 2023-07-14 14:26:28 +02:00
parent 588cb7ebaa
commit e2f5c66b1c
3 changed files with 24 additions and 210 deletions

View file

@ -1,6 +1,5 @@
# This file is used to initialize a new GraphDB repository. # This file is used to initialize a new GraphDB repository.
# In this file the variables ${GRAPHDB_REPO} and ${INSTANCE_DOMAIN} will be replaced. # It is filled at runtime.
# All other variables will be left untouched.
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
@prefix rep: <http://www.openrdf.org/config/repository#>. @prefix rep: <http://www.openrdf.org/config/repository#>.
@ -9,8 +8,8 @@
@prefix owlim: <http://www.ontotext.com/trree/owlim#>. @prefix owlim: <http://www.ontotext.com/trree/owlim#>.
[] a rep:Repository ; [] a rep:Repository ;
rep:repositoryID "${GRAPHDB_REPO}" ; rep:repositoryID "{{ .RepositoryID }}" ;
rdfs:label "${INSTANCE_DOMAIN}" ; rdfs:label "{{ .Label }}" ;
rep:repositoryImpl [ rep:repositoryImpl [
rep:repositoryType "graphdb:SailRepository" ; rep:repositoryType "graphdb:SailRepository" ;
sr:sailImpl [ sr:sailImpl [
@ -18,7 +17,7 @@
owlim:owlim-license "" ; owlim:owlim-license "" ;
owlim:base-URL "http://${INSTANCE_DOMAIN}/" ; owlim:base-URL "{{ .BaseURL }}" ;
owlim:defaultNS "" ; owlim:defaultNS "" ;
owlim:entity-index-size "10000000" ; owlim:entity-index-size "10000000" ;
owlim:entity-id-size "32" ; owlim:entity-id-size "32" ;

View file

@ -5,11 +5,11 @@ import (
"context" "context"
"errors" "errors"
"net/http" "net/http"
"text/template"
_ "embed" _ "embed"
"github.com/FAU-CDI/wisski-distillery/internal/models" "github.com/FAU-CDI/wisski-distillery/internal/models"
"github.com/FAU-CDI/wisski-distillery/pkg/unpack"
"github.com/tkw1536/goprogram/exit" "github.com/tkw1536/goprogram/exit"
) )
@ -18,8 +18,20 @@ var errTripleStoreFailedRepository = exit.Error{
ExitCode: exit.ExitGeneric, ExitCode: exit.ExitGeneric,
} }
//go:embed create-repo.ttl //go:embed create-repo.tpl
var createRepoTTL []byte var createRepoTpl string
// Template for creating repositories.
//
// NOTE(twiesing): The template is not aware of SparQL syntax, thus this template is very unsafe.
// And should only be used with KNOWN GOOD input.
var creteRepoTemplate = template.Must(template.New("create-repo.tpl").Parse(createRepoTpl))
type createRepoContext struct {
RepositoryID string
Label string
BaseURL string
}
func (ts *Triplestore) Provision(ctx context.Context, instance models.Instance, domain string) error { func (ts *Triplestore) Provision(ctx context.Context, instance models.Instance, domain string) error {
return ts.CreateRepository(ctx, instance.GraphDBRepository, domain, instance.GraphDBUsername, instance.GraphDBPassword) return ts.CreateRepository(ctx, instance.GraphDBRepository, domain, instance.GraphDBUsername, instance.GraphDBPassword)
@ -39,12 +51,11 @@ func (ts *Triplestore) CreateRepository(ctx context.Context, name, domain, user,
// prepare the create repo request // prepare the create repo request
var createRepo bytes.Buffer var createRepo bytes.Buffer
if err := creteRepoTemplate.Execute(&createRepo, createRepoContext{
err := unpack.WriteTemplate(&createRepo, map[string]string{ RepositoryID: name,
"GRAPHDB_REPO": name, Label: domain,
"INSTANCE_DOMAIN": domain, BaseURL: "http://" + domain + "/",
}, bytes.NewReader(createRepoTTL)) }); err != nil {
if err != nil {
return err return err
} }

View file

@ -1,196 +0,0 @@
// Package unpack unpacks files and templates to a target directory.
package unpack
import (
"bufio"
"fmt"
"io"
"strings"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
// ts represents state of the template parser
type ts int
const (
tsGobble ts = iota // gobble into dst
tsSawDollar // saw a '$'
tsGobbleVar // gobble into var
)
// MissingTemplateKeyError indicates [WriteTemplate] found missing keys in the context
type MissingTemplateKeyError struct {
Keys []string
}
func (mtke MissingTemplateKeyError) Error() string {
return fmt.Sprintf("missing template keys from context: %v", mtke.Keys)
}
// UnusuedTemplateKeyError indicates [WriteTemplate] found unusued keys in the context
type UnusuedTemplateKeyError struct {
Keys []string
}
func (utke UnusuedTemplateKeyError) Error() string {
return fmt.Sprintf("unused template keys from context: %v", utke.Keys)
}
// WriteTemplate writes the template defined by src with the given context into reader.
//
// To run the template, variables of the form ${NAME} are replaced with their corresponding value from the context.
//
// If an underlying read or write fails, it is returned as is.
// Missing template keys return a [MissingTemplateKeyError], but are replaced with the empty string.
// Unused template keys return a [UnusuedTemplateKeyError], but are replaced with the empty string.
//
// Reader / Writer errors are always returned first; next missing template keys, and finally unused template keys.
func WriteTemplate(dst io.Writer, context map[string]string, src io.Reader) error {
// We keep track of contect keys that have not been used.
//
// We first fill the map with all the keys from the context.
// Then when we use a key, we delete it from the map.
// If there are any keys left at the end of the replacement, that is an error.
unusedKeys := make(map[string]struct{}, len(context))
for key := range context {
unusedKeys[key] = struct{}{}
}
// When we encounter a missing key, put it into this map.
// This is so that we can build an error message below.
missingKeys := make(map[string]struct{}, 0)
// We use a new bufio reader to read data from the input.
// This is a cheap trick to get a ReadRune() method.
reader := bufio.NewReader(src)
//
// MAIN PARSING LOOP
//
// start out in gobble mode!
mode := tsGobble
// keep track of variable names
var varB strings.Builder
parseloop:
for {
r, _, err := reader.ReadRune()
switch {
case err == io.EOF:
// finished parsing the source
break parseloop
case err != nil:
// the reader broke
return err
case mode == tsGobble && r == '$':
// saw a '$' in gobble mode
mode = tsSawDollar
case mode == tsGobble:
// normal gobbleing
// => pass it through
if _, err := dst.Write([]byte{byte(r)}); err != nil {
return err
}
case mode == tsSawDollar && r == '{':
// saw '{', following the '$'
// => read everything else into the buffer
mode = tsGobbleVar
case mode == tsSawDollar && r == '$':
// saw a '$' following the '$'
// => write the first '$', and handle the case $${stuff}
if _, err := dst.Write([]byte("$")); err != nil {
return err
}
case mode == tsSawDollar:
// saw anything else following the '$'
// => write both back and switch back to gobble mode
if _, err := dst.Write([]byte{byte('$'), byte(r)}); err != nil {
return err
}
mode = tsGobble
case mode == tsGobbleVar && r != '}':
// saw anything except for closing bracket
// => keep it in the buffer
if _, err := varB.WriteRune(r); err != nil {
return err
}
case mode == tsGobbleVar:
// saw a closing '}' inside tsGobbleVar mode
// => use the variable
name := varB.String()
// get the variable from the context
value, ok := context[name]
delete(unusedKeys, name) // mark the variable as used!
if !ok {
// store unusued variables
missingKeys[name] = struct{}{}
value = ""
}
// write the replacement into the string
if _, err := io.WriteString(dst, value); err != nil {
return err
}
// reset the builder and go back into normal mode
varB.Reset()
mode = tsGobble
}
}
//
// CLEANUP UNUSUED INPUT
//
switch mode {
case tsSawDollar:
// we had a '$', but no '{'
// => write the trailing '$' into dest
if _, err := dst.Write([]byte("$")); err != nil {
return err
}
case tsGobbleVar:
// we had a "${", followed by somthing unclosed
// => write everything back into the dst
if _, err := dst.Write([]byte("${")); err != nil {
return err
}
if _, err := io.WriteString(dst, varB.String()); err != nil {
return err
}
}
// Check if there were missing template keys.
// If so, we sort them and return an appropriate error.
if len(missingKeys) != 0 {
keys := maps.Keys(missingKeys)
slices.Sort(keys)
return MissingTemplateKeyError{
Keys: keys,
}
}
// Check if there were unused template keys.
// If so, we sort them and return an appropriate error.
if len(unusedKeys) != 0 {
keys := maps.Keys(unusedKeys)
slices.Sort(keys)
return UnusuedTemplateKeyError{
Keys: keys,
}
}
return nil
}