# BaseStep

BaseStep encapsulates defining a step, binding it to an express app and routing
requests to it's `handler` function.

All steps in One per Page extend from BaseStep.

## Usage

BaseStep expects you to implement a `handler` function which will handle the
request.

__For example__

```javascript
const { BaseStep } = require('@hmcts/one-per-page/steps');

class Always404 extends BaseStep {
  handler(req, res) {
    res.sendStatus(404);
  }
}
```

All requests to `/always-404` will respond with the 404 Not Found status.

-------------------------------------------------------------------------------

## Behaviour

### Generated step URL

BaseStep generates a URL for a step based on it's class name. The class name is
slugified to make it url-safe.

| Class declaration                        | default url              |
|------------------------------------------|--------------------------|
|`class MyPage extends BaseStep`           | /my-page                 |
|`class Name extends BaseStep`             | /name                    |
|`class InfoAboutService extends BaseStep` | /info-about-service      |

You can override the default by overriding [`path`](#static-get-path).

### Await long running tasks

BaseStep will block on handling any request until all promises registered to it
are complete or a timeout has been reached.

This allows steps that extend BaseStep to do long running tasks like
[looking up content] or [resolving templates] before handling the request.

This functionality is provided via [`waitFor(promise)`](#waitFor-promise) and
[`ready()`](#ready).

In your Step you can register a long running task using `waitFor()`:

```javascript
class MyStep extends BaseStep {
  constructor(...args) {
    super(...args);
    this.waitFor(someLongRunningProcess);
  }
}
```

### Custom step timeouts

Registering a promise may cause a step to take longer than the standard timeout.

You can set a timeout to wait for a specific step by overriding
[`get timeoutDelay()`](#get-timeoutdelay):

```js
class MyStep extends BaseStep {
  constructor(...args) {
    super(...args);
    this.waitFor(veryLongRunningProcess);
  }
  get timeoutDelay() {
    return 500;
  }
}
```

You can set a global timeout delay when creating your journey:

```js
journey(app, {
  steps: [ ... ],
  timeoutDelay: 500
});
```

-------------------------------------------------------------------------------

## API

### `req`

The express request of the currently handled request. It is bound to the step
so that you can access it from any function inside the step.

### `res`

The express response of the currently handled request. It is bound to the step
so that you can access it from any function inside the step.

### `journey`

The [Journey] that created this step. It is bound to the step so you can access
it from inside the step:

```javascript
class MyQuestion extends Question {
  next() {
    return goTo(this.journey.steps.StepTwo);
  }
}
```

### `waitFor(promise)`

Register a promise to be waited for during construction. These are stored in
`this.promises`.

#### Promise or Middleware?

Certain tasks can be performed in middleware which are also async. There is a
subtle difference:

- **Middleware** are run during handling a request, once for each request.
- **Promises** are run during construction of a step, once only.

Check your Answers and other steps that make use of
[Journeys `collectSteps`][Journey] middleware will not execute middleware as the
steps created by `collectSteps` will not handle the request.

Anything you need to execute for each request and when being collected for
Check your Answers should be implemented as a Promise.

### `get timeoutDelay()`

- __Returns__ a number, the milliseconds to wait for promises to resolve

Used in [`ready()`](#ready) to wait until promises resolve or to timeout.

Will return `50` if no global `timeoutDelay` is set. You can set a global
timeout delay when creating your journey:

```js
journey(app, {
  steps: [ ... ],
  timeoutDelay: 500
});
```

Overriding this function allows you to set a step specific timeout:

```js
class MyStep extends BaseStep {
  get timeoutDelay() {
    return 500;
  }
}
```


### `ready()`

- __Returns__ a promise that resolves to the step.

The returned promise will timeout if the promises registered by `waitFor()`
have not completed. If you are creating a step by hand, rather than using the
[Journey `instance()`][Journey] function to create a step, then you should
chain logic to `ready()`:

```javascript
new MyStep(req, res, journey)
  .ready()
  .then(myStep => /* do something */)
```

### `static get path()`

- __Returns__ the steps name slugified
- __Exposed__ in templates as `{{ path }}`

The URL to route requests to this step. By default it is 
[generated by BaseStep](#generated-step-url).

To override the default you can override `path` and return your preferred string:

```javascript
class PetitionerDetails extends Page {
  static get path() {
    return '/petitioner/details';
  }
}
```

### `get middleware()`

- __Returns__ an array of express middleware functions (`(req, res, next) => {}`).

A list of express style middleware functions to be run before the handler. By
default this is defined with a list of middleware required for a Step to function
correctly.

To add new middleware you can override `middleware`:

```javascript
const first = (req, res, next) => { ... };
const last = (req, res, next) => { ... };

class MyPage extends Page {
  get middleware() {
    return [first, ...super.middleware, last];
  }
}
```
> Be sure to include the contents of `super.middleware` in your implementation.


[looking up content]: /docs/steps/Page#content-i18n-resolution
[resolving templates]: /docs/steps/Page##template-resolution
[Journey]: /docs/flow/Journey
