Skip to content

Commit

Permalink
Daily, Oct 9 (#9)
Browse files Browse the repository at this point in the history
Co-authored-by: manojdbos <[email protected]>
Co-authored-by: Peter Kraft <[email protected]>
  • Loading branch information
3 people authored Oct 10, 2023
1 parent 9668a04 commit e34b046
Show file tree
Hide file tree
Showing 18 changed files with 422 additions and 160 deletions.
14 changes: 14 additions & 0 deletions docs/api-reference/cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
sidebar_position: 2
title: Operon Command line interface (CLI)
---

The operon runtime with the application is started using the command line

```
npx operon start -p <port> -l <loglevel>
```

-p or --port: The http port on which the runtime with listen for http requests

-l or --loglevel: 'info','warn','error','debug'
89 changes: 89 additions & 0 deletions docs/api-reference/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
sidebar_position: 1
title: Operon Configuration
---

Configuration is set of properties and values provided by the application develop to influence the behaviour of the operon runtime as well as the application.

## configuration file

Standard yaml file operon-config.yaml.
The file should be in the root directory of the project.
A sample operon-config.yaml is shown below.

A value in the form ${SOMEVALUE} implies that the runtime will get the value from the environment variable SOMEVALUE.

```
database:
hostname: 'localhost'
port: 5432
username: 'postgres'
password: ${PGPASSWORD}
user_database: 'hello'
system_database: 'hello_systemdb'
connectionTimeoutMillis: 3000
user_dbclient: 'knex'
telemetryExporters:
- 'ConsoleExporter'
```

## Operon configuration

### database
The database section contains configuration parameters needed by operon to connect to user and system databases.

#### hostname
The hostname or ip address of the machine hosting the database.

#### port
The port that the database is listening on.

#### username
The username to use to connect to the database.

#### password
The password to use to connect to the database. It is strongly recommended that you do not put password in cleartext here. Instead use indirection like ${PGPASSWORD} so that the runtime can get the value from the environment variable PGPASSWORD.

#### user_database
This is the database that the application code reads and writes from.

#### system_database
This is the database that the operon runtime reads and writes from.

#### user_dbclient
This is the sql builder or ORM used to communicate with the database. Supported values are knex, prisma or typeorm. Default is knex

#### connectionTimeoutMillis
The timeout in milliseconds after which the database driver will timeout from connecting to the database.

#### observability_database
The name of the database to which the observability database is written to.

#### ssl_ca
The path to ssl certificate to connect to the database.

### localRuntimeConfig
```
localRuntimeConfig
port: 6000
```
This section has properties needed to configure the runtime.

#### port
This is the port on which the embedded http server listens. Default is 3000.


### telemetryExporters

List of exporter to whom telemetry logs are to be sent. Supported values are 'ConsoleExporter', 'JaegerExporter', 'PGExporter'.


## Application configuration

The application section can have any user defined properties and values used by the application.

```
application:
PAYMENTS_SERVICE: 'http://stripe.com/payment'
```
2 changes: 1 addition & 1 deletion docs/api-reference/contexts.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 1
sidebar_position: 4
title: Operon Contexts
---

Expand Down
2 changes: 1 addition & 1 deletion docs/api-reference/decorators.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 3
title: Decorator Reference
description: Usage of decorators in Operon, with exhaustive list
---
Expand Down
2 changes: 1 addition & 1 deletion docs/api-reference/workflow-handles.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 5
title: Workflow handles
description: API documentation for Operon Workflow Handles
---
Expand Down
9 changes: 9 additions & 0 deletions docs/explanations/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"label": "Concepts and Explanations",
"position": 4,
"link": {
"type": "generated-index",
"description": "These explanations help you learn core Operon concepts."
}
}

116 changes: 116 additions & 0 deletions docs/explanations/application-structure-explanation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
sidebar_position: 1
title: Application Structure
description: Learn about the structure of an Operon application
---

In this guide, you'll learn about the structure of an Operon application.

### Directory Structure

When you initialize an Operon project with `npx operon init`, it has the following structure:

```bash
operon-hello-app/
├── README.md
├── knexfile.ts
├── migrations/
├── node_modules/
├── operon-config.yaml
├── package-lock.json
├── package.json
├── src/
│ └── userFunctions.ts
├── start_postgres_docker.sh
└── tsconfig.json
```

The two most important files in an Operon project are `operon-config.yaml` and `src/userFunctions.ts`.

`operon-config.yaml` defines the configuration of an Operon project, including database connection information, ORM configuration, and global logging configuration.
All options are documented in our [configuration reference](..).

`src/userFunctions.ts` is where Operon looks for your code.
At startup, the Operon runtime automatically loads all classes exported from this file, serving their endpoints and registering their transactions and workflows.
If you're writing a small application, you can write all your code directly in this file.
In a larger application, you can write your code wherever you want, but should use `src/userFunctions.ts` as an index file, exporting code written elsewhere.

As for the rest of the directory:

