{
  "profile": "This module uses a Star Wars API to query for character information based on a given input value. If the result set is empty, the module then repeats the same task using a Star Trek API. This tutorial highlights two common combinators, if and retain, that play a significant role in creating complex workflows. It aims to demonstrate their use and emphasize their ability to create nested and modular compositions.",
  "time": "10 minutes",
  "skills": ["Composer Programming", "The Edit-Preview Loop"],
  "steps": [
    {
      "heading": "Introduction",
      "content": "This tutorial reviews an application that searches two APIs for information from the Star Wars or Star Trek universes. You will see how to create and deploy a **new serverless API** by combining existing APIs, simply by writing a Composer program.",
      "transition": "next",
      "extras": {
        "learnMore": {
          "title": "Duration",
          "doc": "It should take you about **10 minutes** to walk through this interactive tutorial."
        }
      }
    },
    {
      "heading": "Previewing the Composition",
      "content": "When drafting a composition, you can preview its structure and validate changes. Use the `preview /path/to/composition.js` command; every time you save that file, the visualization will update **automatically**.\n\nTo see Wookie Chat drawn visually, click on the `preview` command on the right.",
      "transition": "next",
      "preview-nope": {
        "file": "@demos/wookie/app.js"
      },
      "extras": {
        "learnMore": {
          "doc": "- Cloud Functions are drawn as rectangles\n- Striped rectangles depict actions not yet deployed"
        },
        "nextSteps": [
          {
            "display": "preview",
            "command": "preview @demos/wookie/app.js",
            "doc": "This command shows a visual representation of Wookie Chat. The preview command is a helpful way to **validate your work** while coding a composition."
          }
        ]
      }
    },
    {
      "heading": "Coding Compositions",
      "content": "Compositions are crafted by **writing code**. To see the source for the Wookie composition, try using the `source` command. Next, you will explore the details of this code, such as how to code **sequences** and **try/catch** patterns.",
      "transition": "next",
      "extras": {
        "learnMore": {
          "doc": "- Currently, the Composition Service supports Node.JS as a source language\n- Python support is coming soon"
        },
        "nextSteps": [
          {
            "display": "source",
            "command": "source @demos/wookie/app.js",
            "doc": "For convenience, this command shows the source code for Wookie Chat. You will of course be using your favorite code editor when drafting your own compositions."
          }
        ]
      }
    },
    {
      "heading": "Introduction to Combinators",
      "content": "Edges between nodes represent the flow of data and control from one function to the next. Wookie Chat uses `if` and `retain`, two operations that **transfer data in a non-sequential way**. These are two of a suite of \"combinators\" available to you.",
      "preview-nope": {
        "file": "@demos/wookie/app.js"
      },
      "transition": "next",
      "extras": {
        "learnMore": {
          "title": "Learn More",
          "doc": "For the full list of available combinators, consult the [combinator reference guide](https://github.com/apache/incubator-openwhisk-composer/blob/master/docs/COMBINATORS.md)."
        },
        "table": {
          "title": "Selected Combinators",
          "columns": ["Combinator", "Description"],
          "rows": [
            [
              { "value": "seq(a,b,c)", "command": "preview @demos/seq.js" },
              "Form a sequential pipeline, where the output of one is fed to the input of the next."
            ],

            [
              { "value": "par(t1,t2,t3)", "command": "preview @demos/par.js" },
              "Do two or more tasks in parallel, where the input is fed to each of the tasks, and the output is an array of task results."
            ],

            [
              { "value": "try(task,oops)", "command": "preview @demos/try.js" },
              "Form a try/catch structure; if `task` fails, pass the error output to `oops`."
            ],

            ["http", "Call out to a remote service."]
          ]
        }
      }
    },
    {
      "heading": "The If Combinator",
      "content": "In Composer, `if` is used to describe a conditional flow. For example, in Wookie Chat, the `validate-swapi` Cloud Function implements the condition which guides the consequent flow. The code for a simple use of `if`:",
      "transition": "next",
      "preview-nope": {
        "file": "@demos/if.js"
      },
      "extras": {
        "code": {
          "language": "javascript",
          "body": "/**\n * We use Cloud Functions to implement the underlying logic\n * Note how Functions can be referenced via their 'name'\n */\nconst composer = require('@wdpdist/composer')\nmodule.exports = composer.if('authenticate', 'welcome', 'login')"
        },
        "nextSteps": [
          {
            "command": "preview @demos/if.js",
            "doc": "This command gives you a closer look at the visualization for the `if` combinator, using the example shown on the left."
          }
        ]
      }
    },
    {
      "heading": "The Retain Combinator",
      "content": "If you don't trust a function or service with sensitive information, you may need to forward data around them. This can be done using `retain`. A call to retain yields `params`, the forwarded input value, and `result`, the output of the untrusted code.\n\nThe data flow of `retain` is represented by a blue edge.",
      "transition": "next",
      "preview-nope": {
        "file": "@demos/retain.js"
      },
      "extras": {
        "code": {
          "language": "javascript",
          "body": "const { retain, sequence } = require('@wdpdist/composer')\n\nmodule.exports = retain(_ => ({x: _.x+1}))"
        },
        "nextSteps": [
          {
            "command": "preview @demos/retain.js",
            "doc": "This command gives you a closer look at the visualization for `retain`, using the example shown on the left."
          }
        ]
      }
    },
    {
      "heading": "Deploying the Composition",
      "content": "Now that the logic of the application seems reasonable, the next step is deploying it to the cloud. By deploying the composition, you will be able to test it for runtime errors, such as incorrect handling of incoming data.",
      "transition": "next",
      "fontawesome": "fas fa-cloud-upload-alt",
      "extras": {
        "nextSteps": [
          {
            "display": "app create",
            "command": "app create wookie @demos/wookie/app.js",
            "doc": "Deploys a given composition, naming it, in this case \"wookie\". "
          }
        ]
      }
    },
    {
      "heading": "Invoking the Composition",
      "content": "At this point, the Wookie Chat composition has been deployed, and is now accessible as a REST endpoint. You can run some quick tests against the deployed composition by using the `invoke` command.",
      "transition": "next",
      "fontawesome": "fas fa-play-circle",
      "autocomplete-nope": {
        "value": "compose wookie"
      },
      "extras": {
        "nextSteps": [
          {
            "display": "app invoke -p search luke",
            "command": "app invoke wookie -p search luke",
            "doc": "A successful query."
          },
          {
            "display": "app invoke -p search picard",
            "command": "app invoke wookie -p search picard",
            "doc": "A query that turns up empty."
          }
        ]
      }
    },
    {
      "heading": "Revisiting Prior Invocations",
      "content": "As you continue to develop and refine your composition, you may want to revisit previous invocations. In order to see past activations, the `session list` and `grid` commands can prove useful.",
      "transition": "next",
      "fontawesome": "far fa-list-alt",
      "autocompletenope": {
        "value": "session list"
      },
      "extras": {
        "tips": [
          "Tip: Session details can also be viewed using `session get sessionId`, where `sessionId` is the id of the session.",
          "Tip: To view a summary or grid view of recent activations, click on the small icons at the bottom lefthand side of the session list."
        ],
        "nextSteps": [
          {
            "command": "session list",
            "doc": "Shows an overview of recent invocations."
          },
          {
            "command": "grid",
            "doc": "Summarize a large number of recent activations in a grid format."
          }
        ]
      }
    },
    {
      "heading": "Conclusion",
      "content": "Congraulations, you have completed the Wookie Chat Tutorial.",
      "renderingHints": "fifty-fifty",
      "extras": {
        "nextSteps": [
          {
            "command": "play @tutorials/comparo",
            "doc": "Revisit Wookie Chat, using more advanced capabilities including `parallel` and `http`."
          },
          { "command": "tutorial list", "doc": "Choose your own adventure!" }
        ]
      }
    }
  ]
}
