bcchapi 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +38 -0
- package/CHANGELOG.md +11 -1
- package/package.json +4 -4
- package/src/client.ts +6 -6
- package/src/handlers.ts +69 -0
- package/src/types.ts +7 -5
- package/src/utils.ts +19 -70
- package/test/client.test.ts +4 -4
- package/test/{utils/parse-get-series.test.ts → handlers/handle-get-series.test.ts} +6 -6
- package/test/{utils/parse-search-series.test.ts → handlers/handle-search-series.test.ts} +6 -6
- package/dist/client.d.ts +0 -50
- package/dist/client.js +0 -89
- package/dist/errors.d.ts +0 -21
- package/dist/errors.js +0 -37
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -11
- package/dist/types.d.ts +0 -108
- package/dist/types.js +0 -2
- package/dist/utils.d.ts +0 -17
- package/dist/utils.js +0 -73
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
+
|
|
4
|
+
name: Publish to NPM
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: main
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-node@v3
|
|
18
|
+
with:
|
|
19
|
+
node-version: 20
|
|
20
|
+
- run: npm ci
|
|
21
|
+
- run: npm test -- --run
|
|
22
|
+
- run: npm run build
|
|
23
|
+
|
|
24
|
+
publish-npm:
|
|
25
|
+
needs: build
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
permissions:
|
|
28
|
+
id-token: write
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: actions/setup-node@v3
|
|
32
|
+
with:
|
|
33
|
+
node-version: 20
|
|
34
|
+
registry-url: https://registry.npmjs.org/
|
|
35
|
+
- run: npm ci
|
|
36
|
+
- run: npm publish --provenance
|
|
37
|
+
env:
|
|
38
|
+
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [1.0.7] - 2025-02-19
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Moved API response handlers from `utils.ts` to new `handlers.ts` file
|
|
13
|
+
- Improved date handling in `utils.ts`
|
|
14
|
+
- Fixed date validation in client test
|
|
15
|
+
- Updated dependencies:
|
|
16
|
+
- `@vitest/coverage-v8` from `^1.6.0` to `^3.0.6`
|
|
17
|
+
- `rimraf` from `^5.0.7` to `^6.0.1`
|
|
18
|
+
- `vitest` from `^1.6.0` to `^3.0.6`
|
|
9
19
|
|
|
10
20
|
## [1.0.6] - 2024-06-13
|
|
11
21
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bcchapi",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "API para acceder al Web Service del Banco Central de Chile.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -32,15 +32,15 @@
|
|
|
32
32
|
"@types/node": "^20.14.2",
|
|
33
33
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
34
34
|
"@typescript-eslint/parser": "^6.21.0",
|
|
35
|
-
"@vitest/coverage-v8": "^
|
|
35
|
+
"@vitest/coverage-v8": "^3.0.6",
|
|
36
36
|
"eslint": "^8.57.0",
|
|
37
37
|
"eslint-config-prettier": "^9.1.0",
|
|
38
38
|
"eslint-plugin-import": "^2.29.1",
|
|
39
39
|
"prettier": "^3.3.2",
|
|
40
|
-
"rimraf": "^
|
|
40
|
+
"rimraf": "^6.0.1",
|
|
41
41
|
"ts-node": "^10.9.2",
|
|
42
42
|
"typescript": "^5.4.5",
|
|
43
|
-
"vitest": "^
|
|
43
|
+
"vitest": "^3.0.6"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": ">=18.19.0",
|
package/src/client.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as querystring from 'node:querystring';
|
|
2
2
|
import * as assert from 'node:assert/strict';
|
|
3
3
|
import { GetSeriesResponse, SearchSeriesResponse, ApiResponse } from './types';
|
|
4
|
-
import {
|
|
4
|
+
import { handleGetSeriesResponse, handleSearchSeriesResponse } from './handlers';
|
|
5
|
+
import { isValidDate } from './utils';
|
|
5
6
|
|
|
6
7
|
export interface ClientConfig {
|
|
7
8
|
/**
|
|
@@ -25,7 +26,6 @@ export class Client {
|
|
|
25
26
|
static apiURL = 'https://si3.bcentral.cl/SieteRestWS/SieteRestWS.ashx';
|
|
26
27
|
|
|
27
28
|
private username: string;
|
|
28
|
-
|
|
29
29
|
private password: string;
|
|
30
30
|
|
|
31
31
|
constructor(config: ClientConfig) {
|
|
@@ -33,14 +33,14 @@ export class Client {
|
|
|
33
33
|
this.password = config.pass;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
async request(query: Record<string, string>): Promise<
|
|
36
|
+
async request<T extends ApiResponse>(query: Record<string, string>): Promise<T> {
|
|
37
37
|
const queryString = querystring.stringify({
|
|
38
38
|
user: this.username,
|
|
39
39
|
pass: this.password,
|
|
40
40
|
...query,
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
return (await fetch(`${Client.apiURL}?${queryString}`)).json() as Promise<
|
|
43
|
+
return (await fetch(`${Client.apiURL}?${queryString}`)).json() as Promise<T>;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
@@ -83,7 +83,7 @@ export class Client {
|
|
|
83
83
|
assert.ok(query.firstdate <= query.lastdate, 'invalid date range');
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
return this.request(query).then(
|
|
86
|
+
return this.request(query).then(handleGetSeriesResponse);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
@@ -104,6 +104,6 @@ export class Client {
|
|
|
104
104
|
function: 'SearchSeries',
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
-
return this.request(query).then(
|
|
107
|
+
return this.request(query).then(handleSearchSeriesResponse);
|
|
108
108
|
}
|
|
109
109
|
}
|
package/src/handlers.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ErrorCodes,
|
|
3
|
+
InvalidFrequencyError,
|
|
4
|
+
InvalidCredentialsError,
|
|
5
|
+
InvalidSeriesError,
|
|
6
|
+
WebServiceError,
|
|
7
|
+
} from './errors';
|
|
8
|
+
import {
|
|
9
|
+
ApiResponse,
|
|
10
|
+
SeriesObservation,
|
|
11
|
+
SeriesMetadata,
|
|
12
|
+
ErrorResponse,
|
|
13
|
+
GetSeriesResponse,
|
|
14
|
+
SearchSeriesResponse,
|
|
15
|
+
} from './types';
|
|
16
|
+
import { reverseDate } from './utils';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parses the GetSeries function API response.
|
|
20
|
+
*/
|
|
21
|
+
export function handleGetSeriesResponse<T extends ApiResponse>(response: T): GetSeriesResponse {
|
|
22
|
+
if (response.Codigo !== 0) {
|
|
23
|
+
switch (response.Codigo) {
|
|
24
|
+
case ErrorCodes.InvalidCredentials:
|
|
25
|
+
throw new InvalidCredentialsError(response as ApiResponse as ErrorResponse);
|
|
26
|
+
case ErrorCodes.InvalidSeries:
|
|
27
|
+
throw new InvalidSeriesError(response as ApiResponse as ErrorResponse);
|
|
28
|
+
default:
|
|
29
|
+
throw new WebServiceError(response as ApiResponse as ErrorResponse);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
seriesId: response.Series.seriesId || '',
|
|
35
|
+
description: response.Series.descripIng || '',
|
|
36
|
+
data: (response.Series.Obs || []).map((obs: SeriesObservation) => ({
|
|
37
|
+
date: reverseDate(obs.indexDateString),
|
|
38
|
+
value: parseFloat(obs.value),
|
|
39
|
+
})),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Parses the SearchSeries function API response.
|
|
45
|
+
*/
|
|
46
|
+
export function handleSearchSeriesResponse<T extends ApiResponse>(
|
|
47
|
+
response: T,
|
|
48
|
+
): SearchSeriesResponse {
|
|
49
|
+
if (response.Codigo !== 0) {
|
|
50
|
+
switch (response.Codigo) {
|
|
51
|
+
case ErrorCodes.InvalidCredentials:
|
|
52
|
+
throw new InvalidCredentialsError(response as ApiResponse as ErrorResponse);
|
|
53
|
+
case ErrorCodes.InvalidFrequency:
|
|
54
|
+
throw new InvalidFrequencyError(response as ApiResponse as ErrorResponse);
|
|
55
|
+
default:
|
|
56
|
+
throw new WebServiceError(response as ApiResponse as ErrorResponse);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return response.SeriesInfos.map((series: SeriesMetadata) => ({
|
|
61
|
+
seriesId: series.seriesId,
|
|
62
|
+
frequency: series.frequencyCode,
|
|
63
|
+
title: series.englishTitle,
|
|
64
|
+
firstObservedAt: reverseDate(series.firstObservation),
|
|
65
|
+
lastObservedAt: reverseDate(series.lastObservation),
|
|
66
|
+
updatedAt: reverseDate(series.updatedAt),
|
|
67
|
+
createdAt: reverseDate(series.createdAt),
|
|
68
|
+
}));
|
|
69
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export type StatusCode = 'OK' | 'ND';
|
|
2
|
+
|
|
1
3
|
export interface SeriesObservation {
|
|
2
4
|
/**
|
|
3
5
|
* Series observed date in DD-MM-YYYY format.
|
|
@@ -10,7 +12,7 @@ export interface SeriesObservation {
|
|
|
10
12
|
/**
|
|
11
13
|
* Series observed value status code (ND = no data recorded).
|
|
12
14
|
*/
|
|
13
|
-
statusCode:
|
|
15
|
+
statusCode: StatusCode;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export interface SeriesHistory {
|
|
@@ -75,20 +77,20 @@ export interface ApiResponse {
|
|
|
75
77
|
/**
|
|
76
78
|
* Response status code.
|
|
77
79
|
*/
|
|
78
|
-
Codigo: number;
|
|
80
|
+
readonly Codigo: number;
|
|
79
81
|
/**
|
|
80
82
|
* Response status message.
|
|
81
83
|
*/
|
|
82
|
-
Descripcion: string;
|
|
84
|
+
readonly Descripcion: string;
|
|
83
85
|
|
|
84
86
|
/**
|
|
85
87
|
* Series historic information.
|
|
86
88
|
*/
|
|
87
|
-
Series: SeriesHistory | NullSeries
|
|
89
|
+
readonly Series: Readonly<SeriesHistory | NullSeries>;
|
|
88
90
|
/**
|
|
89
91
|
* Series metadata information.
|
|
90
92
|
*/
|
|
91
|
-
SeriesInfos: SeriesMetadata
|
|
93
|
+
readonly SeriesInfos: ReadonlyArray<SeriesMetadata>;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
export interface ErrorResponse extends ApiResponse {
|
package/src/utils.ts
CHANGED
|
@@ -1,29 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ErrorCodes,
|
|
3
|
-
InvalidFrequencyError,
|
|
4
|
-
InvalidCredentialsError,
|
|
5
|
-
InvalidSeriesError,
|
|
6
|
-
WebServiceError,
|
|
7
|
-
} from './errors';
|
|
8
|
-
import {
|
|
9
|
-
ApiResponse,
|
|
10
|
-
SeriesObservation,
|
|
11
|
-
SeriesMetadata,
|
|
12
|
-
ErrorResponse,
|
|
13
|
-
GetSeriesResponse,
|
|
14
|
-
SearchSeriesResponse,
|
|
15
|
-
} from './types';
|
|
16
|
-
|
|
17
1
|
/**
|
|
18
2
|
* Determines wether a given value can be parsed to a valid date.
|
|
19
3
|
*/
|
|
20
4
|
export function isValidDate(date: unknown): boolean {
|
|
21
5
|
if (typeof date === 'string') {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
6
|
+
// Validate format YYYY-MM-DD
|
|
7
|
+
const isISOString = date.match(/^\d{4}-\d{2}-\d{2}$/);
|
|
8
|
+
|
|
9
|
+
if (!isISOString) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Validate parsed date timestamp
|
|
14
|
+
const timestamp = Date.parse(date);
|
|
15
|
+
|
|
16
|
+
return !Number.isNaN(timestamp);
|
|
17
|
+
} else if (date instanceof Date) {
|
|
18
|
+
// Validate date object
|
|
25
19
|
return !Number.isNaN(date.getTime());
|
|
26
20
|
}
|
|
21
|
+
// Is invalid date
|
|
27
22
|
return false;
|
|
28
23
|
}
|
|
29
24
|
|
|
@@ -31,58 +26,12 @@ export function isValidDate(date: unknown): boolean {
|
|
|
31
26
|
* Reverses a date string from DD-MM-YYYY to YYYY-MM-DD format.
|
|
32
27
|
*/
|
|
33
28
|
export function reverseDate(date: string): string {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Parses the GetSeries function API response.
|
|
39
|
-
*/
|
|
40
|
-
export function parseGetSeriesResponse<T extends ApiResponse>(response: T): GetSeriesResponse {
|
|
41
|
-
if (response.Codigo !== 0) {
|
|
42
|
-
switch (response.Codigo) {
|
|
43
|
-
case ErrorCodes.InvalidCredentials:
|
|
44
|
-
throw new InvalidCredentialsError(response as ApiResponse as ErrorResponse);
|
|
45
|
-
case ErrorCodes.InvalidSeries:
|
|
46
|
-
throw new InvalidSeriesError(response as ApiResponse as ErrorResponse);
|
|
47
|
-
default:
|
|
48
|
-
throw new WebServiceError(response as ApiResponse as ErrorResponse);
|
|
49
|
-
}
|
|
29
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
30
|
+
return date;
|
|
50
31
|
}
|
|
51
32
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
description: response.Series.descripIng || '',
|
|
55
|
-
data: (response.Series.Obs || []).map((obs: SeriesObservation) => ({
|
|
56
|
-
date: reverseDate(obs.indexDateString),
|
|
57
|
-
value: parseFloat(obs.value),
|
|
58
|
-
})),
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Parses the SearchSeries function API response.
|
|
64
|
-
*/
|
|
65
|
-
export function parseSearchSeriesResponse<T extends ApiResponse>(
|
|
66
|
-
response: T,
|
|
67
|
-
): SearchSeriesResponse {
|
|
68
|
-
if (response.Codigo !== 0) {
|
|
69
|
-
switch (response.Codigo) {
|
|
70
|
-
case ErrorCodes.InvalidCredentials:
|
|
71
|
-
throw new InvalidCredentialsError(response as ApiResponse as ErrorResponse);
|
|
72
|
-
case ErrorCodes.InvalidFrequency:
|
|
73
|
-
throw new InvalidFrequencyError(response as ApiResponse as ErrorResponse);
|
|
74
|
-
default:
|
|
75
|
-
throw new WebServiceError(response as ApiResponse as ErrorResponse);
|
|
76
|
-
}
|
|
33
|
+
if (!/^\d{2}-\d{2}-\d{4}$/.test(date)) {
|
|
34
|
+
throw new Error('Invalid date format. Expected DD-MM-YYYY');
|
|
77
35
|
}
|
|
78
|
-
|
|
79
|
-
return response.SeriesInfos.map((series: SeriesMetadata) => ({
|
|
80
|
-
seriesId: series.seriesId,
|
|
81
|
-
frequency: series.frequencyCode,
|
|
82
|
-
title: series.englishTitle,
|
|
83
|
-
firstObservedAt: reverseDate(series.firstObservation),
|
|
84
|
-
lastObservedAt: reverseDate(series.lastObservation),
|
|
85
|
-
updatedAt: reverseDate(series.updatedAt),
|
|
86
|
-
createdAt: reverseDate(series.createdAt),
|
|
87
|
-
}));
|
|
36
|
+
return date.split('-').reverse().join('-');
|
|
88
37
|
}
|
package/test/client.test.ts
CHANGED
|
@@ -112,8 +112,8 @@ describe('Client', () => {
|
|
|
112
112
|
expect(series.data).toHaveLength(15);
|
|
113
113
|
|
|
114
114
|
for (let i = 0; i < series.data.length; i += 1) {
|
|
115
|
-
expect(
|
|
116
|
-
fixtures.response.getSeriesSuccess.Series.Obs[i].indexDateString,
|
|
115
|
+
expect(series.data[i].date).toBe(
|
|
116
|
+
reverseDate(fixtures.response.getSeriesSuccess.Series.Obs[i].indexDateString),
|
|
117
117
|
);
|
|
118
118
|
expect(series.data[i].value.toFixed(2)).toBe(
|
|
119
119
|
fixtures.response.getSeriesSuccess.Series.Obs[i].value,
|
|
@@ -140,8 +140,8 @@ describe('Client', () => {
|
|
|
140
140
|
expect(series.data).toHaveLength(5);
|
|
141
141
|
|
|
142
142
|
for (let i = 0; i < series.data.length; i += 1) {
|
|
143
|
-
expect(
|
|
144
|
-
fixtures.response.getSeriesSuccess.Series.Obs[i].indexDateString,
|
|
143
|
+
expect(series.data[i].date).toBe(
|
|
144
|
+
reverseDate(fixtures.response.getSeriesSuccess.Series.Obs[i].indexDateString),
|
|
145
145
|
);
|
|
146
146
|
expect(series.data[i].value.toFixed(2)).toBe(
|
|
147
147
|
fixtures.response.getSeriesSuccess.Series.Obs[i].value,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import * as Errors from '../../src/errors';
|
|
3
|
-
import {
|
|
3
|
+
import { handleGetSeriesResponse } from '../../src/handlers';
|
|
4
4
|
import { ApiResponse } from '../../src/types';
|
|
5
5
|
import fixtures from '../fixtures';
|
|
6
6
|
|
|
7
|
-
describe('
|
|
7
|
+
describe('handleGetSeriesResponse', () => {
|
|
8
8
|
it('should parse correctly a valid response', () => {
|
|
9
|
-
const parsed =
|
|
9
|
+
const parsed = handleGetSeriesResponse(
|
|
10
10
|
fixtures.response.getSeriesSuccess as unknown as ApiResponse,
|
|
11
11
|
);
|
|
12
12
|
|
|
@@ -29,17 +29,17 @@ describe('parseGetSeriesResponse', () => {
|
|
|
29
29
|
|
|
30
30
|
it('should throw an InvalidCredentialsError if the response code is -5', () => {
|
|
31
31
|
expect(() =>
|
|
32
|
-
|
|
32
|
+
handleGetSeriesResponse(fixtures.response.credentialsInvalid as unknown as ApiResponse),
|
|
33
33
|
).toThrow(Errors.InvalidCredentialsError);
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
it('should throw an InvalidSeriesError if the response code is -50', () => {
|
|
37
37
|
expect(() =>
|
|
38
|
-
|
|
38
|
+
handleGetSeriesResponse(fixtures.response.getSeriesInvalid as unknown as ApiResponse),
|
|
39
39
|
).toThrow(Errors.InvalidSeriesError);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
it('should throw a ResponseError if the response code is unknown', () => {
|
|
43
|
-
expect(() =>
|
|
43
|
+
expect(() => handleGetSeriesResponse({} as ApiResponse)).toThrow(Errors.WebServiceError);
|
|
44
44
|
});
|
|
45
45
|
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import * as Errors from '../../src/errors';
|
|
3
3
|
import { ApiResponse } from '../../src/types';
|
|
4
|
-
import {
|
|
4
|
+
import { handleSearchSeriesResponse } from '../../src/handlers';
|
|
5
5
|
import fixtures from '../fixtures';
|
|
6
6
|
|
|
7
|
-
describe('
|
|
7
|
+
describe('handleSearchSeriesResponse', () => {
|
|
8
8
|
it('should parse correctly a valid response', () => {
|
|
9
|
-
const parsed =
|
|
9
|
+
const parsed = handleSearchSeriesResponse(
|
|
10
10
|
fixtures.response.searchSeriesSuccess as unknown as ApiResponse,
|
|
11
11
|
);
|
|
12
12
|
|
|
@@ -32,17 +32,17 @@ describe('parseSearchSeriesResponse', () => {
|
|
|
32
32
|
|
|
33
33
|
it('should throw an InvalidCredentialsError if the response code is -5', () => {
|
|
34
34
|
expect(() =>
|
|
35
|
-
|
|
35
|
+
handleSearchSeriesResponse(fixtures.response.credentialsInvalid as unknown as ApiResponse),
|
|
36
36
|
).toThrow(Errors.InvalidCredentialsError);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
it('should throw an InvalidFrequencyError if the response code is -1', () => {
|
|
40
40
|
expect(() =>
|
|
41
|
-
|
|
41
|
+
handleSearchSeriesResponse(fixtures.response.searchSeriesInvalid as unknown as ApiResponse),
|
|
42
42
|
).toThrow(Errors.InvalidFrequencyError);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
it('should throw a ResponseError if the response code is unknown', () => {
|
|
46
|
-
expect(() =>
|
|
46
|
+
expect(() => handleSearchSeriesResponse({} as ApiResponse)).toThrow(Errors.WebServiceError);
|
|
47
47
|
});
|
|
48
48
|
});
|
package/dist/client.d.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { GetSeriesResponse, SearchSeriesResponse, ApiResponse } from './types';
|
|
2
|
-
export interface ClientConfig {
|
|
3
|
-
/**
|
|
4
|
-
* Client username (registered email).
|
|
5
|
-
*/
|
|
6
|
-
user: string;
|
|
7
|
-
/**
|
|
8
|
-
* Client password.
|
|
9
|
-
*/
|
|
10
|
-
pass: string;
|
|
11
|
-
}
|
|
12
|
-
export declare enum Frequency {
|
|
13
|
-
Daily = "DAILY",
|
|
14
|
-
Monthly = "MONTHLY",
|
|
15
|
-
Quarterly = "QUARTERLY",
|
|
16
|
-
Annual = "ANNUAL"
|
|
17
|
-
}
|
|
18
|
-
export declare class Client {
|
|
19
|
-
static apiURL: string;
|
|
20
|
-
private username;
|
|
21
|
-
private password;
|
|
22
|
-
constructor(config: ClientConfig);
|
|
23
|
-
request(query: Record<string, string>): Promise<ApiResponse>;
|
|
24
|
-
/**
|
|
25
|
-
* Fetches the list of observed values for a given series.
|
|
26
|
-
*/
|
|
27
|
-
getSeries(params: {
|
|
28
|
-
/**
|
|
29
|
-
* Series identifier.
|
|
30
|
-
*/
|
|
31
|
-
series: string;
|
|
32
|
-
/**
|
|
33
|
-
* First date of the range to fetch.
|
|
34
|
-
*/
|
|
35
|
-
since?: string | Date;
|
|
36
|
-
/**
|
|
37
|
-
* Last date of the range to fetch.
|
|
38
|
-
*/
|
|
39
|
-
until?: string | Date;
|
|
40
|
-
}): Promise<GetSeriesResponse>;
|
|
41
|
-
/**
|
|
42
|
-
* Fetches the list of available series by frequency and their metadata.
|
|
43
|
-
*/
|
|
44
|
-
searchSeries(params: {
|
|
45
|
-
/**
|
|
46
|
-
* Frequency for which you want to consult the catalog of available series.
|
|
47
|
-
*/
|
|
48
|
-
frequency: Frequency;
|
|
49
|
-
}): Promise<SearchSeriesResponse>;
|
|
50
|
-
}
|
package/dist/client.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.Client = exports.Frequency = void 0;
|
|
27
|
-
const querystring = __importStar(require("node:querystring"));
|
|
28
|
-
const assert = __importStar(require("node:assert/strict"));
|
|
29
|
-
const utils_1 = require("./utils");
|
|
30
|
-
var Frequency;
|
|
31
|
-
(function (Frequency) {
|
|
32
|
-
Frequency["Daily"] = "DAILY";
|
|
33
|
-
Frequency["Monthly"] = "MONTHLY";
|
|
34
|
-
Frequency["Quarterly"] = "QUARTERLY";
|
|
35
|
-
Frequency["Annual"] = "ANNUAL";
|
|
36
|
-
})(Frequency || (exports.Frequency = Frequency = {}));
|
|
37
|
-
class Client {
|
|
38
|
-
static apiURL = 'https://si3.bcentral.cl/SieteRestWS/SieteRestWS.ashx';
|
|
39
|
-
username;
|
|
40
|
-
password;
|
|
41
|
-
constructor(config) {
|
|
42
|
-
this.username = config.user;
|
|
43
|
-
this.password = config.pass;
|
|
44
|
-
}
|
|
45
|
-
async request(query) {
|
|
46
|
-
const queryString = querystring.stringify({
|
|
47
|
-
user: this.username,
|
|
48
|
-
pass: this.password,
|
|
49
|
-
...query,
|
|
50
|
-
});
|
|
51
|
-
return (await fetch(`${Client.apiURL}?${queryString}`)).json();
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Fetches the list of observed values for a given series.
|
|
55
|
-
*/
|
|
56
|
-
async getSeries(params) {
|
|
57
|
-
const { series, since, until } = params;
|
|
58
|
-
assert.ok(series && typeof series === 'string', 'series must be a non-empty string');
|
|
59
|
-
const query = {
|
|
60
|
-
timeseries: series,
|
|
61
|
-
function: 'GetSeries',
|
|
62
|
-
};
|
|
63
|
-
if (since) {
|
|
64
|
-
assert.ok((0, utils_1.isValidDate)(since), '"since" is not a valid date string or Date object');
|
|
65
|
-
query.firstdate = typeof since === 'string' ? since : since.toISOString().slice(0, 10);
|
|
66
|
-
}
|
|
67
|
-
if (until) {
|
|
68
|
-
assert.ok((0, utils_1.isValidDate)(until), '"until" is not a valid date string or Date object');
|
|
69
|
-
query.lastdate = typeof until === 'string' ? until : until.toISOString().slice(0, 10);
|
|
70
|
-
}
|
|
71
|
-
if (query.firstdate && query.lastdate) {
|
|
72
|
-
assert.ok(query.firstdate <= query.lastdate, 'invalid date range');
|
|
73
|
-
}
|
|
74
|
-
return this.request(query).then(utils_1.parseGetSeriesResponse);
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Fetches the list of available series by frequency and their metadata.
|
|
78
|
-
*/
|
|
79
|
-
async searchSeries(params) {
|
|
80
|
-
const { frequency } = params;
|
|
81
|
-
assert.ok(frequency && typeof frequency === 'string', 'frequency must be a non-empty string');
|
|
82
|
-
const query = {
|
|
83
|
-
frequency,
|
|
84
|
-
function: 'SearchSeries',
|
|
85
|
-
};
|
|
86
|
-
return this.request(query).then(utils_1.parseSearchSeriesResponse);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
exports.Client = Client;
|
package/dist/errors.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { ErrorResponse } from './types';
|
|
2
|
-
export declare enum ErrorCodes {
|
|
3
|
-
InvalidCredentials = -5,
|
|
4
|
-
InvalidFrequency = -1,
|
|
5
|
-
InvalidSeries = -50,
|
|
6
|
-
InvalidDateRange = -1,
|
|
7
|
-
Unknown = -1
|
|
8
|
-
}
|
|
9
|
-
export declare class WebServiceError extends Error {
|
|
10
|
-
response?: ErrorResponse;
|
|
11
|
-
constructor(response?: ErrorResponse, message?: string);
|
|
12
|
-
}
|
|
13
|
-
export declare class InvalidFrequencyError extends WebServiceError {
|
|
14
|
-
constructor(response?: ErrorResponse);
|
|
15
|
-
}
|
|
16
|
-
export declare class InvalidCredentialsError extends WebServiceError {
|
|
17
|
-
constructor(response?: ErrorResponse);
|
|
18
|
-
}
|
|
19
|
-
export declare class InvalidSeriesError extends WebServiceError {
|
|
20
|
-
constructor(response?: ErrorResponse);
|
|
21
|
-
}
|
package/dist/errors.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InvalidSeriesError = exports.InvalidCredentialsError = exports.InvalidFrequencyError = exports.WebServiceError = exports.ErrorCodes = void 0;
|
|
4
|
-
var ErrorCodes;
|
|
5
|
-
(function (ErrorCodes) {
|
|
6
|
-
ErrorCodes[ErrorCodes["InvalidCredentials"] = -5] = "InvalidCredentials";
|
|
7
|
-
ErrorCodes[ErrorCodes["InvalidFrequency"] = -1] = "InvalidFrequency";
|
|
8
|
-
ErrorCodes[ErrorCodes["InvalidSeries"] = -50] = "InvalidSeries";
|
|
9
|
-
ErrorCodes[ErrorCodes["InvalidDateRange"] = -1] = "InvalidDateRange";
|
|
10
|
-
ErrorCodes[ErrorCodes["Unknown"] = -1] = "Unknown";
|
|
11
|
-
})(ErrorCodes || (exports.ErrorCodes = ErrorCodes = {}));
|
|
12
|
-
class WebServiceError extends Error {
|
|
13
|
-
response;
|
|
14
|
-
constructor(response, message) {
|
|
15
|
-
super(message || response?.Descripcion || 'Unknown service error');
|
|
16
|
-
this.response = response;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
exports.WebServiceError = WebServiceError;
|
|
20
|
-
class InvalidFrequencyError extends WebServiceError {
|
|
21
|
-
constructor(response) {
|
|
22
|
-
super(response, 'Invalid frequency code (must be DAILY, MONTHLY, QUARTERLY or ANNUAL)');
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
exports.InvalidFrequencyError = InvalidFrequencyError;
|
|
26
|
-
class InvalidCredentialsError extends WebServiceError {
|
|
27
|
-
constructor(response) {
|
|
28
|
-
super(response, 'Invalid username or password');
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
exports.InvalidCredentialsError = InvalidCredentialsError;
|
|
32
|
-
class InvalidSeriesError extends WebServiceError {
|
|
33
|
-
constructor(response) {
|
|
34
|
-
super(response, 'Invalid series id');
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
exports.InvalidSeriesError = InvalidSeriesError;
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InvalidSeriesError = exports.InvalidFrequencyError = exports.InvalidCredentialsError = exports.WebServiceError = exports.Frequency = exports.Client = void 0;
|
|
4
|
-
var client_1 = require("./client");
|
|
5
|
-
Object.defineProperty(exports, "Client", { enumerable: true, get: function () { return client_1.Client; } });
|
|
6
|
-
Object.defineProperty(exports, "Frequency", { enumerable: true, get: function () { return client_1.Frequency; } });
|
|
7
|
-
var errors_1 = require("./errors");
|
|
8
|
-
Object.defineProperty(exports, "WebServiceError", { enumerable: true, get: function () { return errors_1.WebServiceError; } });
|
|
9
|
-
Object.defineProperty(exports, "InvalidCredentialsError", { enumerable: true, get: function () { return errors_1.InvalidCredentialsError; } });
|
|
10
|
-
Object.defineProperty(exports, "InvalidFrequencyError", { enumerable: true, get: function () { return errors_1.InvalidFrequencyError; } });
|
|
11
|
-
Object.defineProperty(exports, "InvalidSeriesError", { enumerable: true, get: function () { return errors_1.InvalidSeriesError; } });
|
package/dist/types.d.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
export interface SeriesObservation {
|
|
2
|
-
/**
|
|
3
|
-
* Series observed date in DD-MM-YYYY format.
|
|
4
|
-
*/
|
|
5
|
-
indexDateString: string;
|
|
6
|
-
/**
|
|
7
|
-
* Series observed value.
|
|
8
|
-
*/
|
|
9
|
-
value: string;
|
|
10
|
-
/**
|
|
11
|
-
* Series observed value status code (ND = no data recorded).
|
|
12
|
-
*/
|
|
13
|
-
statusCode: 'OK' | 'ND';
|
|
14
|
-
}
|
|
15
|
-
export interface SeriesHistory {
|
|
16
|
-
/**
|
|
17
|
-
* Series identifier.
|
|
18
|
-
*/
|
|
19
|
-
seriesId: string;
|
|
20
|
-
/**
|
|
21
|
-
* Series name in Spanish.
|
|
22
|
-
*/
|
|
23
|
-
descripEsp: string;
|
|
24
|
-
/**
|
|
25
|
-
* Series name in English.
|
|
26
|
-
*/
|
|
27
|
-
descripIng: string;
|
|
28
|
-
/**
|
|
29
|
-
* List of series observed values.
|
|
30
|
-
*/
|
|
31
|
-
Obs: SeriesObservation[];
|
|
32
|
-
}
|
|
33
|
-
export type NullSeries = {
|
|
34
|
-
[key in keyof SeriesHistory]: null;
|
|
35
|
-
};
|
|
36
|
-
export interface SeriesMetadata {
|
|
37
|
-
/**
|
|
38
|
-
* Series identifier.
|
|
39
|
-
*/
|
|
40
|
-
seriesId: string;
|
|
41
|
-
/**
|
|
42
|
-
* Series frequency.
|
|
43
|
-
*/
|
|
44
|
-
frequencyCode: string;
|
|
45
|
-
/**
|
|
46
|
-
* Series name in Spanish.
|
|
47
|
-
*/
|
|
48
|
-
spanishTitle: string;
|
|
49
|
-
/**
|
|
50
|
-
* Series name in English.
|
|
51
|
-
*/
|
|
52
|
-
englishTitle: string;
|
|
53
|
-
/**
|
|
54
|
-
* Date of first observation in DD-MM-YYYY format.
|
|
55
|
-
*/
|
|
56
|
-
firstObservation: string;
|
|
57
|
-
/**
|
|
58
|
-
* Date of last observation in DD-MM-YYYY format.
|
|
59
|
-
*/
|
|
60
|
-
lastObservation: string;
|
|
61
|
-
/**
|
|
62
|
-
* Date of last update in DD-MM-YYYY format.
|
|
63
|
-
*/
|
|
64
|
-
updatedAt: string;
|
|
65
|
-
/**
|
|
66
|
-
* Date of creation in DD-MM-YYYY format.
|
|
67
|
-
*/
|
|
68
|
-
createdAt: string;
|
|
69
|
-
}
|
|
70
|
-
export interface ApiResponse {
|
|
71
|
-
/**
|
|
72
|
-
* Response status code.
|
|
73
|
-
*/
|
|
74
|
-
Codigo: number;
|
|
75
|
-
/**
|
|
76
|
-
* Response status message.
|
|
77
|
-
*/
|
|
78
|
-
Descripcion: string;
|
|
79
|
-
/**
|
|
80
|
-
* Series historic information.
|
|
81
|
-
*/
|
|
82
|
-
Series: SeriesHistory | NullSeries;
|
|
83
|
-
/**
|
|
84
|
-
* Series metadata information.
|
|
85
|
-
*/
|
|
86
|
-
SeriesInfos: SeriesMetadata[];
|
|
87
|
-
}
|
|
88
|
-
export interface ErrorResponse extends ApiResponse {
|
|
89
|
-
Series: NullSeries;
|
|
90
|
-
SeriesInfos: never[];
|
|
91
|
-
}
|
|
92
|
-
export interface GetSeriesResponse {
|
|
93
|
-
seriesId: string;
|
|
94
|
-
description: string;
|
|
95
|
-
data: ReadonlyArray<{
|
|
96
|
-
date: string;
|
|
97
|
-
value: number;
|
|
98
|
-
}>;
|
|
99
|
-
}
|
|
100
|
-
export type SearchSeriesResponse = ReadonlyArray<{
|
|
101
|
-
seriesId: string;
|
|
102
|
-
frequency: string;
|
|
103
|
-
title: string;
|
|
104
|
-
firstObservedAt: string;
|
|
105
|
-
lastObservedAt: string;
|
|
106
|
-
updatedAt: string;
|
|
107
|
-
createdAt: string;
|
|
108
|
-
}>;
|
package/dist/types.js
DELETED
package/dist/utils.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { ApiResponse, GetSeriesResponse, SearchSeriesResponse } from './types';
|
|
2
|
-
/**
|
|
3
|
-
* Determines wether a given value can be parsed to a valid date.
|
|
4
|
-
*/
|
|
5
|
-
export declare function isValidDate(date: unknown): boolean;
|
|
6
|
-
/**
|
|
7
|
-
* Reverses a date string from DD-MM-YYYY to YYYY-MM-DD format.
|
|
8
|
-
*/
|
|
9
|
-
export declare function reverseDate(date: string): string;
|
|
10
|
-
/**
|
|
11
|
-
* Parses the GetSeries function API response.
|
|
12
|
-
*/
|
|
13
|
-
export declare function parseGetSeriesResponse<T extends ApiResponse>(response: T): GetSeriesResponse;
|
|
14
|
-
/**
|
|
15
|
-
* Parses the SearchSeries function API response.
|
|
16
|
-
*/
|
|
17
|
-
export declare function parseSearchSeriesResponse<T extends ApiResponse>(response: T): SearchSeriesResponse;
|
package/dist/utils.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseSearchSeriesResponse = exports.parseGetSeriesResponse = exports.reverseDate = exports.isValidDate = void 0;
|
|
4
|
-
const errors_1 = require("./errors");
|
|
5
|
-
/**
|
|
6
|
-
* Determines wether a given value can be parsed to a valid date.
|
|
7
|
-
*/
|
|
8
|
-
function isValidDate(date) {
|
|
9
|
-
if (typeof date === 'string') {
|
|
10
|
-
return !Number.isNaN(Date.parse(date));
|
|
11
|
-
}
|
|
12
|
-
if (date instanceof Date) {
|
|
13
|
-
return !Number.isNaN(date.getTime());
|
|
14
|
-
}
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
exports.isValidDate = isValidDate;
|
|
18
|
-
/**
|
|
19
|
-
* Reverses a date string from DD-MM-YYYY to YYYY-MM-DD format.
|
|
20
|
-
*/
|
|
21
|
-
function reverseDate(date) {
|
|
22
|
-
return date.split('-').reverse().join('-');
|
|
23
|
-
}
|
|
24
|
-
exports.reverseDate = reverseDate;
|
|
25
|
-
/**
|
|
26
|
-
* Parses the GetSeries function API response.
|
|
27
|
-
*/
|
|
28
|
-
function parseGetSeriesResponse(response) {
|
|
29
|
-
if (response.Codigo !== 0) {
|
|
30
|
-
switch (response.Codigo) {
|
|
31
|
-
case errors_1.ErrorCodes.InvalidCredentials:
|
|
32
|
-
throw new errors_1.InvalidCredentialsError(response);
|
|
33
|
-
case errors_1.ErrorCodes.InvalidSeries:
|
|
34
|
-
throw new errors_1.InvalidSeriesError(response);
|
|
35
|
-
default:
|
|
36
|
-
throw new errors_1.WebServiceError(response);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
seriesId: response.Series.seriesId || '',
|
|
41
|
-
description: response.Series.descripIng || '',
|
|
42
|
-
data: (response.Series.Obs || []).map((obs) => ({
|
|
43
|
-
date: reverseDate(obs.indexDateString),
|
|
44
|
-
value: parseFloat(obs.value),
|
|
45
|
-
})),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
exports.parseGetSeriesResponse = parseGetSeriesResponse;
|
|
49
|
-
/**
|
|
50
|
-
* Parses the SearchSeries function API response.
|
|
51
|
-
*/
|
|
52
|
-
function parseSearchSeriesResponse(response) {
|
|
53
|
-
if (response.Codigo !== 0) {
|
|
54
|
-
switch (response.Codigo) {
|
|
55
|
-
case errors_1.ErrorCodes.InvalidCredentials:
|
|
56
|
-
throw new errors_1.InvalidCredentialsError(response);
|
|
57
|
-
case errors_1.ErrorCodes.InvalidFrequency:
|
|
58
|
-
throw new errors_1.InvalidFrequencyError(response);
|
|
59
|
-
default:
|
|
60
|
-
throw new errors_1.WebServiceError(response);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return response.SeriesInfos.map((series) => ({
|
|
64
|
-
seriesId: series.seriesId,
|
|
65
|
-
frequency: series.frequencyCode,
|
|
66
|
-
title: series.englishTitle,
|
|
67
|
-
firstObservedAt: reverseDate(series.firstObservation),
|
|
68
|
-
lastObservedAt: reverseDate(series.lastObservation),
|
|
69
|
-
updatedAt: reverseDate(series.updatedAt),
|
|
70
|
-
createdAt: reverseDate(series.createdAt),
|
|
71
|
-
}));
|
|
72
|
-
}
|
|
73
|
-
exports.parseSearchSeriesResponse = parseSearchSeriesResponse;
|