npm package: @locustjs/services
Description: This library provides a `ServiceResponse` model, helping services implementing `Result Pattern` that is usable both in ront-end and back-end.
Author: Iron Code
Latest version: 1.1.1

# @locustjs/services
This library provides a `ServiceResponse` model, helping services implementing `Result Pattern` (front-end/back-end).
It supports both camel-case and pascal-case naming and provides status customization.

# Install
```
npm i @locustjs/services
```

# Import

CommonJs
```javascript
var { ServiceResponse } = require('@locustjs/services');
```

ES6
```javascript
import { ServiceResponse } from '@locustjs/services'
```

# ServiceResponse
## Properties

| Property       | Type                       | Nullable | Description |
|----------------|----------------------------|----------|-------------|
| `success`        | `bool`                   | no       | whether or not the operation was succesful |
| `status`         | `string`                 | yes/no   | status of the operation |
| `message`        | `string`                 | yes      | a message describing what happened/done/performed |
| `messageKey`     | `string`                 | yes      | a key that is used in translating current `ServiceResponse` and setting its `message` |
| `messageArgs`    | `object`                 | yes      | an object that is used in translating current `ServiceResponse` |
| `subject`        | `string`                 | yes      | a subject for the operation performed, helping receivers what current `ServiceResponse` is used for. |
| `date`           | `datetime`               | no       | date/time of the operation (issuance of current `ServiceResponse` instance) |
| `data`           | `any`                    | yes      | data returned by the service |
| `exception`      | `object`                 | yes      | an error/exception object that may be thrown during the operation |
| `innerResponses` | `Array<ServiceResponse>` | yes      | an array of inner `ServiceResponse`s produced by inner services |
| `info`           | `string`                 | yes      | an optional extra data in the form of `string` returned by the service |
| `bag`            | `object`                 | yes      | an optional extra data in the form of `object` returned by the service |
| `logs`           | `Array<Log>`             | yes      | an array of debugging `Log` messages generated by the service who issued current `ServiceResponse` |