- `knexfile.ts` is a configuration file for [Knex](https://knexjs.org), which we use as a query builder and migration tool.
- `migrations` is initialized with a Knex database migration used in the [quickstart guide](../getting-started/quickstart). If you're using Knex for schema management, you can create your own migrations here.
- `node_modules`, `package-lock.json`, `package.json`, and `tsconfig.json` are needed by all Node/Typescript projects.
- `start_postgres_docker.sh` is a convenience script that initializes a Docker-hosted Postgres database for use in the [quickstart](../getting-started/quickstart). You can modify this script if you want to use Docker-hosted Postgres for local development.

### Code Structure

Here's the initial source code generated by `npx operon init` (in `src/userFunctions.ts`):

```javascript
import { TransactionContext, OperonTransaction, GetApi, HandlerContext } from '@dbos-inc/operon'
import { Knex } from 'knex';

type KnexTransactionContext = TransactionContext<Knex>;

interface operon_hello {
name: string;
greet_count: number;
}

export class Hello {

@OperonTransaction()
static async helloTransaction(txnCtxt: KnexTransactionContext, name: string) {
// Look up greet_count.
let greet_count = await txnCtxt.client<operon_hello>("operon_hello")
.select("greet_count")
.where({ name: name })
.first()
.then(row => row?.greet_count);
if (greet_count) {
// If greet_count is set, increment it.
greet_count++;
await txnCtxt.client<operon_hello>("operon_hello")
.where({ name: name })
.increment('greet_count', 1);
} else {
// If greet_count is not set, set it to 1.
greet_count = 1;
await txnCtxt.client<operon_hello>("operon_hello")
.insert({ name: name, greet_count: 1 })
}
return `Hello, ${name}! You have been greeted ${greet_count} times.\n`;
}

@GetApi('/greeting/:name')
static async helloHandler(handlerCtxt: HandlerContext, name: string) {
return handlerCtxt.invoke(Hello).helloTransaction(name);
}
}

```
An Operon application like this one is made up of classes encapsulating _functions_, written as decorated static class methods.
There are four basic types of functions.
This example contains two of them:
- [**Transactions**](../tutorials/transaction-tutorial), like `helloTransaction` perform database operations.
- [**Handlers**](../tutorials/http-serving-tutorial), like `helloHandler`, serve HTTP requests.
There are two more:
- [**Communicators**](../tutorials/communicator-tutorial) manage communication with external services and APIs.
- [**Workflows**](../tutorials/workflow-tutorial) reliably orchestrate other functions.
A function needs to follow a few rules:
- It must be a static class method. For Operon to find it, that class must be exported from `src/userFunctions.ts`.
- It must have a decorator telling Operon what kind of function it is: [`@OperonTransaction`](../api-reference/decorators#operontransaction) for transactions, [`@OperonCommunicator`](../api-reference/decorators#operoncommunicator) for communicators, [`@OperonWorkflow`](../api-reference/decorators#operonworkflow) for workflows, or [`GetApi`](../api-reference/decorators#getapi) or [`PostApi`](../api-reference/decorators#postapi) for handlers.
- Its first argument must be the appropriate kind of [Operon context](../api-reference/contexts). Contexts provide functions with useful methods, such as access to a database client for transactions.
- Its input and return types must be serializable to JSON.
Once you've written your functions, there are two basic ways to call them:
1. Any function (not just handlers) can be called from HTTP if it's annotated with the [`GetApi`](../api-reference/decorators#getapi) or [`PostApi`](../api-reference/decorators#postapi) decorators. See our [HTTP serving tutorial](../tutorials/http-serving-tutorial.md) for details.
2. Handlers and workflows can invoke other functions via their contexts' [invoke](..) method.
To learn more about each individual type of function and what it can do, see our [tutorials](../category/tutorials/).
2 changes: 1 addition & 1 deletion docs/getting-started/coreconcepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ sidebar_position: 4

# Core Concepts

Operon is a simple easy to use serverless framework for developing transactional application.
Operon is a simple easy to use serverless framework for developing transactional applications.
Operon applications are made up of transactions and workflows.
Operon workflows group together a set of transactions and provide them with Once-and-Only-Once-Execution guarantees.
This means Operon workflows are guaranteed to run to completion and their composing transactions will be executed only once.
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorials/_category_.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"position": 2,
"link": {
"type": "generated-index",
"description": "These tutorials help you learn core Operon concepts."
"description": "These tutorials help you learn how to build powerful Operon applications."
}
}
2 changes: 1 addition & 1 deletion docs/tutorials/client-workflow-interactions-tutorial.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 8
sidebar_position: 6
title: Workflow events
description: Learn how to send events from workflows and receive them from handlers
---
Expand Down
30 changes: 30 additions & 0 deletions docs/tutorials/communicator-tutorial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
sidebar_position: 3
title: Communicators
description: Learn how to communicate with external APIs and services
---

In this guide, you'll learn how to communicate with external APIs and services from an Operon application.

We recommend that all communication with external services be done in _communicator_ functions.
For example, you can use communicators to serve a file from [AWS S3](https://aws.amazon.com/s3/), call an external API like [Stripe](https://stripe.com/) or access a non-Postgres data store like [Elasticsearch](https://www.elastic.co/elasticsearch/).
Encapsulating these calls in communicators is especially important if you're using [workflows](..) as it lets the workflow know to make their results persistent through server failures.

Communicators must be annotated with the [`@OperonCommunicator`](../api-reference/decorators#operoncommunicator) decorator and must have a [`CommunicatorContext`](..) as their first argument.
Like for other Operon functions, inputs and outputs must be serializable to JSON.
Here's a simple example using [Axios](https://axios-http.com/docs/intro) to call the [Postman Echo API](https://learning.postman.com/docs/developer/echo-api/):


```javascript
@OperonCommunicator()
static async postmanEcho(_ctxt: CommunicatorContext) {
const resp = await axios.get("https://postman-echo.com/get");
return resp.data;
}
```

### Retries

By default, Operon automatically retries any communicator function that throws an exception.
It retries communicator functions a set number of times with exponential backoff, throwing an [`OperonError`](..) if the maximum number of retries is exceed.
Retries are fully configurable through arguments to the [`@OperonCommunicator`](../api-reference/decorators#operoncommunicator) decorator.
28 changes: 0 additions & 28 deletions docs/tutorials/handlers-tutorial.md

This file was deleted.

Loading

0 comments on commit e34b046

Please sign in to comment.