refactor: simplify service interface to use tags/rels for all node properties
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
29
cmd/add.go
29
cmd/add.go
@@ -1,6 +1,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"axolotl/models"
|
||||||
"axolotl/output"
|
"axolotl/output"
|
||||||
"axolotl/service"
|
"axolotl/service"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -9,7 +10,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cDue, cContent, cStatus, cPrio, cType, cNamespace, cAssignee string
|
var cDue, cContent, cType, cStatus, cPrio, cNamespace, cAssignee string
|
||||||
var cTags, cRels []string
|
var cTags, cRels []string
|
||||||
|
|
||||||
var addCmd = &cobra.Command{
|
var addCmd = &cobra.Command{
|
||||||
@@ -25,12 +26,24 @@ var addCmd = &cobra.Command{
|
|||||||
Title: args[0],
|
Title: args[0],
|
||||||
Content: cContent,
|
Content: cContent,
|
||||||
DueDate: cDue,
|
DueDate: cDue,
|
||||||
Type: cType,
|
Tags: append([]string{}, cTags...),
|
||||||
Status: cStatus,
|
}
|
||||||
Priority: cPrio,
|
|
||||||
Namespace: cNamespace,
|
// Shorthand flags expand to tags or rels.
|
||||||
Assignee: cAssignee,
|
if cType != "" {
|
||||||
Tags: cTags,
|
input.Tags = append(input.Tags, "_type::"+cType)
|
||||||
|
}
|
||||||
|
if cStatus != "" {
|
||||||
|
input.Tags = append(input.Tags, "_status::"+cStatus)
|
||||||
|
}
|
||||||
|
if cPrio != "" {
|
||||||
|
input.Tags = append(input.Tags, "_prio::"+cPrio)
|
||||||
|
}
|
||||||
|
if cNamespace != "" {
|
||||||
|
input.Rels = append(input.Rels, service.RelInput{Type: models.RelInNamespace, Target: cNamespace})
|
||||||
|
}
|
||||||
|
if cAssignee != "" {
|
||||||
|
input.Rels = append(input.Rels, service.RelInput{Type: models.RelAssignee, Target: cAssignee})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range cRels {
|
for _, r := range cRels {
|
||||||
@@ -55,7 +68,7 @@ var addCmd = &cobra.Command{
|
|||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(addCmd)
|
rootCmd.AddCommand(addCmd)
|
||||||
f := addCmd.Flags()
|
f := addCmd.Flags()
|
||||||
f.StringVar(&cType, "type", "issue", "node type (issue, note, …)")
|
f.StringVar(&cType, "type", "", "node type (issue, note, …)")
|
||||||
f.StringVar(&cStatus, "status", "", "initial status (open, done)")
|
f.StringVar(&cStatus, "status", "", "initial status (open, done)")
|
||||||
f.StringVar(&cPrio, "prio", "", "priority (high, medium, low)")
|
f.StringVar(&cPrio, "prio", "", "priority (high, medium, low)")
|
||||||
f.StringVar(&cNamespace, "namespace", "", "namespace name or ID")
|
f.StringVar(&cNamespace, "namespace", "", "namespace name or ID")
|
||||||
|
|||||||
29
cmd/list.go
29
cmd/list.go
@@ -1,6 +1,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"axolotl/models"
|
||||||
"axolotl/output"
|
"axolotl/output"
|
||||||
"axolotl/service"
|
"axolotl/service"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -22,13 +23,27 @@ var listCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
filter := service.ListFilter{
|
filter := service.ListFilter{
|
||||||
Tags: lTags,
|
Tags: append([]string{}, lTags...),
|
||||||
Status: lStatus,
|
}
|
||||||
Priority: lPrio,
|
|
||||||
Type: lType,
|
// Shorthand flags expand to tag prefixes or rel filters.
|
||||||
Namespace: lNamespace,
|
if lStatus != "" {
|
||||||
Assignee: lAssignee,
|
filter.Tags = append(filter.Tags, "_status::"+lStatus)
|
||||||
Mention: lMention,
|
}
|
||||||
|
if lPrio != "" {
|
||||||
|
filter.Tags = append(filter.Tags, "_prio::"+lPrio)
|
||||||
|
}
|
||||||
|
if lType != "" {
|
||||||
|
filter.Tags = append(filter.Tags, "_type::"+lType)
|
||||||
|
}
|
||||||
|
if lNamespace != "" {
|
||||||
|
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelInNamespace, Target: lNamespace})
|
||||||
|
}
|
||||||
|
if lAssignee != "" {
|
||||||
|
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelAssignee, Target: lAssignee})
|
||||||
|
}
|
||||||
|
if lMention != "" {
|
||||||
|
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelMentions, Target: lMention})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range lRels {
|
for _, r := range lRels {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"axolotl/models"
|
||||||
"axolotl/output"
|
"axolotl/output"
|
||||||
"axolotl/service"
|
"axolotl/service"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -12,7 +13,8 @@ import (
|
|||||||
var (
|
var (
|
||||||
uTitle, uContent, uDue string
|
uTitle, uContent, uDue string
|
||||||
uClearDue bool
|
uClearDue bool
|
||||||
uStatus, uPrio, uType, uNamespace, uAssignee string
|
uStatus, uPrio, uType string
|
||||||
|
uNamespace, uAssignee string
|
||||||
uAddTags, uRmTags, uAddRels, uRmRels []string
|
uAddTags, uRmTags, uAddRels, uRmRels []string
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,7 +28,7 @@ var updateCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
input := service.UpdateInput{
|
input := service.UpdateInput{
|
||||||
AddTags: uAddTags,
|
AddTags: append([]string{}, uAddTags...),
|
||||||
RemoveTags: uRmTags,
|
RemoveTags: uRmTags,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,20 +45,22 @@ var updateCmd = &cobra.Command{
|
|||||||
empty := ""
|
empty := ""
|
||||||
input.DueDate = &empty
|
input.DueDate = &empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shorthand flags expand to tags or rels.
|
||||||
|
if cmd.Flags().Changed("type") {
|
||||||
|
input.AddTags = append(input.AddTags, "_type::"+uType)
|
||||||
|
}
|
||||||
if cmd.Flags().Changed("status") {
|
if cmd.Flags().Changed("status") {
|
||||||
input.Status = &uStatus
|
input.AddTags = append(input.AddTags, "_status::"+uStatus)
|
||||||
}
|
}
|
||||||
if cmd.Flags().Changed("prio") {
|
if cmd.Flags().Changed("prio") {
|
||||||
input.Priority = &uPrio
|
input.AddTags = append(input.AddTags, "_prio::"+uPrio)
|
||||||
}
|
|
||||||
if cmd.Flags().Changed("type") {
|
|
||||||
input.Type = &uType
|
|
||||||
}
|
}
|
||||||
if cmd.Flags().Changed("namespace") {
|
if cmd.Flags().Changed("namespace") {
|
||||||
input.Namespace = &uNamespace
|
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelInNamespace, Target: uNamespace})
|
||||||
}
|
}
|
||||||
if cmd.Flags().Changed("assignee") {
|
if cmd.Flags().Changed("assignee") {
|
||||||
input.Assignee = &uAssignee
|
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelAssignee, Target: uAssignee})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range uAddRels {
|
for _, r := range uAddRels {
|
||||||
|
|||||||
@@ -24,56 +24,37 @@ type NodeService interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddInput describes a new node to create.
|
// AddInput describes a new node to create.
|
||||||
|
// Tags may include special property tags (_type::, _status::, _prio::); the
|
||||||
|
// service applies defaults (type=issue, status=open for issues) and validates.
|
||||||
|
// Rels may include assignee, in_namespace, blocks, subtask, related, etc.;
|
||||||
|
// user and namespace targets are auto-created as needed.
|
||||||
type AddInput struct {
|
type AddInput struct {
|
||||||
Title string
|
Title string
|
||||||
Content string
|
Content string
|
||||||
DueDate string
|
DueDate string
|
||||||
Type string // default: "issue"
|
|
||||||
Status string // default: "open" when Type is "issue"
|
|
||||||
Priority string
|
|
||||||
// Namespace is a namespace name or node ID. Defaults to the current user.
|
|
||||||
Namespace string
|
|
||||||
// Assignee is a username or node ID.
|
|
||||||
Assignee string
|
|
||||||
// Tags are arbitrary user-defined labels (not system properties).
|
|
||||||
Tags []string
|
Tags []string
|
||||||
// Rels are additional typed edges (e.g. blocks, subtask, related).
|
|
||||||
Rels []RelInput
|
Rels []RelInput
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateInput describes changes to apply to an existing node.
|
// UpdateInput describes changes to apply to an existing node.
|
||||||
// Nil pointer fields mean "no change".
|
// Nil pointer fields mean "no change".
|
||||||
|
// Setting _status::done in AddTags is rejected when the node has open blockers.
|
||||||
|
// Adding assignee or in_namespace rels replaces the previous single target.
|
||||||
type UpdateInput struct {
|
type UpdateInput struct {
|
||||||
Title *string
|
Title *string
|
||||||
Content *string
|
Content *string
|
||||||
DueDate *string // nil = no change; pointer to "" = clear due date
|
DueDate *string // nil = no change; pointer to "" = clear due date
|
||||||
// Status "done" is rejected when the node has open blockers.
|
|
||||||
Status *string
|
|
||||||
Priority *string
|
|
||||||
Type *string
|
|
||||||
// Namespace replaces the current namespace.
|
|
||||||
Namespace *string
|
|
||||||
// Assignee replaces the current assignee.
|
|
||||||
Assignee *string
|
|
||||||
AddTags []string
|
AddTags []string
|
||||||
RemoveTags []string
|
RemoveTags []string
|
||||||
AddRels []RelInput
|
AddRels []RelInput
|
||||||
RemoveRels []RelInput
|
RemoveRels []RelInput
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListFilter specifies which nodes to return. Empty fields are ignored.
|
// ListFilter specifies which nodes to return. Empty slices are ignored.
|
||||||
|
// Tags are matched as exact tag values or prefixes (e.g. "_status::open").
|
||||||
|
// Rels are resolved to node IDs; a missing target returns no results.
|
||||||
type ListFilter struct {
|
type ListFilter struct {
|
||||||
Tags []string
|
Tags []string
|
||||||
Status string
|
|
||||||
Priority string
|
|
||||||
Type string
|
|
||||||
// Namespace filters by namespace name or node ID.
|
|
||||||
Namespace string
|
|
||||||
// Assignee filters by username or node ID.
|
|
||||||
Assignee string
|
|
||||||
// Mention filters to nodes that mention the given username or node ID.
|
|
||||||
Mention string
|
|
||||||
// Rels are additional relation filters (e.g. blocks:someID).
|
|
||||||
Rels []RelInput
|
Rels []RelInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"maps"
|
"maps"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,6 +26,43 @@ func mentions(t string) []string {
|
|||||||
return slices.Collect(maps.Keys(seen))
|
return slices.Collect(maps.Keys(seen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Validation ---
|
||||||
|
|
||||||
|
var (
|
||||||
|
validTypes = map[string]bool{"issue": true, "note": true, "user": true, "namespace": true}
|
||||||
|
validStatuses = map[string]bool{"open": true, "done": true}
|
||||||
|
validPrios = map[string]bool{"high": true, "medium": true, "low": true}
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateTags(tags []string) error {
|
||||||
|
for _, t := range tags {
|
||||||
|
if v, ok := strings.CutPrefix(t, "_type::"); ok {
|
||||||
|
if !validTypes[v] {
|
||||||
|
return fmt.Errorf("invalid type %q: must be one of issue, note, user, namespace", v)
|
||||||
|
}
|
||||||
|
} else if v, ok := strings.CutPrefix(t, "_status::"); ok {
|
||||||
|
if !validStatuses[v] {
|
||||||
|
return fmt.Errorf("invalid status %q: must be one of open, done", v)
|
||||||
|
}
|
||||||
|
} else if v, ok := strings.CutPrefix(t, "_prio::"); ok {
|
||||||
|
if !validPrios[v] {
|
||||||
|
return fmt.Errorf("invalid priority %q: must be one of high, medium, low", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// tagValue returns the value of the first tag with the given prefix, or "".
|
||||||
|
func tagValue(tags []string, prefix string) string {
|
||||||
|
for _, t := range tags {
|
||||||
|
if v, ok := strings.CutPrefix(t, prefix); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// --- Query ---
|
// --- Query ---
|
||||||
|
|
||||||
func (s *nodeServiceImpl) GetByID(id string) (*models.Node, error) {
|
func (s *nodeServiceImpl) GetByID(id string) (*models.Node, error) {
|
||||||
@@ -32,86 +70,51 @@ func (s *nodeServiceImpl) GetByID(id string) (*models.Node, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *nodeServiceImpl) List(filter ListFilter) ([]*models.Node, error) {
|
func (s *nodeServiceImpl) List(filter ListFilter) ([]*models.Node, error) {
|
||||||
// Build tag prefixes from both semantic fields and raw tags.
|
|
||||||
tagPrefixes := append([]string{}, filter.Tags...)
|
|
||||||
if filter.Status != "" {
|
|
||||||
tagPrefixes = append(tagPrefixes, "_status::"+filter.Status)
|
|
||||||
}
|
|
||||||
if filter.Priority != "" {
|
|
||||||
tagPrefixes = append(tagPrefixes, "_prio::"+filter.Priority)
|
|
||||||
}
|
|
||||||
if filter.Type != "" {
|
|
||||||
tagPrefixes = append(tagPrefixes, "_type::"+filter.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build rel filters, resolving names to node IDs (read-only, no auto-creation).
|
|
||||||
type relEntry struct {
|
|
||||||
relType models.RelType
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
namedRels := []relEntry{
|
|
||||||
{models.RelAssignee, filter.Assignee},
|
|
||||||
{models.RelInNamespace, filter.Namespace},
|
|
||||||
{models.RelMentions, filter.Mention},
|
|
||||||
}
|
|
||||||
var relFilters []*models.Rel
|
var relFilters []*models.Rel
|
||||||
for _, e := range namedRels {
|
|
||||||
if e.name == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
id, ok := s.lookupRelTarget(e.relType, e.name)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil // named target doesn't exist; no nodes can match
|
|
||||||
}
|
|
||||||
relFilters = append(relFilters, &models.Rel{Type: e.relType, Target: id})
|
|
||||||
}
|
|
||||||
for _, ri := range filter.Rels {
|
for _, ri := range filter.Rels {
|
||||||
id, ok := s.lookupRelTarget(ri.Type, ri.Target)
|
id, ok := s.lookupRelTarget(ri.Type, ri.Target)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil
|
return nil, nil // named target doesn't exist; no nodes can match
|
||||||
}
|
}
|
||||||
relFilters = append(relFilters, &models.Rel{Type: ri.Type, Target: id})
|
relFilters = append(relFilters, &models.Rel{Type: ri.Type, Target: id})
|
||||||
}
|
}
|
||||||
|
return s.store.FindNodes(filter.Tags, relFilters)
|
||||||
return s.store.FindNodes(tagPrefixes, relFilters)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Lifecycle ---
|
// --- Lifecycle ---
|
||||||
|
|
||||||
func (s *nodeServiceImpl) Add(input AddInput) (*models.Node, error) {
|
func (s *nodeServiceImpl) Add(input AddInput) (*models.Node, error) {
|
||||||
|
// Copy tags so we can extend without mutating the input.
|
||||||
|
tags := make([]string, len(input.Tags))
|
||||||
|
copy(tags, input.Tags)
|
||||||
|
|
||||||
// Apply defaults.
|
// Apply defaults.
|
||||||
nodeType := input.Type
|
nodeType := tagValue(tags, "_type::")
|
||||||
if nodeType == "" {
|
if nodeType == "" {
|
||||||
nodeType = "issue"
|
nodeType = "issue"
|
||||||
|
tags = append(tags, "_type::issue")
|
||||||
}
|
}
|
||||||
status := input.Status
|
if nodeType == "issue" && tagValue(tags, "_status::") == "" {
|
||||||
if status == "" && nodeType == "issue" {
|
tags = append(tags, "_status::open")
|
||||||
status = "open"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build initial tag set from semantic fields.
|
// Validate special tags.
|
||||||
tags := []string{"_type::" + nodeType}
|
if err := validateTags(tags); err != nil {
|
||||||
if status != "" {
|
return nil, err
|
||||||
tags = append(tags, "_status::"+status)
|
|
||||||
}
|
}
|
||||||
if input.Priority != "" {
|
|
||||||
tags = append(tags, "_prio::"+input.Priority)
|
|
||||||
}
|
|
||||||
tags = append(tags, input.Tags...)
|
|
||||||
|
|
||||||
// Build initial relation map from semantic fields.
|
// Build initial relation map from rels input.
|
||||||
rels := make(map[models.RelType][]string)
|
rels := make(map[models.RelType][]string)
|
||||||
if input.Namespace != "" {
|
hasNamespace := false
|
||||||
rels[models.RelInNamespace] = []string{input.Namespace}
|
|
||||||
} else {
|
|
||||||
rels[models.RelInNamespace] = []string{s.userID} // default: creator's namespace
|
|
||||||
}
|
|
||||||
if input.Assignee != "" {
|
|
||||||
rels[models.RelAssignee] = []string{input.Assignee}
|
|
||||||
}
|
|
||||||
for _, ri := range input.Rels {
|
for _, ri := range input.Rels {
|
||||||
|
if ri.Type == models.RelInNamespace {
|
||||||
|
hasNamespace = true
|
||||||
|
}
|
||||||
rels[ri.Type] = append(rels[ri.Type], ri.Target)
|
rels[ri.Type] = append(rels[ri.Type], ri.Target)
|
||||||
}
|
}
|
||||||
|
if !hasNamespace {
|
||||||
|
rels[models.RelInNamespace] = []string{s.userID}
|
||||||
|
}
|
||||||
|
|
||||||
id, err := s.store.GenerateID()
|
id, err := s.store.GenerateID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -176,10 +179,18 @@ func (s *nodeServiceImpl) Add(input AddInput) (*models.Node, error) {
|
|||||||
|
|
||||||
func (s *nodeServiceImpl) Update(id string, input UpdateInput) (*models.Node, error) {
|
func (s *nodeServiceImpl) Update(id string, input UpdateInput) (*models.Node, error) {
|
||||||
// Enforce blocking constraint before allowing status=done.
|
// Enforce blocking constraint before allowing status=done.
|
||||||
if input.Status != nil && *input.Status == "done" {
|
for _, t := range input.AddTags {
|
||||||
|
if t == "_status::done" {
|
||||||
if err := s.checkBlockers(id); err != nil {
|
if err := s.checkBlockers(id); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate tags being added.
|
||||||
|
if err := validateTags(input.AddTags); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.store.Transaction(func(st store.Store) error {
|
err := s.store.Transaction(func(st store.Store) error {
|
||||||
@@ -211,15 +222,6 @@ func (s *nodeServiceImpl) Update(id string, input UpdateInput) (*models.Node, er
|
|||||||
for _, t := range current.Tags() {
|
for _, t := range current.Tags() {
|
||||||
tmp.AddTag(t)
|
tmp.AddTag(t)
|
||||||
}
|
}
|
||||||
if input.Type != nil {
|
|
||||||
tmp.AddTag("_type::" + *input.Type)
|
|
||||||
}
|
|
||||||
if input.Status != nil {
|
|
||||||
tmp.AddTag("_status::" + *input.Status)
|
|
||||||
}
|
|
||||||
if input.Priority != nil {
|
|
||||||
tmp.AddTag("_prio::" + *input.Priority)
|
|
||||||
}
|
|
||||||
for _, t := range input.AddTags {
|
for _, t := range input.AddTags {
|
||||||
tmp.AddTag(t)
|
tmp.AddTag(t)
|
||||||
}
|
}
|
||||||
@@ -251,18 +253,8 @@ func (s *nodeServiceImpl) Update(id string, input UpdateInput) (*models.Node, er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build relation additions, including structured fields.
|
|
||||||
var addRels []RelInput
|
|
||||||
if input.Namespace != nil {
|
|
||||||
addRels = append(addRels, RelInput{Type: models.RelInNamespace, Target: *input.Namespace})
|
|
||||||
}
|
|
||||||
if input.Assignee != nil {
|
|
||||||
addRels = append(addRels, RelInput{Type: models.RelAssignee, Target: *input.Assignee})
|
|
||||||
}
|
|
||||||
addRels = append(addRels, input.AddRels...)
|
|
||||||
|
|
||||||
currentRels := current.Relations()
|
currentRels := current.Relations()
|
||||||
for _, ri := range addRels {
|
for _, ri := range input.AddRels {
|
||||||
resolved, err := s.resolveRelTarget(st, ri)
|
resolved, err := s.resolveRelTarget(st, ri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -485,4 +477,3 @@ func (s *nodeServiceImpl) ensureNamespace(st store.Store, name string) (string,
|
|||||||
}
|
}
|
||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user