1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-09 17:44:59 +09:00
anytype-heart/util/builtintemplate/builtintemplate.go
2023-04-25 18:17:59 +02:00

159 lines
4.8 KiB
Go

package builtintemplate
import (
"archive/zip"
"bytes"
"context"
"crypto/md5"
_ "embed"
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"github.com/anytypeio/any-sync/app"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/core/block/simple"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/relation"
"github.com/anytypeio/go-anytype-middleware/core/block/source"
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core/smartblock"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/addr"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
)
const CName = "builtintemplate"
//go:embed data/bundled_templates.zip
var templatesZip []byte
func New() BuiltinTemplate {
return new(builtinTemplate)
}
type BuiltinTemplate interface {
Hash() string
app.ComponentRunnable
}
type builtinTemplate struct {
source source.Service
generatedHash string
}
func (b *builtinTemplate) Init(a *app.App) (err error) {
b.source = a.MustComponent(source.CName).(source.Service)
b.makeGenHash(4)
return
}
func (b *builtinTemplate) makeGenHash(version uint32) {
h := md5.New()
h.Write(templatesZip)
binary.Write(h, binary.LittleEndian, version)
b.generatedHash = hex.EncodeToString(h.Sum(nil))
}
func (b *builtinTemplate) Name() (name string) {
return CName
}
func (b *builtinTemplate) Run(context.Context) (err error) {
zr, err := zip.NewReader(bytes.NewReader(templatesZip), int64(len(templatesZip)))
if err != nil {
return
}
for _, zf := range zr.File {
rd, e := zf.Open()
if e != nil {
return e
}
if err = b.registerBuiltin(rd); err != nil {
return
}
}
return
}
func (b *builtinTemplate) Hash() string {
return b.generatedHash
}
func (b *builtinTemplate) registerBuiltin(rd io.ReadCloser) (err error) {
defer rd.Close()
data, err := ioutil.ReadAll(rd)
snapshot := &pb.ChangeSnapshot{}
if err = snapshot.Unmarshal(data); err != nil {
return
}
st := state.NewDocFromSnapshot("", snapshot, state.DoNotMigrateTypes).(*state.State)
id, err := smartblock.PatchSmartBlockType(st.RootId(), smartblock.SmartBlockTypeBundledTemplate)
if err != nil {
return
}
st.SetRootId(id)
st = st.Copy()
st.SetLocalDetail(bundle.RelationKeyTemplateIsBundled.String(), pbtypes.Bool(true))
st.RemoveDetail(bundle.RelationKeyCreator.String(), bundle.RelationKeyLastModifiedBy.String())
st.SetLocalDetail(bundle.RelationKeyCreator.String(), pbtypes.String(addr.AnytypeProfileId))
st.SetLocalDetail(bundle.RelationKeyLastModifiedBy.String(), pbtypes.String(addr.AnytypeProfileId))
st.SetLocalDetail(bundle.RelationKeyWorkspaceId.String(), pbtypes.String(addr.AnytypeMarketplaceWorkspace))
st.SetObjectTypes([]string{bundle.TypeKeyTemplate.BundledURL(), pbtypes.Get(st.Details(), bundle.RelationKeyTargetObjectType.String()).GetStringValue()})
st.InjectDerivedDetails()
// fix divergence between extra relations and simple block relations
st.Iterate(func(b simple.Block) (isContinue bool) {
if _, ok := b.(relation.Block); ok {
relKey := b.Model().GetRelation().Key
if !st.HasRelation(relKey) {
st.AddBundledRelations(bundle.RelationKey(relKey))
}
}
return true
})
if err = b.validate(st.Copy()); err != nil {
return
}
log.With("id", id).Info("registering template")
b.source.RegisterStaticSource(id, func() source.Source {
return b.source.NewStaticSource(id, model.SmartBlockType_BundledTemplate, st.Copy(), nil)
})
return
}
func (b *builtinTemplate) validate(st *state.State) (err error) {
cd := st.CombinedDetails()
if st.ObjectType() != bundle.TypeKeyTemplate.BundledURL() {
return fmt.Errorf("bundled template validation: %s unexpected object type: %v", st.RootId(), st.ObjectType())
}
if !pbtypes.GetBool(cd, bundle.RelationKeyTemplateIsBundled.String()) {
return fmt.Errorf("bundled template validation: %s not bundled", st.RootId())
}
if tt := pbtypes.GetString(cd, bundle.RelationKeyTargetObjectType.String()); tt == "" || tt == st.ObjectType() {
return fmt.Errorf("bundled template validation: %s unexpected target object type: %v", st.RootId(), tt)
}
// todo: update templates and return the validation
return nil
var relKeys []string
st.Iterate(func(b simple.Block) (isContinue bool) {
if rb, ok := b.(relation.Block); ok {
relKeys = append(relKeys, rb.Model().GetRelation().Key)
}
return true
})
for _, rk := range relKeys {
if !st.HasRelation(rk) {
return fmt.Errorf("bundled template validation: relation '%v' exists in block but not in extra relations", rk)
}
}
return nil
}
func (b *builtinTemplate) Close(ctx context.Context) (err error) {
return
}