This is an automated email from the ASF dual-hosted git repository.

msciabarra pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-site.git


The following commit(s) were added to refs/heads/main by this push:
     new e38f855  docs(chore): updated tutorial (#58)
e38f855 is described below

commit e38f85575b6936e35b21b95a94ec6518bccfdb7c
Author: Bruno Salzano <[email protected]>
AuthorDate: Mon Mar 24 22:51:18 2025 +0100

    docs(chore): updated tutorial (#58)
    
    Updated tutorial pages (first commit)
---
 Taskfile.yaml                                      |   4 +-
 content/en/docs/tutorial/_index.md                 |   7 -
 content/en/docs/tutorial/first-steps/_index.md     | 196 +++++++-------
 content/en/docs/tutorial/form-validation/_index.md | 289 ++++++++++++++-------
 content/en/docs/tutorial/getting-started/_index.md |  90 ++++++-
 .../en/docs/tutorial/images/example_result.webp    | Bin 0 -> 101970 bytes
 content/en/docs/tutorial/images/form.webp          | Bin 0 -> 25726 bytes
 content/en/docs/tutorial/images/webhook_site.webp  | Bin 0 -> 64822 bytes
 content/en/docs/tutorial/notify-message/_index.md  | 137 +++++++---
 content/en/docs/tutorial/packaging/_index.md       | 249 ++++++++----------
 content/en/docs/tutorial/use-database/_index.md    | 270 ++++++++++++++-----
 11 files changed, 774 insertions(+), 468 deletions(-)

diff --git a/Taskfile.yaml b/Taskfile.yaml
index 6f96619..59e2187 100644
--- a/Taskfile.yaml
+++ b/Taskfile.yaml
@@ -46,9 +46,9 @@ tasks:
   preview:
     desc: preview the websites
     cmds:
-      - hugo server
+      - hugo server -O
 
   debug:
     desc: debug the websites
     cmds:
-      - hugo server --ignoreCache  --debug --disableFastRender
\ No newline at end of file
+      - hugo server --ignoreCache  --debug --disableFastRender
diff --git a/content/en/docs/tutorial/_index.md 
b/content/en/docs/tutorial/_index.md
index f5e3dac..bcf77d7 100644
--- a/content/en/docs/tutorial/_index.md
+++ b/content/en/docs/tutorial/_index.md
@@ -4,13 +4,6 @@ description: Showcase serverless development in action
 weight: 10
 ---
 
-{{< blockquote warning>}}
-<strong>This page is under review.</strong><br/>
-<br/>
-Several changes have been made to the project since its first draft and 
therefore the
-tutorial needs to be updated to the publishing system.
-{{< /blockquote >}}
-
 ## Tutorial
 
 This tutorial walks you through developing a simple OpenServerless
diff --git a/content/en/docs/tutorial/first-steps/_index.md 
b/content/en/docs/tutorial/first-steps/_index.md
index 52d4fee..2e2994a 100644
--- a/content/en/docs/tutorial/first-steps/_index.md
+++ b/content/en/docs/tutorial/first-steps/_index.md
@@ -5,21 +5,14 @@ draft: false
 weight: 20
 ---
 
-{{< blockquote warning>}}
-<strong>This page is under review.</strong><br/>
-<br/>
-Several changes have been made to the project since its first draft and 
therefore the
-tutorial needs to be updated to the publishing system.
-{{< /blockquote >}}
-
 ## First steps
 
 ### Starting at the Front
 
-Right now, after a freshly installation, if we visit the `<apihost>` you
-will see a very simple page with:
+Right now, after a fresh installation, and after added the `opstutorial` user,
+if we visit the `<apihost>` you will see a very simple page with:
 
-> Welcome to OpenServerless static content distributor landing page!!!
+> Welcome to Nuvolaris static content distributor landing page!!!
 
 That’s because we’ve activated the static content, and by default it
 starts with this simple `index.html` page. We will instead have our own
@@ -29,122 +22,121 @@ actions. Let’s write it now.
 Let’s create a folder that will contain all of our app code:
 `contact_us_app`.
 
-Inside that create a new folder called `web` which will store our static
-frontend, and add there a `index.html` file with the following:
+{{< blockquote info>}}
+You can find the full source code of the tutorial at this GitHub Repository:
+[Contact Us App](https://github.com/apache/openserverless-tutorial).
+
+The repository has a tag for each step. So after cloning it in your local 
+directory, follow the istruction on it's README page.
+{{< /blockquote >}}
+
+Inside that create two new folders called `web`, which will store our static
+frontend, and `packages`, which will store our backend actions.
+Inside the `web` folder an `index.html` file.
 
+The directory structure should look like:
+
+```
+contact_us_app
+├── packages
+└── web
+    └── index.html
+```
+
+Paste the following markup inside the `index.html` file:
 ```html
 <!DOCTYPE html>
-<html>
-   <head>
-      <link 
href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" 
rel="stylesheet" id="bootstrap-css">
-   </head>
-
-   <body>
-      <div id="container">
-         <div class="row">
-            <div class="col-md-8 col-md-offset-2">
-               <h4>Get in Touch</h4>
-               <form method="POST">
-                  <div class="form-group">
-                     <input type="text" name="name" class="form-control" 
placeholder="Name">
-                  </div>
-                  <div class="form-group">
-                     <input type="email" name="email" class="form-control" 
placeholder="E-mail">
-                  </div>
-                  <div class="form-group">
-                     <input type="tel" name="phone" class="form-control" 
placeholder="Phone">
-                  </div>
-                  <div class="form-group">
-                     <textarea name="message" rows="3" class="form-control" 
placeholder="Message"></textarea>
-                  </div>
-                  <button class="btn btn-default" type="submit" name="button">
-                     Send
-                  </button>
-               </form>
+<html lang="it">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Get In Touch</title>
+    <link 
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"; 
rel="stylesheet">
+</head>
+<body>
+<nav class="navbar navbar-dark bg-dark">
+    <div class="container">
+        <a class="navbar-brand" href="#">Apache OpenServerless™ Tutorial</a>
+    </div>
+</nav>
+
+<div class="container d-flex justify-content-center align-items-center" 
style="min-height: 80vh;">
+    <div class="w-50 p-4 border rounded bg-light shadow">
+        <h2 class="text-center mb-4">Get In Touch</h2>
+        <form>
+            <div class="mb-3">
+                <label for="name" class="form-label">Name</label>
+                <input type="text" class="form-control" id="name" name="name" 
placeholder="Insert your name">
+            </div>
+            <div class="mb-3">
+                <label for="email" class="form-label">Email</label>
+                <input type="email" class="form-control" id="email" 
name="email" placeholder="Insert your email">
+            </div>
+            <div class="mb-3">
+                <label for="phone" class="form-label">Phone Number</label>
+                <input type="tel" class="form-control" id="phone" name="phone" 
placeholder="Insert you phone number">
             </div>
-         </div>
-      </div>
-   </body>
+            <div class="mb-3">
+                <label for="message" class="form-label">Message</label>
+                <textarea class="form-control" id="message" name="message" 
rows="4" placeholder="Type here your message"></textarea>
+            </div>
+            <button type="submit" class="btn btn-primary w-100">Send !</button>
+        </form>
+    </div>
+</div>
 
+<script 
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js";></script>
+</body>
 </html>
 ```
 
+{{< blockquote warning>}}
+Before move on, be sure to have completed once the login
+as [indicated here](/docs/tutorial/getting-started/#login-as-user)
+{{< /blockquote >}}
+
 Now we just have to upload it to our OpenServerless deployment. You
 could upload it using something like `curl` with a `PUT` to where your
 platform is deployed at, but there is an handy command that does it
 automatically for all files in a folder:
 
 ```bash
-ops util upload web/
+ops ide deploy
 ```
 
-Pass to `ops util upload` the path to folder where the index.html is
-stored in (the `web` folder) and visit again `<apihost>`.
-
-Now you should see the new index page:
-
-![Form](/docs/tutorial/images/form.png)
-
-### The Contact Package
+The output will be:
 
-The contact form we just uploaded does not do anything. To make it work
-let’s start by creating a new package to hold our actions. Moreover, we
-can bind to this package the database url, so the actions can directly
-access it!
-
-With the `debug` command you can see what’s going on in your deployment.
-This time let’s use it to grab the "postgres\_url" value:
-
-```bash
-ops -config -d | grep POSTGRES_URL
 ```
-
-Copy the Postgres URL (something like `postgresql://...`). Now we can
-create a new package for the application:
-
-```bash
-ops package create contact -p dbUri <postgres_url>
-ok: created package contact
+> Scan:
+> Deploying:
+build process exited with code 0
+UPLOAD ASSETS FROM web
+==================| UPLOAD RESULTS |==================
+| FILES      : 1
+| COMPLETED  : 1
+| ERRORS     : 0
+| SKIPPED    : 0
+| EXEC. TIME : 37.83 ms
+======================================================
+URL: http://opstutorial.localhost:80
 ```
 
-The actions under this package will be able to access the "dbUri"
-variable from their args!
+The command will scan both `packages` and `web` directories and will
+upload the index.html to the web bucket. Finally it will show the 
+`URL` where the frontend have been published.
+If you visit the `URL` within your browser, you should see 
+the new index page:
 
-To follow the same structure for our action files, let’s create a folder
-`packages` and inside another folder `contact` to give our actions a
-nice, easy to find, home.
+![Form](/docs/tutorial/images/form.webp)
 
-To manage and check out your packages, you can use the `ops packages`
-subcommands.
+### Development Tools
 
-```bash
-ops package list
+Apache OpenServerless has a set of development tools, inside the `ops ide` 
command, 
+details of which are available in [this section](/docs/reference/tasks/ide/) 
of the guide.
 
-packages
-/openserverless/contact  private
-/openserverless/hello    private     <-- a default package created during 
deployment
-```
+As shown before, we will be using `ops ide` for publishing, as this make the 
process 
+quicker and easier.
 
-And to get specific information on a package:
-
-```bash
-ops package get contact
-
-ok: got package contact
-{
-   "namespace": "openserverless",
-   "name": "contact",
-   "version": "0.0.1",
-   "publish": false,
-   "parameters": [
-      {
-            "key": "dbUri",
-            "value": <postgres_url>
-      }
-   ],
-   "binding": {},
-   "updated": 1696232618540
-}
-```
+The command `ops ide login` will enable the development tools.
 
 ---
diff --git a/content/en/docs/tutorial/form-validation/_index.md 
b/content/en/docs/tutorial/form-validation/_index.md
index bddd227..6d68186 100644
--- a/content/en/docs/tutorial/form-validation/_index.md
+++ b/content/en/docs/tutorial/form-validation/_index.md
@@ -5,26 +5,41 @@ weight: 30
 draft: false
 ---
 
-{{< blockquote warning>}}
-<strong>This page is under review.</strong><br/>
-<br/>
-Several changes have been made to the project since its first draft and 
therefore the
-tutorial needs to be updated to the publishing system.
-{{< /blockquote >}}
+### The Contact Package
+
+The contact form we just uploaded does not do anything. To make it work
+let’s start to fill our `package` directory with backend actions.
 
 ## Form validation
 
-Now that we have a contact form and a package for our actions, we have
-to handle the submission. We can do that by adding a new action that
-will be called when the form is submitted. Let’s create a `submit.js`
-file in our `packages/contact` folder.
+We well start to handle the form submission. We can do that by 
+adding a new action that will be called when the form is submitted. 
+Let’s create a `submit.js` file inside our `packages/contact` folder.
+
+You directory structure should looks like this:
+
+```shell
+contact_us_app
+├── packages
+│   └── contact
+│       └── submit.js
+└── web
+    └── index.html
+```
+
+Paste this content inside the `submit.js` file:
 
 ```javascript
+//--web true
+//--kind nodejs:default
+
 function main(args) {
-  let message = []
-  let errors = []
-  // TODO: Form Validation
-  // TODO: Returning the Result
+    let message = [];
+    let errors = [];
+
+    // TODO: Add here Form Validation code
+
+    // TODO: Add here the code for returning the Result
 }
 ```
 
@@ -33,16 +48,27 @@ args) which will contain the form data (accessible via 
`args.name`,
 `args.email`, etc.). With that. we will do some validation and then
 return the result.
 
+{{< blockquote info>}}
+You may have noticed the strange comments at the beginning of the file. Those 
+comments are used by <code>ops ide</code> to automatically handle the 
publishing of files 
+by calling <code>ops package</code> or <code>ops action</code> as needed.
+In particular:
+<ul>
+<li>the <code>--web true</code> will enable this as a <a 
href="/docs/reference/entities/webactions/">web action</a>;</li>
+<li><code>--kind nodejs:default</code> will ask OpenServerless to run this 
code on the nodejs default runtime.</li>
+</ul>
+{{< /blockquote >}}
+
 ### Validation
 
 Let’s start filling out the "Form Validation" part by checking the name:
 
 ```javascript
 // validate the name
-if(args.name) {
-  message.push("name: "+args.name)
+if (args.name) {
+    message.push("name: " + args.name);
 } else {
-  errors.push("No name provided")
+    errors.push("No name provided");
 }
 ```
 
@@ -51,10 +77,10 @@ Then the email by using a regular expression:
 ```javascript
 // validate the email
 var re = /\S+@\S+\.\S+/;
-if(args.email && re.test(args.email)) {
-    message.push("email: "+args.email)
+if (args.email && re.test(args.email)) {
+    message.push("email: " + args.email);
 } else {
-  errors.push("Email missing or incorrect.")
+    errors.push("Email missing or incorrect.");
 }
 ```
 
@@ -62,10 +88,10 @@ The phone, by checking that it’s at least 10 digits:
 
 ```javascript
 // validate the phone
-if(args.phone && args.phone.match(/\d/g).length >= 10) {
-  message.push("phone: "+args.phone)
+if (args.phone && args.phone.match(/\d/g).length >= 10) {
+    message.push("phone: " + args.phone);
 } else {
-  errors.push("Phone number missing or incorrect.")
+    errors.push("Phone number missing or incorrect.");
 }
 ```
 
@@ -73,8 +99,8 @@ Finally, the message text, if present:
 
 ```javascript
 // validate the message
-if(args.message) {
-  message.push("message:" +args.message)
+if (args.message) {
+    message.push("message:" + args.message);
 }
 ```
 
@@ -87,21 +113,21 @@ the message and return a "thank you" page.
 
 ```javascript
 // return the result
-if(errors.length) {
-  var errs = "<ul><li>"+errors.join("</li><li>")+"</li></ul>"
-  return {
-    body: "<h1>Errors!</h1>"+
-      errs + '<br><a href="javascript:window.history.back()">Back</a>'
-    }
+if (errors.length) {
+    var errs = "<ul><li>" + errors.join("</li><li>") + "</li></ul>";
+    return {
+        body: "<h1>Errors!</h1>" +
+            errs + '<br><a href="javascript:window.history.back()">Back</a>'
+    };
 } else {
-    var data = "<pre>"+message.join("\n")+"</pre>"
+    var data = "<pre>" + message.join("\n") + "</pre>";
     return {
-      body: "<h1>Thank you!</h1>"+ data,
-      name: args.name,
-      email: args.email,
-      phone: args.phone,
-      message: args.message
-    }
+        body: "<h1>Thank you!</h1>" + data,
+        name: args.name,
+        email: args.email,
+        phone: args.phone,
+        message: args.message
+    };
 }
 ```
 
@@ -117,19 +143,47 @@ the database.
 
 Let’s start deploying the action:
 
-    ops action create contact/submit submit.js --web true
-    ok: created action contact/submit
+```bash
+ops ide deploy
+```
 
-The `--web true` specifies it is a web action. We are creating a
-`submit` action in the `contact` package, that’s why we are passing
-`contact/submit`.
+You should see output like this:
+
+```shell
+/home/openserverless/.ops/tmp/deploy.pid
+PID 70925
+> Scan:
+>> Action: packages/contact/submit.js
+> Deploying:
+>> Package: contact
+$ $OPS package update contact 
+ok: updated package contact
+>>> Action: packages/contact/submit.js
+$ $OPS action update contact/submit packages/contact/submit.js --web true 
--kind python:default --param POSTGRES_URL $POSTGRES_URL
+ok: updated action contact/submit
+build process exited with code 0
+UPLOAD ASSETS FROM web
+==================| UPLOAD RESULTS |==================
+| FILES      : 1
+| COMPLETED  : 1
+| ERRORS     : 0
+| SKIPPED    : 0
+| EXEC. TIME : 40.76 ms
+======================================================
+URL: http://opstutorial.localhost:80
+```
 
-You can retrieve the url with:
+You can retrieve the url of the action with:
 
 ```bash
 ops url contact/submit
+```
+
+You should see this output:
 
-$ <apihost>/api/v1/web/openserverless/contact/submit
+```shell
+ok: got action submit
+http://localhost:80/api/v1/web/opstutorial/contact/submit
 ```
 
 If you click on it you will see the Error page with a list of errors,
@@ -138,17 +192,18 @@ directly, without passing in any args. This is meant to 
be used via the
 contact form page!
 
 We need to wire it into the index.html. So let’s open it again and add a
-couple of attributes to the form:
+couple of attributes to the form. Change the `<form>` tag as follow:
 
 ```html
----             <form method="POST"> <-- old
-+++            <form method="POST" 
action="/api/v1/web/openserverless/contact/submit"
-                enctype="application/x-www-form-urlencoded"> <-- new
+<form method="POST" action="/api/v1/web/opstutorial/contact/submit"
+      enctype="application/x-www-form-urlencoded">
 ```
 
 Upload the web folder again with the new changes:
 
-    ops util upload web/
+```bash
+ops ide deploy
+```
 
 Now if you go to the contact form page the send button should work. It
 will invoke the submit action which in turn will return some html.
@@ -166,25 +221,58 @@ actions:
 
 ```bash
 ops action list
+```
 
+```shell
 actions
-/openserverless/contact/submit               private nodejs:18
+/opstutorial/contact/submit                 private nodejs:21
 ```
 
 And you can also get info on a specific action:
 
 ```bash
 ops action get contact/submit
+```
 
+```shell
+ok: got action contact/submit
 {
-    "namespace": "openserverless/contact",
+    "namespace": "opstutorial/contact",
     "name": "submit",
     "version": "0.0.1",
     "exec": {
-        "kind": "nodejs:18",
+        "kind": "nodejs:21",
         "binary": false
     },
-  ...
+    "annotations": [
+        {
+            "key": "web-export",
+            "value": true
+        },
+        {
+            "key": "raw-http",
+            "value": false
+        },
+        {
+            "key": "final",
+            "value": true
+        },
+        {
+            "key": "provide-api-key",
+            "value": false
+        },
+        {
+            "key": "exec",
+            "value": "nodejs:21"
+        }
+    ],
+    "parameters": [
+        {
+            "key": "POSTGRES_URL",
+            "value": 
"postgresql://opstutorial:<password>@nuvolaris-postgres.nuvolaris.svc.cluster.local:5432/opstutorial"
+        }
+    ],
+    ...
 }
 ```
 
@@ -193,54 +281,57 @@ These commands can come in handy when you need to debug 
your actions.
 Here is the complete the `submit.js` action:
 
 ```javascript
+//--web true
+//--kind nodejs:default
+
 function main(args) {
-  let message = []
-  let errors = []
-
-  // validate the name
-  if (args.name) {
-    message.push("name: " + args.name)
-  } else {
-    errors.push("No name provided")
-  }
-
-  // validate the email
-  var re = /\S+@\S+\.\S+/;
-  if (args.email && re.test(args.email)) {
-    message.push("email: " + args.email)
-  } else {
-    errors.push("Email missing or incorrect.")
-  }
-
-  // validate the phone
-  if (args.phone && args.phone.match(/\d/g).length >= 10) {
-    message.push("phone: " + args.phone)
-  } else {
-    errors.push("Phone number missing or incorrect.")
-  }
-
-  // validate the message
-  if (args.message) {
-    message.push("message:" + args.message)
-  }
-
-  // return the result
-  if (errors.length) {
-    var errs = "<ul><li>" + errors.join("</li><li>") + "</li></ul>"
-    return {
-      body: "<h1>Errors!</h1>" +
-        errs + '<br><a href="javascript:window.history.back()">Back</a>'
+    let message = [];
+    let errors = [];
+
+    // validate the name
+    if (args.name) {
+        message.push("name: " + args.name)
+    } else {
+        errors.push("No name provided")
     }
-  } else {
-    var data = "<pre>" + message.join("\n") + "</pre>"
-    return {
-      body: "<h1>Thank you!</h1>" + data,
-      name: args.name,
-      email: args.email,
-      phone: args.phone,
-      message: args.message
+
+    // validate the email
+    var re = /\S+@\S+\.\S+/;
+    if (args.email && re.test(args.email)) {
+        message.push("email: " + args.email);
+    } else {
+        errors.push("Email missing or incorrect.");
+    }
+
+    // validate the phone
+    if (args.phone && args.phone.match(/\d/g).length >= 10) {
+        message.push("phone: " + args.phone);
+    } else {
+        errors.push("Phone number missing or incorrect.");
+    }
+
+    // validate the message
+    if (args.message) {
+        message.push("message:" + args.message);
+    }
+
+    // return the result
+    if (errors.length) {
+        var errs = "<ul><li>" + errors.join("</li><li>") + "</li></ul>";
+        return {
+            body: "<h1>Errors!</h1>" +
+                errs + '<br><a 
href="javascript:window.history.back()">Back</a>'
+        };
+    } else {
+        var data = "<pre>" + message.join("\n") + "</pre>";
+        return {
+            body: "<h1>Thank you!</h1>" + data,
+            name: args.name,
+            email: args.email,
+            phone: args.phone,
+            message: args.message
+        };
     }
-  }
 }
 ```
 
diff --git a/content/en/docs/tutorial/getting-started/_index.md 
b/content/en/docs/tutorial/getting-started/_index.md
index a27d6a6..0ab66f5 100644
--- a/content/en/docs/tutorial/getting-started/_index.md
+++ b/content/en/docs/tutorial/getting-started/_index.md
@@ -5,13 +5,6 @@ weight: 10
 draft: false
 ---
 
-{{< blockquote warning>}}
-<strong>This page is under review.</strong><br/>
-<br/>
-Several changes have been made to the project since its first draft and 
therefore the
-tutorial needs to be updated to the publishing system.
-{{< /blockquote >}}
-
 ## Getting started
 
 ### Build a sample Application
@@ -28,6 +21,8 @@ services which are already part of a OpenServerless 
deployment and
 develop a contact form page for users to fill it with their emails and
 messages, which are then sent via email to us and stored in a database.
 
+Finally, we'll see how to activate external services using [Web 
hooks](https://en.wikipedia.org/wiki/Webhook).
+
 ### Openserverless CLI: Ops
 
 Serverless development is mostly performed on the CLI, and
@@ -50,15 +45,17 @@ come back here!
 
 ### Enabling Services
 
-After installing OpenServerless on a local machine with Docker or on a 
-supported cloud, you can enable or disable the services offered by the 
platform. 
-As we will use Postgres database, the Static content with the Minio S3 
compatible 
-storage and a cron scheduler, let’s run in the terminal:
+After installing OpenServerless on a local machine with Docker or on a
+supported cloud, you can enable or disable the services offered by the 
platform.
+As we will use Postgres database, the Static content with the Minio S3 
compatible
+storage, let’s run in the terminal:
 
 ```bash
 ops config enable --postgres --static --minio --cron
 ```
 
+This is the default set of services.
+
 Since you should already have a deployment running, we have to update it
 with the new services so they get deployed. Simply run:
 
@@ -69,6 +66,77 @@ ops update apply
 And with just that (when it finishes), we have everything we need ready
 to use!
 
+{{< blockquote info>}}
+If you've installed the local development environment using the instructions 
from
+the [Docker installation page](/docs/installation/install/docker/) you've 
already
+the base services enabled by default.
+{{< /blockquote >}}
+
+You can check what services are enabled with the command:
+
+```bash
+ops config status
+```
+
+This should be the output:
+
+```
+OPERATOR_COMPONENT_MINIO=true
+OPERATOR_COMPONENT_MONGODB=true
+OPERATOR_COMPONENT_POSTGRES=true
+OPERATOR_COMPONENT_STATIC=true
+OPERATOR_COMPONENT_CRON=true
+OPERATOR_COMPONENT_REDIS=true
+```
+
+### Create a user 
+
+If you don't have a user, it's the time to create one. We we'll use it to work 
on this tutorial.
+
+{{< blockquote warning>}}
+To create a user, we need to be the administrator, like described in [this 
section](/docs/cli/admin/).
+{{< /blockquote >}}
+
+```bash
+ops admin adduser opstutorial <youremail> SimplePassword --all 
+```
+
+The output will be:
+
+```
+Generated OPSTUTORIAL user secrets.
+Creating user opstutorial...
+whiskuser.nuvolaris.org/opstutorial created
+```
+
+### Login as user
+
+After user creation, it's time to perform ops login.
+
+{{< blockquote info>}}
+The `ops ide login` command will log you in on the server and dump the proper 
configuration of
+active services for your user. The configuration is automatically used by 
`ops` for all the tasks.
+You only need to run `ops ide login` once (unless you need to log in to 
another OpenServerless server or with another
+OpenServerless user).
+{{< /blockquote >}}
+
+Change your APIHOST accordly, if you've specified a custom one during the 
system setup
+
+```bash
+ops ide login opstutorial http://localhost:80
+```
+
+```
+*** Configuring Access to OpenServerless ***
+apihost=http://localhost:80 username=opstutorial
+Logging in http://localhost:80 as opstutorial
+Enter Password: 
+Successfully logged in as opstutorial.
+ok: whisk auth set. Run 'wsk property get --auth' to see the new value.
+ok: whisk API host set to http://localhost:80
+OpenServerless host and auth set successfully. You are now ready to use ops!
+```
+
 ### Cleaning Up
 
 Once you are done and want to clean the services configuration, just
diff --git a/content/en/docs/tutorial/images/example_result.webp 
b/content/en/docs/tutorial/images/example_result.webp
new file mode 100644
index 0000000..c9d45c4
Binary files /dev/null and 
b/content/en/docs/tutorial/images/example_result.webp differ
diff --git a/content/en/docs/tutorial/images/form.webp 
b/content/en/docs/tutorial/images/form.webp
new file mode 100644
index 0000000..819c1d6
Binary files /dev/null and b/content/en/docs/tutorial/images/form.webp differ
diff --git a/content/en/docs/tutorial/images/webhook_site.webp 
b/content/en/docs/tutorial/images/webhook_site.webp
new file mode 100644
index 0000000..eec9275
Binary files /dev/null and b/content/en/docs/tutorial/images/webhook_site.webp 
differ
diff --git a/content/en/docs/tutorial/notify-message/_index.md 
b/content/en/docs/tutorial/notify-message/_index.md
index e6fea97..3a3d022 100644
--- a/content/en/docs/tutorial/notify-message/_index.md
+++ b/content/en/docs/tutorial/notify-message/_index.md
@@ -5,51 +5,73 @@ weight: 50
 draft: false
 ---
 
-{{< blockquote warning>}}
-<strong>This page is under review.</strong><br/>
-<br/>
-Several changes have been made to the project since its first draft and 
therefore the
-tutorial needs to be updated to the publishing system.
-{{< /blockquote >}}
-
 ## Sending notifications
 
 ### Contact notification
 
-It would be great if we receive a notification when an user tries to
-contact us. For this tutorial we will pick slack to receive a message
-when it happens.
+It would be great if we receive a notification when a user tries to
+contact us. 
+
+For this tutorial we will use a free service that instantly generates 
+a unique URL through which you can receive and view webhook payloads in 
+real time.
 
-We need to:
+{{< blockquote info>}}
+You could replace this service with a workflow automation system, with
+your CRM webhook, with Slack hooks etc.
+{{< /blockquote >}}
+
+By navigating to the site https://webhook.site/ you will receive a unique 
+url, as in the image below:
 
-- have a slack workspace where we can send messages;
+![Webhook.site](/docs/tutorial/images/webhook_site.webp)
 
-- create a slack app that will be added to the workspace;
+Take note of the url under the "Your unique URL" title.
 
-- activate a webhook for the app that we can trigger from an action;
+Once we have a webhook we can proceed to create a new action called 
`notify.js` 
+(in the `packages/contact` folder).
 
-Check out the following scheme for the steps:
+The directory structure will be:
 
-![Slack Webhook](/docs/tutorial/images/slackurl.png)
+```
+contact_us_app
+├── packages
+│   └── contact
+│       ├── create-table.js
+│       ├── notify.js
+│       ├── submit.js
+│       └── write.js
+└── web
+└── index.html
+```
 
-Once we have a webhook we can use to send messages we can proceed to
-create a new action called `notify.js` (in the `packages/contact`
-folder):
+Place this content inside the `notify.js` file:
 
 ```javascript
 // notify.js
+
+//--param NOTIFICATION_URL $NOTIFICATION_URL
+
 function main(args) {
     const { name, email, phone, message } = args;
 
-    let text = `New contact request from ${name} (${email}, 
${phone}):\n${message}`;
-    console.log("Built message", text);
+    const subject = `New contact request from Apache OpenServerless`;
+    const payload = {
+        subject,
+        name,
+        email,
+        phone,
+        message,
+    };
 
-    return fetch(args.notifications, {
+    console.log("Built message", payload);
+
+    return fetch(args.NOTIFICATION_URL, {
         method: 'POST',
         headers: {
             'Content-Type': 'application/json',
         },
-        body: JSON.stringify({ text }),
+        body: JSON.stringify(payload),
     })
         .then(response => {
             if (!response.ok) {
@@ -70,27 +92,53 @@ function main(args) {
 }
 ```
 
-This action has the `args.notifications` parameter, which is the
+{{< blockquote info>}}
+In this case, we don't need to annotate the action as web. This because this 
action will
+be invoked in a sequence: so it's an internal action and is not exposed as an 
api.
+{{< /blockquote >}}
+
+This action has the `args.NOTIFICATION_URL` parameter, which is the
 webhook. It also has the usual 4 form fields parameters that receives in
 input, used to build the text of the message. The action will return the
 body of the response from the webhook.
 
-We’ve also put some logs that we can use for debugging purposes.
+The `NOTIFICATION_URL` may contains different values between a development 
environment
+and a production one.
+No problem! Apache OpenServerless deployer supports .env file.
+Create a `.env` file under the `package` directory.
 
-Let’s first set up the action:
+The directory structure now will look like:
 
-```bash
-ops action create contact/notify notify.js -p notifications <your webhook>
-ok: created action contact/notify
 ```
+contact_us_app
+├── packages
+│   ├── .env
+│   └── contact
+│       ├── create-table.js
+│       ├── notify.js
+│       ├── submit.js
+│       └── write.js
+└── web
+└── index.html
+```
+
+Inside the `.env` file put this content:
+
+```env
+NOTIFICATION_URL=<url>
+```
+
+Replace `<url>` with the url received from webhook.site.
 
-We are already setting the `notifications` parameter on action creation,
-which is the webhook. The other one is the text that the submit action
-will give in input at every invocation.
+Now deploy everything as usual, giving:
+
+```bash
+ops ide deploy
+```
 
 ### Creating Another Action Sequence
 
-We have developed an action that can send a Slack message as a
+We have developed an action that can send a message as a
 standalone action, but we designed it to take the output of the submit
 action and return it as is. Time to extend the previous sequence!
 
@@ -103,6 +151,9 @@ Let’s create the sequence, and then test it:
 
 ```bash
 ops action create contact/submit-notify --sequence 
contact/submit-write,contact/notify --web true
+```
+You should see this output:
+```shell
 ok: created action contact/submit-notify
 ```
 
@@ -114,7 +165,10 @@ If you want to get more info about this sequence, you can 
use the
 
 ```bash
 ops action get contact/submit-notify
+```
 
+You should see this output:
+```shell
 {
     "namespace": "openserverless/contact",
     "name": "submit-notify",
@@ -143,19 +197,20 @@ ops url contact/submit-notify
 <apihost>/api/v1/web/openserverless/contact/submit-notify
 ```
 
-And update the `index.html`:
+And update the action inside the file `web/index.html`:
 
 ```html
----            <form method="POST" 
action="/api/v1/web/openserverless/contact/submit-write"
-                enctype="application/x-www-form-urlencoded"> <-- old
-+++            <form method="POST" 
action="/api/v1/web/openserverless/contact/submit-notify"
-                enctype="application/x-www-form-urlencoded"> <-- new
+<form method="POST" action="/api/v1/web/opstutorial/contact/submit-notify"
+    enctype="application/x-www-form-urlencoded">
 ```
 
-Don’t forget to re-upload the web folder with `ops util upload web/`.
+Don’t forget to re-publish everything with `ops ide deploy`.
+
+Now try to fill out the form again and press send! It will execute the 
sequence 
+and you will receive the message piped from action `/contact/submit-write` to 
+`/contact/notify`.
 
-Now try to fill out the form again and press send! It will execute the
-sequence and you will receive the message from your Slack App.
+![Webhook.site result](/docs/tutorial/images/example_result.webp)
 
 The tutorial introduced you to some utilities to retrieve information
 and to the concept of `activation`. Let’s use some more commands to
diff --git a/content/en/docs/tutorial/packaging/_index.md 
b/content/en/docs/tutorial/packaging/_index.md
index f0dfe0f..3c83683 100644
--- a/content/en/docs/tutorial/packaging/_index.md
+++ b/content/en/docs/tutorial/packaging/_index.md
@@ -5,161 +5,107 @@ weight: 60
 draft: false
 ---
 
-{{< blockquote warning>}}
-<strong>This page is under review.</strong><br/>
-<br/>
-Several changes have been made to the project since its first draft and 
therefore the
-tutorial needs to be updated to the publishing system.
-{{< /blockquote >}}
-
 ## App Deployment
 
-### Packaging the App
-
-With OpenServerless you can write a manifest file (in YAML) to have an
-easy way to deploy applications.
+### Deploy
 
-In this last chapter of the tutorial we will package the code to easily
-deploy the app, both frontend and actions.
+Apache OpenServerless makes publishing a project a very simple operation. The 
project, organized in two main folders `packages` for the backend and `web` for 
the frontend, can be published immediately using the command `ops ide deploy`.
 
-### Start The Manifest File
+Once launched, the command takes care of:
+- creating the packages
+- preparing the actions with the relative dependencies
+- publishing the actions
 
-Let’s create a "manifest.yaml" file in the `packages` directory which
-will be used to describe the actions to deploy:
+Through the use of files according to the [OpenWhisk manifests.yml 
standard](https://github.com/apache/openwhisk-wskdeploy/blob/master/docs/programming_guide.md#wskdeploy-utility-by-example),
 it is also possible to publish sequences, triggers and much more at the same 
time.
 
-```yaml
-packages:
-  contact:
-    actions:
-      notify:
-        function: contacts/notify.js
-        web: true
-        inputs:
-          notifications:
-            value: $NOTIFICATIONS
-```
+{{< blockquote info>}}
+An OpenWhisk's manifest file can be useful to automate the deploy of 
sequences, triggers, rules. Action and packages are simpler to deploy using 
<code>ops ide deploy</code>
+{{< /blockquote >}}
 
-This is the basic manifest file with just the `notify` action. At the
-top level we have the standard `packages` keyword, under which we can
-define the packages we want. Until now we created all of our actions in
-the `contact` package so we add it under `packages`.
+The `ops ide deploy` command also takes care of managing the parameters 
inserted in the annotations and injecting the variables from the configuration 
or from the .env file located in the `packages` folder.
 
-Then under each package, the `actions` keyword is needed so we can add
-our action custom names with the path to the code (with `function`).
-Finally we also add `web: true` which is equivalent to `--web true` when
-creating the action manually.
 
-Finally we used the `inputs` keyword to define the parameters to inject
-in the function.
+### Packaging the App
 
-If we apply this manifest file (we will see how soon), it will be the
-same as the previous
-`ops action create contact/notify <path-to-notify.js> -p notifications 
$NOTIFICATIONS --web true`.
-You need to have the webhooks url in the `NOTIFICATIONS` environment
-variable.
+Even if not necessary, we'll package both actions and sequences.
+Let's create, inside the `packages` folder, two files:
+- 01-actions.yaml
+- 02-sequences.yaml
+
+We'll do so, because actions are required to deploy sequences.
+
+The directory structure should be like this:
+
+```shell
+contact_us_app
+├── packages
+│   ├── 01-actions.yaml
+│   ├── 02-sequences.yaml
+│   └── contact
+│       ├── create-table.js
+│       ├── notify.js
+│       ├── submit.js
+│       └── write.js
+└── web
+    └── index.html
+```
 
-### The Submit Action
+### The Action Manifest File
 
-The submit action is quite straightforward:
+Inside the `01-actions.yaml` put this content:
 
 ```yaml
 packages:
   contact:
+    inputs:
+      POSTGRES_URL:
+        type: string
+        value: $POSTGRES_URL    
+
     actions:
-      ...
       submit:
         function: contact/submit.js
         web: true
-```
-
-### The Database Actions
 
-Similarly to the `notify` and `submit` actions, let’s add to the
-manifest file the two actions for the database. We also need to pass as
-a package parameter the DB url, so we will use `inputs` key as before,
-but at the package level:
-
-```yaml
-packages:
-  contact:
-    inputs:
-      dbUri:
-        type: string
-        value: $POSTGRES_URL
-    actions:
-      ...
       write:
         function: contact/write.js
         web: true
 
+      notify:
+        function: contact/notify.js
+        web: true
+        inputs:
+          NOTIFICATION_URL:
+            type: string
+            value: $NOTIFICATION_URL
+
       create-table:
         function: contact/create-table.js
-        annotations:
-          autoexec: true
 ```
 
-Note the `create-table` action does not have the `web` set to true as it
-is not needed to be exposed to the world. Instead it just has the
-annotation for cron scheduler.
-
-### The Sequences
-
-Lastly, we created a sequence with `submit` and `notify` that we have to
-specify it in the manifest file as well.
-
-```yaml
-packages:
-  contact:
-    inputs:
-      ...
+At the top level we have the standard `packages` keyword,  under which 
+we can define the packages we want. 
+Until now we created all of our actions in the `contact` package so we 
+add it under `packages`.
 
-    actions:
-      ...
+Then under each package, the `actions` keyword is needed so we can add
+our action custom names with the path to the code (with `function`).
+Finally we also add `web: true` which is equivalent to `--web true` when
+creating the action manually.
 
-    sequences:
-      submit-write:
-        actions: submit, write
-        web: true
-      submit-notify:
-        actions: submit-write, notify
-        web: true
-```
+Finally we used the `inputs` keyword to define the parameters to inject
+in the function.
 
-We just have to add the `sequences` key at the `contact` level (next to
-`actions`) and define the sequences we want with the available actions.
+This file will be automatically deployed by the `ops ide deploy`
+command. 
 
-### Deployment
+### The Sequences Manifest File
 
-The final version of the manifest file is:
+Inside the `01-actions.yaml` put this content:
 
 ```yaml
 packages:
   contact:
-    inputs:
-      dbUri:
-        type: string
-        value: $POSTGRES_URL
-    actions:
-      notify:
-        function: contact/notify.js
-        web: true
-        inputs:
-          notifications:
-            value: $NOTIFICATIONS
-
-      submit:
-        function: contact/submit.js
-        web: true
-
-      write:
-        function: contact/write.js
-        web: true
-
-      create-table:
-        function: contact/create-table.js
-        annotations:
-          autoexec: true
-
     sequences:
       submit-write:
         actions: submit, write
@@ -169,30 +115,65 @@ packages:
         web: true
 ```
 
-`ops` comes equipped with a handy command to deploy an app:
-`ops project deploy`.
-
-It checks if there is a `packages` folder with inside a manifest file
-and deploys all the specified actions. Then it checks if there is a
-`web` folder and uploads it to the platform.
+At the top level we define the `packages` keyword and immediately after,
+ the `contact` package.
+We just have to add the `sequences` key at the `contact` level and define the 
sequences we want with the available actions.
 
-It does all what we did manually until now in one command.
+Also this file will be automatically deployed by the `ops ide deploy`
+command. 
 
-So, from the top level directory of our app, let’s run (to also set the
-input env var):
+### Test the deploy
 
-```bash
-export POSTGRES_URL=<your-postgres-url>
-export NOTIFICATIONS=<the-webhook>
+To test the deploy, let's run again the command `ops ide deploy`:
 
-ops project deploy
+```shell
+ops ide deploy
+```
 
-Packages and web directory present.
+```shell
+/Users/openserverless/.ops/tmp/deploy.pid
+PID 28177
+> Scan:
+>> Action: packages/contact/write.js
+>> Action: packages/contact/create-table.js
+>> Action: packages/contact/submit.js
+>> Action: packages/contact/notify.js
+> Deploying:
+>> Package: contact
+$ $OPS package update contact 
+ok: updated package contact
+>>> Action: packages/contact/write.js
+$ $OPS action update contact/write packages/contact/write.js --kind 
nodejs:default --param POSTGRES_URL $POSTGRES_URL
+ok: updated action contact/write
+>>> Action: packages/contact/create-table.js
+$ $OPS action update contact/create-table packages/contact/create-table.js 
--kind nodejs:default --param POSTGRES_URL $POSTGRES_URL
+ok: updated action contact/create-table
+>>> Action: packages/contact/submit.js
+$ $OPS action update contact/submit packages/contact/submit.js --web true 
--kind nodejs:default
+ok: updated action contact/submit
+>>> Action: packages/contact/notify.js
+$ $OPS action update contact/notify packages/contact/notify.js --param 
NOTIFICATION_URL $NOTIFICATION_URL
+ok: updated action contact/notify
+Found packages .env file. Reading it
+>>> Manifest: packages/01-actions.yaml
+$ $OPS -wsk project deploy --manifest packages/01-actions.yaml
+Success: Deployment completed successfully.
+>>> Manifest: packages/02-sequences.yaml
+$ $OPS -wsk project deploy --manifest packages/02-sequences.yaml
 Success: Deployment completed successfully.
-Found web directory. Uploading..
+build process exited with code 0
+UPLOAD ASSETS FROM web
+==================| UPLOAD RESULTS |==================
+| FILES      : 1
+| COMPLETED  : 1
+| ERRORS     : 0
+| SKIPPED    : 0
+| EXEC. TIME : 35.72 ms
+======================================================
+URL: http://opstutorial.localhost:80
 ```
 
-With just this command you deployed all the actions (and sequences) and
-uploaded the frontend (from the web folder).
+As you can see, after deploying the actions, the deployer will find
+the manifest files and deploy them in [lexicographic 
order](https://en.wikipedia.org/wiki/Lexicographic_order).
 
 ---
diff --git a/content/en/docs/tutorial/use-database/_index.md 
b/content/en/docs/tutorial/use-database/_index.md
index 6854861..95498dc 100644
--- a/content/en/docs/tutorial/use-database/_index.md
+++ b/content/en/docs/tutorial/use-database/_index.md
@@ -5,13 +5,6 @@ weight: 40
 draft: false
 ---
 
-{{< blockquote warning>}}
-<strong>This page is under review.</strong><br/>
-<br/>
-Several changes have been made to the project since its first draft and 
therefore the
-tutorial needs to be updated to the publishing system.
-{{< /blockquote >}}
-
 ## Use database
 
 ### Storing the Message in the Database
@@ -19,15 +12,36 @@ tutorial needs to be updated to the publishing system.
 We are ready to use the database that we enabled at the beginning of the
 tutorial.
 
-Since we are using a relational database, we need to create a table to
-store the contact data. We can do that by creating a new action called
-`create-table.js` in the `packages/contact` folder:
+Usually, when working with relational databases, the best choice is to use a 
+[schema migration system](https://en.wikipedia.org/wiki/Schema_migration). 
+In our case, to keep things simple, we will emulate a migration using an 
action.
+
+Now, we need to create a table to store the contact data: start by creating a 
+new action called `create-table.js` in the `packages/contact` folder.
+
+The directory structure have to be like this:
+
+```shell
+contact_us_app
+├── packages
+│   └── contact
+│       ├── create-table.js
+│       └── submit.js
+└── web
+    └── index.html
+```
+
+Put this content inside the `create-table.js` file:
 
 ```javascript
+//--kind nodejs:default
+//--param POSTGRES_URL $POSTGRES_URL
+
 const { Client } = require('pg')
 
 async function main(args) {
-    const client = new Client({ connectionString: args.dbUri });
+    console.log('Starting create-table action')
+    const client = new Client({ connectionString: args.POSTGRES_URL });
 
     const createSchema = `CREATE SCHEMA IF NOT EXISTS demo;`
 
@@ -40,41 +54,87 @@ async function main(args) {
         message varchar(300)
     );
     `
-    // Connect to database server
-    await client.connect();
-    console.log('Connected to database');
 
     try {
+        console.log(`Connecting to ${args.POSTGRES_URL}`);
+        await client.connect();
+        console.log('Connected to database');
         await client.query(createSchema);
+        console.log('Schema demo created');
         await client.query(createTable);
         console.log('Contact table created');
+        return { result: 'OK' };
     } catch (e) {
-        console.log(e);
-        throw e;
+        if (e instanceof AggregateError) {
+            for (const err of e.errors) {
+                console.error('[ERROR] - ', err.message || err);
+            }
+        } else if (e instanceof Error) {
+            console.error('[ERROR]  - ', e.message);
+        } else {
+            console.error('[ERROR] - ', e);
+        }
+        return { result: 'ERROR' };
     } finally {
-        client.end();
+        console.log('Closing connection');
+        if (client) {
+            await client.end();
+        }
     }
 }
 ```
 
-We just need to run this once, therefore it doesn’t need to be a web
-action. Here we can take advantage of the `cron` service we enabled!
-There are also a couple of console logs that we can check out.
+{{< blockquote info>}}
+You may have noticed here again the comments on top of the file. As said 
before,
+these comments are used by `ops ide` to automatically handle the publishing of 
files
+by calling `ops package` or `ops action` as needed.
+In particular:
+<ul>
+<li><code>--kind nodejs:default</code> will ask OpenServerless to run this 
code on the nodejs default runtime.</li>
+<li>the <code>--param POSTGRES_URL $POSTGRES_URL</code> will automatically 
fill in the parameters required by the action,
+taking it's value from <code>ops</code>'s configuration file.</li>
+</ul>
+{{< /blockquote >}}
+
 
-With the cron scheduler you can annotate an action with 2 kinds of
-labels. One to make OpenServerless periodically invoke the action, the
-other to automatically execute an action once, on creation.
+The action is [idempotent](https://en.wikipedia.org/wiki/Idempotence), so you
+may call the action multiple times, but the schema and the table is created 
only
+once.
 
-Let’s create the action with the latter, which means annotating the
-action with `autoexec true`:
+You can deploy this action using `ops ide deploy` command.
 
 ```bash
-ops action create contact/create-table create-table.js -a autoexec true
-ok: created action contact/create-table
+ops ide deploy
 ```
 
-With `-a` you can add "annotations" to an action. OpenServerless will
-invoke this action as soon as possible, so we can go on.
+The output will be like:
+```shell
+/home/openserverless/.ops/tmp/deploy.pid
+PID 52906
+> Scan:
+>> Action: packages/contact/create-table.js
+>> Action: packages/contact/submit.js
+> Deploying:
+>> Package: contact
+$ $OPS package update contact 
+ok: updated package contact
+>>> Action: packages/contact/create-table.js
+$ $OPS action update contact/create-table packages/contact/create-table.js 
--kind nodejs:default --param POSTGRES_URL $POSTGRES_URL
+ok: updated action contact/create-table
+>>> Action: packages/contact/submit.js
+$ $OPS action update contact/submit packages/contact/submit.js --web true 
--kind nodejs:default
+ok: updated action contact/submit
+build process exited with code 0
+UPLOAD ASSETS FROM web
+==================| UPLOAD RESULTS |==================
+| FILES      : 1
+| COMPLETED  : 1
+| ERRORS     : 0
+| SKIPPED    : 0
+| EXEC. TIME : 28.37 ms
+======================================================
+URL: http://opstutorial.localhost:80
+```
 
 In OpenServerless an action invocation is called an `activation`. You
 can keep track, retrieve information and check logs from an action with
@@ -89,42 +149,55 @@ time you run the command the list might be empty. Just run 
it again and
 you will see the latest invocations (probably some `hello` actions from
 the deployment).
 
-If we want to make sure `create-table` was invoked, we can do it with
-this command. The cron scheduler can take up to 1 minute to run an
-`autoexec` action, so let’s wait a bit and run `ops activation list`
-again.
+If we want to invoke the `create-table` action, we can do it with
+this command. 
 
 ```bash
-ops activation list
-
-Datetime            Activation ID                    Kind      Start Duration  
 Status  Entity
-2023-10-02 09:52:01 1f02d3ef5c32493682d3ef5c32b936da nodejs:18 cold  312ms     
 success openserverless/create-table:0.0.1
-..
+ops action invoke contact/create-table
+```
+The output will be like:
+```
+ok: invoked /_/contact/create-table with id e67a6c6f5a9c4667ba6c6f5a9c46675b
 ```
 
-Or we could run `ops activation poll` to listen for new logs.
+The activation will return an id: in our case the id is 
`e67a6c6f5a9c4667ba6c6f5a9c46675b`.
+You can retrieve the activation log with the command `ops activation logs 
<id>` or `ops activation logs --last` to retrieve
+the last activation log.
 
 ```bash
-ops activation poll
-
-Enter Ctrl-c to exit.
-Polling for activation logs
+ops activation logs e67a6c6f5a9c4667ba6c6f5a9c46675b
+```
+```shell
+2025-03-17T23:28:03.390748125Z stdout: Starting create-table action
+2025-03-17T23:28:03.391745125Z stdout: Connecting to 
postgresql://opstutorial:[email protected]:5432/opstutorial
+2025-03-17T23:28:03.405132167Z stdout: Connected to database
+2025-03-17T23:28:03.406006792Z stdout: Schema demo created
+2025-03-17T23:28:03.406601042Z stdout: Contact table created
+2025-03-17T23:28:03.406604209Z stdout: Closing connection
+..
 ```
 
-When the logs from the `create-table` action appear, we can stop the
-command with `Ctrl-c`.
-
-Each activation has an `Activation ID` which can be used with other
-`ops activation` subcommands or with the `ops logs` command.
+We could run `ops activation poll` or `ops ide poll` to listen for new logs.
 
-We can also check out the logs with either `ops logs <activation-id>` or
-`ops logs --last` to quickly grab the last activation’s logs:
+To check that the table is really there, and inspect it's schema you can 
+use the `ops devel psql describe` tool:
 
 ```bash
-ops logs --last
+ops devel psql describe "demo.contacts" --format=table
+```
 
-2023-10-15T14:41:01.230674546Z stdout: Connected to database
-2023-10-15T14:41:01.238457338Z stdout: Contact table created
+You should see:
+
+```shell
+┌───┬───────────────┬──────────────┬─────────────┬───────────────────┬─────────────┐
+│   │ table_catalog │ table_schema │ column_name │ data_type         │ 
is_nullable │
+├───┼───────────────┼──────────────┼─────────────┼───────────────────┼─────────────┤
+│ 0 │ opstutorial   │ demo         │ id          │ integer           │ NO      
    │
+│ 1 │ opstutorial   │ demo         │ name        │ character varying │ YES     
    │
+│ 2 │ opstutorial   │ demo         │ email       │ character varying │ YES     
    │
+│ 3 │ opstutorial   │ demo         │ phone       │ character varying │ YES     
    │
+│ 4 │ opstutorial   │ demo         │ message     │ character varying │ YES     
    │
+└───┴───────────────┴──────────────┴─────────────┴───────────────────┴─────────────┘
 ```
 
 ### The Action to Store the Data
@@ -136,15 +209,20 @@ Let’s create a new file called `write.js` in the 
`packages/contact`
 folder:
 
 ```javascript
-const { Client } = require('pg')
+// write.js
+
+//--kind nodejs:default
+//--param POSTGRES_URL $POSTGRES_URL
+
+const {Client} = require('pg')
 
 async function main(args) {
-    const client = new Client({ connectionString: args.dbUri });
+    const client = new Client({connectionString: args.POSTGRES_URL});
 
     // Connect to database server
     await client.connect();
 
-    const { name, email, phone, message } = args;
+    const {name, email, phone, message} = args;
 
     try {
         let res = await client.query(
@@ -165,7 +243,7 @@ async function main(args) {
         email,
         phone,
         message
-        };
+    };
 }
 ```
 
@@ -176,8 +254,39 @@ data into the table by passing the values as parameters. 
There is also a
 Let’s deploy it:
 
 ```bash
-ops action create contact/write write.js
-ok: created action contact/write
+ops ide deploy
+```
+```
+/home/openserverless/.ops/tmp/deploy.pid
+/Users/bruno/.ops/tmp/deploy.pid
+PID 57700
+> Scan:
+>> Action: packages/contact/write.js
+>> Action: packages/contact/create-table.js
+>> Action: packages/contact/submit.js
+> Deploying:
+>> Package: contact
+$ $OPS package update contact 
+ok: updated package contact
+>>> Action: packages/contact/write.js
+$ $OPS action update contact/write packages/contact/write.js --kind 
nodejs:default --param POSTGRES_URL $POSTGRES_URL
+ok: updated action contact/write
+>>> Action: packages/contact/create-table.js
+$ $OPS action update contact/create-table packages/contact/create-table.js 
--kind nodejs:default --param POSTGRES_URL $POSTGRES_URL
+ok: updated action contact/create-table
+>>> Action: packages/contact/submit.js
+$ $OPS action update contact/submit packages/contact/submit.js --web true 
--kind nodejs:default
+ok: updated action contact/submit
+build process exited with code 0
+UPLOAD ASSETS FROM web
+==================| UPLOAD RESULTS |==================
+| FILES      : 1
+| COMPLETED  : 1
+| ERRORS     : 0
+| SKIPPED    : 0
+| EXEC. TIME : 28.92 ms
+======================================================
+URL: http://opstutorial.localhost:80
 ```
 
 ### Finalizing the Submit
@@ -189,6 +298,8 @@ fields to store them. Let’s put them together into a 
`sequence`:
 
 ```bash
 ops action create contact/submit-write  --sequence 
contact/submit,contact/write --web true
+```
+```shell
 ok: created action contact/submit-write
 ```
 
@@ -210,12 +321,14 @@ Let’s check out again the action list:
 
 ```bash
 ops action list
+```
 
+```shell
 actions
-/openserverless/contact/submit-write                  private sequence
-/openserverless/contact/write                         private nodejs:18
-/openserverless/contact/create-table                  private nodejs:18
-/openserverless/contact/submit                        private nodejs:18
+/opstutorial/contact/submit-write                                      private 
sequence
+/opstutorial/contact/submit                                            private 
nodejs:21
+/opstutorial/contact/create-table                                      private 
nodejs:21
+/opstutorial/contact/write                                             private 
nodejs:21
 ```
 
 You probably have something similar. Note the submit-write is managed as
@@ -230,32 +343,45 @@ First let’s get the URL of the `submit-write` action:
 
 ```bash
 ops url contact/submit-write
+```
+```shell
 <apihost>/api/v1/web/openserverless/contact/submit-write
 ```
 
-Then we can update the `index.html` file:
+Then we can update the `index.html` file. Change the form submit 
+action with the url from the previous command:
 
 ```html
----     <form method="POST" action="/api/v1/web/openserverless/contact/submit"
-              enctype="application/x-www-form-urlencoded"> <-- old
-+++     <form method="POST" 
action="/api/v1/web/openserverless/contact/submit-write"
-              enctype="application/x-www-form-urlencoded"> <-- new
+<form method="POST" action="/api/v1/web/opstutorial/contact/submit-write"
+          enctype="application/x-www-form-urlencoded">
 ```
 
 We just need to add `-write` to the action name.
 
+Now give a `ops ide deploy` to publish all the modifications.
+
 Try again to fill the contact form (with correct data) and submit it.
 This time the data will be stored in the database.
 
-If you want to retrive info from you database, ops provides several
+### View data from db
+
+If you want to retrieve data from your database, `ops` provides several
 utilities under the `ops devel` command. They are useful to interact
 with the integrated services, such as the database we are using.
 
-For instance, let’s run:
+For instance, to interact with PostgreSQL database, let’s run:
 
 ```bash
-ops devel psql sql "SELECT * FROM demo.CONTACTS"
+echo "SELECT * FROM demo.CONTACTS" | ops devel psql sql --format=table
+```
 
-[{'id': 1, 'name': 'OpenServerless', 'email': '[email protected]', 'phone': 
'5551233210', 'message': 'This is awesome!'}]
+You should see an output like this:
+```shell
+┌───┬────┬────────────────┬─────────────────────────┬─────────────┬──────────────────────────────┐
+│   │ id │ name           │ email                   │ phone       │ message    
                  │
+├───┼────┼────────────────┼─────────────────────────┼─────────────┼──────────────────────────────┤
+│ 0 │ 1  │ OpenServerless │ [email protected] │ 39123123123 │ Hello 
Apache OpenServerless! │
+└───┴────┴────────────────┴─────────────────────────┴─────────────┴──────────────────────────────┘
 ```
+
 ---

Reply via email to