> `messageKey` and `messageArgs` are used in translating `ServiceResposne` and providing locale-based `message`.
> `logs` is used in debugging mode (see [@locustjs/servicemodel](https://github.com/ironcodev/locustjs-servicemodel)).

## Methods
| Method          | Description |
|-----------------|-|
| `copy(obj)`	  | copies given `obj` to current instance (normally used to copy one `ServiceResponse` to another) |
|`toJson(spacer)`| serializes current instance to json (ignoring empty fields) |
|`setStatus(status, message, ex)`| sets `status`, `message` and `exception` properties with given arguments |
| `is(status)`| checks whether `status` property equals `status` argument. |
| `setData(data)`| sets `data` to the given `data` argument and returns current instance. |
| `setException(ex)`| sets `exception` to the given `ex` argument and returns current instance. |
| `setInfo(info)`| sets `info` to the given `info` argument and returns current instance. |
| `setSubject(subject)`| sets `subject` to the given `subject` argument and returns current instance. |
| `setBag(bag)`| sets `bag` to the given `bag` argument and returns current instance. |
| `setMessageKey(key)`| sets `messageKey` to the given `key` argument and returns current instance. |
| `setArgs(args)`| sets `messageArgs` to the given `args` object and returns current instance. |
| `addArg(args)`| merges and adds `args` object to `messageArgs` and returns current instance. |
| `addArg(key, value)`| adds a `key`/`value` pair to `messageArgs` object and returns current instance. |
| `addResponse(res, subject)`| adds `res` object to `innerResponse` array and returns current instance. `res` must be a `ServiceResponse` object. If `subject` argument is specified, the `subject` prop in `res` object will be set to that argument. |

Example:
```javascript
const sr = new ServiceResponse();

sr.setStatus('failed')

console.log(sr);    // { success: true, status: "failed", date: "2024-12-13 13:45:07" }
console.log(sr.is('failed')); // true
```

### Status Methods
In order to ease working with `ServiceResponse` and making it more friendly, a list of instance methods are provided that set the `status` property with specific values. For every method, there is a corresponding `is` method that testifies whether status of the current `ServiceResponse` instance matches the method name.

| Method                  | Status              | Is Method                |
|-------------------------|---------------------|--------------------------|
| `succeeded()`   	      | succeeded		    | `isSucceeded()`           |
| `failed()`              | failed              | `isFailed()`              |
| `faulted()`             | faulted             | `isFaulted()`             |
| `defected()`            | defected            | `isDefected()`            |
| `flawed()`              | flawed              | `isFlawed()`              |
| `errored()`             | errored             | `isErrored()`             |
| `stopped()`             | stopped             | `isStopped()`             |
| `abandoned()`           | abandoned           | `isAbandoned()`           |
| `aborted()`             | aborted             | `isAborted()`             |
| `rejected()`            | rejected            | `isRejected()`            |
| `refused()`             | refused             | `isRefused()`             |
| `quited()`              | quited              | `isQuited()`              |
| `exited()`              | exited              | `isExited()`              |
| `halted()`              | halted              | `isHalted()`              |
| `blocked()`             | blocked             | `isBlocked()`             |
| `accessDenied()`        | accessDenied        | `isAccessDenied()`        |
| `notAuthenticated()`    | notAuthenticated    | `isNotAuthenticated()`    |
| `notAuthorized()`       | notAuthorized       | `isNotAuthorized()`       |
| `forbidden()`           | forbidden           | `isForbidden()`           |
| `notAllowed()`          | notAllowed          | `isNotAllowed()`          |
| `notPermitted()`        | notPermitted        | `isNotPermitted()`        |
| `notPossible()`         | notPossible         | `isNotPossible()`         |
| `notFound()`            | notFound            | `isNotFound()`            |
| `alreadyExists()`       | alreadyExists       | `isAlreadyExists()`       |
| `notValid()`            | notValid            | `isNotValid()`            |
| `notProvided()`         | notProvided         | `isNotProvided()`         |
| `noData()`              | noData              | `isNoData()`              |
| `invalidData()`         | invalidData         | `isInvalidData()`         |
| `incorrectData()`       | incorrectData       | `isIncorrectData()`       |
| `inUse()`               | inUse               | `isInUse()`               |
| `parentNotFound()`      | parentNotFound      | `isParentNotFound()`      |
| `parentExists()`        | parentExists        | `isParentExists()`        |
| `parentInvalid()`       | parentInvalid       | `isParentInvalid()`       |
| `parentIncorrect()`     | parentIncorrect     | `isParentIncorrect()`     |
| `parentNotValid()`      | parentNotValid      | `isParentNotValid()`      |
| `parentInUse()`         | parentInUse         | `isParentInUse()`         |
| `parentAccessDenied()`  | parentAccessDenied  | `isParentAccessDenied()`  |
| `childNotFound()`       | childNotFound       | `isChildNotFound()`       |
| `childExists()`         | childExists         | `isChildExists()`         |
| `childInvalid()`        | childInvalid        | `isChildInvalid()`        |
| `childIncorrect()`      | childIncorrect      | `isChildIncorrect()`      |
| `childNotValid()`       | childNotValid       | `isChildNotValid()`       |
| `childInUse()`          | childInUse          | `isChildInUse()`          |
| `childAccessDenied()`   | childAccessDenied   | `isChildAccessDenied()`   |
| `missingDependency()`   | missingDependency   | `isMissingDependency()`   |
| `invalidDependency()`   | invalidDependency   | `isInvalidDependency()`   |
| `incorrectDependency()` | incorrectDependency | `isIncorrectDependency()` |

Example:
```javascript
const sr = new ServiceResponse();

sr.failed()

console.log(sr);    // { success: true, status: "failed", date: "2024/12/06 08:11:35" }
console.log(sr.isFailed()); // true
```

All the methods set `success` to `false` and have a signature as below:

```javascript
method(message, ex)
```

The only exception is `succeeded()` method that sets `success` to `true` and its signature is as below:

```javascript
succeeded(data, message)
```

There is a static `ServiceResponse.fromStatus(status)` method that returns a new `ServiceResponse` instance whose `status` is set using the argument passed to `fromStatus()`.

```javascript
const sr = ServiceResponse.fromStatus('my-status');

console.log(sr.status); // my-status
```
# Fluent methods
All stuas methods return current `ServiceResponse` instance. In addition, `ServiceResponse` provides a set of instance methods that enable fluent way of setting props on a `ServiceResponse` instance by chaining them together.

Example:
```javascript
const sr = new ServiceResponse();

sr.failed()
  .setException(ex)
  .setMessageKey('my-service.my-action')
  .addArg('name', 'John Doe')
  .addResponse(ServiceResponse.invalidParent(), 'addParent');
```

# Customization
## Casing of Properties
As it is said, it is possible to use `ServiceResponse` both in pascal-case and camel-case. By default, `ServiceResponse` uses camel-case naming. In order to use pascal-casing there are two ways.

### `usePascalProps`
We can set `usePascalProps` instance prop to `true` in a `ServiceResponse` instance. The `ServiceResponse`s properties will instantly change to pascal-case.

```javascript
const sr = new ServiceResponse();

// sample status
sr.failed();

console.log(sr.status); // failed
console.log(sr.toJson());   // { "success": false, "status": "failed", "date": "2024-12-13 08:49:32" }

sr.usePascalProps = true;

console.log(sr.status); // undefined
console.log(sr.Status); // failed
console.log(sr.toJson());   // { "Success": false, "Status": "failed", "Date": "2024-12-13 08:49:32" }

sr.usePascalProps = false;

console.log(sr.Status); // undefined
console.log(sr.status); // failed
console.log(sr.toJson());   // { "success": false, "status": "failed", "date": "2024-12-13 08:49:32" }
```

This property only affects a single instance.

> **Note:** The instance `copy()` method, is a smart method and distinguishes casing in current instance and given instance.

```javascript
const sr1 = new ServiceResponse();
const sr2 = new ServiceResponse();

sr2.usePascalProps = true;

sr1.failed();

sr2.copy(sr1);

console.log(sr2.status); // undefined
console.log(sr2.Status); // failed
```

### `ServiceResponse.usePascalProps`
We can also set the static `ServiceResponse.usePascalProps` to `true`. The effect of this setting is global and affects all newly instantiated `ServiceResponse` objects.

```javascript
const sr1 = new ServiceResponse();
const sr2 = new ServiceResponse();

console.log(sr1.status === undefined); // false
console.log(sr1.Status === undefined); // true
console.log(sr2.status === undefined); // false
console.log(sr2.Status === undefined); // true

ServiceResponse.usePascalProps = true;

const sr3 = new ServiceResponse();
const sr4 = new ServiceResponse();

console.log(sr3.status === undefined); // true
console.log(sr4.Status === undefined); // false
console.log(sr3.status === undefined); // true
console.log(sr4.Status === undefined); // false
```

## Status casing
By default, `ServiceResponse` uses camel-casing in its intrinsic statuses, like `notfound`, `accessdenied`. We can change this to pascal-casing by setting the static `ServiceResponse.usePascalStatus` to `true`.

```javascript
const sr1 = new ServiceResponse();

sr1.notFound();

console.log(sr1.status); // notfound

ServiceResponse.usePascalStatus = true;

const sr2 = new ServiceResponse();

sr2.notFound();

console.log(sr2.status); // NotFound
```

The effect of this seting is global. It affects all `ServiceResponse` objects that are newly created.

## Status separator
Using the static `ServiceResponse.statusSeparator`, we can specify a separator in `status` values.

```javascript
const sr1 = new ServiceResponse();

sr1.notFound();

console.log(sr1.status); // notfound

ServiceResponse.statusSeparator = "-";

const sr2 = new ServiceResponse();

sr2.notFound();

console.log(sr2.status); // not-found
```

Note that, `ServiceResponse.statusSeparator` does not affect existing `ServiceResponse` instances.

> It is recommended to set customization using static properties in the begining of an application.