@defra-fish/fulfilment-job 1.55.0 → 1.56.0
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/README.md +10 -15
- package/package.json +5 -6
- package/src/__tests__/config.spec.js +1 -32
- package/src/config.js +1 -67
- package/src/staging/__tests__/deliver-fulfilment-files.spec.js +4 -36
- package/src/staging/deliver-fulfilment-files.js +1 -7
- package/src/__mocks__/ssh2-sftp-client.js +0 -9
- package/src/transport/__tests__/ftp.spec.js +0 -63
- package/src/transport/ftp.js +0 -28
package/README.md
CHANGED
|
@@ -19,21 +19,16 @@ provider.
|
|
|
19
19
|
|
|
20
20
|
# Environment variables
|
|
21
21
|
|
|
22
|
-
| name | description
|
|
23
|
-
| ----------------------------------- |
|
|
24
|
-
| NODE_ENV | Node environment
|
|
25
|
-
| FULFILMENT_FILE_SIZE | The maximum number of records written to an aggregated fulfilment file
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
| FULFILMENT_SEND_UNENCRYPTED_FILE | Flag for whether to send the unencrypted fulfilment file | no | false | true, false, 0, 1 | |
|
|
33
|
-
| FULFILMENT_PGP_PUBLIC_KEY_SECRET_ID | The secret id for the file encryption public key | yes | | | |
|
|
34
|
-
| DEBUG | Use to enable output of debug information to the console | yes | | fulfilment:\*, fulfilment:staging, fulfilment:transport, fulfilment:ftp | |
|
|
35
|
-
| AIRBRAKE_HOST | URL of airbrake host | no | | | |
|
|
36
|
-
| AIRBRAKE_PROJECT_KEY | Project key for airbrake logging | no | | | |
|
|
22
|
+
| name | description | required | default | valid | notes |
|
|
23
|
+
| ----------------------------------- | ----------------------------------------------------------------------------- | :------: | ------- | ----------------------------------------------------------------------- | ----- |
|
|
24
|
+
| NODE_ENV | Node environment | no | | development, test, production | |
|
|
25
|
+
| FULFILMENT_FILE_SIZE | The maximum number of records written to an aggregated fulfilment file | yes | | | |
|
|
26
|
+
| FULFILMENT_S3_BUCKET | The name of the AWS S3 bucket in which to stage and aggregate fulfilment data | yes | | | |
|
|
27
|
+
| FULFILMENT_SEND_UNENCRYPTED_FILE | Flag for whether to send the unencrypted fulfilment file | no | false | true, false, 0, 1 | |
|
|
28
|
+
| FULFILMENT_PGP_PUBLIC_KEY_SECRET_ID | The secret id for the file encryption public key | yes | | | |
|
|
29
|
+
| DEBUG | Use to enable output of debug information to the console | yes | | fulfilment:\*, fulfilment:staging, fulfilment:transport, fulfilment:ftp | |
|
|
30
|
+
| AIRBRAKE_HOST | URL of airbrake host | no | | | |
|
|
31
|
+
| AIRBRAKE_PROJECT_KEY | Project key for airbrake logging | no | | | |
|
|
37
32
|
|
|
38
33
|
### See also:
|
|
39
34
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defra-fish/fulfilment-job",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.56.0",
|
|
4
4
|
"description": "Rod Licensing Sales Fulfilment Job",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -35,15 +35,14 @@
|
|
|
35
35
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@defra-fish/connectors-lib": "1.
|
|
39
|
-
"@defra-fish/dynamics-lib": "1.
|
|
38
|
+
"@defra-fish/connectors-lib": "1.56.0",
|
|
39
|
+
"@defra-fish/dynamics-lib": "1.56.0",
|
|
40
40
|
"commander": "^7.2.0",
|
|
41
41
|
"debug": "^4.3.3",
|
|
42
42
|
"merge2": "^1.4.1",
|
|
43
43
|
"moment": "^2.29.1",
|
|
44
44
|
"openpgp": "^5.0.0-1",
|
|
45
|
-
"pluralize": "^8.0.0"
|
|
46
|
-
"ssh2-sftp-client": "^6.0.1"
|
|
45
|
+
"pluralize": "^8.0.0"
|
|
47
46
|
},
|
|
48
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "4edcdd349b077ad0bdb3e7b4029df3aba7c04b49"
|
|
49
48
|
}
|
|
@@ -17,11 +17,6 @@ const clearEnvVars = () => {
|
|
|
17
17
|
|
|
18
18
|
const envVars = Object.freeze({
|
|
19
19
|
FULFILMENT_FILE_SIZE: 1234,
|
|
20
|
-
FULFILMENT_FTP_HOST: 'test-host',
|
|
21
|
-
FULFILMENT_FTP_PORT: 2222,
|
|
22
|
-
FULFILMENT_FTP_PATH: '/remote/share',
|
|
23
|
-
FULFILMENT_FTP_USERNAME: 'test-user',
|
|
24
|
-
FULFILMENT_FTP_KEY_SECRET_ID: 'test-secret-id',
|
|
25
20
|
FULFILMENT_S3_BUCKET: 'test-bucket',
|
|
26
21
|
FULFILMENT_PGP_PUBLIC_KEY_SECRET_ID: 'pgp-key-secret-id',
|
|
27
22
|
FULFILMENT_SEND_UNENCRYPTED_FILE: 'false'
|
|
@@ -44,32 +39,6 @@ describe('config', () => {
|
|
|
44
39
|
})
|
|
45
40
|
})
|
|
46
41
|
|
|
47
|
-
describe('ftp', () => {
|
|
48
|
-
it('provides properties relating the use of SFTP', async () => {
|
|
49
|
-
expect(config.ftp).toEqual(
|
|
50
|
-
expect.objectContaining({
|
|
51
|
-
host: 'test-host',
|
|
52
|
-
port: '2222',
|
|
53
|
-
path: '/remote/share',
|
|
54
|
-
username: 'test-user',
|
|
55
|
-
privateKey: 'test-ssh-key',
|
|
56
|
-
algorithms: { cipher: expect.any(Array), kex: expect.any(Array) },
|
|
57
|
-
// Wait up to 60 seconds for the SSH handshake
|
|
58
|
-
readyTimeout: expect.any(Number),
|
|
59
|
-
// Retry 5 times over a minute
|
|
60
|
-
retries: expect.any(Number),
|
|
61
|
-
retry_minTimeout: expect.any(Number),
|
|
62
|
-
debug: expect.any(Function)
|
|
63
|
-
})
|
|
64
|
-
)
|
|
65
|
-
})
|
|
66
|
-
it('defaults the sftp port to 22 if the environment variable is not configured', async () => {
|
|
67
|
-
delete process.env.FULFILMENT_FTP_PORT
|
|
68
|
-
await config.initialise()
|
|
69
|
-
expect(config.ftp.port).toEqual('22')
|
|
70
|
-
})
|
|
71
|
-
})
|
|
72
|
-
|
|
73
42
|
describe('s3', () => {
|
|
74
43
|
it('provides properties relating the use of Amazon S3', async () => {
|
|
75
44
|
expect(config.s3.bucket).toEqual('test-bucket')
|
|
@@ -79,7 +48,7 @@ describe('config', () => {
|
|
|
79
48
|
|
|
80
49
|
describe('pgp config', () => {
|
|
81
50
|
const init = async (samplePublicKey = 'sample-pgp-key') => {
|
|
82
|
-
AwsMock.SecretsManager.__setNextResponses('getSecretValue', { SecretString:
|
|
51
|
+
AwsMock.SecretsManager.__setNextResponses('getSecretValue', { SecretString: samplePublicKey })
|
|
83
52
|
await config.initialise()
|
|
84
53
|
}
|
|
85
54
|
beforeAll(setEnvVars)
|
package/src/config.js
CHANGED
|
@@ -1,45 +1,6 @@
|
|
|
1
1
|
import { AWS } from '@defra-fish/connectors-lib'
|
|
2
|
-
import db from 'debug'
|
|
3
|
-
const { secretsManager } = AWS()
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
* Key exchange algorithms for public key authentication - in descending order of priority
|
|
7
|
-
* @type {string[]}
|
|
8
|
-
*/
|
|
9
|
-
export const SFTP_KEY_EXCHANGE_ALGORITHMS = [
|
|
10
|
-
'curve25519-sha256@libssh.org',
|
|
11
|
-
'curve25519-sha256',
|
|
12
|
-
'ecdh-sha2-nistp521',
|
|
13
|
-
'ecdh-sha2-nistp384',
|
|
14
|
-
'ecdh-sha2-nistp256',
|
|
15
|
-
'diffie-hellman-group-exchange-sha256',
|
|
16
|
-
'diffie-hellman-group14-sha256',
|
|
17
|
-
'diffie-hellman-group16-sha512',
|
|
18
|
-
'diffie-hellman-group18-sha512',
|
|
19
|
-
'diffie-hellman-group14-sha1',
|
|
20
|
-
'diffie-hellman-group-exchange-sha1',
|
|
21
|
-
'diffie-hellman-group1-sha1'
|
|
22
|
-
]
|
|
23
|
-
/**
|
|
24
|
-
* Ciphers for SFTP support - in descending order of priority
|
|
25
|
-
* @type {string[]}
|
|
26
|
-
*/
|
|
27
|
-
export const SFTP_CIPHERS = [
|
|
28
|
-
// http://tools.ietf.org/html/rfc4344#section-4
|
|
29
|
-
'aes256-ctr',
|
|
30
|
-
'aes192-ctr',
|
|
31
|
-
'aes128-ctr',
|
|
32
|
-
'aes256-gcm',
|
|
33
|
-
'aes256-gcm@openssh.com',
|
|
34
|
-
'aes128-gcm',
|
|
35
|
-
'aes128-gcm@openssh.com',
|
|
36
|
-
'aes256-cbc',
|
|
37
|
-
'aes192-cbc',
|
|
38
|
-
'aes128-cbc',
|
|
39
|
-
'blowfish-cbc',
|
|
40
|
-
'3des-cbc',
|
|
41
|
-
'cast128-cbc'
|
|
42
|
-
]
|
|
3
|
+
const { secretsManager } = AWS()
|
|
43
4
|
const falseRegEx = /(false|0)/i
|
|
44
5
|
const trueRegEx = /(true|1)/i
|
|
45
6
|
const toBoolean = val => {
|
|
@@ -54,7 +15,6 @@ const toBoolean = val => {
|
|
|
54
15
|
|
|
55
16
|
class Config {
|
|
56
17
|
_file
|
|
57
|
-
_ftp
|
|
58
18
|
_s3
|
|
59
19
|
_pgp
|
|
60
20
|
|
|
@@ -68,20 +28,6 @@ class Config {
|
|
|
68
28
|
*/
|
|
69
29
|
partFileSize: Math.min(Number.parseInt(process.env.FULFILMENT_FILE_SIZE), 999)
|
|
70
30
|
}
|
|
71
|
-
this.ftp = {
|
|
72
|
-
host: process.env.FULFILMENT_FTP_HOST,
|
|
73
|
-
port: process.env.FULFILMENT_FTP_PORT || '22',
|
|
74
|
-
path: process.env.FULFILMENT_FTP_PATH,
|
|
75
|
-
username: process.env.FULFILMENT_FTP_USERNAME,
|
|
76
|
-
privateKey: (await secretsManager.getSecretValue({ SecretId: process.env.FULFILMENT_FTP_KEY_SECRET_ID }).promise()).SecretString,
|
|
77
|
-
algorithms: { cipher: SFTP_CIPHERS, kex: SFTP_KEY_EXCHANGE_ALGORITHMS },
|
|
78
|
-
// Wait up to 60 seconds for the SSH handshake
|
|
79
|
-
readyTimeout: 60000,
|
|
80
|
-
// Retry 5 times over a minute
|
|
81
|
-
retries: 5,
|
|
82
|
-
retry_minTimeout: 12000,
|
|
83
|
-
debug: db('fulfilment:ftp')
|
|
84
|
-
}
|
|
85
31
|
this.s3 = {
|
|
86
32
|
bucket: process.env.FULFILMENT_S3_BUCKET
|
|
87
33
|
}
|
|
@@ -104,18 +50,6 @@ class Config {
|
|
|
104
50
|
this._file = cfg
|
|
105
51
|
}
|
|
106
52
|
|
|
107
|
-
/**
|
|
108
|
-
* FTP configuration settings
|
|
109
|
-
* @type {object}
|
|
110
|
-
*/
|
|
111
|
-
get ftp () {
|
|
112
|
-
return this._ftp
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
set ftp (cfg) {
|
|
116
|
-
this._ftp = cfg
|
|
117
|
-
}
|
|
118
|
-
|
|
119
53
|
/**
|
|
120
54
|
* S3 configuration settings
|
|
121
55
|
* @type {object}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Readable, PassThrough, Writable } from 'stream'
|
|
2
2
|
import { deliverFulfilmentFiles } from '../deliver-fulfilment-files.js'
|
|
3
3
|
import { createS3WriteStream, readS3PartFiles } from '../../transport/s3.js'
|
|
4
|
-
import { createFtpWriteStream } from '../../transport/ftp.js'
|
|
5
4
|
import { FULFILMENT_FILE_STATUS_OPTIONSET, getOptionSetEntry } from '../staging-common.js'
|
|
6
5
|
import { FulfilmentRequestFile, executeQuery, persist } from '@defra-fish/dynamics-lib'
|
|
7
6
|
import openpgp from 'openpgp'
|
|
@@ -10,7 +9,6 @@ import streamHelper from '../streamHelper.js'
|
|
|
10
9
|
import merge2 from 'merge2'
|
|
11
10
|
|
|
12
11
|
jest.mock('../../transport/s3.js')
|
|
13
|
-
jest.mock('../../transport/ftp.js')
|
|
14
12
|
jest.mock('openpgp', () => ({
|
|
15
13
|
readKey: jest.fn(() => ({})),
|
|
16
14
|
encrypt: jest.fn(({ message: readableStream }) => readableStream),
|
|
@@ -46,20 +44,10 @@ describe('deliverFulfilmentFiles', () => {
|
|
|
46
44
|
executeQuery.mockResolvedValue([{ entity: mockFulfilmentRequestFile2 }, { entity: mockFulfilmentRequestFile1 }])
|
|
47
45
|
|
|
48
46
|
// Streams for file1
|
|
49
|
-
const {
|
|
50
|
-
s3DataStreamFile: s3DataStreamFile1,
|
|
51
|
-
ftpDataStreamFile: ftpDataStreamFile1,
|
|
52
|
-
s3HashStreamFile: s3HashStreamFile1,
|
|
53
|
-
ftpHashStreamFile: ftpHashStreamFile1
|
|
54
|
-
} = createMockFileStreams()
|
|
47
|
+
const { s3DataStreamFile: s3DataStreamFile1, s3HashStreamFile: s3HashStreamFile1 } = createMockFileStreams()
|
|
55
48
|
|
|
56
49
|
// Streams for file2
|
|
57
|
-
const {
|
|
58
|
-
s3DataStreamFile: s3DataStreamFile2,
|
|
59
|
-
ftpDataStreamFile: ftpDataStreamFile2,
|
|
60
|
-
s3HashStreamFile: s3HashStreamFile2,
|
|
61
|
-
ftpHashStreamFile: ftpHashStreamFile2
|
|
62
|
-
} = createMockFileStreams()
|
|
50
|
+
const { s3DataStreamFile: s3DataStreamFile2, s3HashStreamFile: s3HashStreamFile2 } = createMockFileStreams()
|
|
63
51
|
|
|
64
52
|
// Run the delivery
|
|
65
53
|
await expect(deliverFulfilmentFiles()).resolves.toBeUndefined()
|
|
@@ -69,22 +57,14 @@ describe('deliverFulfilmentFiles', () => {
|
|
|
69
57
|
// File 1 expectations
|
|
70
58
|
expect(createS3WriteStream).toHaveBeenNthCalledWith(1, 'EAFF202006180001.json')
|
|
71
59
|
expect(createS3WriteStream).toHaveBeenNthCalledWith(3, 'EAFF202006180001.json.sha256')
|
|
72
|
-
expect(createFtpWriteStream).toHaveBeenNthCalledWith(1, 'EAFF202006180001.json')
|
|
73
|
-
expect(createFtpWriteStream).toHaveBeenNthCalledWith(3, 'EAFF202006180001.json.sha256')
|
|
74
60
|
expect(JSON.parse(s3DataStreamFile1.dataProcessed)).toEqual({ licences: [{ part: 0 }, { part: 1 }] })
|
|
75
|
-
expect(JSON.parse(ftpDataStreamFile1.dataProcessed)).toEqual({ licences: [{ part: 0 }, { part: 1 }] })
|
|
76
61
|
expect(s3HashStreamFile1.dataProcessed).toEqual(fileShaHash) // validated
|
|
77
|
-
expect(ftpHashStreamFile1.dataProcessed).toEqual(fileShaHash) // validated
|
|
78
62
|
|
|
79
63
|
// File 2 expectations
|
|
80
64
|
expect(createS3WriteStream).toHaveBeenNthCalledWith(4, 'EAFF202006180002.json')
|
|
81
65
|
expect(createS3WriteStream).toHaveBeenNthCalledWith(6, 'EAFF202006180002.json.sha256')
|
|
82
|
-
expect(createFtpWriteStream).toHaveBeenNthCalledWith(4, 'EAFF202006180002.json')
|
|
83
|
-
expect(createFtpWriteStream).toHaveBeenNthCalledWith(6, 'EAFF202006180002.json.sha256')
|
|
84
66
|
expect(JSON.parse(s3DataStreamFile2.dataProcessed)).toEqual({ licences: [{ part: 0 }, { part: 1 }] })
|
|
85
|
-
expect(JSON.parse(ftpDataStreamFile2.dataProcessed)).toEqual({ licences: [{ part: 0 }, { part: 1 }] })
|
|
86
67
|
expect(s3HashStreamFile2.dataProcessed).toEqual(fileShaHash) // validated
|
|
87
|
-
expect(ftpHashStreamFile2.dataProcessed).toEqual(fileShaHash) // validated
|
|
88
68
|
|
|
89
69
|
// Persist to dynamics for file 1
|
|
90
70
|
expect(persist).toHaveBeenNthCalledWith(1, [
|
|
@@ -197,10 +177,7 @@ describe('deliverFulfilmentFiles', () => {
|
|
|
197
177
|
const s3 = createTestableStream()
|
|
198
178
|
streamHelper.pipelinePromise.mockResolvedValue()
|
|
199
179
|
openpgp.encrypt.mockResolvedValue(s2)
|
|
200
|
-
merge2
|
|
201
|
-
.mockReturnValueOnce(s1)
|
|
202
|
-
.mockReturnValueOnce(s2)
|
|
203
|
-
.mockReturnValueOnce(s3)
|
|
180
|
+
merge2.mockReturnValueOnce(s1).mockReturnValueOnce(s2).mockReturnValueOnce(s3)
|
|
204
181
|
await mockExecuteQuery()
|
|
205
182
|
createMockFileStreams()
|
|
206
183
|
|
|
@@ -227,27 +204,18 @@ const createMockFulfilmentRequestFile = async (fileName, date) =>
|
|
|
227
204
|
|
|
228
205
|
const createMockFileStreams = () => {
|
|
229
206
|
const s3DataStreamFile = createTestableStream()
|
|
230
|
-
const ftpDataStreamFile = createTestableStream()
|
|
231
207
|
createS3WriteStream.mockReturnValueOnce({ s3WriteStream: s3DataStreamFile, managedUpload: Promise.resolve() })
|
|
232
|
-
createFtpWriteStream.mockReturnValueOnce({ ftpWriteStream: ftpDataStreamFile, managedUpload: Promise.resolve() })
|
|
233
208
|
|
|
234
209
|
const s3EncryptedDataStreamFile = createTestableStream()
|
|
235
|
-
const ftpEncryptedDataStreamFile = createTestableStream()
|
|
236
210
|
createS3WriteStream.mockReturnValueOnce({ s3WriteStream: s3EncryptedDataStreamFile, managedUpload: Promise.resolve() })
|
|
237
|
-
createFtpWriteStream.mockReturnValueOnce({ ftpWriteStream: ftpEncryptedDataStreamFile, managedUpload: Promise.resolve() })
|
|
238
211
|
|
|
239
212
|
const s3HashStreamFile = createTestableStream()
|
|
240
|
-
const ftpHashStreamFile = createTestableStream()
|
|
241
213
|
createS3WriteStream.mockReturnValueOnce({ s3WriteStream: s3HashStreamFile, managedUpload: Promise.resolve() })
|
|
242
|
-
createFtpWriteStream.mockReturnValueOnce({ ftpWriteStream: ftpHashStreamFile, managedUpload: Promise.resolve() })
|
|
243
214
|
|
|
244
215
|
return {
|
|
245
216
|
s3DataStreamFile,
|
|
246
|
-
ftpDataStreamFile,
|
|
247
217
|
s3EncryptedDataStreamFile,
|
|
248
|
-
|
|
249
|
-
s3HashStreamFile,
|
|
250
|
-
ftpHashStreamFile
|
|
218
|
+
s3HashStreamFile
|
|
251
219
|
}
|
|
252
220
|
}
|
|
253
221
|
|
|
@@ -4,7 +4,6 @@ import merge2 from 'merge2'
|
|
|
4
4
|
import moment from 'moment'
|
|
5
5
|
import { executeQuery, persist, findFulfilmentFiles } from '@defra-fish/dynamics-lib'
|
|
6
6
|
import { createS3WriteStream, readS3PartFiles } from '../transport/s3.js'
|
|
7
|
-
import { createFtpWriteStream } from '../transport/ftp.js'
|
|
8
7
|
import { FULFILMENT_FILE_STATUS_OPTIONSET, getOptionSetEntry } from './staging-common.js'
|
|
9
8
|
import db from 'debug'
|
|
10
9
|
import openpgp from 'openpgp'
|
|
@@ -69,11 +68,6 @@ const createEncryptedDataReadStream = async file => {
|
|
|
69
68
|
*/
|
|
70
69
|
const deliver = async (targetFileName, readableStream, ...transforms) => {
|
|
71
70
|
const { s3WriteStream: s3DataStream, managedUpload: s3DataManagedUpload } = createS3WriteStream(targetFileName)
|
|
72
|
-
const { ftpWriteStream: ftpDataStream, managedUpload: ftpDataManagedUpload } = createFtpWriteStream(targetFileName)
|
|
73
71
|
|
|
74
|
-
await Promise.all([
|
|
75
|
-
streamHelper.pipelinePromise([readableStream, ...transforms, s3DataStream, ftpDataStream]),
|
|
76
|
-
s3DataManagedUpload,
|
|
77
|
-
ftpDataManagedUpload
|
|
78
|
-
])
|
|
72
|
+
await Promise.all([streamHelper.pipelinePromise([readableStream, ...transforms, s3DataStream]), s3DataManagedUpload])
|
|
79
73
|
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
const ssh2sftpClient = jest.genMockFromModule('ssh2-sftp-client')
|
|
2
|
-
|
|
3
|
-
export const mockedFtpMethods = {
|
|
4
|
-
connect: jest.fn(async () => {}),
|
|
5
|
-
put: jest.fn(async () => {}),
|
|
6
|
-
end: jest.fn()
|
|
7
|
-
}
|
|
8
|
-
ssh2sftpClient.mockImplementation(() => mockedFtpMethods)
|
|
9
|
-
export default ssh2sftpClient
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { createFtpWriteStream } from '../ftp.js'
|
|
2
|
-
import { mockedFtpMethods } from 'ssh2-sftp-client'
|
|
3
|
-
|
|
4
|
-
jest.mock('stream')
|
|
5
|
-
jest.mock('../../config.js', () => ({
|
|
6
|
-
ftp: {
|
|
7
|
-
host: 'testhost',
|
|
8
|
-
port: 2222,
|
|
9
|
-
path: 'testpath/',
|
|
10
|
-
username: 'testusername',
|
|
11
|
-
privateKey: 'testprivatekey'
|
|
12
|
-
}
|
|
13
|
-
}))
|
|
14
|
-
|
|
15
|
-
describe('ftp', () => {
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
jest.clearAllMocks()
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
describe('createFtpWriteStream', () => {
|
|
21
|
-
it('creates a stream to write to the configured FTP server', async () => {
|
|
22
|
-
const { ftpWriteStream, managedUpload } = createFtpWriteStream('testfile.json')
|
|
23
|
-
ftpWriteStream.write('Some data')
|
|
24
|
-
ftpWriteStream.end()
|
|
25
|
-
await managedUpload
|
|
26
|
-
expect(mockedFtpMethods.connect).toHaveBeenCalledWith(
|
|
27
|
-
expect.objectContaining({
|
|
28
|
-
host: 'testhost',
|
|
29
|
-
port: 2222,
|
|
30
|
-
username: 'testusername',
|
|
31
|
-
privateKey: 'testprivatekey'
|
|
32
|
-
})
|
|
33
|
-
)
|
|
34
|
-
expect(mockedFtpMethods.put).toHaveBeenCalledWith(ftpWriteStream, 'testpath/testfile.json', {
|
|
35
|
-
flags: 'w',
|
|
36
|
-
encoding: 'UTF-8',
|
|
37
|
-
autoClose: false
|
|
38
|
-
})
|
|
39
|
-
expect(mockedFtpMethods.end).toHaveBeenCalled()
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('rejects the managed upload promise if an FTP upload error occurs', async () => {
|
|
43
|
-
const testError = new Error('Test error')
|
|
44
|
-
mockedFtpMethods.put.mockImplementationOnce(() => Promise.reject(testError))
|
|
45
|
-
const { ftpWriteStream, managedUpload } = createFtpWriteStream('testfile.json')
|
|
46
|
-
await expect(managedUpload).rejects.toThrow('Test error')
|
|
47
|
-
expect(mockedFtpMethods.connect).toHaveBeenCalledWith(
|
|
48
|
-
expect.objectContaining({
|
|
49
|
-
host: 'testhost',
|
|
50
|
-
port: 2222,
|
|
51
|
-
username: 'testusername',
|
|
52
|
-
privateKey: 'testprivatekey'
|
|
53
|
-
})
|
|
54
|
-
)
|
|
55
|
-
expect(mockedFtpMethods.put).toHaveBeenCalledWith(ftpWriteStream, 'testpath/testfile.json', {
|
|
56
|
-
flags: 'w',
|
|
57
|
-
encoding: 'UTF-8',
|
|
58
|
-
autoClose: false
|
|
59
|
-
})
|
|
60
|
-
expect(mockedFtpMethods.end).toHaveBeenCalled()
|
|
61
|
-
})
|
|
62
|
-
})
|
|
63
|
-
})
|
package/src/transport/ftp.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import FtpClient from 'ssh2-sftp-client'
|
|
2
|
-
import Path from 'path'
|
|
3
|
-
import { PassThrough } from 'stream'
|
|
4
|
-
import config from '../config.js'
|
|
5
|
-
import db from 'debug'
|
|
6
|
-
const debug = db('fulfilment:transport')
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Create a stream to write to the configured FTP server
|
|
10
|
-
*
|
|
11
|
-
* @param {string} filename The name of the file to be written to the remote server
|
|
12
|
-
* @returns {{ftpWriteStream: module:stream.internal.PassThrough, managedUpload: Promise<*>}}
|
|
13
|
-
*/
|
|
14
|
-
export const createFtpWriteStream = filename => {
|
|
15
|
-
const sftp = new FtpClient()
|
|
16
|
-
const passThrough = new PassThrough()
|
|
17
|
-
const remoteFilePath = Path.join(config.ftp.path, filename)
|
|
18
|
-
return {
|
|
19
|
-
ftpWriteStream: passThrough,
|
|
20
|
-
managedUpload: sftp
|
|
21
|
-
.connect(config.ftp)
|
|
22
|
-
.then(() => sftp.put(passThrough, remoteFilePath, { flags: 'w', encoding: 'UTF-8', autoClose: false }))
|
|
23
|
-
.then(() =>
|
|
24
|
-
debug('File successfully uploaded to fulfilment provider at sftp://%s:%s%s', config.ftp.host, config.ftp.port, remoteFilePath)
|
|
25
|
-
)
|
|
26
|
-
.finally(() => sftp.end())
|
|
27
|
-
}
|
|
28
|
-
}
|