Skip to content

Commit

Permalink
labctl content create skill-path
Browse files Browse the repository at this point in the history
  • Loading branch information
iximiuz committed Sep 6, 2024
1 parent 35fe44d commit 35832bb
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 36 deletions.
66 changes: 42 additions & 24 deletions cmd/content/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ type createOptions struct {

dirOptions

// noSample bool
noSample bool
}

func newCreateCommand(cli labcli.CLI) *cobra.Command {
var opts createOptions

cmd := &cobra.Command{
Use: "create [flags] <challenge|tutorial|course> <name>",
Use: "create [flags] <challenge|tutorial|skill-path|course> <name>",
Short: "Create a new piece of content (visible only to the author)",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -44,12 +44,12 @@ func newCreateCommand(cli labcli.CLI) *cobra.Command {

flags := cmd.Flags()

// flags.BoolVar(
// &opts.noSample,
// "no-sample",
// false,
// `Don't create a sample piece of content`,
// )
flags.BoolVar(
&opts.noSample,
"no-sample",
false,
`Don't create a sample piece of content`,
)

opts.AddDirFlag(flags)

Expand Down Expand Up @@ -78,11 +78,14 @@ func runCreateContent(ctx context.Context, cli labcli.CLI, opts *createOptions)
case content.KindChallenge:
cont, err = createChallenge(ctx, cli, opts)

case content.KindCourse:
cont, err = createCourse(ctx, cli, opts)

case content.KindTutorial:
cont, err = createTutorial(ctx, cli, opts)

case content.KindCourse:
cont, err = createCourse(ctx, cli, opts)
case content.KindSkillPath:
cont, err = createSkillPath(ctx, cli, opts)
}

if err != nil {
Expand All @@ -101,8 +104,8 @@ func runCreateContent(ctx context.Context, cli labcli.CLI, opts *createOptions)

if _, err := os.Stat(dir); err == nil {
cli.PrintErr("WARNING: Directory %s already exists and not empty.\n", dir)
cli.PrintErr("Skipping pulling the sample content files to avoid\noverwriting existing local files\n.")
cli.PrintErr("Use `labctl pull %s %s --dir <some-other-dir>` to\npull the sample content files manually.\n")
cli.PrintErr("Skipping pulling sample content to avoid overwriting existing local files.\n")
cli.PrintErr("Use `labctl pull %s %s --dir <some-other-dir>`\nto pull the sample content files manually.\n", cont.GetKind(), cont.GetName())
return nil
}

Expand Down Expand Up @@ -137,7 +140,8 @@ func runCreateContent(ctx context.Context, cli labcli.CLI, opts *createOptions)

func createChallenge(ctx context.Context, cli labcli.CLI, opts *createOptions) (content.Content, error) {
ch, err := cli.Client().CreateChallenge(ctx, api.CreateChallengeRequest{
Name: opts.name,
Name: opts.name,
Sample: !opts.noSample,
})
if err != nil {
return nil, fmt.Errorf("couldn't create challenge: %w", err)
Expand All @@ -146,21 +150,11 @@ func createChallenge(ctx context.Context, cli labcli.CLI, opts *createOptions) (
return ch, nil
}

func createTutorial(ctx context.Context, cli labcli.CLI, opts *createOptions) (content.Content, error) {
t, err := cli.Client().CreateTutorial(ctx, api.CreateTutorialRequest{
Name: opts.name,
})
if err != nil {
return nil, fmt.Errorf("couldn't create tutorial: %w", err)
}

return t, nil
}

func createCourse(ctx context.Context, cli labcli.CLI, opts *createOptions) (content.Content, error) {
c, err := cli.Client().CreateCourse(ctx, api.CreateCourseRequest{
Name: opts.name,
Variant: api.CourseVariantModular,
Sample: !opts.noSample,
})
if err != nil {
return nil, fmt.Errorf("couldn't create course: %w", err)
Expand All @@ -169,6 +163,30 @@ func createCourse(ctx context.Context, cli labcli.CLI, opts *createOptions) (con
return c, nil
}

func createSkillPath(ctx context.Context, cli labcli.CLI, opts *createOptions) (content.Content, error) {
sp, err := cli.Client().CreateSkillPath(ctx, api.CreateSkillPathRequest{
Name: opts.name,
Sample: !opts.noSample,
})
if err != nil {
return nil, fmt.Errorf("couldn't create skill path: %w", err)
}

return sp, nil
}

func createTutorial(ctx context.Context, cli labcli.CLI, opts *createOptions) (content.Content, error) {
t, err := cli.Client().CreateTutorial(ctx, api.CreateTutorialRequest{
Name: opts.name,
Sample: !opts.noSample,
})
if err != nil {
return nil, fmt.Errorf("couldn't create tutorial: %w", err)
}

return t, nil
}

func hasAuthorProfile(ctx context.Context, cli labcli.CLI) (bool, error) {
me, err := cli.Client().GetMe(ctx)
if err != nil {
Expand Down
14 changes: 12 additions & 2 deletions cmd/content/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func newListCommand(cli labcli.CLI) *cobra.Command {
var opts listOptions

cmd := &cobra.Command{
Use: "list [--kind challenge|tutorial|course]",
Use: "list [--kind challenge|tutorial|skill-path|course]",
Aliases: []string{"ls"},
Short: "List authored content, possibly filtered by kind.",
Args: cobra.NoArgs,
Expand All @@ -34,7 +34,7 @@ func newListCommand(cli labcli.CLI) *cobra.Command {
flags.Var(
&opts.kind,
"kind",
`Content kind to filter by - one of challenge, tutorial, course (an empty string means all)`,
`Content kind to filter by - one of challenge, tutorial, skill-path, course (an empty string means all)`,
)

return cmd
Expand All @@ -43,6 +43,7 @@ func newListCommand(cli labcli.CLI) *cobra.Command {
type AuthoredContent struct {
Challenges []api.Challenge `json:"challenges" yaml:"challenges"`
Tutorials []api.Tutorial `json:"tutorials" yaml:"tutorials"`
SkillPaths []api.SkillPath `json:"skill-paths" yaml:"skill-paths"`
Courses []api.Course `json:"courses" yaml:"courses"`
}

Expand All @@ -67,6 +68,15 @@ func runListContent(ctx context.Context, cli labcli.CLI, opts *listOptions) erro
authored.Tutorials = tutorials
}

if opts.kind == "" || opts.kind == content.KindSkillPath {
skillPaths, err := cli.Client().ListAuthoredSkillPaths(ctx)
if err != nil {
return fmt.Errorf("cannot list authored skill paths: %w", err)
}

authored.SkillPaths = skillPaths
}

if opts.kind == "" || opts.kind == content.KindCourse {
courses, err := cli.Client().ListAuthoredCourses(ctx)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/content/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func newPullCommand(cli labcli.CLI) *cobra.Command {
var opts pullOptions

cmd := &cobra.Command{
Use: "pull [flags] <challenge|tutorial|course> <name>",
Use: "pull [flags] <challenge|tutorial|skill-path|course> <name>",
Short: "Pull remote content files to the local directory for backup or editing",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
Expand Down
2 changes: 1 addition & 1 deletion cmd/content/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func newPushCommand(cli labcli.CLI) *cobra.Command {
var opts pushOptions

cmd := &cobra.Command{
Use: "push [flags] <challenge|tutorial|course> <name>",
Use: "push [flags] <challenge|tutorial|skill-path|course> <name>",
Short: `Push content files from the local directory to the remote content repository (the "inner author loop").`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
Expand Down
9 changes: 6 additions & 3 deletions cmd/content/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func newRemoveCommand(cli labcli.CLI) *cobra.Command {
var opts removeOptions

cmd := &cobra.Command{
Use: "remove [flags] <challenge|tutorial|course> <name>",
Use: "remove [flags] <challenge|tutorial|skill-path|course> <name>",
Aliases: []string{"rm"},
Short: "Remove a piece of content you authored.",
Args: cobra.ExactArgs(2),
Expand Down Expand Up @@ -64,11 +64,14 @@ func runRemoveContent(ctx context.Context, cli labcli.CLI, opts *removeOptions)
case content.KindChallenge:
return cli.Client().DeleteChallenge(ctx, opts.name)

case content.KindCourse:
return cli.Client().DeleteCourse(ctx, opts.name)

case content.KindTutorial:
return cli.Client().DeleteTutorial(ctx, opts.name)

case content.KindCourse:
return cli.Client().DeleteCourse(ctx, opts.name)
case content.KindSkillPath:
return cli.Client().DeleteSkillPath(ctx, opts.name)

default:
return fmt.Errorf("unknown content kind %q", opts.kind)
Expand Down
3 changes: 2 additions & 1 deletion internal/api/challenges.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ func (ch *Challenge) CountCompletedInitTasks() int {
}

type CreateChallengeRequest struct {
Name string `json:"name"`
Name string `json:"name"`
Sample bool `json:"sample"`
}

func (c *Client) CreateChallenge(ctx context.Context, req CreateChallengeRequest) (*Challenge, error) {
Expand Down
1 change: 1 addition & 0 deletions internal/api/courses.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const (
type CreateCourseRequest struct {
Name string `json:"name"`
Variant CourseVariant `json:"variant"`
Sample bool `json:"sample"`
}

func (c *Client) CreateCourse(ctx context.Context, req CreateCourseRequest) (*Course, error) {
Expand Down
70 changes: 70 additions & 0 deletions internal/api/skillpath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package api

import (
"context"

"github.com/iximiuz/labctl/internal/content"
)

type SkillPath struct {
CreatedAt string `json:"createdAt" yaml:"createdAt"`
UpdatedAt string `json:"updatedAt" yaml:"updatedAt"`

Name string `json:"name" yaml:"name"`
Title string `json:"title" yaml:"title"`

PageURL string `json:"pageUrl" yaml:"pageUrl"`
}

var _ content.Content = (*SkillPath)(nil)

func (t *SkillPath) GetKind() content.ContentKind {
return content.KindSkillPath
}

func (t *SkillPath) GetName() string {
return t.Name
}

func (t *SkillPath) GetPageURL() string {
return t.PageURL
}

type CreateSkillPathRequest struct {
Name string `json:"name"`
Sample bool `json:"sample"`
}

func (c *Client) CreateSkillPath(ctx context.Context, req CreateSkillPathRequest) (*SkillPath, error) {
body, err := toJSONBody(req)
if err != nil {
return nil, err
}

var t SkillPath
return &t, c.PostInto(ctx, "/skill-paths", nil, nil, body, &t)
}

func (c *Client) GetSkillPath(ctx context.Context, name string) (*SkillPath, error) {
var t SkillPath
return &t, c.GetInto(ctx, "/skill-paths/"+name, nil, nil, &t)
}

func (c *Client) ListSkillPaths(ctx context.Context) ([]SkillPath, error) {
var skillPaths []SkillPath
return skillPaths, c.GetInto(ctx, "/skill-paths", nil, nil, &skillPaths)
}

func (c *Client) ListAuthoredSkillPaths(ctx context.Context) ([]SkillPath, error) {
var skillPaths []SkillPath
return skillPaths, c.GetInto(ctx, "/skill-paths/authored", nil, nil, &skillPaths)
}

func (c *Client) DeleteSkillPath(ctx context.Context, name string) error {
resp, err := c.Delete(ctx, "/skill-paths/"+name, nil, nil)
if err != nil {
return err
}
resp.Body.Close()
return nil
}
3 changes: 2 additions & 1 deletion internal/api/tutorials.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ func (t *Tutorial) GetPageURL() string {
}

type CreateTutorialRequest struct {
Name string `json:"name"`
Name string `json:"name"`
Sample bool `json:"sample"`
}

func (c *Client) CreateTutorial(ctx context.Context, req CreateTutorialRequest) (*Tutorial, error) {
Expand Down
11 changes: 8 additions & 3 deletions internal/content/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@ type ContentKind string

const (
KindChallenge ContentKind = "challenge"
KindTutorial ContentKind = "tutorial"
KindCourse ContentKind = "course"
KindSkillPath ContentKind = "skill-path"
KindTutorial ContentKind = "tutorial"
)

func (k *ContentKind) Set(v string) error {
switch string(v) {
case string(KindChallenge):
*k = KindChallenge
case string(KindTutorial):
*k = KindTutorial
case string(KindCourse):
*k = KindCourse
case string(KindSkillPath):
*k = KindSkillPath
case string(KindTutorial):
*k = KindTutorial
default:
return fmt.Errorf("unknown content kind: %s", v)
}
Expand All @@ -37,6 +40,8 @@ func (k *ContentKind) Plural() string {
return "tutorials"
case KindCourse:
return "courses"
case KindSkillPath:
return "skill-paths"
default:
panic(fmt.Sprintf("unknown content kind: %s", k))
}
Expand Down

0 comments on commit 35832bb

Please sign in to comment.