Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: initial api #2

Merged
merged 28 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0aa5299
feat: first api poc
CptSchnitz Apr 11, 2024
0cd8f17
feat: progress
CptSchnitz Jun 4, 2024
07bed5a
feat: wip
CptSchnitz Jun 10, 2024
1b4a40d
feat: progress
CptSchnitz Jun 18, 2024
b89cd3c
feat: more progress
CptSchnitz Jun 19, 2024
2d1cf6a
feat: finished error handling
CptSchnitz Jun 20, 2024
4052293
feat: added debug
CptSchnitz Jun 20, 2024
0fe3639
fix: pr changes
CptSchnitz Jun 26, 2024
0bf937c
refactor: renamed and exported ConfigInstance
CptSchnitz Jun 26, 2024
5fef476
fix: exported lodash types for use in get function
CptSchnitz Jun 27, 2024
6b9a2ae
fix: changed ConfigInstance generics
CptSchnitz Jun 27, 2024
86de770
fix: changed ConfigInstance to depends on type instead of on schema
CptSchnitz Jun 27, 2024
5a00dbb
refactor: simplified debug msg
CptSchnitz Jun 30, 2024
9746d3f
fix: change envOptions to not have the keys instead of being undefined
CptSchnitz Jun 30, 2024
5d2e00f
fix: added keyword to ajv
CptSchnitz Jun 30, 2024
72b525b
fix: added keyword to correct ajv this time
CptSchnitz Jun 30, 2024
f92b966
refactor: changed options validator name
CptSchnitz Jun 30, 2024
a97a836
fix: changed request to dereference the config
CptSchnitz Jul 4, 2024
d3c1d6b
feat: added new option to set the local config folder
CptSchnitz Jul 4, 2024
567d2a3
docs: added readme
CptSchnitz Jul 4, 2024
c1fdff2
test: progress
CptSchnitz Jul 7, 2024
4f40d19
test: all except config
CptSchnitz Jul 8, 2024
4418ce6
test: done
CptSchnitz Jul 9, 2024
f144eb5
style: prettier
CptSchnitz Jul 10, 2024
acdc597
docs: apply suggestions from code review
CptSchnitz Jul 23, 2024
46d8e5b
test: apply suggestions from code review
shimoncohen Jul 23, 2024
a9727a3
fix: changed localConfigPath to optional
CptSchnitz Jul 31, 2024
94d1209
refactor: requested changes
CptSchnitz Aug 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
290 changes: 290 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,291 @@
# Config
This package provide a solution for handling the configuration for NodeJs applications.
The package supports both working as a standalone solution and online using [config-server](https://github.com/MapColonies/config-server).

The configs are defined in the [schemas](https://github.com/MapColonies/schemas) package. It is used for validation, defining environment variable override, and for generating typescript types.

# Installation
In order to use the package you need to install both the package itself, and the schemas package that handles all the config schemas.
```sh
npm install @map-colonies/schemas @map-colonies/config
```

# Usage
```typescript
import { config } from '@map-colonies/config';
import { commonBoilerplateV4 } from '@map-colonies/schemas';

const configInstance = await config({
configName: 'boiler-config',
configServerUrl: 'http://localhost:8080',
schema: commonBoilerplateV4,
version: 'latest',
offlineMode: false
});

const port = configInstance.get('server.port');
```

## API Documentation

This section describes the API provided by the package for interacting with the configuration.

### `ConfigInstance<T>`

The `ConfigInstance` interface represents the your way to interact with the configuration. It provides methods to retrieve configuration values and parts.
`T` is the typescript type associated with the chosen schema. it can be imported from the `@map-colonies/schemas` package.

#### Methods

##### `get<TPath extends string>(path: TPath): _.GetFieldType<T, TPath>`

- **Description**: Retrieves the value at the specified path from the configuration object. Note that the type of returned object is based on the path in the schema.
- **Parameters**:
- `path` (`TPath`): The path to the desired value.
- **Returns**: The value at the specified path.

##### `getAll(): T`

- **Description**: Retrieves the entire configuration object.
- **Returns**: The entire configuration object.

##### `getConfigParts(): { localConfig: object; config: object; envConfig: object }`

- **Description**: Retrieves different parts of the configuration object before being merged and validated. Useful for debugging.
- **Returns**: An object containing the `localConfig`, `config`, and `envConfig` parts of the configuration.
- `localConfig`: The local configuration object.
- `config`: The remote configuration object.
- `envConfig`: The environment configuration object.

##### `getResolvedOptions(): BaseOptions`

- **Description**: Retrieves the resolved options from the configuration object. Useful for debugging.
- **Returns**: The resolved options, which are an instance of `BaseOptions`.


# Configuration Options

This package allows you to configure various options for loading and managing configurations. Below are the available options and their descriptions.

## Options

### `schema`
- **Type**: `T extends SchemaWithType`
- **Description**: The schema of the configuration object.

### `configName`
- **Type**: `string`
- **Description**: The name of the remote configuration.
- **Environment Variable**: `CONFIG_NAME`

### `version`
- **Type**: `'latest' | number`
- **Description**: The version of the remote configuration. It can be either `'latest'` or a number.
- **Environment Variable**: `CONFIG_VERSION`

### `configServerUrl`
- **Type**: `string`
- **Description**: The URL of the configuration server.
- **Environment Variable**: `CONFIG_SERVER_URL`

### `offlineMode`
- **Type**: `boolean`
- **Optional**: `true`
- **Description**: Indicates whether the configuration should be loaded in offline mode.
- **Environment Variable**: `CONFIG_OFFLINE_MODE`

### `ignoreServerIsOlderVersionError`
- **Type**: `boolean`
- **Optional**: `true`
- **Description**: Indicates whether to ignore the error when the server version is older than the requested version.
- **Environment Variable**: `CONFIG_IGNORE_SERVER_IS_OLDER_VERSION_ERROR`

### `localConfigPath`
- **Type**: `string`
- **Default**: `./config`
- **Description**: The path to the local configuration folder.

## JSON Schema

The options are validated against the following JSON schema:

```json
{
  "required": ["configName", "configServerUrl", "version"],
  "additionalProperties": false,
  "type": "object",
  "properties": {
    "configName": { "type": "string" },
    "version": {
      "oneOf": [
        { "type": "string", "const": "latest" },
        { "type": "integer", "minimum": 1 }
      ]
    },
    "configServerUrl": { "type": "string" },
    "offlineMode": { "type": "boolean", "nullable": true },
    "ignoreServerIsOlderVersionError": { "type": "boolean", "nullable": true },
    "localConfigPath": { "type": "string", "default": "./config" }
  }
}
```

## Environment Variable Configuration

The following environment variables can be used to configure the options:

- `CONFIG_NAME`: Sets the `configName` option.
- `CONFIG_VERSION`: Sets the `version` option.
- `CONFIG_SERVER_URL`: Sets the `configServerUrl` option.
- `CONFIG_OFFLINE_MODE`: Sets the `offlineMode` option.
- `CONFIG_IGNORE_SERVER_IS_OLDER_VERSION_ERROR`: Sets the `ignoreServerIsOlderVersionError` option.

## Configuration Merging and Validation

The package supports merging configurations from multiple sources (local, remote, and environment variables) and then validates the merged configuration against the schema.

### Local Configuration

1. The local configuration is loaded from the path specified by the `localConfigPath` option. The default path is `./config`.

### Remote Configuration

1. The remote configuration is fetched from the server specified by the `configServerUrl` option.
2. If the `version` is set to `'latest'`, the latest version of the configuration is fetched. Otherwise, the specified version is fetched.

### Environment Variables

Configuration options can be overridden by setting the corresponding environment variables as described in schema using the `x-env-value` key.

### Merging Configurations

1. The configurations are merged in the following order of precedence:
- Environment variables
- Remote configuration
- Local configuration

2. If a configuration option is specified in multiple sources, the value from the source with higher precedence (as listed above) is used.

### Validation

1. After merging, the final configuration is validated against the defined schema using ajv.
2. The validation ensures that all required properties are present, and the types and values of properties conform to the schema.
3. Any default value according to the schema is added to the final object.
4. If the validation fails, an error is thrown, indicating the invalid properties and their issues.


# Error handling

This section describes the possible errors that can occur when using the package, along with their codes and payload structures.

## Identifying errors
The package exposes a helper function called `isConfigError` to assert what is the error that was thrown and handle it as needed.
```typescript
import { config, isConfigError } from '@map-colonies/config';

try {
const configInstance = await config({
configName: 'boiler-config',
configServerUrl: 'http://localhost:8080',
schema: commonBoilerplateV4,
version: 'latest',
offlineMode: false
});

} catch (error) {
if (isConfigError(error, 'configValidationError')) {
console.error('Config validation error:', error.payload);
}
}
```

## Errors
### `optionValidationError`
- **Code**: `1`
- **Payload**: `ValidationError[]`
- **Description**: This error occurs when there is a validation error with one of the configuration options.

### `configValidationError`
- **Code**: `2`
- **Payload**: `ValidationError[]`
- **Description**: This error occurs when the configuration as a whole fails validation.

### `httpResponseError`
- **Code**: `3`
- **Payload**:
```typescript
{
headers: Record<string, string>;
statusCode: number;
body: string;
}
```
- **Description**: This error occurs when an HTTP request results in an error response. The payload includes the response headers, status code and body.

### `httpGeneralError`
- **Code**: `4`
- **Payload**: `Error`
- **Description**: This error occurs when there is a general HTTP error. The payload contains the error object.

### `schemaNotFoundError`
- **Code**: `5`
- **Payload**:
```typescript
{
schemaPath: string;
}
```
- **Description**: This error occurs when the specified schema cannot be found. The payload includes the path of the missing schema.

### `schemasPackageVersionMismatchError`
- **Code**: `6`
- **Payload**:
```typescript
{
remotePackageVersion: string;
localPackageVersion: string;
}
```
- **Description**: This error occurs when there is a version mismatch between the remote and local schema packages. The payload includes the versions of both the remote and local packages.

### `schemaVersionMismatchError`
- **Code**: `7`
- **Payload**:
```typescript
{
remoteSchemaVersion: string;
localSchemaVersion: string;
}
```
- **Description**: This error occurs when there is a version mismatch between the remote and local schemas. The payload includes the versions of both the remote and local schemas.

# Debugging
If for some reason you want to debug the package you can either use the `getConfigParts` or the `getResolvedOptions` functions described in the API or use the more powerful debug logger.

The package debug logger is implemented using the [`debug`](https://www.npmjs.com/package/debug) npm package and is configured using the `DEBUG` Environment variable.

The following are the values you can configure to use the debug option.

### `DEBUG=*`
Enables all the logs. Note that setting this option might enable debug logging of other packages.

### `DEBUG=@map-colonies/config*`
Enables all the logs available in this package.

### `DEBUG=@map-colonies/config:config`
Enables only the logs related to the main logic of the package.

### `DEBUG=@map-colonies/config:env`
Enables only the logs related to parsing environment variables from schemas, and retrieving them for use in the configuration.

### `DEBUG=@map-colonies/config:http`
Enables only the logs related to http requests to the `config-server`.

### `DEBUG=@map-colonies/config:options`
Enables only the logs related to parsing and validation of the package initialization options.

### `DEBUG=@map-colonies/config:schemas`
Enables only the logs related to the retrieving of schemas.

### `DEBUG=@map-colonies/config:validator`
Enables only the logs related to the validation of configurations.
1 change: 1 addition & 0 deletions config/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions config/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Loading
Loading