From 9fcffb0f2613a9641d8313e839b6114d7439cbf0 Mon Sep 17 00:00:00 2001 From: dobarx Date: Fri, 22 Mar 2024 11:20:55 +0200 Subject: [PATCH 1/7] Add InvocationOrder & Content Location to plugin schema --- parser/caller.go | 18 ++- parser/definitions/parsed_document.go | 7 +- parser/definitions/parsed_plugin.go | 5 + parser/definitions/section.go | 5 +- parser/evaluation/plugincaller.go | 3 +- parser/evaluation/result.go | 38 ++++- plugin/content.go | 94 ++++++++++++ plugin/content_provider.go | 26 +++- plugin/pluginapi/v1/content.pb.go | 202 +++++++++++++++++++++---- plugin/pluginapi/v1/content_decoder.go | 22 +++ plugin/pluginapi/v1/content_encoder.go | 22 +++ plugin/pluginapi/v1/schema.pb.go | 156 ++++++++++++++----- plugin/pluginapi/v1/schema_decoder.go | 16 +- plugin/pluginapi/v1/schema_encoder.go | 16 +- plugin/schema.go | 11 +- proto/pluginapi/v1/content.proto | 15 +- proto/pluginapi/v1/schema.proto | 11 +- 17 files changed, 573 insertions(+), 94 deletions(-) diff --git a/parser/caller.go b/parser/caller.go index bdbc8064..49b40da9 100644 --- a/parser/caller.go +++ b/parser/caller.go @@ -147,29 +147,35 @@ func (c *Caller) callPlugin(ctx context.Context, kind, name string, config evalu Args: pluginArgs, DataContext: dataCtx, }) - res = "" - if content != nil { - res = content.Markdown - } + res = content diags.ExtendHcl(diag) } return } -func (c *Caller) CallContent(ctx context.Context, name string, config evaluation.Configuration, invocation evaluation.Invocation, dataCtx plugin.MapData) (result string, diag diagnostics.Diag) { +func (c *Caller) CallContent(ctx context.Context, name string, config evaluation.Configuration, invocation evaluation.Invocation, dataCtx plugin.MapData) (result *plugin.Content, diag diagnostics.Diag) { var ok bool var res any res, diag = c.callPlugin(ctx, definitions.BlockKindContent, name, config, invocation, dataCtx) if diag.HasErrors() { return } - result, ok = res.(string) + result, ok = res.(*plugin.Content) if !ok { panic("Incorrect plugin result type") } return } +func (c *Caller) ContentInvocationOrder(ctx context.Context, name string) (order plugin.InvocationOrder, diag diagnostics.Diag) { + content, hclDiag := c.plugins.ContentProvider(name) + if hclDiag.HasErrors() { + return plugin.InvocationOrderUnspecified, diagnostics.Diag(hclDiag) + } + order = content.InvocationOrder + return +} + func (c *Caller) CallData(ctx context.Context, name string, config evaluation.Configuration, invocation evaluation.Invocation) (result plugin.Data, diag diagnostics.Diag) { var ok bool var res any diff --git a/parser/definitions/parsed_document.go b/parser/definitions/parsed_document.go index 813ea400..d138b99d 100644 --- a/parser/definitions/parsed_document.go +++ b/parser/definitions/parsed_document.go @@ -2,6 +2,7 @@ package definitions import ( "context" + "slices" "github.com/hashicorp/hcl/v2" @@ -72,7 +73,11 @@ func (d *ParsedDocument) Render(ctx context.Context, caller evaluation.PluginCal BlockKindData: dataResult, BlockKindDocument: document, }) - + slices.SortStableFunc(d.Content, func(a, b Renderable) int { + ao, _ := caller.ContentInvocationOrder(ctx, a.Name()) + bo, _ := caller.ContentInvocationOrder(ctx, b.Name()) + return ao.Weight() - bo.Weight() + }) for _, content := range d.Content { diags.Extend( content.Render(ctx, caller, dataCtx.Share(), &res), diff --git a/parser/definitions/parsed_plugin.go b/parser/definitions/parsed_plugin.go index 515d253d..e74e91be 100644 --- a/parser/definitions/parsed_plugin.go +++ b/parser/definitions/parsed_plugin.go @@ -36,6 +36,10 @@ type ( ParsedData ParsedPlugin ) +func (c *ParsedContent) Name() string { + return c.PluginName +} + // Render implements Renderable. func (c *ParsedContent) Render(ctx context.Context, caller evaluation.ContentCaller, dataCtx evaluation.DataContext, result *evaluation.Result) (diags diagnostics.Diag) { if c.Meta != nil { @@ -120,5 +124,6 @@ func runQuery(query string, dataCtx *evaluation.DataContext) (result plugin.Data } type Renderable interface { + Name() string Render(ctx context.Context, caller evaluation.ContentCaller, dataCtx evaluation.DataContext, result *evaluation.Result) diagnostics.Diag } diff --git a/parser/definitions/section.go b/parser/definitions/section.go index dbb52f1b..d7e054d0 100644 --- a/parser/definitions/section.go +++ b/parser/definitions/section.go @@ -26,6 +26,10 @@ type ParsedSection struct { Content []Renderable } +func (s ParsedSection) Name() string { + return "" +} + func (s ParsedSection) Render(ctx context.Context, caller evaluation.ContentCaller, dataCtx evaluation.DataContext, result *evaluation.Result) (diags diagnostics.Diag) { if s.Meta != nil { dataCtx.Set(BlockKindSection, plugin.ConvMapData{ @@ -37,7 +41,6 @@ func (s ParsedSection) Render(ctx context.Context, caller evaluation.ContentCall if title := s.Title; title != nil { diags.Extend(title.Render(ctx, caller, dataCtx.Share(), result)) } - for _, content := range s.Content { diags.Extend( content.Render(ctx, caller, dataCtx.Share(), result), diff --git a/parser/evaluation/plugincaller.go b/parser/evaluation/plugincaller.go index 5bd73e08..95e80617 100644 --- a/parser/evaluation/plugincaller.go +++ b/parser/evaluation/plugincaller.go @@ -12,7 +12,8 @@ type DataCaller interface { } type ContentCaller interface { - CallContent(ctx context.Context, name string, config Configuration, invocation Invocation, context plugin.MapData) (result string, diag diagnostics.Diag) + CallContent(ctx context.Context, name string, config Configuration, invocation Invocation, context plugin.MapData) (result *plugin.Content, diag diagnostics.Diag) + ContentInvocationOrder(ctx context.Context, name string) (order plugin.InvocationOrder, diag diagnostics.Diag) } type PluginCaller interface { diff --git a/parser/evaluation/result.go b/parser/evaluation/result.go index 6e459aeb..c45a2722 100644 --- a/parser/evaluation/result.go +++ b/parser/evaluation/result.go @@ -1,6 +1,8 @@ package evaluation -import "github.com/blackstork-io/fabric/plugin" +import ( + "github.com/blackstork-io/fabric/plugin" +) type Result plugin.ListData @@ -12,11 +14,39 @@ func (d Result) AsGoType() (result []string) { result = make([]string, len(d)) for i, s := range d { // The only way to modify resultsList is through append, so this is always correct - result[i] = string(s.(plugin.StringData)) + content := plugin.ParseContentData(s.(plugin.MapData)) + if content == nil { + continue + } + result[i] = content.Markdown } return } -func (d *Result) Append(s string) { - *d = append(*d, plugin.StringData(s)) +func (d *Result) Append(s *plugin.Content) { + if s == nil { + return + } + if s.Location != nil { + idx := s.Location.Index + if idx < 0 { + idx = 0 + } + if idx >= len(*d) { + *d = append(*d, s.AsData()) + return + } + switch s.Location.Effect { + case plugin.LocationEffectBefore: + *d = append((*d)[:idx], append(plugin.ListData{s.AsData()}, (*d)[idx:]...)...) + return + case plugin.LocationEffectAfter: + *d = append((*d)[:idx+1], append(plugin.ListData{s.AsData()}, (*d)[idx+1:]...)...) + return + default: + (*d)[idx] = s.AsData() + return + } + } + *d = append(*d, s.AsData()) } diff --git a/plugin/content.go b/plugin/content.go index 03bd4fd6..aa586a66 100644 --- a/plugin/content.go +++ b/plugin/content.go @@ -1,5 +1,99 @@ package plugin +type LocationEffect int + +const ( + LocationEffectUnspecified LocationEffect = iota + LocationEffectBefore + LocationEffectAfter +) + +type Location struct { + Index int + Effect LocationEffect +} + +func (loc *Location) AsData() Data { + if loc == nil { + return nil + } + return MapData{ + "index": NumberData(loc.Index), + "effect": NumberData(loc.Effect), + } +} + type Content struct { Markdown string + Location *Location + meta *ContentMeta +} + +func (c *Content) Meta() *ContentMeta { + if c == nil { + return nil + } + return c.meta +} + +func (c *Content) AsData() Data { + if c == nil { + return nil + } + return MapData{ + "markdown": StringData(c.Markdown), + "location": c.Location.AsData(), + "meta": c.meta.AsData(), + } +} + +type ContentMeta struct { + Provider string + Plugin string + Version string +} + +func (meta *ContentMeta) AsData() Data { + if meta == nil { + return nil + } + return MapData{ + "provider": StringData(meta.Provider), + } +} + +func ParseContentData(data MapData) *Content { + if data == nil { + return nil + } + md, _ := data["markdown"].(StringData) + return &Content{ + Markdown: string(md), + Location: ParseLocationData(data["location"]), + meta: ParseContentMeta(data["meta"]), + } +} + +func ParseLocationData(data Data) *Location { + if data == nil { + return nil + } + loc := data.(MapData) + index, _ := loc["index"].(NumberData) + effect, _ := loc["effect"].(NumberData) + return &Location{ + Index: int(index), + Effect: LocationEffect(effect), + } +} + +func ParseContentMeta(data Data) *ContentMeta { + if data == nil { + return nil + } + meta := data.(MapData) + provider, _ := meta["provider"].(StringData) + return &ContentMeta{ + Provider: string(provider), + } } diff --git a/plugin/content_provider.go b/plugin/content_provider.go index 930fd7fc..ab823fc7 100644 --- a/plugin/content_provider.go +++ b/plugin/content_provider.go @@ -26,10 +26,30 @@ func (cp ContentProviders) Validate() hcl.Diagnostics { return diags } +type InvocationOrder int + +const ( + InvocationOrderUnspecified InvocationOrder = iota + InvocationOrderBegin + InvocationOrderEnd +) + +func (order InvocationOrder) Weight() int { + switch order { + case InvocationOrderBegin: + return 0 + case InvocationOrderEnd: + return 2 + default: + return 1 + } +} + type ContentProvider struct { - ContentFunc ProvideContentFunc - Args hcldec.Spec - Config hcldec.Spec + ContentFunc ProvideContentFunc + Args hcldec.Spec + Config hcldec.Spec + InvocationOrder InvocationOrder } func (cg *ContentProvider) Validate() hcl.Diagnostics { diff --git a/plugin/pluginapi/v1/content.pb.go b/plugin/pluginapi/v1/content.pb.go index 2920ff6d..5b1aad8c 100644 --- a/plugin/pluginapi/v1/content.pb.go +++ b/plugin/pluginapi/v1/content.pb.go @@ -20,18 +20,123 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type LocationEffect int32 + +const ( + LocationEffect_LOCATION_EFFECT_UNSPECIFIED LocationEffect = 0 + LocationEffect_LOCATION_EFFECT_BEFORE LocationEffect = 1 + LocationEffect_LOCATION_EFFECT_AFTER LocationEffect = 2 +) + +// Enum value maps for LocationEffect. +var ( + LocationEffect_name = map[int32]string{ + 0: "LOCATION_EFFECT_UNSPECIFIED", + 1: "LOCATION_EFFECT_BEFORE", + 2: "LOCATION_EFFECT_AFTER", + } + LocationEffect_value = map[string]int32{ + "LOCATION_EFFECT_UNSPECIFIED": 0, + "LOCATION_EFFECT_BEFORE": 1, + "LOCATION_EFFECT_AFTER": 2, + } +) + +func (x LocationEffect) Enum() *LocationEffect { + p := new(LocationEffect) + *p = x + return p +} + +func (x LocationEffect) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (LocationEffect) Descriptor() protoreflect.EnumDescriptor { + return file_pluginapi_v1_content_proto_enumTypes[0].Descriptor() +} + +func (LocationEffect) Type() protoreflect.EnumType { + return &file_pluginapi_v1_content_proto_enumTypes[0] +} + +func (x LocationEffect) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use LocationEffect.Descriptor instead. +func (LocationEffect) EnumDescriptor() ([]byte, []int) { + return file_pluginapi_v1_content_proto_rawDescGZIP(), []int{0} +} + +type Location struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Index uint32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + Effect LocationEffect `protobuf:"varint,2,opt,name=effect,proto3,enum=pluginapi.v1.LocationEffect" json:"effect,omitempty"` +} + +func (x *Location) Reset() { + *x = Location{} + if protoimpl.UnsafeEnabled { + mi := &file_pluginapi_v1_content_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Location) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Location) ProtoMessage() {} + +func (x *Location) ProtoReflect() protoreflect.Message { + mi := &file_pluginapi_v1_content_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Location.ProtoReflect.Descriptor instead. +func (*Location) Descriptor() ([]byte, []int) { + return file_pluginapi_v1_content_proto_rawDescGZIP(), []int{0} +} + +func (x *Location) GetIndex() uint32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *Location) GetEffect() LocationEffect { + if x != nil { + return x.Effect + } + return LocationEffect_LOCATION_EFFECT_UNSPECIFIED +} + type Content struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Markdown string `protobuf:"bytes,1,opt,name=markdown,proto3" json:"markdown,omitempty"` + Markdown string `protobuf:"bytes,1,opt,name=markdown,proto3" json:"markdown,omitempty"` + Location *Location `protobuf:"bytes,2,opt,name=location,proto3" json:"location,omitempty"` } func (x *Content) Reset() { *x = Content{} if protoimpl.UnsafeEnabled { - mi := &file_pluginapi_v1_content_proto_msgTypes[0] + mi := &file_pluginapi_v1_content_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -44,7 +149,7 @@ func (x *Content) String() string { func (*Content) ProtoMessage() {} func (x *Content) ProtoReflect() protoreflect.Message { - mi := &file_pluginapi_v1_content_proto_msgTypes[0] + mi := &file_pluginapi_v1_content_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -57,7 +162,7 @@ func (x *Content) ProtoReflect() protoreflect.Message { // Deprecated: Use Content.ProtoReflect.Descriptor instead. func (*Content) Descriptor() ([]byte, []int) { - return file_pluginapi_v1_content_proto_rawDescGZIP(), []int{0} + return file_pluginapi_v1_content_proto_rawDescGZIP(), []int{1} } func (x *Content) GetMarkdown() string { @@ -67,26 +172,49 @@ func (x *Content) GetMarkdown() string { return "" } +func (x *Content) GetLocation() *Location { + if x != nil { + return x.Location + } + return nil +} + var File_pluginapi_v1_content_proto protoreflect.FileDescriptor var file_pluginapi_v1_content_proto_rawDesc = []byte{ 0x0a, 0x1a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x22, 0x25, 0x0a, 0x07, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, - 0x6e, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x6f, 0x72, 0x6b, 0x2d, 0x69, 0x6f, - 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x0c, - 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x50, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x50, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, - 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x22, 0x56, 0x0a, 0x08, 0x4c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x34, 0x0a, 0x06, + 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x66, 0x66, 0x65, 0x63, 0x74, 0x52, 0x06, 0x65, 0x66, 0x66, 0x65, + 0x63, 0x74, 0x22, 0x59, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x32, 0x0a, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x68, 0x0a, + 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x66, 0x66, 0x65, 0x63, 0x74, 0x12, + 0x1f, 0x0a, 0x1b, 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, 0x45, + 0x43, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x1a, 0x0a, 0x16, 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, + 0x45, 0x43, 0x54, 0x5f, 0x42, 0x45, 0x46, 0x4f, 0x52, 0x45, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, + 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, + 0x41, 0x46, 0x54, 0x45, 0x52, 0x10, 0x02, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x73, 0x74, + 0x6f, 0x72, 0x6b, 0x2d, 0x69, 0x6f, 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x2f, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2f, 0x76, + 0x31, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, + 0x50, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, + 0x56, 0x31, 0xca, 0x02, 0x0c, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, + 0x31, 0xe2, 0x02, 0x18, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, + 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x50, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -101,16 +229,21 @@ func file_pluginapi_v1_content_proto_rawDescGZIP() []byte { return file_pluginapi_v1_content_proto_rawDescData } -var file_pluginapi_v1_content_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_pluginapi_v1_content_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_pluginapi_v1_content_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_pluginapi_v1_content_proto_goTypes = []interface{}{ - (*Content)(nil), // 0: pluginapi.v1.Content + (LocationEffect)(0), // 0: pluginapi.v1.LocationEffect + (*Location)(nil), // 1: pluginapi.v1.Location + (*Content)(nil), // 2: pluginapi.v1.Content } var file_pluginapi_v1_content_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 0, // 0: pluginapi.v1.Location.effect:type_name -> pluginapi.v1.LocationEffect + 1, // 1: pluginapi.v1.Content.location:type_name -> pluginapi.v1.Location + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_pluginapi_v1_content_proto_init() } @@ -120,6 +253,18 @@ func file_pluginapi_v1_content_proto_init() { } if !protoimpl.UnsafeEnabled { file_pluginapi_v1_content_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Location); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pluginapi_v1_content_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Content); i { case 0: return &v.state @@ -137,13 +282,14 @@ func file_pluginapi_v1_content_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_pluginapi_v1_content_proto_rawDesc, - NumEnums: 0, - NumMessages: 1, + NumEnums: 1, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_pluginapi_v1_content_proto_goTypes, DependencyIndexes: file_pluginapi_v1_content_proto_depIdxs, + EnumInfos: file_pluginapi_v1_content_proto_enumTypes, MessageInfos: file_pluginapi_v1_content_proto_msgTypes, }.Build() File_pluginapi_v1_content_proto = out.File diff --git a/plugin/pluginapi/v1/content_decoder.go b/plugin/pluginapi/v1/content_decoder.go index 3ed31acc..b3acf666 100644 --- a/plugin/pluginapi/v1/content_decoder.go +++ b/plugin/pluginapi/v1/content_decoder.go @@ -8,5 +8,27 @@ func decodeContent(src *Content) *plugin.Content { } return &plugin.Content{ Markdown: src.GetMarkdown(), + Location: decodeLocation(src.GetLocation()), + } +} + +func decodeLocation(src *Location) *plugin.Location { + if src == nil { + return nil + } + return &plugin.Location{ + Index: int(src.GetIndex()), + Effect: decodeLocationEffect(src.GetEffect()), + } +} + +func decodeLocationEffect(src LocationEffect) plugin.LocationEffect { + switch src { + case LocationEffect_LOCATION_EFFECT_BEFORE: + return plugin.LocationEffectBefore + case LocationEffect_LOCATION_EFFECT_AFTER: + return plugin.LocationEffectAfter + default: + return plugin.LocationEffectUnspecified } } diff --git a/plugin/pluginapi/v1/content_encoder.go b/plugin/pluginapi/v1/content_encoder.go index 1a4e9c33..fe76195a 100644 --- a/plugin/pluginapi/v1/content_encoder.go +++ b/plugin/pluginapi/v1/content_encoder.go @@ -8,5 +8,27 @@ func encodeContent(src *plugin.Content) *Content { } return &Content{ Markdown: src.Markdown, + Location: encodeLocation(src.Location), + } +} + +func encodeLocation(src *plugin.Location) *Location { + if src == nil { + return nil + } + return &Location{ + Index: uint32(src.Index), + Effect: encodeLocationEffect(src.Effect), + } +} + +func encodeLocationEffect(src plugin.LocationEffect) LocationEffect { + switch src { + case plugin.LocationEffectBefore: + return LocationEffect_LOCATION_EFFECT_BEFORE + case plugin.LocationEffectAfter: + return LocationEffect_LOCATION_EFFECT_AFTER + default: + return LocationEffect_LOCATION_EFFECT_UNSPECIFIED } } diff --git a/plugin/pluginapi/v1/schema.pb.go b/plugin/pluginapi/v1/schema.pb.go index 94604ccf..d5d6ddee 100644 --- a/plugin/pluginapi/v1/schema.pb.go +++ b/plugin/pluginapi/v1/schema.pb.go @@ -20,6 +20,55 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type InvocationOrder int32 + +const ( + InvocationOrder_INVOCATION_ORDER_UNSPECIFIED InvocationOrder = 0 + InvocationOrder_INVOCATION_ORDER_BEGIN InvocationOrder = 2 + InvocationOrder_INVOCATION_ORDER_END InvocationOrder = 3 +) + +// Enum value maps for InvocationOrder. +var ( + InvocationOrder_name = map[int32]string{ + 0: "INVOCATION_ORDER_UNSPECIFIED", + 2: "INVOCATION_ORDER_BEGIN", + 3: "INVOCATION_ORDER_END", + } + InvocationOrder_value = map[string]int32{ + "INVOCATION_ORDER_UNSPECIFIED": 0, + "INVOCATION_ORDER_BEGIN": 2, + "INVOCATION_ORDER_END": 3, + } +) + +func (x InvocationOrder) Enum() *InvocationOrder { + p := new(InvocationOrder) + *p = x + return p +} + +func (x InvocationOrder) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (InvocationOrder) Descriptor() protoreflect.EnumDescriptor { + return file_pluginapi_v1_schema_proto_enumTypes[0].Descriptor() +} + +func (InvocationOrder) Type() protoreflect.EnumType { + return &file_pluginapi_v1_schema_proto_enumTypes[0] +} + +func (x InvocationOrder) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use InvocationOrder.Descriptor instead. +func (InvocationOrder) EnumDescriptor() ([]byte, []int) { + return file_pluginapi_v1_schema_proto_rawDescGZIP(), []int{0} +} + type Schema struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -152,8 +201,9 @@ type ContentProviderSchema struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Args *HclSpec `protobuf:"bytes,1,opt,name=args,proto3" json:"args,omitempty"` - Config *HclSpec `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` + Args *HclSpec `protobuf:"bytes,1,opt,name=args,proto3" json:"args,omitempty"` + Config *HclSpec `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` + InvocationOrder InvocationOrder `protobuf:"varint,3,opt,name=invocation_order,json=invocationOrder,proto3,enum=pluginapi.v1.InvocationOrder" json:"invocation_order,omitempty"` } func (x *ContentProviderSchema) Reset() { @@ -202,6 +252,13 @@ func (x *ContentProviderSchema) GetConfig() *HclSpec { return nil } +func (x *ContentProviderSchema) GetInvocationOrder() InvocationOrder { + if x != nil { + return x.InvocationOrder + } + return InvocationOrder_INVOCATION_ORDER_UNSPECIFIED +} + var File_pluginapi_v1_schema_proto protoreflect.FileDescriptor var file_pluginapi_v1_schema_proto_rawDesc = []byte{ @@ -242,26 +299,37 @@ var file_pluginapi_v1_schema_proto_rawDesc = []byte{ 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x63, 0x6c, 0x53, 0x70, 0x65, - 0x63, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x71, 0x0a, 0x15, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x12, 0x29, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x48, 0x63, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x2d, 0x0a, - 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x63, 0x6c, - 0x53, 0x70, 0x65, 0x63, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0xb1, 0x01, 0x0a, - 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x42, 0x0b, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x61, - 0x63, 0x6b, 0x73, 0x74, 0x6f, 0x72, 0x6b, 0x2d, 0x69, 0x6f, 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, - 0x63, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, - 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x76, - 0x31, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x61, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, - 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, - 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0xea, 0x02, 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xbb, 0x01, 0x0a, 0x15, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x12, 0x29, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x48, 0x63, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x2d, + 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x63, + 0x6c, 0x53, 0x70, 0x65, 0x63, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x48, 0x0a, + 0x10, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x0f, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2a, 0x69, 0x0a, 0x0f, 0x49, 0x6e, 0x76, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x1c, 0x49, 0x4e, + 0x56, 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, + 0x49, 0x4e, 0x56, 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x52, 0x44, 0x45, 0x52, + 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x49, 0x4e, 0x56, 0x4f, + 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x45, 0x4e, 0x44, + 0x10, 0x03, 0x42, 0xb1, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x6f, 0x72, 0x6b, 0x2d, 0x69, 0x6f, + 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x0c, + 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x50, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x50, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, + 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -276,29 +344,32 @@ func file_pluginapi_v1_schema_proto_rawDescGZIP() []byte { return file_pluginapi_v1_schema_proto_rawDescData } +var file_pluginapi_v1_schema_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_pluginapi_v1_schema_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_pluginapi_v1_schema_proto_goTypes = []interface{}{ - (*Schema)(nil), // 0: pluginapi.v1.Schema - (*DataSourceSchema)(nil), // 1: pluginapi.v1.DataSourceSchema - (*ContentProviderSchema)(nil), // 2: pluginapi.v1.ContentProviderSchema - nil, // 3: pluginapi.v1.Schema.DataSourcesEntry - nil, // 4: pluginapi.v1.Schema.ContentProvidersEntry - (*HclSpec)(nil), // 5: pluginapi.v1.HclSpec + (InvocationOrder)(0), // 0: pluginapi.v1.InvocationOrder + (*Schema)(nil), // 1: pluginapi.v1.Schema + (*DataSourceSchema)(nil), // 2: pluginapi.v1.DataSourceSchema + (*ContentProviderSchema)(nil), // 3: pluginapi.v1.ContentProviderSchema + nil, // 4: pluginapi.v1.Schema.DataSourcesEntry + nil, // 5: pluginapi.v1.Schema.ContentProvidersEntry + (*HclSpec)(nil), // 6: pluginapi.v1.HclSpec } var file_pluginapi_v1_schema_proto_depIdxs = []int32{ - 3, // 0: pluginapi.v1.Schema.data_sources:type_name -> pluginapi.v1.Schema.DataSourcesEntry - 4, // 1: pluginapi.v1.Schema.content_providers:type_name -> pluginapi.v1.Schema.ContentProvidersEntry - 5, // 2: pluginapi.v1.DataSourceSchema.args:type_name -> pluginapi.v1.HclSpec - 5, // 3: pluginapi.v1.DataSourceSchema.config:type_name -> pluginapi.v1.HclSpec - 5, // 4: pluginapi.v1.ContentProviderSchema.args:type_name -> pluginapi.v1.HclSpec - 5, // 5: pluginapi.v1.ContentProviderSchema.config:type_name -> pluginapi.v1.HclSpec - 1, // 6: pluginapi.v1.Schema.DataSourcesEntry.value:type_name -> pluginapi.v1.DataSourceSchema - 2, // 7: pluginapi.v1.Schema.ContentProvidersEntry.value:type_name -> pluginapi.v1.ContentProviderSchema - 8, // [8:8] is the sub-list for method output_type - 8, // [8:8] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 4, // 0: pluginapi.v1.Schema.data_sources:type_name -> pluginapi.v1.Schema.DataSourcesEntry + 5, // 1: pluginapi.v1.Schema.content_providers:type_name -> pluginapi.v1.Schema.ContentProvidersEntry + 6, // 2: pluginapi.v1.DataSourceSchema.args:type_name -> pluginapi.v1.HclSpec + 6, // 3: pluginapi.v1.DataSourceSchema.config:type_name -> pluginapi.v1.HclSpec + 6, // 4: pluginapi.v1.ContentProviderSchema.args:type_name -> pluginapi.v1.HclSpec + 6, // 5: pluginapi.v1.ContentProviderSchema.config:type_name -> pluginapi.v1.HclSpec + 0, // 6: pluginapi.v1.ContentProviderSchema.invocation_order:type_name -> pluginapi.v1.InvocationOrder + 2, // 7: pluginapi.v1.Schema.DataSourcesEntry.value:type_name -> pluginapi.v1.DataSourceSchema + 3, // 8: pluginapi.v1.Schema.ContentProvidersEntry.value:type_name -> pluginapi.v1.ContentProviderSchema + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_pluginapi_v1_schema_proto_init() } @@ -350,13 +421,14 @@ func file_pluginapi_v1_schema_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_pluginapi_v1_schema_proto_rawDesc, - NumEnums: 0, + NumEnums: 1, NumMessages: 5, NumExtensions: 0, NumServices: 0, }, GoTypes: file_pluginapi_v1_schema_proto_goTypes, DependencyIndexes: file_pluginapi_v1_schema_proto_depIdxs, + EnumInfos: file_pluginapi_v1_schema_proto_enumTypes, MessageInfos: file_pluginapi_v1_schema_proto_msgTypes, }.Build() File_pluginapi_v1_schema_proto = out.File diff --git a/plugin/pluginapi/v1/schema_decoder.go b/plugin/pluginapi/v1/schema_decoder.go index e080762a..3afc4b88 100644 --- a/plugin/pluginapi/v1/schema_decoder.go +++ b/plugin/pluginapi/v1/schema_decoder.go @@ -77,7 +77,19 @@ func decodeContentProviderSchema(src *ContentProviderSchema) (*plugin.ContentPro return nil, err } return &plugin.ContentProvider{ - Args: args, - Config: config, + Args: args, + Config: config, + InvocationOrder: decodeInvocationOrder(src.InvocationOrder), }, nil } + +func decodeInvocationOrder(src InvocationOrder) plugin.InvocationOrder { + switch src { + case InvocationOrder_INVOCATION_ORDER_BEGIN: + return plugin.InvocationOrderBegin + case InvocationOrder_INVOCATION_ORDER_END: + return plugin.InvocationOrderEnd + default: + return plugin.InvocationOrderUnspecified + } +} diff --git a/plugin/pluginapi/v1/schema_encoder.go b/plugin/pluginapi/v1/schema_encoder.go index 6f0e4e04..4c86dae0 100644 --- a/plugin/pluginapi/v1/schema_encoder.go +++ b/plugin/pluginapi/v1/schema_encoder.go @@ -77,7 +77,19 @@ func encodeContentProviderSchema(src *plugin.ContentProvider) (*ContentProviderS return nil, err } return &ContentProviderSchema{ - Args: args, - Config: config, + Args: args, + Config: config, + InvocationOrder: encodeInvocationOrder(src.InvocationOrder), }, nil } + +func encodeInvocationOrder(src plugin.InvocationOrder) InvocationOrder { + switch src { + case plugin.InvocationOrderBegin: + return InvocationOrder_INVOCATION_ORDER_BEGIN + case plugin.InvocationOrderEnd: + return InvocationOrder_INVOCATION_ORDER_END + default: + return InvocationOrder_INVOCATION_ORDER_UNSPECIFIED + } +} diff --git a/plugin/schema.go b/plugin/schema.go index e130bf89..5c0d7fa4 100644 --- a/plugin/schema.go +++ b/plugin/schema.go @@ -87,5 +87,14 @@ func (p *Schema) ProvideContent(ctx context.Context, name string, params *Provid Detail: "Content provider '" + name + "' not found in schema", }} } - return provider.Execute(ctx, params) + content, diags := provider.Execute(ctx, params) + if diags.HasErrors() { + return nil, diags + } + content.meta = &ContentMeta{ + Provider: name, + Plugin: p.Name, + Version: p.Version, + } + return content, diags } diff --git a/proto/pluginapi/v1/content.proto b/proto/pluginapi/v1/content.proto index db976d05..b49e253d 100644 --- a/proto/pluginapi/v1/content.proto +++ b/proto/pluginapi/v1/content.proto @@ -2,6 +2,19 @@ syntax = "proto3"; package pluginapi.v1; + +enum LocationEffect { + LOCATION_EFFECT_UNSPECIFIED = 0; + LOCATION_EFFECT_BEFORE = 1; + LOCATION_EFFECT_AFTER = 2; +} + +message Location { + uint32 index = 1; + LocationEffect effect = 2; +} + message Content { - string markdown = 1; + string markdown = 1; + Location location = 2; } diff --git a/proto/pluginapi/v1/schema.proto b/proto/pluginapi/v1/schema.proto index 8ce045f7..5c5bb670 100644 --- a/proto/pluginapi/v1/schema.proto +++ b/proto/pluginapi/v1/schema.proto @@ -17,7 +17,14 @@ message DataSourceSchema { HclSpec config = 2; } +enum InvocationOrder { + INVOCATION_ORDER_UNSPECIFIED = 0; + INVOCATION_ORDER_BEGIN = 2; + INVOCATION_ORDER_END = 3; +} + message ContentProviderSchema { - HclSpec args = 1; - HclSpec config = 2; + HclSpec args = 1; + HclSpec config = 2; + InvocationOrder invocation_order = 3; } \ No newline at end of file From 127c4cd88df109702ba3f2c44b053df1585a02fb Mon Sep 17 00:00:00 2001 From: dobarx Date: Fri, 22 Mar 2024 11:22:23 +0200 Subject: [PATCH 2/7] Update TOC plugin rendering --- examples/templates/toc/example.fabric | 13 +++--- internal/builtin/content_toc.go | 17 ++++--- internal/builtin/content_toc_test.go | 64 ++++++++++++++++++++------- 3 files changed, 67 insertions(+), 27 deletions(-) diff --git a/examples/templates/toc/example.fabric b/examples/templates/toc/example.fabric index 2a297a91..f0ab3636 100644 --- a/examples/templates/toc/example.fabric +++ b/examples/templates/toc/example.fabric @@ -1,7 +1,11 @@ document "example" { title = "Using TOC content provider" - + content toc { + start_level = 1 + end_level = 4 + ordered = true + } content text { text = "Sub title 1" format_as = "title" @@ -17,6 +21,7 @@ document "example" { format_as = "title" absolute_title_size = 2 } + content text { text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." } @@ -40,9 +45,5 @@ document "example" { content text { text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." } - content toc { - start_level = 1 - end_level = 4 - ordered = true - } + } \ No newline at end of file diff --git a/internal/builtin/content_toc.go b/internal/builtin/content_toc.go index 4dbe69d3..47756521 100644 --- a/internal/builtin/content_toc.go +++ b/internal/builtin/content_toc.go @@ -49,7 +49,8 @@ func makeTOCContentProvider() *plugin.ContentProvider { Required: false, }, }, - ContentFunc: genTOC, + InvocationOrder: plugin.InvocationOrderEnd, + ContentFunc: genTOC, } } @@ -152,6 +153,10 @@ func genTOC(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.C return &plugin.Content{ Markdown: titles.render(0, args.ordered), + Location: &plugin.Location{ + Index: 0, + Effect: plugin.LocationEffectBefore, + }, }, nil } @@ -209,16 +214,18 @@ func parseContentTitles(data plugin.Data, startLvl, endLvl int) (tocNodeList, er } var result tocNodeList for _, item := range list { - line, ok := item.(plugin.StringData) + elem, ok := item.(plugin.MapData) if !ok { return nil, fmt.Errorf("expected a string") } - if strings.HasPrefix(string(line), "#") { - level := strings.Count(string(line), "#") + content := plugin.ParseContentData(elem) + line := strings.TrimSpace(content.Markdown) + if strings.HasPrefix(line, "#") { + level := strings.Count(line, "#") if level < startLvl || level > endLvl { continue } - title := strings.TrimSpace(string(line)[level:]) + title := strings.TrimSpace(line[level:]) result = result.add(tocNode{level: level, title: title}) } } diff --git a/internal/builtin/content_toc_test.go b/internal/builtin/content_toc_test.go index f70a0009..d76d9b94 100644 --- a/internal/builtin/content_toc_test.go +++ b/internal/builtin/content_toc_test.go @@ -44,12 +44,24 @@ func (s *TOCContentTestSuite) TestSimple() { DataContext: plugin.MapData{ "document": plugin.MapData{ "content": plugin.ListData{ - plugin.StringData("# Header 1"), - plugin.StringData("Lorem ipsum dolor sit amet, consectetur adipiscing elit."), - plugin.StringData("## Header 2"), - plugin.StringData("Vestibulum nec odio."), - plugin.StringData("### Header 3"), - plugin.StringData("Integer sit amet."), + plugin.MapData{ + "markdown": plugin.StringData("# Header 1"), + }, + plugin.MapData{ + "markdown": plugin.StringData("Lorem ipsum dolor sit amet, consectetur adipiscing elit."), + }, + plugin.MapData{ + "markdown": plugin.StringData("## Header 2"), + }, + plugin.MapData{ + "markdown": plugin.StringData("Vestibulum nec odio."), + }, + plugin.MapData{ + "markdown": plugin.StringData("### Header 3"), + }, + plugin.MapData{ + "markdown": plugin.StringData("Integer sit amet."), + }, }, }, }, @@ -75,16 +87,36 @@ func (s *TOCContentTestSuite) TestAdvanced() { DataContext: plugin.MapData{ "document": plugin.MapData{ "content": plugin.ListData{ - plugin.StringData("# Header 1"), - plugin.StringData("Lorem ipsum dolor sit amet, consectetur adipiscing elit."), - plugin.StringData("## Header 2"), - plugin.StringData("Vestibulum nec odio."), - plugin.StringData("### Header 3"), - plugin.StringData("Integer sit amet."), - plugin.StringData("## Header 4"), - plugin.StringData("Vestibulum nec odio."), - plugin.StringData("## Header 5"), - plugin.StringData("Vestibulum nec odio."), + plugin.MapData{ + "markdown": plugin.StringData("# Header 1"), + }, + plugin.MapData{ + "markdown": plugin.StringData("Lorem ipsum dolor sit amet, consectetur adipiscing elit."), + }, + plugin.MapData{ + "markdown": plugin.StringData("## Header 2"), + }, + plugin.MapData{ + "markdown": plugin.StringData("Vestibulum nec odio."), + }, + plugin.MapData{ + "markdown": plugin.StringData("### Header 3"), + }, + plugin.MapData{ + "markdown": plugin.StringData("Integer sit amet."), + }, + plugin.MapData{ + "markdown": plugin.StringData("## Header 4"), + }, + plugin.MapData{ + "markdown": plugin.StringData("Vestibulum nec odio."), + }, + plugin.MapData{ + "markdown": plugin.StringData("## Header 5"), + }, + plugin.MapData{ + "markdown": plugin.StringData("Vestibulum nec odio."), + }, }, }, }, From 9fa8e603540d0f070a573695acc0ca2481397114 Mon Sep 17 00:00:00 2001 From: dobarx Date: Fri, 22 Mar 2024 11:22:42 +0200 Subject: [PATCH 3/7] Update FrontMatter plugin rendering --- examples/templates/frontmatter/example.fabric | 34 +++++++++++++++++++ internal/builtin/content_frontmatter.go | 4 +++ 2 files changed, 38 insertions(+) create mode 100644 examples/templates/frontmatter/example.fabric diff --git a/examples/templates/frontmatter/example.fabric b/examples/templates/frontmatter/example.fabric new file mode 100644 index 00000000..45a8cf54 --- /dev/null +++ b/examples/templates/frontmatter/example.fabric @@ -0,0 +1,34 @@ + +document "example" { + title = "Using FrontMatter content provider" + + content frontmatter { + format = "yaml" + content = { + some = "value" + other = "value2" + } + } + + content text { + text = "Sub title 1" + format_as = "title" + absolute_title_size = 2 + } + + content text { + text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." + } + + content text { + text = "Sub title 2" + format_as = "title" + absolute_title_size = 2 + } + + content text { + text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." + } + + +} \ No newline at end of file diff --git a/internal/builtin/content_frontmatter.go b/internal/builtin/content_frontmatter.go index 9ca1c394..2ab0c906 100644 --- a/internal/builtin/content_frontmatter.go +++ b/internal/builtin/content_frontmatter.go @@ -61,6 +61,10 @@ func genFrontMatterContent(ctx context.Context, params *plugin.ProvideContentPar } return &plugin.Content{ Markdown: result, + Location: &plugin.Location{ + Index: 0, + Effect: plugin.LocationEffectBefore, + }, }, nil } From c19acd1ac9e44b58120c78007d61345bb5bf6dc1 Mon Sep 17 00:00:00 2001 From: dobarx Date: Fri, 22 Mar 2024 11:23:09 +0200 Subject: [PATCH 4/7] Update Text plugin rendering --- examples/templates/text/example.fabric | 31 ++++++++++++++++++ internal/builtin/content_text.go | 32 +++++++++++++++++- internal/builtin/content_text_test.go | 45 ++++++++++++-------------- 3 files changed, 82 insertions(+), 26 deletions(-) create mode 100644 examples/templates/text/example.fabric diff --git a/examples/templates/text/example.fabric b/examples/templates/text/example.fabric new file mode 100644 index 00000000..720fc8de --- /dev/null +++ b/examples/templates/text/example.fabric @@ -0,0 +1,31 @@ + +document "example" { + title = "Using Text content provider" + + content text { + text = "Title 1" + format_as = "title" + } + + content text { + text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." + } + + content text { + text = "Sub title 2" + format_as = "title" + } + + content text { + text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." + } + content text { + text = "Sub title 3" + format_as = "title" + } + + content text { + text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." + } + +} diff --git a/internal/builtin/content_text.go b/internal/builtin/content_text.go index b9c89858..f6fb5dd4 100644 --- a/internal/builtin/content_text.go +++ b/internal/builtin/content_text.go @@ -76,7 +76,7 @@ func genTextContent(ctx context.Context, params *plugin.ProvideContentParams) (* } absoluteTitleSize := params.Args.GetAttr("absolute_title_size") if absoluteTitleSize.IsNull() { - absoluteTitleSize = cty.NumberIntVal(textDefaultAbsoluteTitleSize) + absoluteTitleSize = cty.NumberIntVal(findDefaultTitleSize(params.DataContext)) } titleSize, _ := absoluteTitleSize.AsBigFloat().Int64() if titleSize < textMinAbsoluteTitleSize || titleSize > textMaxAbsoluteTitleSize { @@ -122,6 +122,36 @@ func genTextContent(ctx context.Context, params *plugin.ProvideContentParams) (* }, nil } +func findDefaultTitleSize(datactx plugin.MapData) int64 { + doc, ok := datactx["document"] + if !ok { + return textDefaultAbsoluteTitleSize + } + content, ok := doc.(plugin.MapData)["content"] + if !ok { + return textDefaultAbsoluteTitleSize + } + list, ok := content.(plugin.ListData) + if !ok { + return textDefaultAbsoluteTitleSize + } + n := textMinAbsoluteTitleSize + for _, elem := range list { + content := plugin.ParseContentData(elem.(plugin.MapData)) + if content == nil { + continue + } + md := strings.TrimSpace(content.Markdown) + if strings.HasPrefix(md, "#") { + n++ + } + } + if n > textMaxAbsoluteTitleSize { + n = textMaxAbsoluteTitleSize + } + return n +} + func genTextContentText(text string, datactx plugin.MapData) (string, error) { tmpl, err := template.New("text").Parse(text) if err != nil { diff --git a/internal/builtin/content_text_test.go b/internal/builtin/content_text_test.go index 4efa8faa..01f273ec 100644 --- a/internal/builtin/content_text_test.go +++ b/internal/builtin/content_text_test.go @@ -13,7 +13,7 @@ import ( type TextGeneratorTestSuite struct { suite.Suite - plugin *plugin.Schema + schema *plugin.ContentProvider } func TestTextGeneratorSuite(t *testing.T) { @@ -21,23 +21,18 @@ func TestTextGeneratorSuite(t *testing.T) { } func (s *TextGeneratorTestSuite) SetupSuite() { - s.plugin = &plugin.Schema{ - ContentProviders: plugin.ContentProviders{ - "text": makeTextContentProvider(), - }, - } + s.schema = makeTextContentProvider() } func (s *TextGeneratorTestSuite) TestSchema() { - schema := makeTextContentProvider() - s.Nil(schema.Config) - s.NotNil(schema.Args) - s.NotNil(schema.ContentFunc) + s.Nil(s.schema.Config) + s.NotNil(s.schema.Args) + s.NotNil(s.schema.ContentFunc) } func (s *TextGeneratorTestSuite) TestMissingText() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.NullVal(cty.String), "format_as": cty.NullVal(cty.String), @@ -58,7 +53,7 @@ func (s *TextGeneratorTestSuite) TestMissingText() { func (s *TextGeneratorTestSuite) TestBasic() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal("Hello {{.name}}!"), "format_as": cty.NullVal(cty.String), @@ -77,7 +72,7 @@ func (s *TextGeneratorTestSuite) TestBasic() { func (s *TextGeneratorTestSuite) TestNoTemplate() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal("Hello World!"), "format_as": cty.NullVal(cty.String), @@ -94,7 +89,7 @@ func (s *TextGeneratorTestSuite) TestNoTemplate() { func (s *TextGeneratorTestSuite) TestTitleDefault() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal("Hello {{.name}}!"), "format_as": cty.StringVal("title"), @@ -113,7 +108,7 @@ func (s *TextGeneratorTestSuite) TestTitleDefault() { func (s *TextGeneratorTestSuite) TestTitleWithTextMultiline() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal("Hello\n{{.name}}\nfor you!"), "format_as": cty.StringVal("title"), @@ -132,7 +127,7 @@ func (s *TextGeneratorTestSuite) TestTitleWithTextMultiline() { func (s *TextGeneratorTestSuite) TestTitleWithSize() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal("Hello {{.name}}!"), "format_as": cty.StringVal("title"), @@ -151,7 +146,7 @@ func (s *TextGeneratorTestSuite) TestTitleWithSize() { func (s *TextGeneratorTestSuite) TestTitleWithSizeTooSmall() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal("Hello {{.name}}!"), "format_as": cty.StringVal("title"), @@ -172,7 +167,7 @@ func (s *TextGeneratorTestSuite) TestTitleWithSizeTooSmall() { func (s *TextGeneratorTestSuite) TestTitleWithSizeTooBig() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal("Hello {{.name}}!"), "format_as": cty.StringVal("title"), @@ -193,7 +188,7 @@ func (s *TextGeneratorTestSuite) TestTitleWithSizeTooBig() { func (s *TextGeneratorTestSuite) TestCallInvalidFormat() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal("Hello World!"), "format_as": cty.StringVal("unknown"), @@ -212,7 +207,7 @@ func (s *TextGeneratorTestSuite) TestCallInvalidFormat() { func (s *TextGeneratorTestSuite) TestCallInvalidTemplate() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal("Hello {{.name}!"), "format_as": cty.NullVal(cty.String), @@ -233,7 +228,7 @@ func (s *TextGeneratorTestSuite) TestCallInvalidTemplate() { func (s *TextGeneratorTestSuite) TestCallCodeDefault() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal(`Hello {{.name}}!`), "format_as": cty.StringVal("code"), @@ -252,7 +247,7 @@ func (s *TextGeneratorTestSuite) TestCallCodeDefault() { func (s *TextGeneratorTestSuite) TestCallCodeWithLanguage() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal(`{"hello": "{{.name}}"}`), "format_as": cty.StringVal("code"), @@ -271,7 +266,7 @@ func (s *TextGeneratorTestSuite) TestCallCodeWithLanguage() { func (s *TextGeneratorTestSuite) TestCallBlockquote() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal(`Hello {{.name}}!`), "format_as": cty.StringVal("blockquote"), @@ -290,7 +285,7 @@ func (s *TextGeneratorTestSuite) TestCallBlockquote() { func (s *TextGeneratorTestSuite) TestCallBlockquoteMultiline() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal("Hello\n{{.name}}\nfor you!"), "format_as": cty.StringVal("blockquote"), @@ -309,7 +304,7 @@ func (s *TextGeneratorTestSuite) TestCallBlockquoteMultiline() { func (s *TextGeneratorTestSuite) TestCallBlockquoteMultilineDoubleNewline() { ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "text": cty.StringVal("Hello\n{{.name}}\n\nfor you!"), "format_as": cty.StringVal("blockquote"), From a5c293f08bb3173331bc3908d9ef09cdc73f71fa Mon Sep 17 00:00:00 2001 From: dobarx Date: Fri, 22 Mar 2024 11:23:18 +0200 Subject: [PATCH 5/7] Cleanup --- cmd/lint.go | 8 +++-- internal/builtin/content_image_test.go | 16 ++++----- internal/builtin/content_list_test.go | 39 +++++++++------------ internal/builtin/content_table_test.go | 37 +++++++++---------- internal/builtin/plugin.go | 4 ++- internal/elastic/data_elasticsearch_test.go | 20 +++++------ internal/openai/content_openai_text_test.go | 27 +++++++------- test/e2e/render_test.go | 8 ++--- tools/docgen/main.go | 12 +++---- 9 files changed, 80 insertions(+), 91 deletions(-) diff --git a/cmd/lint.go b/cmd/lint.go index c862b2a2..abbcbd1c 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -16,8 +16,12 @@ import ( type noopPluginCaller struct{} // CallContent implements evaluation.PluginCaller. -func (n *noopPluginCaller) CallContent(ctx context.Context, name string, config evaluation.Configuration, invocation evaluation.Invocation, context plugin.MapData) (result string, diag diagnostics.Diag) { - return "", nil +func (n *noopPluginCaller) CallContent(ctx context.Context, name string, config evaluation.Configuration, invocation evaluation.Invocation, context plugin.MapData) (result *plugin.Content, diag diagnostics.Diag) { + return nil, nil +} + +func (n *noopPluginCaller) ContentInvocationOrder(ctx context.Context, name string) (order plugin.InvocationOrder, diag diagnostics.Diag) { + return plugin.InvocationOrderUnspecified, nil } // CallData implements evaluation.PluginCaller. diff --git a/internal/builtin/content_image_test.go b/internal/builtin/content_image_test.go index 77b91c0b..9a5606ef 100644 --- a/internal/builtin/content_image_test.go +++ b/internal/builtin/content_image_test.go @@ -13,7 +13,7 @@ import ( type ImageGeneratorTestSuite struct { suite.Suite - plugin *plugin.Schema + schema *plugin.ContentProvider } func TestImageGeneratorTestSuite(t *testing.T) { @@ -21,11 +21,7 @@ func TestImageGeneratorTestSuite(t *testing.T) { } func (s *ImageGeneratorTestSuite) SetupSuite() { - s.plugin = &plugin.Schema{ - ContentProviders: plugin.ContentProviders{ - "image": makeImageContentProvider(), - }, - } + s.schema = makeImageContentProvider() } func (s *ImageGeneratorTestSuite) TestSchema() { @@ -41,7 +37,7 @@ func (s *ImageGeneratorTestSuite) TestMissingImageSource() { "alt": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "image", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, }) s.Nil(content) @@ -58,7 +54,7 @@ func (s *ImageGeneratorTestSuite) TestCallImageSourceEmpty() { "alt": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "image", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, }) s.Nil(content) @@ -75,7 +71,7 @@ func (s *ImageGeneratorTestSuite) TestCallImageSourceValid() { "alt": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "image", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, }) s.Equal(&plugin.Content{ @@ -90,7 +86,7 @@ func (s *ImageGeneratorTestSuite) TestCallImageSourceValidWithAlt() { "alt": cty.StringVal("alt text"), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "image", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, }) s.Equal(&plugin.Content{ diff --git a/internal/builtin/content_list_test.go b/internal/builtin/content_list_test.go index cb412acd..6e1f398c 100644 --- a/internal/builtin/content_list_test.go +++ b/internal/builtin/content_list_test.go @@ -13,7 +13,7 @@ import ( type ListGeneratorTestSuite struct { suite.Suite - plugin *plugin.Schema + schema *plugin.ContentProvider } func TestListGeneratorTestSuite(t *testing.T) { @@ -21,19 +21,14 @@ func TestListGeneratorTestSuite(t *testing.T) { } func (s *ListGeneratorTestSuite) SetupSuite() { - s.plugin = &plugin.Schema{ - ContentProviders: plugin.ContentProviders{ - "list": makeListContentProvider(), - }, - } + s.schema = makeListContentProvider() } func (s *ListGeneratorTestSuite) TestSchema() { - schema := s.plugin.ContentProviders["list"] - s.NotNil(schema) - s.Nil(schema.Config) - s.NotNil(schema.Args) - s.NotNil(schema.ContentFunc) + s.NotNil(s.schema) + s.Nil(s.schema.Config) + s.NotNil(s.schema.Args) + s.NotNil(s.schema.ContentFunc) } func (s *ListGeneratorTestSuite) TestNilQueryResult() { @@ -42,7 +37,7 @@ func (s *ListGeneratorTestSuite) TestNilQueryResult() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "list", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{}, }) @@ -60,7 +55,7 @@ func (s *ListGeneratorTestSuite) TestNonArrayQueryResult() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "list", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.StringData("not_an_array"), @@ -80,7 +75,7 @@ func (s *ListGeneratorTestSuite) TestUnordered() { "format": cty.StringVal("unordered"), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "list", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{ @@ -101,7 +96,7 @@ func (s *ListGeneratorTestSuite) TestOrdered() { "format": cty.StringVal("ordered"), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "list", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{ @@ -122,7 +117,7 @@ func (s *ListGeneratorTestSuite) TestTaskList() { "format": cty.StringVal("tasklist"), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "list", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{ @@ -143,7 +138,7 @@ func (s *ListGeneratorTestSuite) TestBasic() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "list", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{ @@ -164,7 +159,7 @@ func (s *ListGeneratorTestSuite) TestAdvanced() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "list", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{ @@ -191,7 +186,7 @@ func (s *ListGeneratorTestSuite) TestEmptyQueryResult() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "list", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{}, @@ -209,7 +204,7 @@ func (s *ListGeneratorTestSuite) TestMissingItemTemplate() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "list", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{}, @@ -229,7 +224,7 @@ func (s *ListGeneratorTestSuite) TestInvalidFormat() { "format": cty.StringVal("invalid"), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "list", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{}, @@ -249,7 +244,7 @@ func (s *ListGeneratorTestSuite) TestMissingDataContext() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "list", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, }) s.Nil(content) diff --git a/internal/builtin/content_table_test.go b/internal/builtin/content_table_test.go index 8e7f72de..56444002 100644 --- a/internal/builtin/content_table_test.go +++ b/internal/builtin/content_table_test.go @@ -13,7 +13,7 @@ import ( type TableGeneratorTestSuite struct { suite.Suite - plugin *plugin.Schema + schema *plugin.ContentProvider } func TestTableGeneratorTestSuite(t *testing.T) { @@ -21,19 +21,14 @@ func TestTableGeneratorTestSuite(t *testing.T) { } func (s *TableGeneratorTestSuite) SetupSuite() { - s.plugin = &plugin.Schema{ - ContentProviders: plugin.ContentProviders{ - "table": makeTableContentProvider(), - }, - } + s.schema = makeTableContentProvider() } func (s *TableGeneratorTestSuite) TestSchema() { - schema := s.plugin.ContentProviders["table"] - s.NotNil(schema) - s.Nil(schema.Config) - s.NotNil(schema.Args) - s.NotNil(schema.ContentFunc) + s.NotNil(s.schema) + s.Nil(s.schema.Config) + s.NotNil(s.schema.Args) + s.NotNil(s.schema.ContentFunc) } func (s *TableGeneratorTestSuite) TestNilQueryResult() { @@ -50,7 +45,7 @@ func (s *TableGeneratorTestSuite) TestNilQueryResult() { }), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "table", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -77,7 +72,7 @@ func (s *TableGeneratorTestSuite) TestEmptyQueryResult() { }), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "table", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -104,7 +99,7 @@ func (s *TableGeneratorTestSuite) TestBasic() { }), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "table", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -138,7 +133,7 @@ func (s *TableGeneratorTestSuite) TestMissingHeader() { }), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "table", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -176,7 +171,7 @@ func (s *TableGeneratorTestSuite) TestNilHeader() { }), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "table", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -214,7 +209,7 @@ func (s *TableGeneratorTestSuite) TestNilValue() { }), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "table", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -243,7 +238,7 @@ func (s *TableGeneratorTestSuite) TestNilColumns() { "columns": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{}))), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "table", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -272,7 +267,7 @@ func (s *TableGeneratorTestSuite) TestEmptyColumns() { "columns": cty.ListValEmpty(cty.Object(map[string]cty.Type{})), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "table", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -310,7 +305,7 @@ func (s *TableGeneratorTestSuite) TestInvalidHeaderTemplate() { }), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "table", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -348,7 +343,7 @@ func (s *TableGeneratorTestSuite) TestInvalidValueTemplate() { }), }) ctx := context.Background() - content, diags := s.plugin.ProvideContent(ctx, "table", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), diff --git a/internal/builtin/plugin.go b/internal/builtin/plugin.go index f095f89b..0c15dfc5 100644 --- a/internal/builtin/plugin.go +++ b/internal/builtin/plugin.go @@ -4,9 +4,11 @@ import ( "github.com/blackstork-io/fabric/plugin" ) +const Name = "blackstork/builtin" + func Plugin(version string) *plugin.Schema { return &plugin.Schema{ - Name: "blackstork/builtin", + Name: Name, Version: version, DataSources: plugin.DataSources{ "csv": makeCSVDataSource(), diff --git a/internal/elastic/data_elasticsearch_test.go b/internal/elastic/data_elasticsearch_test.go index 3fd3c59f..7a33df14 100644 --- a/internal/elastic/data_elasticsearch_test.go +++ b/internal/elastic/data_elasticsearch_test.go @@ -23,7 +23,7 @@ type IntegrationTestSuite struct { suite.Suite container *elasticsearch.ElasticsearchContainer client *es.Client - plugin *plugin.Schema + schema *plugin.DataSource cfg cty.Value ctx context.Context } @@ -65,7 +65,7 @@ func (s *IntegrationTestSuite) SetupSuite() { "bearer_auth": cty.NullVal(cty.String), "ca_certs": cty.StringVal(string(s.container.Settings.CACert)), }) - s.plugin = Plugin("") + s.schema = makeElasticSearchDataSource() } func (s *IntegrationTestSuite) TearDownSuite() { @@ -121,7 +121,7 @@ func (s *IntegrationTestSuite) TestSearchDefaults() { "fields": cty.NullVal(cty.String), "size": cty.NullVal(cty.Number), }) - data, diags := s.plugin.RetrieveData(s.ctx, "elasticsearch", &plugin.RetrieveDataParams{ + data, diags := s.schema.DataFunc(s.ctx, &plugin.RetrieveDataParams{ Config: s.cfg, Args: args, }) @@ -180,7 +180,7 @@ func (s *IntegrationTestSuite) TestSearchFields() { "fields": cty.ListVal([]cty.Value{cty.StringVal("name"), cty.StringVal("age")}), "size": cty.NullVal(cty.Number), }) - data, diags := s.plugin.RetrieveData(s.ctx, "elasticsearch", &plugin.RetrieveDataParams{ + data, diags := s.schema.DataFunc(s.ctx, &plugin.RetrieveDataParams{ Config: s.cfg, Args: args, }) @@ -237,7 +237,7 @@ func (s *IntegrationTestSuite) TestSearchQueryString() { "fields": cty.NullVal(cty.String), "size": cty.NullVal(cty.Number), }) - data, diags := s.plugin.RetrieveData(s.ctx, "elasticsearch", &plugin.RetrieveDataParams{ + data, diags := s.schema.DataFunc(s.ctx, &plugin.RetrieveDataParams{ Config: s.cfg, Args: args, }) @@ -293,7 +293,7 @@ func (s *IntegrationTestSuite) TestSearchQuery() { "fields": cty.NullVal(cty.String), "size": cty.NullVal(cty.Number), }) - data, diags := s.plugin.RetrieveData(s.ctx, "elasticsearch", &plugin.RetrieveDataParams{ + data, diags := s.schema.DataFunc(s.ctx, &plugin.RetrieveDataParams{ Config: s.cfg, Args: args, }) @@ -359,7 +359,7 @@ func (s *IntegrationTestSuite) TestSearchSize() { "fields": cty.NullVal(cty.String), "size": cty.NumberIntVal(1), }) - data, diags := s.plugin.RetrieveData(s.ctx, "elasticsearch", &plugin.RetrieveDataParams{ + data, diags := s.schema.DataFunc(s.ctx, &plugin.RetrieveDataParams{ Config: s.cfg, Args: args, }) @@ -400,7 +400,7 @@ func (s *IntegrationTestSuite) TestGetByID() { "aggs": cty.NullVal(cty.DynamicPseudoType), "fields": cty.NullVal(cty.String), }) - data, diags := s.plugin.RetrieveData(s.ctx, "elasticsearch", &plugin.RetrieveDataParams{ + data, diags := s.schema.DataFunc(s.ctx, &plugin.RetrieveDataParams{ Config: s.cfg, Args: args, }) @@ -435,7 +435,7 @@ func (s *IntegrationTestSuite) TestGetByIDFields() { "aggs": cty.NullVal(cty.DynamicPseudoType), "fields": cty.ListVal([]cty.Value{cty.StringVal("name"), cty.StringVal("age")}), }) - data, diags := s.plugin.RetrieveData(s.ctx, "elasticsearch", &plugin.RetrieveDataParams{ + data, diags := s.schema.DataFunc(s.ctx, &plugin.RetrieveDataParams{ Config: s.cfg, Args: args, }) @@ -467,7 +467,7 @@ func (s *IntegrationTestSuite) TestGetByIDNotFound() { "aggs": cty.NullVal(cty.DynamicPseudoType), "fields": cty.NullVal(cty.String), }) - data, diags := s.plugin.RetrieveData(s.ctx, "elasticsearch", &plugin.RetrieveDataParams{ + data, diags := s.schema.DataFunc(s.ctx, &plugin.RetrieveDataParams{ Config: s.cfg, Args: args, }) diff --git a/internal/openai/content_openai_text_test.go b/internal/openai/content_openai_text_test.go index 2f1c8341..fa41abcb 100644 --- a/internal/openai/content_openai_text_test.go +++ b/internal/openai/content_openai_text_test.go @@ -16,7 +16,7 @@ import ( type OpenAITextContentTestSuite struct { suite.Suite - schema *plugin.Schema + schema *plugin.ContentProvider cli *client_mocks.Client } @@ -25,7 +25,7 @@ func TestOpenAITextContentSuite(t *testing.T) { } func (s *OpenAITextContentTestSuite) SetupSuite() { - s.schema = Plugin("", func(opts ...client.Option) client.Client { + s.schema = makeOpenAITextContentSchema(func(opts ...client.Option) client.Client { return s.cli }) } @@ -39,11 +39,10 @@ func (s *OpenAITextContentTestSuite) TearDownTest() { } func (s *OpenAITextContentTestSuite) TestSchema() { - provider := s.schema.ContentProviders["openai_text"] - s.Require().NotNil(provider) - s.NotNil(provider.Args) - s.NotNil(provider.ContentFunc) - s.NotNil(provider.Config) + s.Require().NotNil(s.schema) + s.NotNil(s.schema.Args) + s.NotNil(s.schema.ContentFunc) + s.NotNil(s.schema.Config) } func (s *OpenAITextContentTestSuite) TestBasic() { @@ -68,7 +67,7 @@ func (s *OpenAITextContentTestSuite) TestBasic() { }, }, nil) ctx := context.Background() - content, diags := s.schema.ProvideContent(ctx, "openai_text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.StringVal("Tell me a story"), "model": cty.NullVal(cty.String), @@ -112,7 +111,7 @@ func (s *OpenAITextContentTestSuite) TestAdvanced() { }, }, nil) ctx := context.Background() - content, diags := s.schema.ProvideContent(ctx, "openai_text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.StringVal("Tell me a story"), "model": cty.StringVal("model_123"), @@ -136,7 +135,7 @@ func (s *OpenAITextContentTestSuite) TestAdvanced() { func (s *OpenAITextContentTestSuite) TestMissingPrompt() { ctx := context.Background() - content, diags := s.schema.ProvideContent(ctx, "openai_text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.NullVal(cty.String), "model": cty.NullVal(cty.String), @@ -158,7 +157,7 @@ func (s *OpenAITextContentTestSuite) TestMissingPrompt() { func (s *OpenAITextContentTestSuite) TestMissingAPIKey() { ctx := context.Background() - content, diags := s.schema.ProvideContent(ctx, "openai_text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.StringVal("Tell me a story"), "model": cty.NullVal(cty.String), @@ -184,7 +183,7 @@ func (s *OpenAITextContentTestSuite) TestFailingClient() { Message: "error_message", }) ctx := context.Background() - content, diags := s.schema.ProvideContent(ctx, "openai_text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.StringVal("Tell me a story"), "model": cty.NullVal(cty.String), @@ -208,7 +207,7 @@ func (s *OpenAITextContentTestSuite) TestCancellation() { s.cli.On("GenerateChatCompletion", mock.Anything, mock.Anything).Return(nil, context.Canceled) ctx, cancel := context.WithCancel(context.Background()) cancel() - content, diags := s.schema.ProvideContent(ctx, "openai_text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.StringVal("Tell me a story"), "model": cty.NullVal(cty.String), @@ -235,7 +234,7 @@ func (s *OpenAITextContentTestSuite) TestErrorEncoding() { } s.cli.On("GenerateChatCompletion", mock.Anything, mock.Anything).Return(nil, want) ctx := context.Background() - content, diags := s.schema.ProvideContent(ctx, "openai_text", &plugin.ProvideContentParams{ + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.StringVal("Tell me a story"), "model": cty.NullVal(cty.String), diff --git a/test/e2e/render_test.go b/test/e2e/render_test.go index 4d66535d..b14db273 100644 --- a/test/e2e/render_test.go +++ b/test/e2e/render_test.go @@ -295,10 +295,10 @@ func TestE2ERender(t *testing.T) { "# sect1", "s1", "some_text", - "# sect2", + "## sect2", "some_text", "s2", - "# sect3", + "### sect3", "s3", "some_text", "s3 extra", @@ -540,11 +540,11 @@ func TestE2ERender(t *testing.T) { text = "first result" } content text { - query = ".document.content[0]" + query = ".document.content[0].markdown" text = "content[0] = {{ .query_result }}" } content text { - query = ".document.content[1]" + query = ".document.content[1].markdown" text = "content[1] = {{ .query_result }}" } } diff --git a/tools/docgen/main.go b/tools/docgen/main.go index d2dac580..fbdc3e6e 100644 --- a/tools/docgen/main.go +++ b/tools/docgen/main.go @@ -62,7 +62,7 @@ func generateDataSourceDocs(log *slog.Logger, p *plugin.Schema, outputDir string dataSourcesDir := filepath.Join(outputDir, "data-sources") // Create a directory for plugin's data sources if it doesn't exist - err := os.MkdirAll(dataSourcesDir, 0766) + err := os.MkdirAll(dataSourcesDir, 0o766) if err != nil { log.Error("Can't create a directory", "path", dataSourcesDir) panic(err) @@ -86,7 +86,7 @@ func generateContentProviderDocs(log *slog.Logger, p *plugin.Schema, outputDir s contentProvidersDir := filepath.Join(outputDir, "content-providers") // Create a directory for plugin's content providers if it doesn't exist - err := os.MkdirAll(contentProvidersDir, 0766) + err := os.MkdirAll(contentProvidersDir, 0o766) if err != nil { log.Error("Can't create a directory", "path", contentProvidersDir) panic(err) @@ -106,7 +106,6 @@ func generateContentProviderDocs(log *slog.Logger, p *plugin.Schema, outputDir s } func marshalDataSource(name string, ds *plugin.DataSource) PluginResourceMeta { - var configParams []string configSpec, ok := ds.Config.(hcldec.ObjectSpec) if ok && configSpec != nil { @@ -131,7 +130,6 @@ func marshalDataSource(name string, ds *plugin.DataSource) PluginResourceMeta { } func marshalContentProvider(name string, p *plugin.ContentProvider) PluginResourceMeta { - var configParams []string configSpec, ok := p.Config.(hcldec.ObjectSpec) if ok && configSpec != nil { @@ -228,7 +226,7 @@ func main() { pluginOutputDir := filepath.Join(outputDir, pluginShortname) // Create a plugin directory if it doesn't exist - err := os.MkdirAll(pluginOutputDir, 0766) + err := os.MkdirAll(pluginOutputDir, 0o766) if err != nil { log.Error("Can't create a plugin directory", "path", pluginOutputDir) panic(err) @@ -271,7 +269,7 @@ func renderContentProviderDoc(pluginSchema *plugin.Schema, contentProviderName s } defer f.Close() - var templContext = map[string]any{ + templContext := map[string]any{ "plugin": pluginSchema, "plugin_shortname": shortname(pluginSchema.Name), "name": contentProviderName, @@ -287,7 +285,7 @@ func renderDataSourceDoc(pluginSchema *plugin.Schema, dataSourceName string, dat } defer f.Close() - var templContext = map[string]any{ + templContext := map[string]any{ "plugin": pluginSchema, "plugin_shortname": shortname(pluginSchema.Name), "name": dataSourceName, From ffdace350fdf99eca8bf61699f764792c222a4a1 Mon Sep 17 00:00:00 2001 From: dobarx Date: Mon, 25 Mar 2024 10:50:44 +0200 Subject: [PATCH 6/7] Content positioning & title size fixes --- cmd/lint.go | 2 +- cmd/render.go | 13 +- .../builtin/content-providers/blockquote.md | 34 ++ .../plugins/builtin/content-providers/code.md | 35 ++ .../plugins/builtin/content-providers/text.md | 5 +- .../builtin/content-providers/title.md | 36 ++ docs/plugins/plugins.json | 161 ++++--- examples/plugins/basic/content_greeting.go | 8 +- examples/templates/basic_hello/hello.fabric | 2 +- examples/templates/frontmatter/example.fabric | 16 +- examples/templates/hackerone/example.fabric | 6 +- examples/templates/openai/example.fabric | 6 +- examples/templates/sections/example.fabric | 64 +++ examples/templates/text/example.fabric | 31 -- examples/templates/toc/example.fabric | 49 --- examples/templates/virustotal/example.fabric | 2 +- internal/builtin/content_blockquote.go | 50 +++ internal/builtin/content_blockquote_test.go | 103 +++++ internal/builtin/content_code.go | 63 +++ internal/builtin/content_code_test.go | 88 ++++ internal/builtin/content_frontmatter.go | 10 +- internal/builtin/content_frontmatter_test.go | 20 +- internal/builtin/content_image.go | 8 +- internal/builtin/content_image_test.go | 12 +- internal/builtin/content_list.go | 8 +- internal/builtin/content_list_test.go | 56 +-- internal/builtin/content_table.go | 8 +- internal/builtin/content_table_test.go | 46 +- internal/builtin/content_text.go | 150 +------ internal/builtin/content_text_test.go | 273 +----------- internal/builtin/content_title.go | 166 ++++++++ internal/builtin/content_title_test.go | 119 ++++++ internal/builtin/content_toc.go | 92 ++-- internal/builtin/content_toc_test.go | 140 +++--- internal/builtin/plugin.go | 3 + internal/openai/content_openai_text.go | 8 +- internal/openai/content_openai_text_test.go | 32 +- internal/stixview/content_stixview.go | 8 +- internal/stixview/content_stixview_test.go | 14 +- justfile | 2 +- parser/caller.go | 4 +- parser/definitions/parsed_document.go | 31 +- parser/definitions/parsed_plugin.go | 21 +- parser/definitions/section.go | 45 +- parser/definitions/title.go | 12 +- parser/evaluation/plugincaller.go | 2 +- parser/evaluation/result.go | 47 +- plugin/content.go | 320 ++++++++++++-- plugin/content_provider.go | 5 +- plugin/data.go | 4 + plugin/errors.go | 5 + plugin/pluginapi/v1/content.pb.go | 402 ++++++++++++++++-- plugin/pluginapi/v1/content_decoder.go | 53 ++- plugin/pluginapi/v1/content_encoder.go | 63 ++- plugin/pluginapi/v1/plugin.go | 7 +- plugin/pluginapi/v1/plugin.pb.go | 100 +++-- plugin/pluginapi/v1/server.go | 5 +- plugin/runner/loader.go | 12 +- plugin/schema.go | 10 +- proto/pluginapi/v1/content.proto | 22 +- proto/pluginapi/v1/plugin.proto | 3 +- test/e2e/e2e_test.go | 28 -- test/e2e/render_test.go | 64 +-- 63 files changed, 2134 insertions(+), 1080 deletions(-) create mode 100644 docs/plugins/builtin/content-providers/blockquote.md create mode 100644 docs/plugins/builtin/content-providers/code.md create mode 100644 docs/plugins/builtin/content-providers/title.md create mode 100644 examples/templates/sections/example.fabric delete mode 100644 examples/templates/text/example.fabric delete mode 100644 examples/templates/toc/example.fabric create mode 100644 internal/builtin/content_blockquote.go create mode 100644 internal/builtin/content_blockquote_test.go create mode 100644 internal/builtin/content_code.go create mode 100644 internal/builtin/content_code_test.go create mode 100644 internal/builtin/content_title.go create mode 100644 internal/builtin/content_title_test.go create mode 100644 plugin/errors.go delete mode 100644 test/e2e/e2e_test.go diff --git a/cmd/lint.go b/cmd/lint.go index abbcbd1c..fa968109 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -16,7 +16,7 @@ import ( type noopPluginCaller struct{} // CallContent implements evaluation.PluginCaller. -func (n *noopPluginCaller) CallContent(ctx context.Context, name string, config evaluation.Configuration, invocation evaluation.Invocation, context plugin.MapData) (result *plugin.Content, diag diagnostics.Diag) { +func (n *noopPluginCaller) CallContent(ctx context.Context, name string, config evaluation.Configuration, invocation evaluation.Invocation, context plugin.MapData) (result *plugin.ContentResult, diag diagnostics.Diag) { return nil, nil } diff --git a/cmd/render.go b/cmd/render.go index 90b66148..3e131ae5 100644 --- a/cmd/render.go +++ b/cmd/render.go @@ -15,7 +15,7 @@ import ( "github.com/blackstork-io/fabric/pkg/diagnostics" ) -func Render(ctx context.Context, blocks *parser.DefinedBlocks, pluginCaller *parser.Caller, docName string) (results []string, diags diagnostics.Diag) { +func Render(ctx context.Context, blocks *parser.DefinedBlocks, pluginCaller *parser.Caller, docName string) (results string, diags diagnostics.Diag) { doc, found := blocks.Documents[docName] if !found { diags.Add( @@ -40,20 +40,13 @@ func Render(ctx context.Context, blocks *parser.DefinedBlocks, pluginCaller *par return } -func writeResults(dest io.Writer, results []string) (diags diagnostics.Diag) { +func writeResults(dest io.Writer, results string) (diags diagnostics.Diag) { if len(results) == 0 { diags.Add("Empty output", "No content was produced") return } w := bufio.NewWriter(dest) - - // bufio.Writer preserves the first encountered error, - // so we're only cheking it once at flush - _, _ = w.WriteString(results[0]) - for _, result := range results[1:] { - _, _ = w.WriteString("\n\n") - _, _ = w.WriteString(result) - } + w.WriteString(results) _ = w.WriteByte('\n') err := w.Flush() diags.AppendErr(err, "Error while outputing result") diff --git a/docs/plugins/builtin/content-providers/blockquote.md b/docs/plugins/builtin/content-providers/blockquote.md new file mode 100644 index 00000000..e5031aa9 --- /dev/null +++ b/docs/plugins/builtin/content-providers/blockquote.md @@ -0,0 +1,34 @@ +--- +title: blockquote +plugin: + name: blackstork/builtin + description: "" + tags: [] + version: "v0.4.0" + source_github: "https://github.com/blackstork-io/fabric/tree/main/internal/builtin/" +resource: + type: content-provider +type: docs +--- + +{{< breadcrumbs 2 >}} + +{{< plugin-resource-header "blackstork/builtin" "builtin" "v0.4.0" "blockquote" "content provider" >}} + +The content provider is built-in, which means it's a part of `fabric` binary. It's available out-of-the-box, no installation required. + + +#### Configuration + +The content provider doesn't support any configuration parameters. + +#### Usage + +The content provider supports the following execution parameters: + +```hcl +content blockquote { + value = # required +} +``` + diff --git a/docs/plugins/builtin/content-providers/code.md b/docs/plugins/builtin/content-providers/code.md new file mode 100644 index 00000000..b1cd5fbd --- /dev/null +++ b/docs/plugins/builtin/content-providers/code.md @@ -0,0 +1,35 @@ +--- +title: code +plugin: + name: blackstork/builtin + description: "" + tags: [] + version: "v0.4.0" + source_github: "https://github.com/blackstork-io/fabric/tree/main/internal/builtin/" +resource: + type: content-provider +type: docs +--- + +{{< breadcrumbs 2 >}} + +{{< plugin-resource-header "blackstork/builtin" "builtin" "v0.4.0" "code" "content provider" >}} + +The content provider is built-in, which means it's a part of `fabric` binary. It's available out-of-the-box, no installation required. + + +#### Configuration + +The content provider doesn't support any configuration parameters. + +#### Usage + +The content provider supports the following execution parameters: + +```hcl +content code { + language = # optional + value = # required +} +``` + diff --git a/docs/plugins/builtin/content-providers/text.md b/docs/plugins/builtin/content-providers/text.md index 5f09ba9c..e047a124 100644 --- a/docs/plugins/builtin/content-providers/text.md +++ b/docs/plugins/builtin/content-providers/text.md @@ -28,10 +28,7 @@ The content provider supports the following execution parameters: ```hcl content text { - absolute_title_size = # optional - code_language = # optional - format_as = # optional - text = # required + value = # required } ``` diff --git a/docs/plugins/builtin/content-providers/title.md b/docs/plugins/builtin/content-providers/title.md new file mode 100644 index 00000000..a67130fb --- /dev/null +++ b/docs/plugins/builtin/content-providers/title.md @@ -0,0 +1,36 @@ +--- +title: title +plugin: + name: blackstork/builtin + description: "" + tags: [] + version: "v0.4.0" + source_github: "https://github.com/blackstork-io/fabric/tree/main/internal/builtin/" +resource: + type: content-provider +type: docs +--- + +{{< breadcrumbs 2 >}} + +{{< plugin-resource-header "blackstork/builtin" "builtin" "v0.4.0" "title" "content provider" >}} + +The content provider is built-in, which means it's a part of `fabric` binary. It's available out-of-the-box, no installation required. + + +#### Configuration + +The content provider doesn't support any configuration parameters. + +#### Usage + +The content provider supports the following execution parameters: + +```hcl +content title { + absolute_size = # optional + relative_size = # optional + value = # required +} +``` + diff --git a/docs/plugins/plugins.json b/docs/plugins/plugins.json index bbda848e..0895adbd 100644 --- a/docs/plugins/plugins.json +++ b/docs/plugins/plugins.json @@ -44,18 +44,15 @@ "name": "text", "type": "content-provider", "arguments": [ - "absolute_title_size", - "code_language", - "text", - "format_as" + "value" ] }, { - "name": "image", + "name": "code", "type": "content-provider", "arguments": [ - "src", - "alt" + "value", + "language" ] }, { @@ -67,18 +64,42 @@ ] }, { - "name": "table", + "name": "frontmatter", "type": "content-provider", "arguments": [ - "columns" + "content", + "format" ] }, { - "name": "frontmatter", + "name": "title", "type": "content-provider", "arguments": [ - "format", - "content" + "value", + "absolute_size", + "relative_size" + ] + }, + { + "name": "blockquote", + "type": "content-provider", + "arguments": [ + "value" + ] + }, + { + "name": "image", + "type": "content-provider", + "arguments": [ + "src", + "alt" + ] + }, + { + "name": "table", + "type": "content-provider", + "arguments": [ + "columns" ] } ], @@ -92,24 +113,24 @@ "name": "elasticsearch", "type": "data-source", "config_params": [ + "api_key", "basic_auth_username", "basic_auth_password", "bearer_auth", "ca_certs", "base_url", "cloud_id", - "api_key_str", - "api_key" + "api_key_str" ], "arguments": [ + "fields", + "size", "index", "id", "query_string", "query", "aggs", - "only_hits", - "fields", - "size" + "only_hits" ] } ], @@ -126,17 +147,17 @@ "github_token" ], "arguments": [ - "labels", + "mentioned", + "sort", "direction", "since", "limit", "repository", - "assignee", - "creator", - "mentioned", "milestone", + "creator", + "labels", "state", - "sort" + "assignee" ] } ], @@ -168,13 +189,13 @@ "name": "openai_text", "type": "content-provider", "config_params": [ - "system_prompt", "api_key", - "organization_id" + "organization_id", + "system_prompt" ], "arguments": [ - "model", - "prompt" + "prompt", + "model" ] } ], @@ -260,52 +281,52 @@ "api_token" ], "arguments": [ - "hacker_published", - "created_at__lt", + "closed_at__gt", + "disclosed_at__lt", + "bounty_awarded_at__lt", + "created_at__gt", + "submitted_at__gt", "submitted_at__lt", "bounty_awarded_at__gt", - "swag_awarded_at__lt", + "last_report_activity_at__gt", + "reporter", + "id", + "disclosed_at__null", "last_program_activity_at__gt", - "page_number", - "submitted_at__gt", - "triaged_at__gt", + "last_program_activity_at__null", + "last_public_activity_at__lt", + "inbox_ids", "triaged_at__null", - "disclosed_at__lt", + "closed_at__null", + "hacker_published", + "triaged_at__lt", + "last_public_activity_at__gt", "first_program_activity_at__lt", + "first_program_activity_at__null", "last_program_activity_at__lt", - "last_public_activity_at__gt", - "created_at__gt", - "last_report_activity_at__gt", - "closed_at__lt", - "closed_at__null", - "disclosed_at__gt", - "bounty_awarded_at__null", "last_activity_at__gt", "keyword", - "triaged_at__lt", - "assignee", - "severity", + "bounty_awarded_at__null", + "swag_awarded_at__null", "last_report_activity_at__lt", "first_program_activity_at__gt", - "last_activity_at__lt", - "size", + "custom_fields", + "assignee", "weakness_id", - "disclosed_at__null", - "bounty_awarded_at__lt", - "swag_awarded_at__gt", - "swag_awarded_at__null", - "id", - "inbox_ids", - "reporter", - "closed_at__gt", + "triaged_at__gt", + "state", + "severity", + "program", + "created_at__lt", + "closed_at__lt", + "disclosed_at__gt", "reporter_agreed_on_going_public", - "first_program_activity_at__null", - "last_program_activity_at__null", - "custom_fields", + "size", + "page_number", "sort", - "state", - "last_public_activity_at__lt", - "program" + "swag_awarded_at__gt", + "swag_awarded_at__lt", + "last_activity_at__lt" ] } ], @@ -322,10 +343,10 @@ "api_key" ], "arguments": [ + "end_date", "user_id", "group_id", - "start_date", - "end_date" + "start_date" ] } ], @@ -344,12 +365,12 @@ "deployment_name" ], "arguments": [ - "latest_time", - "search_query", "max_count", "status_buckets", "rf", - "earliest_time" + "earliest_time", + "latest_time", + "search_query" ] } ], @@ -363,17 +384,17 @@ "name": "stixview", "type": "content-provider", "arguments": [ - "gist_id", + "show_labels", + "height", "caption", - "show_sidebar", "show_tlp_as_tags", "show_marking_nodes", - "show_labels", - "stix_url", - "show_footer", + "show_sidebar", "show_idrefs", "width", - "height" + "gist_id", + "stix_url", + "show_footer" ] } ], diff --git a/examples/plugins/basic/content_greeting.go b/examples/plugins/basic/content_greeting.go index eb6e381e..691dbd8f 100644 --- a/examples/plugins/basic/content_greeting.go +++ b/examples/plugins/basic/content_greeting.go @@ -28,7 +28,7 @@ func makeGreetingContentProvider() *plugin.ContentProvider { } } -func renderGreetingMessage(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.Content, hcl.Diagnostics) { +func renderGreetingMessage(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { name := params.Config.GetAttr("name") if name.IsNull() || name.AsString() == "" { return nil, hcl.Diagnostics{{ @@ -37,7 +37,9 @@ func renderGreetingMessage(ctx context.Context, params *plugin.ProvideContentPar Detail: "name is required", }} } - return &plugin.Content{ - Markdown: fmt.Sprintf("Hello, %s!", name.AsString()), + return &plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: fmt.Sprintf("Hello, %s!", name.AsString()), + }, }, nil } diff --git a/examples/templates/basic_hello/hello.fabric b/examples/templates/basic_hello/hello.fabric index 4a5640be..75589a80 100644 --- a/examples/templates/basic_hello/hello.fabric +++ b/examples/templates/basic_hello/hello.fabric @@ -1,6 +1,6 @@ document "hello" { title = "Welcome" content text { - text = "Hello from fabric" + value = "Hello from fabric" } } \ No newline at end of file diff --git a/examples/templates/frontmatter/example.fabric b/examples/templates/frontmatter/example.fabric index 45a8cf54..a3ef9ccf 100644 --- a/examples/templates/frontmatter/example.fabric +++ b/examples/templates/frontmatter/example.fabric @@ -10,24 +10,20 @@ document "example" { } } - content text { - text = "Sub title 1" - format_as = "title" - absolute_title_size = 2 + content title { + value = "Sub title 1" } content text { - text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." + value = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." } - content text { - text = "Sub title 2" - format_as = "title" - absolute_title_size = 2 + content title { + value = "Sub title 2" } content text { - text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." + value = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." } diff --git a/examples/templates/hackerone/example.fabric b/examples/templates/hackerone/example.fabric index 0972afa9..dea10d1f 100644 --- a/examples/templates/hackerone/example.fabric +++ b/examples/templates/hackerone/example.fabric @@ -14,10 +14,8 @@ document "example" { data hackerone_reports "my_reports" { program = [from_env_variable("HACKERONE_PROGRAM")] } - content text { - format_as = "title" - text = "My HackerOne Reports" - absolute_title_size = 2 + content title { + value = "My HackerOne Reports" } content list { query = "[.data.hackerone_reports.my_reports[].attributes.title]" diff --git a/examples/templates/openai/example.fabric b/examples/templates/openai/example.fabric index 64c6c2b7..836cff55 100644 --- a/examples/templates/openai/example.fabric +++ b/examples/templates/openai/example.fabric @@ -10,10 +10,8 @@ document "example" { data csv "csv_file" { path = "./data.csv" } - content text { - text = "Values from the CSV file" - format_as = "title" - absolute_title_size = 2 + content title { + value = "Values from the CSV file" } content table { query = ".data.csv.csv_file" diff --git a/examples/templates/sections/example.fabric b/examples/templates/sections/example.fabric new file mode 100644 index 00000000..caa493a5 --- /dev/null +++ b/examples/templates/sections/example.fabric @@ -0,0 +1,64 @@ +document "example" { + title = "Document title" + + content title { + value = "TOC" + } + + content toc {} + + content title { + value = "Subtitle 0" + } + + section { + title = "Section 1" + + content title { + value = "Section TOC" + } + + content toc { + start_level = 1 + end_level = 4 + ordered = true + } + + content text { + value = "Text value 1" + } + + content title { + value = "Subtitle 1" + } + + content text { + value = "Text value 2" + } + + section { + title = "Subsection 1" + + content text { + value = "Text value 3" + } + } + + content title { + value = "Subtitle 2" + } + } + + section { + title = "Section 2" + + section { + title = "Subsection 2" + + content text { + value = "Text value 4" + } + } + } +} + diff --git a/examples/templates/text/example.fabric b/examples/templates/text/example.fabric deleted file mode 100644 index 720fc8de..00000000 --- a/examples/templates/text/example.fabric +++ /dev/null @@ -1,31 +0,0 @@ - -document "example" { - title = "Using Text content provider" - - content text { - text = "Title 1" - format_as = "title" - } - - content text { - text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." - } - - content text { - text = "Sub title 2" - format_as = "title" - } - - content text { - text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." - } - content text { - text = "Sub title 3" - format_as = "title" - } - - content text { - text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." - } - -} diff --git a/examples/templates/toc/example.fabric b/examples/templates/toc/example.fabric deleted file mode 100644 index f0ab3636..00000000 --- a/examples/templates/toc/example.fabric +++ /dev/null @@ -1,49 +0,0 @@ - -document "example" { - title = "Using TOC content provider" - content toc { - start_level = 1 - end_level = 4 - ordered = true - } - content text { - text = "Sub title 1" - format_as = "title" - absolute_title_size = 2 - } - - content text { - text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." - } - - content text { - text = "Sub title 2" - format_as = "title" - absolute_title_size = 2 - } - - content text { - text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." - } - content text { - text = "Sub title 3" - format_as = "title" - absolute_title_size = 3 - } - - content text { - text = "Sub title 4.1" - format_as = "title" - absolute_title_size = 4 - } - content text { - text = "Sub title 4.2" - format_as = "title" - absolute_title_size = 4 - } - - content text { - text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor dolore magna." - } - -} \ No newline at end of file diff --git a/examples/templates/virustotal/example.fabric b/examples/templates/virustotal/example.fabric index 5d4a6653..cba79aae 100644 --- a/examples/templates/virustotal/example.fabric +++ b/examples/templates/virustotal/example.fabric @@ -16,6 +16,6 @@ document "example" { end_date = "20240203" } content text { - text = "{{.data.virustotal_api_usage.my_usage.daily}}" + value = "{{.data.virustotal_api_usage.my_usage.daily}}" } } \ No newline at end of file diff --git a/internal/builtin/content_blockquote.go b/internal/builtin/content_blockquote.go new file mode 100644 index 00000000..e01a8045 --- /dev/null +++ b/internal/builtin/content_blockquote.go @@ -0,0 +1,50 @@ +package builtin + +import ( + "context" + "strings" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" + + "github.com/blackstork-io/fabric/plugin" +) + +func makeBlockQuoteContentProvider() *plugin.ContentProvider { + return &plugin.ContentProvider{ + ContentFunc: genBlockQuoteContent, + Args: hcldec.ObjectSpec{ + "value": &hcldec.AttrSpec{ + Name: "value", + Type: cty.String, + Required: true, + }, + }, + } +} + +func genBlockQuoteContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { + value := params.Args.GetAttr("value") + if value.IsNull() { + return nil, hcl.Diagnostics{{ + Severity: hcl.DiagError, + Summary: "Failed to parse arguments", + Detail: "value is required", + }} + } + text, err := genTextContentText(value.AsString(), params.DataContext) + if err != nil { + return nil, hcl.Diagnostics{{ + Severity: hcl.DiagError, + Summary: "Failed to render blockquote", + Detail: err.Error(), + }} + } + text = "> " + strings.ReplaceAll(text, "\n", "\n> ") + return &plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: text, + }, + }, nil +} diff --git a/internal/builtin/content_blockquote_test.go b/internal/builtin/content_blockquote_test.go new file mode 100644 index 00000000..0e6e8d82 --- /dev/null +++ b/internal/builtin/content_blockquote_test.go @@ -0,0 +1,103 @@ +package builtin + +import ( + "context" + "testing" + + "github.com/hashicorp/hcl/v2" + "github.com/stretchr/testify/suite" + "github.com/zclconf/go-cty/cty" + + "github.com/blackstork-io/fabric/plugin" +) + +type BlockQuoteTestSuite struct { + suite.Suite + schema *plugin.ContentProvider +} + +func TestBlockQuoteSuite(t *testing.T) { + suite.Run(t, &BlockQuoteTestSuite{}) +} + +func (s *BlockQuoteTestSuite) SetupSuite() { + s.schema = makeBlockQuoteContentProvider() +} + +func (s *BlockQuoteTestSuite) TestSchema() { + s.Nil(s.schema.Config) + s.NotNil(s.schema.Args) + s.NotNil(s.schema.ContentFunc) +} + +func (s *BlockQuoteTestSuite) TestMissingText() { + ctx := context.Background() + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + Args: cty.ObjectVal(map[string]cty.Value{ + "value": cty.NullVal(cty.String), + }), + DataContext: plugin.MapData{ + "name": plugin.StringData("World"), + }, + }) + s.Nil(content) + s.Equal(hcl.Diagnostics{{ + Severity: hcl.DiagError, + Summary: "Failed to parse arguments", + Detail: "value is required", + }}, diags) +} + +func (s *BlockQuoteTestSuite) TestCallBlockquote() { + ctx := context.Background() + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + Args: cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal(`Hello {{.name}}!`), + }), + DataContext: plugin.MapData{ + "name": plugin.StringData("World"), + }, + }) + s.Empty(diags) + s.Equal(&plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: "> Hello World!", + }, + }, content) +} + +func (s *BlockQuoteTestSuite) TestCallBlockquoteMultiline() { + ctx := context.Background() + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + Args: cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal("Hello\n{{.name}}\nfor you!"), + }), + DataContext: plugin.MapData{ + "name": plugin.StringData("World"), + }, + }) + s.Empty(diags) + s.Equal(&plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: "> Hello\n> World\n> for you!", + }, + }, content) +} + +func (s *BlockQuoteTestSuite) TestCallBlockquoteMultilineDoubleNewline() { + ctx := context.Background() + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + Args: cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal("Hello\n{{.name}}\n\nfor you!"), + }), + DataContext: plugin.MapData{ + "name": plugin.StringData("World"), + }, + }) + s.Empty(diags) + s.Equal(&plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: "> Hello\n> World\n> \n> for you!", + }, + }, content) +} diff --git a/internal/builtin/content_code.go b/internal/builtin/content_code.go new file mode 100644 index 00000000..70625575 --- /dev/null +++ b/internal/builtin/content_code.go @@ -0,0 +1,63 @@ +package builtin + +import ( + "context" + "fmt" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" + + "github.com/blackstork-io/fabric/plugin" +) + +const ( + defaultCodeLanguage = "" +) + +func makeCodeContentProvider() *plugin.ContentProvider { + return &plugin.ContentProvider{ + ContentFunc: genCodeContent, + Args: hcldec.ObjectSpec{ + "value": &hcldec.AttrSpec{ + Name: "value", + Type: cty.String, + Required: true, + }, + "language": &hcldec.AttrSpec{ + Name: "language", + Type: cty.String, + Required: false, + }, + }, + } +} + +func genCodeContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { + value := params.Args.GetAttr("value") + if value.IsNull() { + return nil, hcl.Diagnostics{{ + Severity: hcl.DiagError, + Summary: "Failed to parse arguments", + Detail: "value is required", + }} + } + lang := params.Args.GetAttr("language") + if lang.IsNull() { + lang = cty.StringVal(defaultCodeLanguage) + } + text, err := genTextContentText(value.AsString(), params.DataContext) + if err != nil { + return nil, hcl.Diagnostics{{ + Severity: hcl.DiagError, + Summary: "Failed to render code", + Detail: err.Error(), + }} + } + text = fmt.Sprintf("```%s\n%s\n```", lang.AsString(), text) + return &plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: text, + }, + }, nil +} diff --git a/internal/builtin/content_code_test.go b/internal/builtin/content_code_test.go new file mode 100644 index 00000000..758a0bde --- /dev/null +++ b/internal/builtin/content_code_test.go @@ -0,0 +1,88 @@ +package builtin + +import ( + "context" + "testing" + + "github.com/hashicorp/hcl/v2" + "github.com/stretchr/testify/suite" + "github.com/zclconf/go-cty/cty" + + "github.com/blackstork-io/fabric/plugin" +) + +type CodeTestSuite struct { + suite.Suite + schema *plugin.ContentProvider +} + +func TestCodeSuite(t *testing.T) { + suite.Run(t, &CodeTestSuite{}) +} + +func (s *CodeTestSuite) SetupSuite() { + s.schema = makeCodeContentProvider() +} + +func (s *CodeTestSuite) TestSchema() { + s.Nil(s.schema.Config) + s.NotNil(s.schema.Args) + s.NotNil(s.schema.ContentFunc) +} + +func (s *CodeTestSuite) TestMissingValue() { + ctx := context.Background() + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + Args: cty.ObjectVal(map[string]cty.Value{ + "value": cty.NullVal(cty.String), + "language": cty.NullVal(cty.String), + }), + DataContext: plugin.MapData{ + "name": plugin.StringData("World"), + }, + }) + s.Nil(content) + s.Equal(hcl.Diagnostics{{ + Severity: hcl.DiagError, + Summary: "Failed to parse arguments", + Detail: "value is required", + }}, diags) +} + +func (s *CodeTestSuite) TestCallCodeDefault() { + ctx := context.Background() + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + Args: cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal(`Hello {{.name}}!`), + "language": cty.NullVal(cty.String), + }), + DataContext: plugin.MapData{ + "name": plugin.StringData("World"), + }, + }) + s.Empty(diags) + s.Equal(&plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: "```\nHello World!\n```", + }, + }, content) +} + +func (s *CodeTestSuite) TestCallCodeWithLanguage() { + ctx := context.Background() + content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + Args: cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal(`{"hello": "{{.name}}"}`), + "language": cty.StringVal("json"), + }), + DataContext: plugin.MapData{ + "name": plugin.StringData("world"), + }, + }) + s.Empty(diags) + s.Equal(&plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: "```json\n{\"hello\": \"world\"}\n```", + }, + }, content) +} diff --git a/internal/builtin/content_frontmatter.go b/internal/builtin/content_frontmatter.go index 2ab0c906..79e4f926 100644 --- a/internal/builtin/content_frontmatter.go +++ b/internal/builtin/content_frontmatter.go @@ -42,7 +42,7 @@ func makeFrontMatterContentProvider() *plugin.ContentProvider { } } -func genFrontMatterContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.Content, hcl.Diagnostics) { +func genFrontMatterContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { format, m, err := parseFrontMatterArgs(params.Args, params.DataContext) if err != nil { return nil, hcl.Diagnostics{{ @@ -59,10 +59,12 @@ func genFrontMatterContent(ctx context.Context, params *plugin.ProvideContentPar Detail: err.Error(), }} } - return &plugin.Content{ - Markdown: result, + return &plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: result, + }, Location: &plugin.Location{ - Index: 0, + Index: 1, Effect: plugin.LocationEffectBefore, }, }, nil diff --git a/internal/builtin/content_frontmatter_test.go b/internal/builtin/content_frontmatter_test.go index 71094c94..1dd09c97 100644 --- a/internal/builtin/content_frontmatter_test.go +++ b/internal/builtin/content_frontmatter_test.go @@ -123,7 +123,7 @@ func (s *FrontMatterGeneratorTestSuite) TestWithContent() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.schema.ProvideContent(ctx, "frontmatter", &plugin.ProvideContentParams{ + result, diags := s.schema.ProvideContent(ctx, "frontmatter", &plugin.ProvideContentParams{ Args: args, }) s.Equal("---\n"+ @@ -136,7 +136,7 @@ func (s *FrontMatterGeneratorTestSuite) TestWithContent() { "waldo:\n"+ " - fred\n"+ " - plugh\n"+ - "---\n", content.Markdown) + "---\n", result.Content.Print()) s.Nil(diags) } @@ -146,7 +146,7 @@ func (s *FrontMatterGeneratorTestSuite) TestWithQueryResult() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.schema.ProvideContent(ctx, "frontmatter", &plugin.ProvideContentParams{ + result, diags := s.schema.ProvideContent(ctx, "frontmatter", &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.MapData{ @@ -174,7 +174,7 @@ func (s *FrontMatterGeneratorTestSuite) TestWithQueryResult() { "waldo:\n"+ " - fred\n"+ " - plugh\n"+ - "---\n", content.Markdown) + "---\n", result.Content.Print()) s.Nil(diags) } @@ -184,7 +184,7 @@ func (s *FrontMatterGeneratorTestSuite) TestFormatYaml() { "format": cty.StringVal("yaml"), }) ctx := context.Background() - content, diags := s.schema.ProvideContent(ctx, "frontmatter", &plugin.ProvideContentParams{ + result, diags := s.schema.ProvideContent(ctx, "frontmatter", &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.MapData{ @@ -212,7 +212,7 @@ func (s *FrontMatterGeneratorTestSuite) TestFormatYaml() { "waldo:\n"+ " - fred\n"+ " - plugh\n"+ - "---\n", content.Markdown) + "---\n", result.Content.Print()) s.Nil(diags) } @@ -222,7 +222,7 @@ func (s *FrontMatterGeneratorTestSuite) TestFormatTOML() { "format": cty.StringVal("toml"), }) ctx := context.Background() - content, diags := s.schema.ProvideContent(ctx, "frontmatter", &plugin.ProvideContentParams{ + result, diags := s.schema.ProvideContent(ctx, "frontmatter", &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.MapData{ @@ -248,7 +248,7 @@ func (s *FrontMatterGeneratorTestSuite) TestFormatTOML() { "[quux]\n"+ "corge = 'grault'\n"+ "garply = false\n"+ - "+++\n", content.Markdown) + "+++\n", result.Content.Print()) s.Nil(diags) } @@ -258,7 +258,7 @@ func (s *FrontMatterGeneratorTestSuite) TestFormatJSON() { "format": cty.StringVal("json"), }) ctx := context.Background() - content, diags := s.schema.ProvideContent(ctx, "frontmatter", &plugin.ProvideContentParams{ + result, diags := s.schema.ProvideContent(ctx, "frontmatter", &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.MapData{ @@ -288,6 +288,6 @@ func (s *FrontMatterGeneratorTestSuite) TestFormatJSON() { " \"fred\",\n"+ " \"plugh\"\n"+ " ]\n"+ - "}\n", content.Markdown) + "}\n", result.Content.Print()) s.Nil(diags) } diff --git a/internal/builtin/content_image.go b/internal/builtin/content_image.go index 27c88ad0..412f52fc 100644 --- a/internal/builtin/content_image.go +++ b/internal/builtin/content_image.go @@ -30,7 +30,7 @@ func makeImageContentProvider() *plugin.ContentProvider { } } -func genImageContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.Content, hcl.Diagnostics) { +func genImageContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { src := params.Args.GetAttr("src") if src.IsNull() || src.AsString() == "" { return nil, hcl.Diagnostics{{ @@ -45,7 +45,9 @@ func genImageContent(ctx context.Context, params *plugin.ProvideContentParams) ( } srcStr := strings.TrimSpace(strings.ReplaceAll(src.AsString(), "\n", "")) altStr := strings.TrimSpace(strings.ReplaceAll(alt.AsString(), "\n", "")) - return &plugin.Content{ - Markdown: fmt.Sprintf("![%s](%s)", altStr, srcStr), + return &plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: fmt.Sprintf("![%s](%s)", altStr, srcStr), + }, }, nil } diff --git a/internal/builtin/content_image_test.go b/internal/builtin/content_image_test.go index 9a5606ef..84edb31b 100644 --- a/internal/builtin/content_image_test.go +++ b/internal/builtin/content_image_test.go @@ -71,12 +71,10 @@ func (s *ImageGeneratorTestSuite) TestCallImageSourceValid() { "alt": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, }) - s.Equal(&plugin.Content{ - Markdown: "![](https://example.com/image.png)", - }, content) + s.Equal("![](https://example.com/image.png)", result.Content.Print()) s.Empty(diags) } @@ -86,11 +84,9 @@ func (s *ImageGeneratorTestSuite) TestCallImageSourceValidWithAlt() { "alt": cty.StringVal("alt text"), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, }) - s.Equal(&plugin.Content{ - Markdown: "![alt text](https://example.com/image.png)", - }, content) + s.Equal("![alt text](https://example.com/image.png)", result.Content.Print()) s.Empty(diags) } diff --git a/internal/builtin/content_list.go b/internal/builtin/content_list.go index 924afdb4..c54e970b 100644 --- a/internal/builtin/content_list.go +++ b/internal/builtin/content_list.go @@ -41,7 +41,7 @@ func makeListContentProvider() *plugin.ContentProvider { } } -func genListContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.Content, hcl.Diagnostics) { +func genListContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { format, tmpl, err := parseListContentArgs(params) if err != nil { return nil, hcl.Diagnostics{{ @@ -58,8 +58,10 @@ func genListContent(ctx context.Context, params *plugin.ProvideContentParams) (* Detail: err.Error(), }} } - return &plugin.Content{ - Markdown: result, + return &plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: result, + }, }, nil } diff --git a/internal/builtin/content_list_test.go b/internal/builtin/content_list_test.go index 6e1f398c..83e26469 100644 --- a/internal/builtin/content_list_test.go +++ b/internal/builtin/content_list_test.go @@ -37,11 +37,11 @@ func (s *ListGeneratorTestSuite) TestNilQueryResult() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{}, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to render template", @@ -55,13 +55,13 @@ func (s *ListGeneratorTestSuite) TestNonArrayQueryResult() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.StringData("not_an_array"), }, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to render template", @@ -75,7 +75,7 @@ func (s *ListGeneratorTestSuite) TestUnordered() { "format": cty.StringVal("unordered"), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{ @@ -84,9 +84,7 @@ func (s *ListGeneratorTestSuite) TestUnordered() { }, }, }) - s.Equal(&plugin.Content{ - Markdown: "* foo bar\n* foo baz\n", - }, content) + s.Equal("* foo bar\n* foo baz\n", result.Content.Print()) s.Empty(diags) } @@ -96,7 +94,7 @@ func (s *ListGeneratorTestSuite) TestOrdered() { "format": cty.StringVal("ordered"), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{ @@ -105,9 +103,7 @@ func (s *ListGeneratorTestSuite) TestOrdered() { }, }, }) - s.Equal(&plugin.Content{ - Markdown: "1. foo bar\n2. foo baz\n", - }, content) + s.Equal("1. foo bar\n2. foo baz\n", result.Content.Print()) s.Empty(diags) } @@ -117,7 +113,7 @@ func (s *ListGeneratorTestSuite) TestTaskList() { "format": cty.StringVal("tasklist"), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{ @@ -126,9 +122,7 @@ func (s *ListGeneratorTestSuite) TestTaskList() { }, }, }) - s.Equal(&plugin.Content{ - Markdown: "* [ ] foo bar\n* [ ] foo baz\n", - }, content) + s.Equal("* [ ] foo bar\n* [ ] foo baz\n", result.Content.Print()) s.Empty(diags) } @@ -138,7 +132,7 @@ func (s *ListGeneratorTestSuite) TestBasic() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{ @@ -147,9 +141,7 @@ func (s *ListGeneratorTestSuite) TestBasic() { }, }, }) - s.Equal(&plugin.Content{ - Markdown: "* foo bar\n* foo baz\n", - }, content) + s.Equal("* foo bar\n* foo baz\n", result.Content.Print()) s.Empty(diags) } @@ -159,7 +151,7 @@ func (s *ListGeneratorTestSuite) TestAdvanced() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{ @@ -174,9 +166,7 @@ func (s *ListGeneratorTestSuite) TestAdvanced() { }, }, }) - s.Equal(&plugin.Content{ - Markdown: "* foo bar1 baz1\n* foo bar2 baz2\n", - }, content) + s.Equal("* foo bar1 baz1\n* foo bar2 baz2\n", result.Content.Print()) s.Empty(diags) } @@ -186,15 +176,13 @@ func (s *ListGeneratorTestSuite) TestEmptyQueryResult() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{}, }, }) - s.Equal(&plugin.Content{ - Markdown: "", - }, content) + s.Equal("", result.Content.Print()) s.Empty(diags) } @@ -204,13 +192,13 @@ func (s *ListGeneratorTestSuite) TestMissingItemTemplate() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{}, }, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to parse template", @@ -224,13 +212,13 @@ func (s *ListGeneratorTestSuite) TestInvalidFormat() { "format": cty.StringVal("invalid"), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "query_result": plugin.ListData{}, }, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to parse template", @@ -244,10 +232,10 @@ func (s *ListGeneratorTestSuite) TestMissingDataContext() { "format": cty.NullVal(cty.String), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to render template", diff --git a/internal/builtin/content_table.go b/internal/builtin/content_table.go index 2d7637fb..b11177bb 100644 --- a/internal/builtin/content_table.go +++ b/internal/builtin/content_table.go @@ -32,7 +32,7 @@ func makeTableContentProvider() *plugin.ContentProvider { } } -func genTableContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.Content, hcl.Diagnostics) { +func genTableContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { headers, values, err := parseTableContentArgs(params) if err != nil { return nil, hcl.Diagnostics{{ @@ -49,8 +49,10 @@ func genTableContent(ctx context.Context, params *plugin.ProvideContentParams) ( Detail: err.Error(), }} } - return &plugin.Content{ - Markdown: result, + return &plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: result, + }, }, nil } diff --git a/internal/builtin/content_table_test.go b/internal/builtin/content_table_test.go index 56444002..fdc44ab1 100644 --- a/internal/builtin/content_table_test.go +++ b/internal/builtin/content_table_test.go @@ -45,16 +45,14 @@ func (s *TableGeneratorTestSuite) TestNilQueryResult() { }), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), "query_result": nil, }, }) - s.Equal(&plugin.Content{ - Markdown: "|User Name|User Age|\n|---|---|\n", - }, content) + s.Equal("|User Name|User Age|\n|---|---|\n", result.Content.Print()) s.Nil(diags) } @@ -72,16 +70,14 @@ func (s *TableGeneratorTestSuite) TestEmptyQueryResult() { }), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), "query_result": plugin.ListData{}, }, }) - s.Equal(&plugin.Content{ - Markdown: "|User Name|User Age|\n|---|---|\n", - }, content) + s.Equal("|User Name|User Age|\n|---|---|\n", result.Content.Print()) s.Nil(diags) } @@ -99,7 +95,7 @@ func (s *TableGeneratorTestSuite) TestBasic() { }), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -115,9 +111,7 @@ func (s *TableGeneratorTestSuite) TestBasic() { }, }, }) - s.Equal(&plugin.Content{ - Markdown: "|User Name|User Age|\n|---|---|\n|John|42|\n|Jane|43|\n", - }, content) + s.Equal("|User Name|User Age|\n|---|---|\n|John|42|\n|Jane|43|\n", result.Content.Print()) s.Nil(diags) } @@ -133,7 +127,7 @@ func (s *TableGeneratorTestSuite) TestMissingHeader() { }), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -149,7 +143,7 @@ func (s *TableGeneratorTestSuite) TestMissingHeader() { }, }, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to parse arguments", @@ -171,7 +165,7 @@ func (s *TableGeneratorTestSuite) TestNilHeader() { }), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -187,7 +181,7 @@ func (s *TableGeneratorTestSuite) TestNilHeader() { }, }, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to parse arguments", @@ -209,7 +203,7 @@ func (s *TableGeneratorTestSuite) TestNilValue() { }), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -225,7 +219,7 @@ func (s *TableGeneratorTestSuite) TestNilValue() { }, }, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to parse arguments", @@ -238,7 +232,7 @@ func (s *TableGeneratorTestSuite) TestNilColumns() { "columns": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{}))), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -254,7 +248,7 @@ func (s *TableGeneratorTestSuite) TestNilColumns() { }, }, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to parse arguments", @@ -267,7 +261,7 @@ func (s *TableGeneratorTestSuite) TestEmptyColumns() { "columns": cty.ListValEmpty(cty.Object(map[string]cty.Type{})), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -283,7 +277,7 @@ func (s *TableGeneratorTestSuite) TestEmptyColumns() { }, }, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to parse arguments", @@ -305,7 +299,7 @@ func (s *TableGeneratorTestSuite) TestInvalidHeaderTemplate() { }), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -321,7 +315,7 @@ func (s *TableGeneratorTestSuite) TestInvalidHeaderTemplate() { }, }, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to parse arguments", @@ -343,7 +337,7 @@ func (s *TableGeneratorTestSuite) TestInvalidValueTemplate() { }), }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: args, DataContext: plugin.MapData{ "col_prefix": plugin.StringData("User"), @@ -359,7 +353,7 @@ func (s *TableGeneratorTestSuite) TestInvalidValueTemplate() { }, }, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to parse arguments", diff --git a/internal/builtin/content_text.go b/internal/builtin/content_text.go index f6fb5dd4..e091f878 100644 --- a/internal/builtin/content_text.go +++ b/internal/builtin/content_text.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "slices" "strings" "text/template" @@ -15,101 +14,30 @@ import ( "github.com/blackstork-io/fabric/plugin" ) -var textAllowedFormats = []string{"text", "title", "code", "blockquote"} - -const ( - textMinAbsoluteTitleSize = int64(1) - textMaxAbsoluteTitleSize = int64(6) - textDefaultAbsoluteTitleSize = int64(1) - textDefaultFormat = "text" - textDefaultCodeLanguage = "" -) - func makeTextContentProvider() *plugin.ContentProvider { return &plugin.ContentProvider{ ContentFunc: genTextContent, Args: hcldec.ObjectSpec{ - "text": &hcldec.AttrSpec{ - Name: "text", + "value": &hcldec.AttrSpec{ + Name: "value", Type: cty.String, Required: true, }, - "format_as": &hcldec.AttrSpec{ - Name: "format_as", - Type: cty.String, - Required: false, - }, - "absolute_title_size": &hcldec.AttrSpec{ - Name: "absolute_title_size", - Type: cty.Number, - Required: false, - }, - "code_language": &hcldec.AttrSpec{ - Name: "code_language", - Type: cty.String, - Required: false, - }, }, } } -func genTextContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.Content, hcl.Diagnostics) { - text := params.Args.GetAttr("text") - if text.IsNull() { - return nil, hcl.Diagnostics{{ - Severity: hcl.DiagError, - Summary: "Failed to parse arguments", - Detail: "text is required", - }} - } - format := params.Args.GetAttr("format_as") - if !format.IsNull() { - if !slices.Contains(textAllowedFormats, format.AsString()) { - return nil, hcl.Diagnostics{{ - Severity: hcl.DiagError, - Summary: "Failed to parse arguments", - Detail: "format_as must be one of " + strings.Join(textAllowedFormats, ", "), - }} - } - } else { - format = cty.StringVal(textDefaultFormat) - } - absoluteTitleSize := params.Args.GetAttr("absolute_title_size") - if absoluteTitleSize.IsNull() { - absoluteTitleSize = cty.NumberIntVal(findDefaultTitleSize(params.DataContext)) - } - titleSize, _ := absoluteTitleSize.AsBigFloat().Int64() - if titleSize < textMinAbsoluteTitleSize || titleSize > textMaxAbsoluteTitleSize { - return nil, hcl.Diagnostics{{ - Severity: hcl.DiagError, - Summary: "Failed to parse arguments", - Detail: fmt.Sprintf("absolute_title_size must be between %d and %d", textMinAbsoluteTitleSize, textMaxAbsoluteTitleSize), - }} - } - codeLanguage := params.Args.GetAttr("code_language") - if codeLanguage.IsNull() { - codeLanguage = cty.StringVal(textDefaultCodeLanguage) - } - var ( - md string - err error - ) - switch format.AsString() { - case "text": - md, err = genTextContentText(text.AsString(), params.DataContext) - case "title": - md, err = genTextContentTitle(text.AsString(), params.DataContext, titleSize) - case "code": - md, err = genTextContentCode(text.AsString(), params.DataContext, codeLanguage.AsString()) - case "blockquote": - md, err = genTextContentBlockquote(text.AsString(), params.DataContext) - default: +func genTextContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { + value := params.Args.GetAttr("value") + if value.IsNull() { return nil, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to parse arguments", - Detail: fmt.Sprintf("format_as must be one of %s", strings.Join(textAllowedFormats, ", ")), + Detail: "value is required", }} } + + text, err := genTextContentText(value.AsString(), params.DataContext) if err != nil { return nil, hcl.Diagnostics{{ Severity: hcl.DiagError, @@ -117,41 +45,13 @@ func genTextContent(ctx context.Context, params *plugin.ProvideContentParams) (* Detail: err.Error(), }} } - return &plugin.Content{ - Markdown: md, + return &plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: text, + }, }, nil } -func findDefaultTitleSize(datactx plugin.MapData) int64 { - doc, ok := datactx["document"] - if !ok { - return textDefaultAbsoluteTitleSize - } - content, ok := doc.(plugin.MapData)["content"] - if !ok { - return textDefaultAbsoluteTitleSize - } - list, ok := content.(plugin.ListData) - if !ok { - return textDefaultAbsoluteTitleSize - } - n := textMinAbsoluteTitleSize - for _, elem := range list { - content := plugin.ParseContentData(elem.(plugin.MapData)) - if content == nil { - continue - } - md := strings.TrimSpace(content.Markdown) - if strings.HasPrefix(md, "#") { - n++ - } - } - if n > textMaxAbsoluteTitleSize { - n = textMaxAbsoluteTitleSize - } - return n -} - func genTextContentText(text string, datactx plugin.MapData) (string, error) { tmpl, err := template.New("text").Parse(text) if err != nil { @@ -165,29 +65,3 @@ func genTextContentText(text string, datactx plugin.MapData) (string, error) { } return strings.TrimSpace(buf.String()), nil } - -func genTextContentTitle(text string, datactx plugin.MapData, titleSize int64) (string, error) { - text, err := genTextContentText(text, datactx) - if err != nil { - return "", err - } - // remove all newlines - text = strings.ReplaceAll(text, "\n", " ") - return strings.Repeat("#", int(titleSize)) + " " + text, nil -} - -func genTextContentCode(text string, datactx plugin.MapData, language string) (string, error) { - text, err := genTextContentText(text, datactx) - if err != nil { - return "", err - } - return fmt.Sprintf("```%s\n%s\n```", language, text), nil -} - -func genTextContentBlockquote(text string, datactx plugin.MapData) (string, error) { - text, err := genTextContentText(text, datactx) - if err != nil { - return "", err - } - return "> " + strings.ReplaceAll(text, "\n", "\n> "), nil -} diff --git a/internal/builtin/content_text_test.go b/internal/builtin/content_text_test.go index 01f273ec..22a9ec87 100644 --- a/internal/builtin/content_text_test.go +++ b/internal/builtin/content_text_test.go @@ -11,312 +11,83 @@ import ( "github.com/blackstork-io/fabric/plugin" ) -type TextGeneratorTestSuite struct { +type TextTestSuite struct { suite.Suite schema *plugin.ContentProvider } -func TestTextGeneratorSuite(t *testing.T) { - suite.Run(t, &TextGeneratorTestSuite{}) +func TestTextSuite(t *testing.T) { + suite.Run(t, &TextTestSuite{}) } -func (s *TextGeneratorTestSuite) SetupSuite() { +func (s *TextTestSuite) SetupSuite() { s.schema = makeTextContentProvider() } -func (s *TextGeneratorTestSuite) TestSchema() { +func (s *TextTestSuite) TestSchema() { s.Nil(s.schema.Config) s.NotNil(s.schema.Args) s.NotNil(s.schema.ContentFunc) } -func (s *TextGeneratorTestSuite) TestMissingText() { +func (s *TextTestSuite) TestMissingText() { ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.NullVal(cty.String), - "format_as": cty.NullVal(cty.String), - "absolute_title_size": cty.NullVal(cty.Number), - "code_language": cty.NullVal(cty.String), + "value": cty.NullVal(cty.String), }), DataContext: plugin.MapData{ "name": plugin.StringData("World"), }, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to parse arguments", - Detail: "text is required", + Detail: "value is required", }}, diags) } -func (s *TextGeneratorTestSuite) TestBasic() { +func (s *TextTestSuite) TestBasic() { ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal("Hello {{.name}}!"), - "format_as": cty.NullVal(cty.String), - "absolute_title_size": cty.NullVal(cty.Number), - "code_language": cty.NullVal(cty.String), + "value": cty.StringVal("Hello {{.name}}!"), }), DataContext: plugin.MapData{ "name": plugin.StringData("World"), }, }) s.Empty(diags) - s.Equal(&plugin.Content{ - Markdown: "Hello World!", - }, content) + s.Equal("Hello World!", result.Content.Print()) } -func (s *TextGeneratorTestSuite) TestNoTemplate() { +func (s *TextTestSuite) TestNoTemplate() { ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal("Hello World!"), - "format_as": cty.NullVal(cty.String), - "absolute_title_size": cty.NullVal(cty.Number), - "code_language": cty.NullVal(cty.String), + "value": cty.StringVal("Hello World!"), }), DataContext: nil, }) s.Empty(diags) - s.Equal(&plugin.Content{ - Markdown: "Hello World!", - }, content) + s.Equal("Hello World!", result.Content.Print()) } -func (s *TextGeneratorTestSuite) TestTitleDefault() { +func (s *TextTestSuite) TestCallInvalidTemplate() { ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal("Hello {{.name}}!"), - "format_as": cty.StringVal("title"), - "absolute_title_size": cty.NullVal(cty.Number), - "code_language": cty.NullVal(cty.String), + "value": cty.StringVal("Hello {{.name}!"), }), DataContext: plugin.MapData{ "name": plugin.StringData("World"), }, }) - s.Empty(diags) - s.Equal(&plugin.Content{ - Markdown: "# Hello World!", - }, content) -} - -func (s *TextGeneratorTestSuite) TestTitleWithTextMultiline() { - ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ - Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal("Hello\n{{.name}}\nfor you!"), - "format_as": cty.StringVal("title"), - "absolute_title_size": cty.NullVal(cty.Number), - "code_language": cty.NullVal(cty.String), - }), - DataContext: plugin.MapData{ - "name": plugin.StringData("World"), - }, - }) - s.Empty(diags) - s.Equal(&plugin.Content{ - Markdown: "# Hello World for you!", - }, content) -} - -func (s *TextGeneratorTestSuite) TestTitleWithSize() { - ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ - Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal("Hello {{.name}}!"), - "format_as": cty.StringVal("title"), - "absolute_title_size": cty.NumberIntVal(3), - "code_language": cty.NullVal(cty.String), - }), - DataContext: plugin.MapData{ - "name": plugin.StringData("World"), - }, - }) - s.Empty(diags) - s.Equal(&plugin.Content{ - Markdown: "### Hello World!", - }, content) -} - -func (s *TextGeneratorTestSuite) TestTitleWithSizeTooSmall() { - ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ - Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal("Hello {{.name}}!"), - "format_as": cty.StringVal("title"), - "absolute_title_size": cty.NumberIntVal(0), - "code_language": cty.NullVal(cty.String), - }), - DataContext: plugin.MapData{ - "name": plugin.StringData("World"), - }, - }) - s.Equal(hcl.Diagnostics{{ - Severity: hcl.DiagError, - Summary: "Failed to parse arguments", - Detail: "absolute_title_size must be between 1 and 6", - }}, diags) - s.Nil(content) -} - -func (s *TextGeneratorTestSuite) TestTitleWithSizeTooBig() { - ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ - Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal("Hello {{.name}}!"), - "format_as": cty.StringVal("title"), - "absolute_title_size": cty.NumberIntVal(7), - "code_language": cty.NullVal(cty.String), - }), - DataContext: plugin.MapData{ - "name": plugin.StringData("World"), - }, - }) - s.Equal(hcl.Diagnostics{{ - Severity: hcl.DiagError, - Summary: "Failed to parse arguments", - Detail: "absolute_title_size must be between 1 and 6", - }}, diags) - s.Nil(content) -} - -func (s *TextGeneratorTestSuite) TestCallInvalidFormat() { - ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ - Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal("Hello World!"), - "format_as": cty.StringVal("unknown"), - "absolute_title_size": cty.NullVal(cty.Number), - "code_language": cty.NullVal(cty.String), - }), - DataContext: nil, - }) - s.Nil(content) - s.Equal(hcl.Diagnostics{{ - Severity: hcl.DiagError, - Summary: "Failed to parse arguments", - Detail: "format_as must be one of text, title, code, blockquote", - }}, diags) -} - -func (s *TextGeneratorTestSuite) TestCallInvalidTemplate() { - ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ - Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal("Hello {{.name}!"), - "format_as": cty.NullVal(cty.String), - "absolute_title_size": cty.NullVal(cty.Number), - "code_language": cty.NullVal(cty.String), - }), - DataContext: plugin.MapData{ - "name": plugin.StringData("World"), - }, - }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to render text", Detail: "failed to parse text template: template: text:1: bad character U+007D '}'", }}, diags) } - -func (s *TextGeneratorTestSuite) TestCallCodeDefault() { - ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ - Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal(`Hello {{.name}}!`), - "format_as": cty.StringVal("code"), - "absolute_title_size": cty.NullVal(cty.Number), - "code_language": cty.NullVal(cty.String), - }), - DataContext: plugin.MapData{ - "name": plugin.StringData("World"), - }, - }) - s.Empty(diags) - s.Equal(&plugin.Content{ - Markdown: "```\nHello World!\n```", - }, content) -} - -func (s *TextGeneratorTestSuite) TestCallCodeWithLanguage() { - ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ - Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal(`{"hello": "{{.name}}"}`), - "format_as": cty.StringVal("code"), - "absolute_title_size": cty.NullVal(cty.Number), - "code_language": cty.StringVal("json"), - }), - DataContext: plugin.MapData{ - "name": plugin.StringData("world"), - }, - }) - s.Empty(diags) - s.Equal(&plugin.Content{ - Markdown: "```json\n{\"hello\": \"world\"}\n```", - }, content) -} - -func (s *TextGeneratorTestSuite) TestCallBlockquote() { - ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ - Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal(`Hello {{.name}}!`), - "format_as": cty.StringVal("blockquote"), - "absolute_title_size": cty.NullVal(cty.Number), - "code_language": cty.NullVal(cty.String), - }), - DataContext: plugin.MapData{ - "name": plugin.StringData("World"), - }, - }) - s.Empty(diags) - s.Equal(&plugin.Content{ - Markdown: "> Hello World!", - }, content) -} - -func (s *TextGeneratorTestSuite) TestCallBlockquoteMultiline() { - ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ - Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal("Hello\n{{.name}}\nfor you!"), - "format_as": cty.StringVal("blockquote"), - "absolute_title_size": cty.NullVal(cty.Number), - "code_language": cty.NullVal(cty.String), - }), - DataContext: plugin.MapData{ - "name": plugin.StringData("World"), - }, - }) - s.Empty(diags) - s.Equal(&plugin.Content{ - Markdown: "> Hello\n> World\n> for you!", - }, content) -} - -func (s *TextGeneratorTestSuite) TestCallBlockquoteMultilineDoubleNewline() { - ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ - Args: cty.ObjectVal(map[string]cty.Value{ - "text": cty.StringVal("Hello\n{{.name}}\n\nfor you!"), - "format_as": cty.StringVal("blockquote"), - "absolute_title_size": cty.NullVal(cty.Number), - "code_language": cty.NullVal(cty.String), - }), - DataContext: plugin.MapData{ - "name": plugin.StringData("World"), - }, - }) - s.Empty(diags) - s.Equal(&plugin.Content{ - Markdown: "> Hello\n> World\n> \n> for you!", - }, content) -} diff --git a/internal/builtin/content_title.go b/internal/builtin/content_title.go new file mode 100644 index 00000000..36093023 --- /dev/null +++ b/internal/builtin/content_title.go @@ -0,0 +1,166 @@ +package builtin + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" + + "github.com/blackstork-io/fabric/plugin" +) + +const ( + minAbsoluteTitleSize = int64(1) + maxAbsoluteTitleSize = int64(6) + defaultAbsoluteTitleSize = int64(1) +) + +func makeTitleContentProvider() *plugin.ContentProvider { + return &plugin.ContentProvider{ + ContentFunc: genTitleContent, + Args: hcldec.ObjectSpec{ + "value": &hcldec.AttrSpec{ + Name: "value", + Type: cty.String, + Required: true, + }, + "absolute_size": &hcldec.AttrSpec{ + Name: "absolute_size", + Type: cty.Number, + Required: false, + }, + "relative_size": &hcldec.AttrSpec{ + Name: "relative_size", + Type: cty.Number, + Required: false, + }, + }, + } +} + +func genTitleContent(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { + value := params.Args.GetAttr("value") + if value.IsNull() { + return nil, hcl.Diagnostics{{ + Severity: hcl.DiagError, + Summary: "Failed to parse arguments", + Detail: "value is required", + }} + } + absoluteSize := params.Args.GetAttr("absolute_size") + if absoluteSize.IsNull() { + absoluteSize = cty.NumberIntVal(findDefaultTitleSize(params.DataContext) + 1) + } + relativeSize := params.Args.GetAttr("relative_size") + if relativeSize.IsNull() { + relativeSize = cty.NumberIntVal(0) + } + + titleSize, _ := absoluteSize.AsBigFloat().Int64() + relationSize, _ := relativeSize.AsBigFloat().Int64() + titleSize += relationSize + if titleSize < minAbsoluteTitleSize { + titleSize = minAbsoluteTitleSize + } + if titleSize < minAbsoluteTitleSize || titleSize > maxAbsoluteTitleSize { + return nil, hcl.Diagnostics{{ + Severity: hcl.DiagError, + Summary: "Failed to parse arguments", + Detail: fmt.Sprintf("absolute_size must be between %d and %d", minAbsoluteTitleSize, maxAbsoluteTitleSize), + }} + } + + text, err := genTextContentText(value.AsString(), params.DataContext) + if err != nil { + return nil, hcl.Diagnostics{{ + Severity: hcl.DiagError, + Summary: "Failed to render value", + Detail: err.Error(), + }} + } + // remove all newlines + text = strings.ReplaceAll(text, "\n", " ") + text = strings.Repeat("#", int(titleSize)) + " " + text + return &plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: text, + }, + }, nil +} + +func parseScope(datactx plugin.MapData) (document *plugin.ContentSection, section *plugin.ContentSection) { + documentMap, ok := datactx["document"] + if !ok { + return + } + + contentMap, ok := documentMap.(plugin.MapData)["content"] + if !ok { + return + } + + content, err := plugin.ParseContentData(contentMap.(plugin.MapData)) + if err != nil { + return + } + + document, ok = content.(*plugin.ContentSection) + if !ok { + return + } + + sectionMap, ok := datactx["section"] + if !ok || sectionMap == nil { + return + } + contentMap, ok = sectionMap.(plugin.MapData)["content"] + if !ok { + return + } + content, err = plugin.ParseContentData(contentMap.(plugin.MapData)) + if err != nil { + return + } + section, ok = content.(*plugin.ContentSection) + if !ok { + return + } + return document, section +} + +func findDepth(parent *plugin.ContentSection, id uint32, depth int) int { + if parent.ID() == id { + return depth + } + for _, child := range parent.Children { + if child.ID() == id { + return depth + } + if child, ok := child.(*plugin.ContentSection); ok { + if d := findDepth(child, id, depth+1); d > -1 { + return d + } + } + } + return -1 +} + +func findDefaultTitleSize(datactx plugin.MapData) int64 { + document, section := parseScope(datactx) + if section == nil { + return defaultAbsoluteTitleSize + } + + depth := findDepth(document, section.ID(), 1) + if depth == 0 { + return defaultAbsoluteTitleSize + } + size := defaultAbsoluteTitleSize + int64(depth) + if size > maxAbsoluteTitleSize { + return maxAbsoluteTitleSize + } + return size +} diff --git a/internal/builtin/content_title_test.go b/internal/builtin/content_title_test.go new file mode 100644 index 00000000..daad3437 --- /dev/null +++ b/internal/builtin/content_title_test.go @@ -0,0 +1,119 @@ +package builtin + +import ( + "context" + "testing" + + "github.com/hashicorp/hcl/v2" + "github.com/stretchr/testify/suite" + "github.com/zclconf/go-cty/cty" + + "github.com/blackstork-io/fabric/plugin" +) + +type TitleTestSuite struct { + suite.Suite + schema *plugin.ContentProvider +} + +func TestTitleSuite(t *testing.T) { + suite.Run(t, &TitleTestSuite{}) +} + +func (s *TitleTestSuite) SetupSuite() { + s.schema = makeTitleContentProvider() +} + +func (s *TitleTestSuite) TestSchema() { + s.Nil(s.schema.Config) + s.NotNil(s.schema.Args) + s.NotNil(s.schema.ContentFunc) +} + +func (s *TitleTestSuite) TestMissingValue() { + ctx := context.Background() + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + Args: cty.ObjectVal(map[string]cty.Value{ + "value": cty.NullVal(cty.String), + "absolute_size": cty.NullVal(cty.Number), + "relative_size": cty.NullVal(cty.Number), + }), + DataContext: plugin.MapData{ + "name": plugin.StringData("World"), + }, + }) + s.Nil(result) + s.Equal(hcl.Diagnostics{{ + Severity: hcl.DiagError, + Summary: "Failed to parse arguments", + Detail: "value is required", + }}, diags) +} + +func (s *TitleTestSuite) TestTDefault() { + ctx := context.Background() + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + Args: cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal("Hello {{.name}}!"), + "absolute_size": cty.NullVal(cty.Number), + "relative_size": cty.NullVal(cty.Number), + }), + DataContext: plugin.MapData{ + "name": plugin.StringData("World"), + }, + }) + s.Empty(diags) + s.Equal("## Hello World!", result.Content.Print()) +} + +func (s *TitleTestSuite) TestWithTextMultiline() { + ctx := context.Background() + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + Args: cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal("Hello\n{{.name}}\nfor you!"), + "absolute_size": cty.NullVal(cty.Number), + "relative_size": cty.NullVal(cty.Number), + }), + DataContext: plugin.MapData{ + "name": plugin.StringData("World"), + }, + }) + s.Empty(diags) + s.Equal("## Hello World for you!", result.Content.Print()) +} + +func (s *TitleTestSuite) TestWithSize() { + ctx := context.Background() + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + Args: cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal("Hello {{.name}}!"), + "absolute_size": cty.NumberIntVal(3), + "relative_size": cty.NullVal(cty.Number), + }), + DataContext: plugin.MapData{ + "name": plugin.StringData("World"), + }, + }) + s.Empty(diags) + s.Equal("### Hello World!", result.Content.Print()) +} + +func (s *TitleTestSuite) TestWithSizeTooBig() { + ctx := context.Background() + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + Args: cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal("Hello {{.name}}!"), + "absolute_size": cty.NumberIntVal(7), + "relative_size": cty.NullVal(cty.Number), + }), + DataContext: plugin.MapData{ + "name": plugin.StringData("World"), + }, + }) + s.Equal(hcl.Diagnostics{{ + Severity: hcl.DiagError, + Summary: "Failed to parse arguments", + Detail: "absolute_size must be between 1 and 6", + }}, diags) + s.Nil(result) +} diff --git a/internal/builtin/content_toc.go b/internal/builtin/content_toc.go index 47756521..02c3b409 100644 --- a/internal/builtin/content_toc.go +++ b/internal/builtin/content_toc.go @@ -20,10 +20,9 @@ const ( defaultTOCStartLevel = 1 defaultTOCEndLevel = 3 defaultTOCOrdered = false - defaultTOCScope = "document" ) -var availableTOCScopes = []string{"document", "section"} +var availableTOCScopes = []string{"document", "section", "auto"} func makeTOCContentProvider() *plugin.ContentProvider { return &plugin.ContentProvider{ @@ -89,7 +88,7 @@ func parseTOCArgs(args cty.Value) (*tocArgs, error) { } scope := args.GetAttr("scope") if scope.IsNull() { - scope = cty.StringVal(defaultTOCScope) + scope = cty.StringVal("auto") } else if !slices.Contains(availableTOCScopes, scope.AsString()) { return nil, fmt.Errorf("scope should be one of %s", strings.Join(availableTOCScopes, ", ")) } @@ -103,7 +102,7 @@ func parseTOCArgs(args cty.Value) (*tocArgs, error) { }, nil } -func genTOC(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.Content, hcl.Diagnostics) { +func genTOC(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { args, err := parseTOCArgs(params.Args) if err != nil { return nil, hcl.Diagnostics{{ @@ -112,37 +111,7 @@ func genTOC(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.C Detail: err.Error(), }} } - var scopedCtx plugin.Data - if args.scope == "section" { - section, ok := params.DataContext["section"] - if !ok { - return nil, hcl.Diagnostics{{ - Severity: hcl.DiagError, - Summary: "No section context", - Detail: "No section context found", - }} - } - scopedCtx = section - } else { - doc, ok := params.DataContext["document"] - if !ok { - return nil, hcl.Diagnostics{{ - Severity: hcl.DiagError, - Summary: "No document context", - Detail: "No document context found", - }} - } - scopedCtx = doc - } - content, ok := scopedCtx.(plugin.MapData)["content"] - if !ok { - return nil, hcl.Diagnostics{{ - Severity: hcl.DiagError, - Summary: "No content context", - Detail: "No content context found", - }} - } - titles, err := parseContentTitles(content, args.startLevel, args.endLevel) + titles, err := parseContentTitles(params.DataContext, args.startLevel, args.endLevel, args.scope) if err != nil { return nil, hcl.Diagnostics{{ Severity: hcl.DiagError, @@ -151,11 +120,9 @@ func genTOC(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.C }} } - return &plugin.Content{ - Markdown: titles.render(0, args.ordered), - Location: &plugin.Location{ - Index: 0, - Effect: plugin.LocationEffectBefore, + return &plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: titles.render(0, args.ordered), }, }, nil } @@ -192,7 +159,6 @@ func (l tocNodeList) add(node tocNode) tocNodeList { if len(l) == 0 { return append(l, node) } - last := l[len(l)-1] if last.level < node.level { last.children = last.children.add(node) @@ -207,19 +173,43 @@ func anchorize(s string) string { return strings.ToLower(strings.ReplaceAll(s, " ", "-")) } -func parseContentTitles(data plugin.Data, startLvl, endLvl int) (tocNodeList, error) { - list, ok := data.(plugin.ListData) - if !ok { - return nil, fmt.Errorf("expected a list of content titles") +func extractTitles(section *plugin.ContentSection) []string { + var titles []string + for _, content := range section.Children { + switch content := content.(type) { + case *plugin.ContentSection: + titles = append(titles, extractTitles(content)...) + case *plugin.ContentElement: + meta := content.Meta() + if meta == nil || meta.Plugin != Name || meta.Provider != "title" { + continue + } + titles = append(titles, content.Markdown) + } + } + return titles +} + +func parseContentTitles(data plugin.MapData, startLvl, endLvl int, scope string) (tocNodeList, error) { + document, section := parseScope(data) + var list []string + if scope == "auto" { + if section != nil { + scope = "section" + } else { + scope = "document" + } + } + if scope == "document" { + list = extractTitles(document) + } else if scope == "section" && section != nil { + list = extractTitles(section) + } else { + return nil, fmt.Errorf("no content to parse") } var result tocNodeList for _, item := range list { - elem, ok := item.(plugin.MapData) - if !ok { - return nil, fmt.Errorf("expected a string") - } - content := plugin.ParseContentData(elem) - line := strings.TrimSpace(content.Markdown) + line := strings.TrimSpace(item) if strings.HasPrefix(line, "#") { level := strings.Count(line, "#") if level < startLvl || level > endLvl { diff --git a/internal/builtin/content_toc_test.go b/internal/builtin/content_toc_test.go index d76d9b94..f1891f51 100644 --- a/internal/builtin/content_toc_test.go +++ b/internal/builtin/content_toc_test.go @@ -34,6 +34,10 @@ func (s *TOCContentTestSuite) TestSchema() { func (s *TOCContentTestSuite) TestSimple() { schema := makeTOCContentProvider() ctx := context.Background() + titleMeta := plugin.MapData{ + "provider": plugin.StringData("title"), + "plugin": plugin.StringData("blackstork/builtin"), + } res, diags := schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "start_level": cty.NullVal(cty.Number), @@ -43,24 +47,36 @@ func (s *TOCContentTestSuite) TestSimple() { }), DataContext: plugin.MapData{ "document": plugin.MapData{ - "content": plugin.ListData{ - plugin.MapData{ - "markdown": plugin.StringData("# Header 1"), - }, - plugin.MapData{ - "markdown": plugin.StringData("Lorem ipsum dolor sit amet, consectetur adipiscing elit."), - }, - plugin.MapData{ - "markdown": plugin.StringData("## Header 2"), - }, - plugin.MapData{ - "markdown": plugin.StringData("Vestibulum nec odio."), - }, - plugin.MapData{ - "markdown": plugin.StringData("### Header 3"), - }, - plugin.MapData{ - "markdown": plugin.StringData("Integer sit amet."), + "content": plugin.MapData{ + "type": plugin.StringData("section"), + "children": plugin.ListData{ + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("# Header 1"), + "meta": titleMeta, + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("Lorem ipsum dolor sit amet, consectetur adipiscing elit."), + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("## Header 2"), + "meta": titleMeta, + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("Vestibulum nec odio."), + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("### Header 3"), + "meta": titleMeta, + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("Integer sit amet."), + }, }, }, }, @@ -71,12 +87,16 @@ func (s *TOCContentTestSuite) TestSimple() { "- [Header 1](#header-1)", " - [Header 2](#header-2)", " - [Header 3](#header-3)", - }, "\n")+"\n", res.Markdown) + }, "\n")+"\n", res.Content.Print()) } func (s *TOCContentTestSuite) TestAdvanced() { schema := makeTOCContentProvider() ctx := context.Background() + titleMeta := plugin.MapData{ + "provider": plugin.StringData("title"), + "plugin": plugin.StringData("blackstork/builtin"), + } res, diags := schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "start_level": cty.NumberIntVal(2), @@ -84,38 +104,58 @@ func (s *TOCContentTestSuite) TestAdvanced() { "ordered": cty.True, "scope": cty.StringVal("document"), }), + DataContext: plugin.MapData{ "document": plugin.MapData{ - "content": plugin.ListData{ - plugin.MapData{ - "markdown": plugin.StringData("# Header 1"), - }, - plugin.MapData{ - "markdown": plugin.StringData("Lorem ipsum dolor sit amet, consectetur adipiscing elit."), - }, - plugin.MapData{ - "markdown": plugin.StringData("## Header 2"), - }, - plugin.MapData{ - "markdown": plugin.StringData("Vestibulum nec odio."), - }, - plugin.MapData{ - "markdown": plugin.StringData("### Header 3"), - }, - plugin.MapData{ - "markdown": plugin.StringData("Integer sit amet."), - }, - plugin.MapData{ - "markdown": plugin.StringData("## Header 4"), - }, - plugin.MapData{ - "markdown": plugin.StringData("Vestibulum nec odio."), - }, - plugin.MapData{ - "markdown": plugin.StringData("## Header 5"), - }, - plugin.MapData{ - "markdown": plugin.StringData("Vestibulum nec odio."), + "content": plugin.MapData{ + "type": plugin.StringData("section"), + "children": plugin.ListData{ + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("# Header 1"), + "meta": titleMeta, + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("Lorem ipsum dolor sit amet, consectetur adipiscing elit."), + "meta": titleMeta, + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("## Header 2"), + "meta": titleMeta, + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("Vestibulum nec odio."), + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("### Header 3"), + "meta": titleMeta, + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("Integer sit amet."), + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("## Header 4"), + "meta": titleMeta, + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("Vestibulum nec odio."), + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("## Header 5"), + "meta": titleMeta, + }, + plugin.MapData{ + "type": plugin.StringData("element"), + "markdown": plugin.StringData("Vestibulum nec odio."), + }, }, }, }, @@ -127,5 +167,5 @@ func (s *TOCContentTestSuite) TestAdvanced() { " 1. [Header 3](#header-3)", "2. [Header 4](#header-4)", "3. [Header 5](#header-5)", - }, "\n")+"\n", res.Markdown) + }, "\n")+"\n", res.Content.Print()) } diff --git a/internal/builtin/plugin.go b/internal/builtin/plugin.go index 0c15dfc5..82db4f6c 100644 --- a/internal/builtin/plugin.go +++ b/internal/builtin/plugin.go @@ -19,6 +19,9 @@ func Plugin(version string) *plugin.Schema { ContentProviders: plugin.ContentProviders{ "toc": makeTOCContentProvider(), "text": makeTextContentProvider(), + "title": makeTitleContentProvider(), + "code": makeCodeContentProvider(), + "blockquote": makeBlockQuoteContentProvider(), "image": makeImageContentProvider(), "list": makeListContentProvider(), "table": makeTableContentProvider(), diff --git a/internal/openai/content_openai_text.go b/internal/openai/content_openai_text.go index 0f759a47..965962d2 100644 --- a/internal/openai/content_openai_text.go +++ b/internal/openai/content_openai_text.go @@ -49,7 +49,7 @@ func makeOpenAITextContentSchema(loader ClientLoadFn) *plugin.ContentProvider { } func genOpenAIText(loader ClientLoadFn) plugin.ProvideContentFunc { - return func(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.Content, hcl.Diagnostics) { + return func(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { cli, err := makeClient(loader, params.Config) if err != nil { return nil, hcl.Diagnostics{{ @@ -66,8 +66,10 @@ func genOpenAIText(loader ClientLoadFn) plugin.ProvideContentFunc { Detail: err.Error(), }} } - return &plugin.Content{ - Markdown: result, + return &plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: result, + }, }, nil } } diff --git a/internal/openai/content_openai_text_test.go b/internal/openai/content_openai_text_test.go index fa41abcb..8119e730 100644 --- a/internal/openai/content_openai_text_test.go +++ b/internal/openai/content_openai_text_test.go @@ -67,7 +67,7 @@ func (s *OpenAITextContentTestSuite) TestBasic() { }, }, nil) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.StringVal("Tell me a story"), "model": cty.NullVal(cty.String), @@ -80,9 +80,7 @@ func (s *OpenAITextContentTestSuite) TestBasic() { DataContext: plugin.MapData{}, }) s.Nil(diags) - s.Equal(&plugin.Content{ - Markdown: "Once upon a time.", - }, content) + s.Equal("Once upon a time.", result.Content.Print()) } func (s *OpenAITextContentTestSuite) TestAdvanced() { @@ -111,7 +109,7 @@ func (s *OpenAITextContentTestSuite) TestAdvanced() { }, }, nil) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.StringVal("Tell me a story"), "model": cty.StringVal("model_123"), @@ -128,14 +126,12 @@ func (s *OpenAITextContentTestSuite) TestAdvanced() { }, }) s.Nil(diags) - s.Equal(&plugin.Content{ - Markdown: "Once upon a time.", - }, content) + s.Equal("Once upon a time.", result.Content.Print()) } func (s *OpenAITextContentTestSuite) TestMissingPrompt() { ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.NullVal(cty.String), "model": cty.NullVal(cty.String), @@ -147,7 +143,7 @@ func (s *OpenAITextContentTestSuite) TestMissingPrompt() { }), DataContext: plugin.MapData{}, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to generate text", @@ -157,7 +153,7 @@ func (s *OpenAITextContentTestSuite) TestMissingPrompt() { func (s *OpenAITextContentTestSuite) TestMissingAPIKey() { ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.StringVal("Tell me a story"), "model": cty.NullVal(cty.String), @@ -169,7 +165,7 @@ func (s *OpenAITextContentTestSuite) TestMissingAPIKey() { }), DataContext: plugin.MapData{}, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to create client", @@ -183,7 +179,7 @@ func (s *OpenAITextContentTestSuite) TestFailingClient() { Message: "error_message", }) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.StringVal("Tell me a story"), "model": cty.NullVal(cty.String), @@ -195,7 +191,7 @@ func (s *OpenAITextContentTestSuite) TestFailingClient() { }), DataContext: plugin.MapData{}, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to generate text", @@ -207,7 +203,7 @@ func (s *OpenAITextContentTestSuite) TestCancellation() { s.cli.On("GenerateChatCompletion", mock.Anything, mock.Anything).Return(nil, context.Canceled) ctx, cancel := context.WithCancel(context.Background()) cancel() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.StringVal("Tell me a story"), "model": cty.NullVal(cty.String), @@ -219,7 +215,7 @@ func (s *OpenAITextContentTestSuite) TestCancellation() { }), DataContext: plugin.MapData{}, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to generate text", @@ -234,7 +230,7 @@ func (s *OpenAITextContentTestSuite) TestErrorEncoding() { } s.cli.On("GenerateChatCompletion", mock.Anything, mock.Anything).Return(nil, want) ctx := context.Background() - content, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ + result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "prompt": cty.StringVal("Tell me a story"), "model": cty.NullVal(cty.String), @@ -246,7 +242,7 @@ func (s *OpenAITextContentTestSuite) TestErrorEncoding() { }), DataContext: plugin.MapData{}, }) - s.Nil(content) + s.Nil(result) s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to generate text", diff --git a/internal/stixview/content_stixview.go b/internal/stixview/content_stixview.go index a4c1c6a8..bf6abca3 100644 --- a/internal/stixview/content_stixview.go +++ b/internal/stixview/content_stixview.go @@ -97,7 +97,7 @@ func makeStixViewContentProvider() *plugin.ContentProvider { } } -func renderStixView(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.Content, hcl.Diagnostics) { +func renderStixView(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { args, err := parseStixViewArgs(params.Args) if err != nil { return nil, hcl.Diagnostics{{ @@ -145,8 +145,10 @@ func renderStixView(ctx context.Context, params *plugin.ProvideContentParams) (* }} } - return &plugin.Content{ - Markdown: buf.String(), + return &plugin.ContentResult{ + Content: &plugin.ContentElement{ + Markdown: buf.String(), + }, }, nil } diff --git a/internal/stixview/content_stixview_test.go b/internal/stixview/content_stixview_test.go index 1b89edaf..18373b89 100644 --- a/internal/stixview/content_stixview_test.go +++ b/internal/stixview/content_stixview_test.go @@ -53,7 +53,7 @@ func (s *StixViewTestSuite) TestGistID() { ``, `
`, `
`, - }, "\n"), res.Markdown) + }, "\n"), res.Content.Print()) } func (s *StixViewTestSuite) TestStixURL() { @@ -78,7 +78,7 @@ func (s *StixViewTestSuite) TestStixURL() { ``, `
`, `
`, - }, "\n"), res.Markdown) + }, "\n"), res.Content.Print()) } func (s *StixViewTestSuite) TestAllArgs() { @@ -103,7 +103,7 @@ func (s *StixViewTestSuite) TestAllArgs() { ``, `
`, `
`, - }, "\n"), res.Markdown) + }, "\n"), res.Content.Print()) } func (s *StixViewTestSuite) TestQueryResult() { @@ -130,8 +130,8 @@ func (s *StixViewTestSuite) TestQueryResult() { }, }) s.Len(diags, 0) - s.Contains(res.Markdown, ``) - s.Contains(res.Markdown, `
`) + s.Contains(res.Content.Print(), `
= len(*d) { - *d = append(*d, s.AsData()) - return - } - switch s.Location.Effect { - case plugin.LocationEffectBefore: - *d = append((*d)[:idx], append(plugin.ListData{s.AsData()}, (*d)[idx:]...)...) - return - case plugin.LocationEffectAfter: - *d = append((*d)[:idx+1], append(plugin.ListData{s.AsData()}, (*d)[idx+1:]...)...) - return - default: - (*d)[idx] = s.AsData() - return - } - } - *d = append(*d, s.AsData()) -} +type Result = plugin.ContentSection diff --git a/plugin/content.go b/plugin/content.go index aa586a66..ac8f5476 100644 --- a/plugin/content.go +++ b/plugin/content.go @@ -1,5 +1,24 @@ package plugin +import ( + "fmt" + "slices" + "strings" + "sync" +) + +type idStore struct { + id uint32 + sync.Mutex +} + +func (i *idStore) next() uint32 { + i.Lock() + defer i.Unlock() + i.id++ + return i.id +} + type LocationEffect int const ( @@ -9,40 +28,220 @@ const ( ) type Location struct { - Index int + Index uint32 Effect LocationEffect } -func (loc *Location) AsData() Data { +type ContentResult struct { + Location *Location + Content Content +} + +type Content interface { + setID(id uint32) + setMeta(meta *ContentMeta) + AsData() Data + ID() uint32 + AsJQData() Data + Meta() *ContentMeta + Print() string +} + +type ContentEmpty struct { + id uint32 + meta *ContentMeta +} + +func (n *ContentEmpty) setID(id uint32) { + n.id = id +} + +func (n *ContentEmpty) setMeta(meta *ContentMeta) { + n.meta = meta +} + +func (n *ContentEmpty) AsData() Data { + return MapData{ + "type": StringData("empty"), + "id": NumberData(n.id), + "meta": n.meta.AsData(), + } +} + +func (n *ContentEmpty) ID() uint32 { + return n.id +} + +func (n *ContentEmpty) Meta() *ContentMeta { + return n.meta +} + +func (n *ContentEmpty) Print() string { + return "" +} + +func (n *ContentEmpty) AsJQData() Data { + return n.AsData() +} + +type ContentSection struct { + *idStore + id uint32 + Children []Content + meta *ContentMeta +} + +// Print returns the content tree as a string. +func (c *ContentSection) Print() string { + list := make([]string, len(c.Children)) + for i, child := range c.Children { + list[i] = child.Print() + } + return strings.Join(list, "\n\n") +} + +// Add content to the content tree. +func (c *ContentSection) Add(content Content, loc *Location) error { + return addContent(c, content, loc) +} + +func addContent(parent *ContentSection, child Content, loc *Location) error { + if parent.idStore == nil { + parent.idStore = &idStore{} + } + if section, ok := child.(*ContentSection); ok { + section.idStore = parent.idStore + } if loc == nil { + child.setID(parent.next()) + parent.Children = append(parent.Children, child) return nil } - return MapData{ - "index": NumberData(loc.Index), - "effect": NumberData(loc.Effect), + if loc.Effect != LocationEffectUnspecified { + child.setID(parent.next()) + } else { + child.setID(loc.Index) + } + foundIdx := slices.IndexFunc(parent.Children, func(c Content) bool { + return c.ID() == loc.Index + }) + if foundIdx > -1 { + switch loc.Effect { + case LocationEffectBefore: + parent.Children = append(parent.Children[:foundIdx], append([]Content{child}, parent.Children[foundIdx:]...)...) + case LocationEffectAfter: + parent.Children = append(parent.Children[:foundIdx+1], append([]Content{child}, parent.Children[foundIdx+1:]...)...) + default: + parent.Children[foundIdx] = child + } + return nil + } + for _, c := range parent.Children { + section, ok := c.(*ContentSection) + if !ok { + continue + } + err := addContent(section, child, loc) + if err == ErrContentLocationNotFound { + continue + } else if err != nil { + return err + } } + return ErrContentLocationNotFound } -type Content struct { - Markdown string - Location *Location - meta *ContentMeta +func (c *ContentSection) setID(id uint32) { + c.id = id +} + +func (c *ContentSection) setMeta(meta *ContentMeta) { + c.meta = meta + for _, child := range c.Children { + child.setMeta(meta) + } } -func (c *Content) Meta() *ContentMeta { +func (c *ContentSection) ID() uint32 { + return c.id +} + +func (c *ContentSection) Meta() *ContentMeta { + return c.meta +} + +func (c *ContentSection) AsJQData() Data { + return c.AsData() +} + +// Compact removes empty sections from the content tree. +func (c *ContentSection) Compact() { + c.Children = slices.DeleteFunc(c.Children, func(c Content) bool { + _, ok := c.(*ContentEmpty) + return ok + }) + for _, child := range c.Children { + if section, ok := child.(*ContentSection); ok { + section.Compact() + } + } +} + +// AsData returns the content tree as a map. +func (c *ContentSection) AsData() Data { if c == nil { return nil } + children := make(ListData, len(c.Children)) + for i, child := range c.Children { + children[i] = child.AsData() + } + return MapData{ + "type": StringData("section"), + "id": NumberData(c.id), + "children": children, + "meta": c.meta.AsData(), + } +} + +type ContentElement struct { + id uint32 + Markdown string + meta *ContentMeta +} + +func (c *ContentElement) ID() uint32 { + return c.id +} + +func (c *ContentElement) Print() string { + return c.Markdown +} + +func (c *ContentElement) setID(id uint32) { + c.id = id +} + +func (c *ContentElement) Meta() *ContentMeta { return c.meta } -func (c *Content) AsData() Data { +func (c *ContentElement) setMeta(meta *ContentMeta) { + c.meta = meta +} + +func (c *ContentElement) AsJQData() Data { + return c.AsData() +} + +func (c *ContentElement) AsData() Data { if c == nil { return nil } return MapData{ + "type": StringData("element"), + "id": NumberData(c.id), "markdown": StringData(c.Markdown), - "location": c.Location.AsData(), "meta": c.meta.AsData(), } } @@ -59,32 +258,95 @@ func (meta *ContentMeta) AsData() Data { } return MapData{ "provider": StringData(meta.Provider), + "plugin": StringData(meta.Plugin), + "version": StringData(meta.Version), } } -func ParseContentData(data MapData) *Content { +func ParseContentData(data MapData) (Content, error) { if data == nil { - return nil + return nil, nil } - md, _ := data["markdown"].(StringData) - return &Content{ - Markdown: string(md), - Location: ParseLocationData(data["location"]), - meta: ParseContentMeta(data["meta"]), + typ, ok := data["type"].(StringData) + if !ok { + return nil, fmt.Errorf("missing type") + } + switch string(typ) { + case "section": + return parseContentSection(data) + case "element": + return parseContentElement(data) + case "empty": + return parseContentEmpty(data) + default: + return nil, fmt.Errorf("unknown type: %s", typ) } } -func ParseLocationData(data Data) *Location { +func parseContentSection(data MapData) (*ContentSection, error) { if data == nil { - return nil + return nil, nil + } + section := &ContentSection{} + children, ok := data["children"].(ListData) + if !ok { + return nil, fmt.Errorf("missing children") + } + section.Children = make([]Content, len(children)) + var err error + for i, child := range children { + section.Children[i], err = ParseContentData(child.(MapData)) + if err != nil { + return nil, err + } + } + id, ok := data["id"].(NumberData) + if ok { + section.id = uint32(id) + } + meta, ok := data["meta"].(MapData) + if ok { + section.meta = ParseContentMeta(meta) + } + return section, nil +} + +func parseContentElement(data MapData) (*ContentElement, error) { + if data == nil { + return nil, nil + } + elem := &ContentElement{} + markdown, ok := data["markdown"].(StringData) + if !ok { + return nil, fmt.Errorf("missing markdown") + } + elem.Markdown = string(markdown) + id, ok := data["id"].(NumberData) + if ok { + elem.id = uint32(id) + } + meta, ok := data["meta"].(MapData) + if ok { + elem.meta = ParseContentMeta(meta) + } + return elem, nil +} + +func parseContentEmpty(data MapData) (*ContentEmpty, error) { + if data == nil { + return nil, nil + } + empty := &ContentEmpty{} + id, ok := data["id"].(NumberData) + if !ok { + return nil, fmt.Errorf("missing id") } - loc := data.(MapData) - index, _ := loc["index"].(NumberData) - effect, _ := loc["effect"].(NumberData) - return &Location{ - Index: int(index), - Effect: LocationEffect(effect), + empty.id = uint32(id) + meta, ok := data["meta"].(MapData) + if ok { + empty.meta = ParseContentMeta(meta) } + return empty, nil } func ParseContentMeta(data Data) *ContentMeta { @@ -93,7 +355,11 @@ func ParseContentMeta(data Data) *ContentMeta { } meta := data.(MapData) provider, _ := meta["provider"].(StringData) + plugin, _ := meta["plugin"].(StringData) + version, _ := meta["version"].(StringData) return &ContentMeta{ Provider: string(provider), + Plugin: string(plugin), + Version: string(version), } } diff --git a/plugin/content_provider.go b/plugin/content_provider.go index ab823fc7..3492589e 100644 --- a/plugin/content_provider.go +++ b/plugin/content_provider.go @@ -71,7 +71,7 @@ func (cg *ContentProvider) Validate() hcl.Diagnostics { return diags } -func (cg *ContentProvider) Execute(ctx context.Context, params *ProvideContentParams) (*Content, hcl.Diagnostics) { +func (cg *ContentProvider) Execute(ctx context.Context, params *ProvideContentParams) (*ContentResult, hcl.Diagnostics) { if cg == nil { return nil, hcl.Diagnostics{{ Severity: hcl.DiagError, @@ -92,6 +92,7 @@ type ProvideContentParams struct { Config cty.Value Args cty.Value DataContext MapData + ContentID uint32 } -type ProvideContentFunc func(ctx context.Context, params *ProvideContentParams) (*Content, hcl.Diagnostics) +type ProvideContentFunc func(ctx context.Context, params *ProvideContentParams) (*ContentResult, hcl.Diagnostics) diff --git a/plugin/data.go b/plugin/data.go index 55bf0d69..7e961f40 100644 --- a/plugin/data.go +++ b/plugin/data.go @@ -167,6 +167,10 @@ func (d ConvMapData) AsJQData() Data { func (d ConvMapData) Any() any { dst := make(map[string]any, len(d)) for k, v := range d { + if v == nil { + dst[k] = nil + continue + } dst[k] = v.AsJQData().Any() } return dst diff --git a/plugin/errors.go b/plugin/errors.go new file mode 100644 index 00000000..8e77bbbc --- /dev/null +++ b/plugin/errors.go @@ -0,0 +1,5 @@ +package plugin + +import "fmt" + +var ErrContentLocationNotFound = fmt.Errorf("content location not found") diff --git a/plugin/pluginapi/v1/content.pb.go b/plugin/pluginapi/v1/content.pb.go index 5b1aad8c..ddeee2a4 100644 --- a/plugin/pluginapi/v1/content.pb.go +++ b/plugin/pluginapi/v1/content.pb.go @@ -124,19 +124,78 @@ func (x *Location) GetEffect() LocationEffect { return LocationEffect_LOCATION_EFFECT_UNSPECIFIED } -type Content struct { +type ContentResult struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Markdown string `protobuf:"bytes,1,opt,name=markdown,proto3" json:"markdown,omitempty"` + Content *Content `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` Location *Location `protobuf:"bytes,2,opt,name=location,proto3" json:"location,omitempty"` } +func (x *ContentResult) Reset() { + *x = ContentResult{} + if protoimpl.UnsafeEnabled { + mi := &file_pluginapi_v1_content_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContentResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContentResult) ProtoMessage() {} + +func (x *ContentResult) ProtoReflect() protoreflect.Message { + mi := &file_pluginapi_v1_content_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContentResult.ProtoReflect.Descriptor instead. +func (*ContentResult) Descriptor() ([]byte, []int) { + return file_pluginapi_v1_content_proto_rawDescGZIP(), []int{1} +} + +func (x *ContentResult) GetContent() *Content { + if x != nil { + return x.Content + } + return nil +} + +func (x *ContentResult) GetLocation() *Location { + if x != nil { + return x.Location + } + return nil +} + +type Content struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Value: + // + // *Content_Element + // *Content_Section + // *Content_Empty + Value isContent_Value `protobuf_oneof:"value"` +} + func (x *Content) Reset() { *x = Content{} if protoimpl.UnsafeEnabled { - mi := &file_pluginapi_v1_content_proto_msgTypes[1] + mi := &file_pluginapi_v1_content_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -149,7 +208,7 @@ func (x *Content) String() string { func (*Content) ProtoMessage() {} func (x *Content) ProtoReflect() protoreflect.Message { - mi := &file_pluginapi_v1_content_proto_msgTypes[1] + mi := &file_pluginapi_v1_content_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -162,21 +221,189 @@ func (x *Content) ProtoReflect() protoreflect.Message { // Deprecated: Use Content.ProtoReflect.Descriptor instead. func (*Content) Descriptor() ([]byte, []int) { - return file_pluginapi_v1_content_proto_rawDescGZIP(), []int{1} + return file_pluginapi_v1_content_proto_rawDescGZIP(), []int{2} +} + +func (m *Content) GetValue() isContent_Value { + if m != nil { + return m.Value + } + return nil +} + +func (x *Content) GetElement() *ContentElement { + if x, ok := x.GetValue().(*Content_Element); ok { + return x.Element + } + return nil +} + +func (x *Content) GetSection() *ContentSection { + if x, ok := x.GetValue().(*Content_Section); ok { + return x.Section + } + return nil +} + +func (x *Content) GetEmpty() *ContentEmpty { + if x, ok := x.GetValue().(*Content_Empty); ok { + return x.Empty + } + return nil +} + +type isContent_Value interface { + isContent_Value() +} + +type Content_Element struct { + Element *ContentElement `protobuf:"bytes,1,opt,name=element,proto3,oneof"` +} + +type Content_Section struct { + Section *ContentSection `protobuf:"bytes,2,opt,name=section,proto3,oneof"` +} + +type Content_Empty struct { + Empty *ContentEmpty `protobuf:"bytes,3,opt,name=empty,proto3,oneof"` +} + +func (*Content_Element) isContent_Value() {} + +func (*Content_Section) isContent_Value() {} + +func (*Content_Empty) isContent_Value() {} + +type ContentSection struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Children []*Content `protobuf:"bytes,1,rep,name=children,proto3" json:"children,omitempty"` +} + +func (x *ContentSection) Reset() { + *x = ContentSection{} + if protoimpl.UnsafeEnabled { + mi := &file_pluginapi_v1_content_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContentSection) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContentSection) ProtoMessage() {} + +func (x *ContentSection) ProtoReflect() protoreflect.Message { + mi := &file_pluginapi_v1_content_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContentSection.ProtoReflect.Descriptor instead. +func (*ContentSection) Descriptor() ([]byte, []int) { + return file_pluginapi_v1_content_proto_rawDescGZIP(), []int{3} } -func (x *Content) GetMarkdown() string { +func (x *ContentSection) GetChildren() []*Content { + if x != nil { + return x.Children + } + return nil +} + +type ContentElement struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Markdown string `protobuf:"bytes,1,opt,name=markdown,proto3" json:"markdown,omitempty"` +} + +func (x *ContentElement) Reset() { + *x = ContentElement{} + if protoimpl.UnsafeEnabled { + mi := &file_pluginapi_v1_content_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContentElement) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContentElement) ProtoMessage() {} + +func (x *ContentElement) ProtoReflect() protoreflect.Message { + mi := &file_pluginapi_v1_content_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContentElement.ProtoReflect.Descriptor instead. +func (*ContentElement) Descriptor() ([]byte, []int) { + return file_pluginapi_v1_content_proto_rawDescGZIP(), []int{4} +} + +func (x *ContentElement) GetMarkdown() string { if x != nil { return x.Markdown } return "" } -func (x *Content) GetLocation() *Location { - if x != nil { - return x.Location +type ContentEmpty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ContentEmpty) Reset() { + *x = ContentEmpty{} + if protoimpl.UnsafeEnabled { + mi := &file_pluginapi_v1_content_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil +} + +func (x *ContentEmpty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContentEmpty) ProtoMessage() {} + +func (x *ContentEmpty) ProtoReflect() protoreflect.Message { + mi := &file_pluginapi_v1_content_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContentEmpty.ProtoReflect.Descriptor instead. +func (*ContentEmpty) Descriptor() ([]byte, []int) { + return file_pluginapi_v1_content_proto_rawDescGZIP(), []int{5} } var File_pluginapi_v1_content_proto protoreflect.FileDescriptor @@ -190,31 +417,52 @@ var file_pluginapi_v1_content_proto_rawDesc = []byte{ 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x66, 0x66, 0x65, 0x63, 0x74, 0x52, 0x06, 0x65, 0x66, 0x66, 0x65, - 0x63, 0x74, 0x22, 0x59, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x32, 0x0a, 0x08, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x68, 0x0a, - 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x66, 0x66, 0x65, 0x63, 0x74, 0x12, - 0x1f, 0x0a, 0x1b, 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, 0x45, - 0x43, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, - 0x12, 0x1a, 0x0a, 0x16, 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, - 0x45, 0x43, 0x54, 0x5f, 0x42, 0x45, 0x46, 0x4f, 0x52, 0x45, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, - 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, - 0x41, 0x46, 0x54, 0x45, 0x52, 0x10, 0x02, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x73, 0x74, - 0x6f, 0x72, 0x6b, 0x2d, 0x69, 0x6f, 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x2f, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x31, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, - 0x50, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, - 0x56, 0x31, 0xca, 0x02, 0x0c, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, - 0x31, 0xe2, 0x02, 0x18, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, - 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x50, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x74, 0x22, 0x74, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xba, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x45, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x38, + 0x0a, 0x07, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, + 0x07, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x65, 0x6d, 0x70, 0x74, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x07, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x43, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, + 0x72, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x2c, 0x0a, 0x0e, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x0e, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x2a, 0x68, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x66, 0x66, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x1b, 0x4c, 0x4f, + 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x4c, + 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, 0x42, + 0x45, 0x46, 0x4f, 0x52, 0x45, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x4c, 0x4f, 0x43, 0x41, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, 0x41, 0x46, 0x54, 0x45, 0x52, + 0x10, 0x02, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x6f, 0x72, 0x6b, 0x2d, 0x69, + 0x6f, 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, + 0x0c, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, + 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x50, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x61, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -230,20 +478,29 @@ func file_pluginapi_v1_content_proto_rawDescGZIP() []byte { } var file_pluginapi_v1_content_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_pluginapi_v1_content_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_pluginapi_v1_content_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_pluginapi_v1_content_proto_goTypes = []interface{}{ - (LocationEffect)(0), // 0: pluginapi.v1.LocationEffect - (*Location)(nil), // 1: pluginapi.v1.Location - (*Content)(nil), // 2: pluginapi.v1.Content + (LocationEffect)(0), // 0: pluginapi.v1.LocationEffect + (*Location)(nil), // 1: pluginapi.v1.Location + (*ContentResult)(nil), // 2: pluginapi.v1.ContentResult + (*Content)(nil), // 3: pluginapi.v1.Content + (*ContentSection)(nil), // 4: pluginapi.v1.ContentSection + (*ContentElement)(nil), // 5: pluginapi.v1.ContentElement + (*ContentEmpty)(nil), // 6: pluginapi.v1.ContentEmpty } var file_pluginapi_v1_content_proto_depIdxs = []int32{ 0, // 0: pluginapi.v1.Location.effect:type_name -> pluginapi.v1.LocationEffect - 1, // 1: pluginapi.v1.Content.location:type_name -> pluginapi.v1.Location - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 3, // 1: pluginapi.v1.ContentResult.content:type_name -> pluginapi.v1.Content + 1, // 2: pluginapi.v1.ContentResult.location:type_name -> pluginapi.v1.Location + 5, // 3: pluginapi.v1.Content.element:type_name -> pluginapi.v1.ContentElement + 4, // 4: pluginapi.v1.Content.section:type_name -> pluginapi.v1.ContentSection + 6, // 5: pluginapi.v1.Content.empty:type_name -> pluginapi.v1.ContentEmpty + 3, // 6: pluginapi.v1.ContentSection.children:type_name -> pluginapi.v1.Content + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_pluginapi_v1_content_proto_init() } @@ -265,6 +522,18 @@ func file_pluginapi_v1_content_proto_init() { } } file_pluginapi_v1_content_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContentResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pluginapi_v1_content_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Content); i { case 0: return &v.state @@ -276,6 +545,47 @@ func file_pluginapi_v1_content_proto_init() { return nil } } + file_pluginapi_v1_content_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContentSection); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pluginapi_v1_content_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContentElement); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pluginapi_v1_content_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContentEmpty); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_pluginapi_v1_content_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*Content_Element)(nil), + (*Content_Section)(nil), + (*Content_Empty)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -283,7 +593,7 @@ func file_pluginapi_v1_content_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_pluginapi_v1_content_proto_rawDesc, NumEnums: 1, - NumMessages: 2, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/plugin/pluginapi/v1/content_decoder.go b/plugin/pluginapi/v1/content_decoder.go index b3acf666..627fee96 100644 --- a/plugin/pluginapi/v1/content_decoder.go +++ b/plugin/pluginapi/v1/content_decoder.go @@ -2,13 +2,58 @@ package pluginapiv1 import "github.com/blackstork-io/fabric/plugin" -func decodeContent(src *Content) *plugin.Content { +func decodeContentResult(src *ContentResult) *plugin.ContentResult { if src == nil { return nil } - return &plugin.Content{ + return &plugin.ContentResult{ + Content: decodeContent(src.Content), + Location: decodeLocation(src.Location), + } +} + +func decodeContent(src *Content) plugin.Content { + if src == nil { + return nil + } + switch val := src.Value.(type) { + case *Content_Element: + return decodeContentElement(val.Element) + case *Content_Section: + return decodeContentSection(val.Section) + case *Content_Empty: + return decodeContentEmpty(val.Empty) + default: + return nil + } +} + +func decodeContentElement(src *ContentElement) *plugin.ContentElement { + if src == nil { + return nil + } + return &plugin.ContentElement{ Markdown: src.GetMarkdown(), - Location: decodeLocation(src.GetLocation()), + } +} + +func decodeContentEmpty(src *ContentEmpty) *plugin.ContentEmpty { + if src == nil { + return nil + } + return &plugin.ContentEmpty{} +} + +func decodeContentSection(src *ContentSection) *plugin.ContentSection { + if src == nil { + return nil + } + children := make([]plugin.Content, len(src.GetChildren())) + for i, child := range src.GetChildren() { + children[i] = decodeContent(child) + } + return &plugin.ContentSection{ + Children: children, } } @@ -17,7 +62,7 @@ func decodeLocation(src *Location) *plugin.Location { return nil } return &plugin.Location{ - Index: int(src.GetIndex()), + Index: src.GetIndex(), Effect: decodeLocationEffect(src.GetEffect()), } } diff --git a/plugin/pluginapi/v1/content_encoder.go b/plugin/pluginapi/v1/content_encoder.go index fe76195a..70a67c1e 100644 --- a/plugin/pluginapi/v1/content_encoder.go +++ b/plugin/pluginapi/v1/content_encoder.go @@ -2,16 +2,73 @@ package pluginapiv1 import "github.com/blackstork-io/fabric/plugin" -func encodeContent(src *plugin.Content) *Content { +func encodeContentResult(src *plugin.ContentResult) *ContentResult { if src == nil { return nil } - return &Content{ - Markdown: src.Markdown, + return &ContentResult{ + Content: encodeContent(src.Content), Location: encodeLocation(src.Location), } } +func encodeContent(src plugin.Content) *Content { + if src == nil { + return nil + } + switch val := src.(type) { + case *plugin.ContentElement: + return &Content{ + Value: &Content_Element{ + Element: encodeContentElement(val), + }, + } + case *plugin.ContentSection: + return &Content{ + Value: &Content_Section{ + Section: encodeContentSection(val), + }, + } + case *plugin.ContentEmpty: + return &Content{ + Value: &Content_Empty{ + Empty: encodeContentEmpty(val), + }, + } + default: + return nil + } +} + +func encodeContentSection(src *plugin.ContentSection) *ContentSection { + if src == nil { + return nil + } + children := make([]*Content, len(src.Children)) + for i, child := range src.Children { + children[i] = encodeContent(child) + } + return &ContentSection{ + Children: children, + } +} + +func encodeContentElement(src *plugin.ContentElement) *ContentElement { + if src == nil { + return nil + } + return &ContentElement{ + Markdown: src.Markdown, + } +} + +func encodeContentEmpty(src *plugin.ContentEmpty) *ContentEmpty { + if src == nil { + return nil + } + return &ContentEmpty{} +} + func encodeLocation(src *plugin.Location) *Location { if src == nil { return nil diff --git a/plugin/pluginapi/v1/plugin.go b/plugin/pluginapi/v1/plugin.go index cf4a6c4a..e947bcb3 100644 --- a/plugin/pluginapi/v1/plugin.go +++ b/plugin/pluginapi/v1/plugin.go @@ -67,7 +67,7 @@ func (p *grpcPlugin) callOptions() []grpc.CallOption { } func (p *grpcPlugin) clientGenerateFunc(name string, client PluginServiceClient) plugin.ProvideContentFunc { - return func(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.Content, hcl.Diagnostics) { + return func(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { p.logger.Debug("Calling content provider", "name", name) defer func(start time.Time) { p.logger.Debug("Called content provider", "name", name, "took", time.Since(start)) @@ -100,6 +100,7 @@ func (p *grpcPlugin) clientGenerateFunc(name string, client PluginServiceClient) Config: cfgEncoded, Args: argsEncoded, DataContext: encodeMapData(params.DataContext), + ContentId: params.ContentID, }, p.callOptions()...) if err != nil { return nil, hcl.Diagnostics{{ @@ -108,9 +109,9 @@ func (p *grpcPlugin) clientGenerateFunc(name string, client PluginServiceClient) Detail: err.Error(), }} } - content := decodeContent(res.Content) + result := decodeContentResult(res.Result) diags := decodeDiagnosticList(res.Diagnostics) - return content, diags + return result, diags } } diff --git a/plugin/pluginapi/v1/plugin.pb.go b/plugin/pluginapi/v1/plugin.pb.go index b368ddea..fe31a820 100644 --- a/plugin/pluginapi/v1/plugin.pb.go +++ b/plugin/pluginapi/v1/plugin.pb.go @@ -232,6 +232,7 @@ type ProvideContentRequest struct { Args *CtyValue `protobuf:"bytes,2,opt,name=args,proto3" json:"args,omitempty"` Config *CtyValue `protobuf:"bytes,3,opt,name=config,proto3" json:"config,omitempty"` DataContext *MapData `protobuf:"bytes,4,opt,name=data_context,json=dataContext,proto3" json:"data_context,omitempty"` + ContentId uint32 `protobuf:"varint,5,opt,name=content_id,json=contentId,proto3" json:"content_id,omitempty"` } func (x *ProvideContentRequest) Reset() { @@ -294,13 +295,20 @@ func (x *ProvideContentRequest) GetDataContext() *MapData { return nil } +func (x *ProvideContentRequest) GetContentId() uint32 { + if x != nil { + return x.ContentId + } + return 0 +} + type ProvideContentResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Content *Content `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` - Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + Result *ContentResult `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` + Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` } func (x *ProvideContentResponse) Reset() { @@ -335,9 +343,9 @@ func (*ProvideContentResponse) Descriptor() ([]byte, []int) { return file_pluginapi_v1_plugin_proto_rawDescGZIP(), []int{5} } -func (x *ProvideContentResponse) GetContent() *Content { +func (x *ProvideContentResponse) GetResult() *ContentResult { if x != nil { - return x.Content + return x.Result } return nil } @@ -385,7 +393,7 @@ var file_pluginapi_v1_plugin_proto_rawDesc = []byte{ 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, - 0x22, 0xc9, 0x01, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, + 0x22, 0xe8, 0x01, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, @@ -397,45 +405,47 @@ var file_pluginapi_v1_plugin_proto_rawDesc = []byte{ 0x69, 0x67, 0x12, 0x38, 0x0a, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x70, 0x44, 0x61, 0x74, 0x61, 0x52, - 0x0b, 0x64, 0x61, 0x74, 0x61, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x85, 0x01, 0x0a, - 0x16, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, - 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, - 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x61, - 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, - 0x74, 0x69, 0x63, 0x73, 0x32, 0x97, 0x02, 0x0a, 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, - 0x65, 0x6d, 0x61, 0x12, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0c, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, - 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x0b, 0x64, 0x61, 0x74, 0x61, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x89, 0x01, 0x0a, 0x16, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x3a, 0x0a, 0x0b, 0x64, + 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, + 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x32, 0x97, 0x02, 0x0a, 0x0d, 0x50, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x47, 0x65, 0x74, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0c, 0x52, 0x65, 0x74, + 0x72, 0x69, 0x65, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, - 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x5d, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x12, 0x23, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xb1, - 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, - 0x6c, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x6f, 0x72, 0x6b, 0x2d, 0x69, 0x6f, 0x2f, 0x66, 0x61, 0x62, - 0x72, 0x69, 0x63, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, - 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x50, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x50, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0xea, 0x02, 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x3a, 0x3a, - 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74, 0x72, + 0x69, 0x65, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x42, 0xb1, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x6f, 0x72, 0x6b, 0x2d, 0x69, 0x6f, 0x2f, + 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x50, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x50, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x50, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, + 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -463,7 +473,7 @@ var file_pluginapi_v1_plugin_proto_goTypes = []interface{}{ (*Data)(nil), // 8: pluginapi.v1.Data (*Diagnostic)(nil), // 9: pluginapi.v1.Diagnostic (*MapData)(nil), // 10: pluginapi.v1.MapData - (*Content)(nil), // 11: pluginapi.v1.Content + (*ContentResult)(nil), // 11: pluginapi.v1.ContentResult } var file_pluginapi_v1_plugin_proto_depIdxs = []int32{ 6, // 0: pluginapi.v1.GetSchemaResponse.schema:type_name -> pluginapi.v1.Schema @@ -474,7 +484,7 @@ var file_pluginapi_v1_plugin_proto_depIdxs = []int32{ 7, // 5: pluginapi.v1.ProvideContentRequest.args:type_name -> pluginapi.v1.CtyValue 7, // 6: pluginapi.v1.ProvideContentRequest.config:type_name -> pluginapi.v1.CtyValue 10, // 7: pluginapi.v1.ProvideContentRequest.data_context:type_name -> pluginapi.v1.MapData - 11, // 8: pluginapi.v1.ProvideContentResponse.content:type_name -> pluginapi.v1.Content + 11, // 8: pluginapi.v1.ProvideContentResponse.result:type_name -> pluginapi.v1.ContentResult 9, // 9: pluginapi.v1.ProvideContentResponse.diagnostics:type_name -> pluginapi.v1.Diagnostic 0, // 10: pluginapi.v1.PluginService.GetSchema:input_type -> pluginapi.v1.GetSchemaRequest 2, // 11: pluginapi.v1.PluginService.RetrieveData:input_type -> pluginapi.v1.RetrieveDataRequest diff --git a/plugin/pluginapi/v1/server.go b/plugin/pluginapi/v1/server.go index aeef2048..28b97ac4 100644 --- a/plugin/pluginapi/v1/server.go +++ b/plugin/pluginapi/v1/server.go @@ -68,13 +68,14 @@ func (srv *grpcServer) ProvideContent(ctx context.Context, req *ProvideContentRe return nil, status.Errorf(codes.InvalidArgument, "failed to decode args: %v", err) } datactx := decodeMapData(req.GetDataContext()) - content, diags := srv.schema.ProvideContent(ctx, provider, &plugin.ProvideContentParams{ + result, diags := srv.schema.ProvideContent(ctx, provider, &plugin.ProvideContentParams{ Config: cfg, Args: args, DataContext: datactx, + ContentID: req.GetContentId(), }) return &ProvideContentResponse{ - Content: encodeContent(content), + Result: encodeContentResult(result), Diagnostics: encodeDiagnosticList(diags), }, nil } diff --git a/plugin/runner/loader.go b/plugin/runner/loader.go index 9461205b..75fc0c4e 100644 --- a/plugin/runner/loader.go +++ b/plugin/runner/loader.go @@ -1,6 +1,7 @@ package runner import ( + "context" "fmt" "log/slog" "os" @@ -102,8 +103,15 @@ func (l *loader) registerContentProvider(name string, schema *plugin.Schema, cp }} } l.contentMap[name] = loadedContentProvider{ - plugin: schema, - ContentProvider: cp, + plugin: schema, + ContentProvider: &plugin.ContentProvider{ + Config: cp.Config, + Args: cp.Args, + ContentFunc: func(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.ContentResult, hcl.Diagnostics) { + return schema.ProvideContent(ctx, name, params) + }, + InvocationOrder: cp.InvocationOrder, + }, } return nil } diff --git a/plugin/schema.go b/plugin/schema.go index 5c0d7fa4..1a07b2b4 100644 --- a/plugin/schema.go +++ b/plugin/schema.go @@ -64,7 +64,7 @@ func (p *Schema) RetrieveData(ctx context.Context, name string, params *Retrieve return source.Execute(ctx, params) } -func (p *Schema) ProvideContent(ctx context.Context, name string, params *ProvideContentParams) (*Content, hcl.Diagnostics) { +func (p *Schema) ProvideContent(ctx context.Context, name string, params *ProvideContentParams) (*ContentResult, hcl.Diagnostics) { if p == nil { return nil, hcl.Diagnostics{{ Severity: hcl.DiagError, @@ -87,14 +87,14 @@ func (p *Schema) ProvideContent(ctx context.Context, name string, params *Provid Detail: "Content provider '" + name + "' not found in schema", }} } - content, diags := provider.Execute(ctx, params) + result, diags := provider.Execute(ctx, params) if diags.HasErrors() { return nil, diags } - content.meta = &ContentMeta{ + result.Content.setMeta(&ContentMeta{ Provider: name, Plugin: p.Name, Version: p.Version, - } - return content, diags + }) + return result, diags } diff --git a/proto/pluginapi/v1/content.proto b/proto/pluginapi/v1/content.proto index b49e253d..0038bfe0 100644 --- a/proto/pluginapi/v1/content.proto +++ b/proto/pluginapi/v1/content.proto @@ -14,7 +14,25 @@ message Location { LocationEffect effect = 2; } +message ContentResult { + Content content = 1; + Location location = 2; +} + message Content { - string markdown = 1; - Location location = 2; + oneof value { + ContentElement element = 1; + ContentSection section = 2; + ContentEmpty empty = 3; + }; +} + +message ContentSection { + repeated Content children = 1; } + +message ContentElement { + string markdown = 1; +} + +message ContentEmpty {} \ No newline at end of file diff --git a/proto/pluginapi/v1/plugin.proto b/proto/pluginapi/v1/plugin.proto index 1fa4c704..19bcba12 100644 --- a/proto/pluginapi/v1/plugin.proto +++ b/proto/pluginapi/v1/plugin.proto @@ -36,10 +36,11 @@ message ProvideContentRequest { CtyValue args = 2; CtyValue config = 3; MapData data_context = 4; + uint32 content_id = 5; } message ProvideContentResponse { - Content content = 1; + ContentResult result = 1; repeated Diagnostic diagnostics = 2; } diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go deleted file mode 100644 index c1ae65b0..00000000 --- a/test/e2e/e2e_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package e2e_test - -import ( - "log" - "os" - "path/filepath" - "runtime" -) - -var pluginsDir = func() (pluginsDir string) { - _, filename, _, _ := runtime.Caller(0) - prevPath, err := filepath.Abs(filename) - if err != nil { - log.Fatalf("Failed to get working dir: %s", err) - } - path := filepath.Clean(filepath.Join(prevPath, "..")) - for prevPath != path { - _, err := os.Stat(filepath.Join(path, "go.mod")) - if err == nil { - pluginsDir = filepath.Join(path, "dist", "plugins") - return - } - prevPath = path - path = filepath.Clean(filepath.Join(path, "..")) - } - log.Fatal("Failed to find go.mod in parent folders") - return -}() diff --git a/test/e2e/render_test.go b/test/e2e/render_test.go index b14db273..a9284760 100644 --- a/test/e2e/render_test.go +++ b/test/e2e/render_test.go @@ -4,6 +4,7 @@ import ( "fmt" "log/slog" "os" + "strings" "testing" "testing/fstest" @@ -33,7 +34,7 @@ func renderTest(t *testing.T, testName string, files []string, docName string, e eval.Cleanup(nil) }() - var res []string + var res string diags := eval.ParseFabricFiles(sourceDir) ctx := fabctx.New(fabctx.NoSignals) if !diags.HasErrors() { @@ -50,7 +51,7 @@ func renderTest(t *testing.T, testName string, files []string, docName string, e } else { assert.EqualValues( t, - expectedResult, + strings.Join(expectedResult, "\n\n"), res, ) } @@ -72,14 +73,14 @@ func TestE2ERender(t *testing.T) { document "hello" { title = "Welcome" content text { - text = "Hello from fabric" + value = "Hello from fabric" } } document "goodbye" { title = "Goodbye" content text { - text = "Goodbye from fabric" + value = "Goodbye from fabric" } } `, @@ -103,7 +104,7 @@ func TestE2ERender(t *testing.T) { } content text "external_block" { - text = "Hello from ref" + value = "Hello from ref" } `, }, @@ -127,7 +128,7 @@ func TestE2ERender(t *testing.T) { `, ` content text "external_block" { - text = "Hello from ref" + value = "Hello from ref" } `, }, @@ -148,14 +149,13 @@ func TestE2ERender(t *testing.T) { } } - content text "actual_block" { - text = "Hello from ref chain" + content blockquote "actual_block" { + value = "Hello from ref chain" } `, ` content ref "add_format_as" { - base = content.text.actual_block - format_as = "blockquote" + base = content.blockquote.actual_block } `, }, @@ -189,7 +189,7 @@ func TestE2ERender(t *testing.T) { } content text "actual_block" { - text = "Near refloop" + value = "Near refloop" } `, }, @@ -240,7 +240,7 @@ func TestE2ERender(t *testing.T) { ` section "sect4" { content text { - text = "final section" + value = "final section" } } document "test-doc" { @@ -253,7 +253,7 @@ func TestE2ERender(t *testing.T) { section "sect1" { title = "sect1" content text { - text = "s1" + value = "s1" } content ref { base = content.text.some_text @@ -264,18 +264,18 @@ func TestE2ERender(t *testing.T) { base = content.text.some_text } content text { - text = "s2" + value = "s2" } section { title = "sect3" content text { - text = "s3" + value = "s3" } content ref { base = content.text.some_text } content text { - text = "s3 extra" + value = "s3 extra" } section ref { base = section.sect4 @@ -285,20 +285,20 @@ func TestE2ERender(t *testing.T) { } content text "some_text" { - text = "some_text" + value = "some_text" } `, }, "test-doc", []string{ - "# sect1", + "## sect1", "s1", "some_text", - "## sect2", + "### sect2", "some_text", "s2", - "### sect3", + "#### sect3", "s3", "some_text", "s3 extra", @@ -312,7 +312,7 @@ func TestE2ERender(t *testing.T) { ` document "test-doc" { content text { - text = "${2+2}" + value = "${2+2}" } } `, @@ -373,7 +373,7 @@ func TestE2ERender(t *testing.T) { []string{ ` content text "name" { - text = "txt" + value = "txt" } document "test-doc" { content ref { @@ -404,7 +404,7 @@ func TestE2ERender(t *testing.T) { attr = "val" } content text { - text = "From data block: {{.data.inline.name.attr}}" + value = "From data block: {{.data.inline.name.attr}}" } } `, @@ -425,7 +425,7 @@ func TestE2ERender(t *testing.T) { } content text { query = ".data.inline.foo.items | length" - text = "There are {{ .query_result }} items" + value = "There are {{ .query_result }} items" } } `, @@ -444,7 +444,7 @@ func TestE2ERender(t *testing.T) { } content text { query = ".document.meta.author" - text = "author = {{ .query_result }}" + value = "author = {{ .query_result }}" } } `, @@ -470,7 +470,7 @@ func TestE2ERender(t *testing.T) { author = "baz" } query = "(.document.meta.author + .section.meta.author + .content.meta.author)" // - text = "author = {{ .query_result }}" + value = "author = {{ .query_result }}" } } } @@ -486,7 +486,7 @@ func TestE2ERender(t *testing.T) { ` content text get_section_author { query = ".section.meta.author // \"unknown\"" - text = "author = {{ .query_result }}" + value = "author = {{ .query_result }}" } document "test" { content ref { @@ -537,15 +537,15 @@ func TestE2ERender(t *testing.T) { ` document "test" { content text { - text = "first result" + value = "first result" } content text { - query = ".document.content[0].markdown" - text = "content[0] = {{ .query_result }}" + query = ".document.content.children[0].markdown" + value = "content[0] = {{ .query_result }}" } content text { - query = ".document.content[1].markdown" - text = "content[1] = {{ .query_result }}" + query = ".document.content.children[1].markdown" + value = "content[1] = {{ .query_result }}" } } `, From 4c41745f474e2e8d03480468a321290e383b0e88 Mon Sep 17 00:00:00 2001 From: dobarx Date: Mon, 25 Mar 2024 21:07:26 +0200 Subject: [PATCH 7/7] Decrease title size low values to zero --- internal/builtin/content_title.go | 8 ++++---- internal/builtin/content_title_test.go | 4 ++-- internal/builtin/content_toc.go | 12 ++++++------ internal/builtin/content_toc_test.go | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/builtin/content_title.go b/internal/builtin/content_title.go index 36093023..583dc06f 100644 --- a/internal/builtin/content_title.go +++ b/internal/builtin/content_title.go @@ -13,9 +13,9 @@ import ( ) const ( - minAbsoluteTitleSize = int64(1) - maxAbsoluteTitleSize = int64(6) - defaultAbsoluteTitleSize = int64(1) + minAbsoluteTitleSize = int64(0) + maxAbsoluteTitleSize = int64(5) + defaultAbsoluteTitleSize = int64(0) ) func makeTitleContentProvider() *plugin.ContentProvider { @@ -83,7 +83,7 @@ func genTitleContent(ctx context.Context, params *plugin.ProvideContentParams) ( } // remove all newlines text = strings.ReplaceAll(text, "\n", " ") - text = strings.Repeat("#", int(titleSize)) + " " + text + text = strings.Repeat("#", int(titleSize)+1) + " " + text return &plugin.ContentResult{ Content: &plugin.ContentElement{ Markdown: text, diff --git a/internal/builtin/content_title_test.go b/internal/builtin/content_title_test.go index daad3437..2bf8b120 100644 --- a/internal/builtin/content_title_test.go +++ b/internal/builtin/content_title_test.go @@ -87,7 +87,7 @@ func (s *TitleTestSuite) TestWithSize() { result, diags := s.schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ "value": cty.StringVal("Hello {{.name}}!"), - "absolute_size": cty.NumberIntVal(3), + "absolute_size": cty.NumberIntVal(2), "relative_size": cty.NullVal(cty.Number), }), DataContext: plugin.MapData{ @@ -113,7 +113,7 @@ func (s *TitleTestSuite) TestWithSizeTooBig() { s.Equal(hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Failed to parse arguments", - Detail: "absolute_size must be between 1 and 6", + Detail: "absolute_size must be between 0 and 5", }}, diags) s.Nil(result) } diff --git a/internal/builtin/content_toc.go b/internal/builtin/content_toc.go index 02c3b409..8ae234a5 100644 --- a/internal/builtin/content_toc.go +++ b/internal/builtin/content_toc.go @@ -15,10 +15,10 @@ import ( ) const ( - minTOCLevel = 1 - maxTOCLevel = 6 - defaultTOCStartLevel = 1 - defaultTOCEndLevel = 3 + minTOCLevel = 0 + maxTOCLevel = 5 + defaultTOCStartLevel = 0 + defaultTOCEndLevel = 2 defaultTOCOrdered = false ) @@ -211,11 +211,11 @@ func parseContentTitles(data plugin.MapData, startLvl, endLvl int, scope string) for _, item := range list { line := strings.TrimSpace(item) if strings.HasPrefix(line, "#") { - level := strings.Count(line, "#") + level := strings.Count(line, "#") - 1 if level < startLvl || level > endLvl { continue } - title := strings.TrimSpace(line[level:]) + title := strings.TrimSpace(line[level+1:]) result = result.add(tocNode{level: level, title: title}) } } diff --git a/internal/builtin/content_toc_test.go b/internal/builtin/content_toc_test.go index f1891f51..11796ddc 100644 --- a/internal/builtin/content_toc_test.go +++ b/internal/builtin/content_toc_test.go @@ -99,8 +99,8 @@ func (s *TOCContentTestSuite) TestAdvanced() { } res, diags := schema.ContentFunc(ctx, &plugin.ProvideContentParams{ Args: cty.ObjectVal(map[string]cty.Value{ - "start_level": cty.NumberIntVal(2), - "end_level": cty.NumberIntVal(3), + "start_level": cty.NumberIntVal(1), + "end_level": cty.NumberIntVal(2), "ordered": cty.True, "scope": cty.StringVal("document"), }),