Skip to content

Commit ef7f422

Browse files
committed
feat(letMixin): support a let stage on all services
1 parent a7e4283 commit ef7f422

17 files changed

+250
-39
lines changed

Diff for: src/services/aws/s3_service.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { serviceResponse } from '../../util';
1+
import { serviceResponse, letMixin } from '../../util';
22

33
/**
44
* Convenience wrapper around AWS S3 service (not meant to be instantiated directly).
@@ -24,7 +24,7 @@ class S3Service {
2424
* @return {Promise}
2525
*/
2626
put(bucket, key, acl, contentType) {
27-
return serviceResponse(this.client, {
27+
return serviceResponse(this, {
2828
service: this.serviceName,
2929
action: 'put',
3030
args: { bucket, key, acl, contentType }
@@ -41,12 +41,12 @@ class S3Service {
4141
* @return {Promise}
4242
*/
4343
signPolicy(bucket, key, acl, contentType) {
44-
return serviceResponse(this.client, {
44+
return serviceResponse(this, {
4545
service: this.serviceName,
4646
action: 'signPolicy',
4747
args: { bucket, key, acl, contentType }
4848
});
4949
}
5050
}
5151

52-
export default S3Service;
52+
export default letMixin(S3Service);

Diff for: src/services/aws/ses_service.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { serviceResponse } from '../../util';
1+
import { serviceResponse, letMixin } from '../../util';
22

33
/**
44
* Convenience wrapper around AWS SES service (not meant to be instantiated directly).
@@ -23,12 +23,12 @@ class SESService {
2323
* @return {Promise}
2424
*/
2525
send(from, to, subject, body) {
26-
return serviceResponse(this.client, {
26+
return serviceResponse(this, {
2727
service: this.serviceName,
2828
action: 'send',
2929
args: { from, to, subject, body }
3030
});
3131
}
3232
}
3333

34-
export default SESService;
34+
export default letMixin(SESService);

Diff for: src/services/aws/sqs_service.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { serviceResponse } from '../../util';
1+
import { serviceResponse, letMixin } from '../../util';
22

33
/**
44
* Convenience wrapper for AWS SQS (not meant to be instantiated directly).
@@ -7,7 +7,7 @@ import { serviceResponse } from '../../util';
77
* @return {SQSService} a SQSService instance.
88
*/
99
class SQSService {
10-
constructor(stitchClient, serviceName) {
10+
constructor(client, serviceName) {
1111
this.client = client;
1212
this.serviceName = serviceName;
1313
}
@@ -18,7 +18,7 @@ class SQSService {
1818
* @return {Promise}
1919
*/
2020
send() {
21-
return serviceResponse(this.client, {
21+
return serviceResponse(this, {
2222
service: this.serviceName,
2323
action: 'send'
2424
});
@@ -30,11 +30,11 @@ class SQSService {
3030
* @return {Promise}
3131
*/
3232
receive() {
33-
return serviceResponse(this.client, {
33+
return serviceResponse(this, {
3434
service: this.serviceName,
3535
action: 'receive'
3636
});
3737
}
3838
}
3939

40-
export default SQSService;
40+
export default letMixin(SQSService);

Diff for: src/services/http/http_service.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { serviceResponse } from '../../util';
1+
import { serviceResponse, letMixin } from '../../util';
22

33
/**
44
* Convenience wrapper for HTTP service (not meant to be instantiated directly).
@@ -104,11 +104,11 @@ function buildArgs(urlOrOptions, options) {
104104
}
105105

106106
function buildResponse(action, service, args) {
107-
return serviceResponse(service.client, {
107+
return serviceResponse(service, {
108108
service: service.serviceName,
109109
action: action,
110110
args: args
111111
});
112112
}
113113

114-
export default HTTPService;
114+
export default letMixin(HTTPService);

Diff for: src/services/mongodb/collection.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ function insertOp(self, docs, options) {
164164
}
165165
});
166166

167-
return serviceResponse(self.db.client, stages, response => {
167+
return serviceResponse(self.db, stages, response => {
168168
return {
169169
insertedIds: response.result.map(doc => doc._id)
170170
};
@@ -178,7 +178,7 @@ function deleteOp(self, query, options) {
178178
query: query
179179
}, options);
180180

181-
return serviceResponse(self.db.client, {
181+
return serviceResponse(self.db, {
182182
service: self.db.service,
183183
action: 'delete',
184184
args: args
@@ -197,7 +197,7 @@ function updateOp(self, query, update, options) {
197197
update: update
198198
}, options);
199199

200-
return serviceResponse(self.db.client, {
200+
return serviceResponse(self.db, {
201201
service: self.db.service,
202202
action: 'update',
203203
args: args
@@ -218,7 +218,7 @@ function findOp(self, query, options, finalizer) {
218218
delete args.projection;
219219
}
220220

221-
return serviceResponse(self.db.client, {
221+
return serviceResponse(self.db, {
222222
service: self.db.service,
223223
action: 'find',
224224
args: args

Diff for: src/services/pubnub/pubnub_service.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { serviceResponse } from '../../util';
1+
import { serviceResponse, letMixin } from '../../util';
22

33
/**
44
* Convenience wrapper around Pubnub API (not meant to be instantiated directly).
@@ -21,12 +21,12 @@ class PubnubService {
2121
* @return {Promise}
2222
*/
2323
publish(channel, message) {
24-
return serviceResponse(this.client, {
24+
return serviceResponse(this, {
2525
service: this.serviceName,
2626
action: 'publish',
2727
args: { channel, message }
2828
});
2929
}
3030
}
3131

32-
export default PubnubService;
32+
export default letMixin(PubnubService);

Diff for: src/services/slack/slack_service.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { serviceResponse } from '../../util';
1+
import { serviceResponse, letMixin } from '../../util';
22

33
/**
44
* Convenience wrapper around Slack API (not meant to be instantiated directly).
@@ -32,12 +32,12 @@ class SlackService {
3232
if (!!options.iconEmoji) args.iconEmoji = options.iconEmoji;
3333
if (!!options.attachments) args.attachments = options.attachments;
3434

35-
return serviceResponse(this.client, {
35+
return serviceResponse(this, {
3636
service: this.serviceName,
3737
action: 'publish',
3838
args: args
3939
});
4040
}
4141
}
4242

43-
export default SlackService;
43+
export default letMixin(SlackService);

Diff for: src/services/twilio/twilio_service.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { serviceResponse } from '../../util';
1+
import { serviceResponse, letMixin } from '../../util';
22

33
/**
44
* Create a new TwilioService instance (not meant to be instantiated directly).
@@ -22,12 +22,12 @@ class TwilioService {
2222
* @return {Promise}
2323
*/
2424
send(from, to, body) {
25-
return serviceResponse(this.client, {
25+
return serviceResponse(this, {
2626
service: this.serviceName,
2727
action: 'send',
2828
args: { from, to, body }
2929
});
3030
}
3131
}
3232

33-
export default TwilioService;
33+
export default letMixin(TwilioService);

Diff for: src/util.js

+34-3
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,28 @@ function deprecate(fn, msg) {
2929
* API for calling helper methods (single-stage pipelines) and
3030
* pipeline building.
3131
*
32-
* @param {Object} client the client to execute the stages on
32+
* @param {Object} service the service to execute the stages on
3333
* @param {Array} stages the pipeline stages to execute
3434
* @param {Function} [finalizer] optional function to call on the result of the response
3535
*/
36-
function serviceResponse(client, stages, finalizer) {
36+
function serviceResponse(service, stages, finalizer) {
37+
if (service && !service.client) {
38+
throw new Error('Service has no client');
39+
}
40+
3741
if (finalizer && typeof finalizer !== 'function') {
3842
throw new Error('Service response finalizer must be a function');
3943
}
4044

45+
if (service.hasOwnProperty('__let__')) {
46+
if (Array.isArray(stages)) {
47+
// what do we do here?
48+
} else {
49+
stages.let = service.__let__;
50+
}
51+
}
52+
53+
const client = service.client;
4154
Object.defineProperties(stages, {
4255
then: {
4356
enumerable: false,
@@ -64,4 +77,22 @@ function serviceResponse(client, stages, finalizer) {
6477
return stages;
6578
}
6679

67-
export { deprecate, serviceResponse };
80+
/**
81+
* Mixin that allows a definition of an optional `let` stage for
82+
* services is mixes in with.
83+
*
84+
* @param {*} Type the service to mixin
85+
*/
86+
function letMixin(Type) {
87+
Type.prototype.let = function(options) {
88+
Object.defineProperty(this, '__let__', {
89+
enumerable: false, configurable: false, writable: false, value: options
90+
});
91+
92+
return this;
93+
};
94+
95+
return Type;
96+
}
97+
98+
export { deprecate, serviceResponse, letMixin };

Diff for: test/services/aws_s3.test.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const sinon = require('sinon');
2+
const S3Service = require('../../src/services/aws/s3_service');
3+
4+
class TestFixture {}
5+
TestFixture.prototype.setup = function() {
6+
this.client = { executePipeline: sinon.stub().resolves('foo') };
7+
this.service = new S3Service(test.client, 'testS3');
8+
};
9+
10+
const test = new TestFixture();
11+
describe('S3Service', function() {
12+
describe('substages', () => {
13+
beforeEach(() => test.setup());
14+
15+
it('should support a `let` argument', () => {
16+
return test.service
17+
.let({ test: '%%values.test' })
18+
.put('bucket', 'key', 'acl', 'contentType')
19+
.then(() => {
20+
const stage = test.client.executePipeline.getCall(0).args[0][0];
21+
expect(stage).toHaveProperty('let');
22+
expect(stage.let).toEqual({ test: '%%values.test' });
23+
});
24+
});
25+
});
26+
});

Diff for: test/services/aws_ses.test.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const sinon = require('sinon');
2+
const SESService = require('../../src/services/aws/ses_service');
3+
4+
class TestFixture {}
5+
TestFixture.prototype.setup = function() {
6+
this.client = { executePipeline: sinon.stub().resolves('foo') };
7+
this.service = new SESService(test.client, 'testSES');
8+
};
9+
10+
const test = new TestFixture();
11+
describe('SESService', function() {
12+
describe('substages', () => {
13+
beforeEach(() => test.setup());
14+
15+
it('should support a `let` argument', () => {
16+
return test.service
17+
.let({ test: '%%values.test' })
18+
.send('from', 'to', 'subject', 'body')
19+
.then(() => {
20+
const stage = test.client.executePipeline.getCall(0).args[0][0];
21+
expect(stage).toHaveProperty('let');
22+
expect(stage.let).toEqual({ test: '%%values.test' });
23+
});
24+
});
25+
});
26+
});

Diff for: test/services/aws_sqs.test.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const sinon = require('sinon');
2+
const SQSService = require('../../src/services/aws/sqs_service');
3+
4+
class TestFixture {}
5+
TestFixture.prototype.setup = function() {
6+
this.client = { executePipeline: sinon.stub().resolves('foo') };
7+
this.service = new SQSService(test.client, 'testSQS');
8+
};
9+
10+
const test = new TestFixture();
11+
describe('SQSService', function() {
12+
describe('substages', () => {
13+
beforeEach(() => test.setup());
14+
15+
it('should support a `let` argument', () => {
16+
return test.service
17+
.let({ test: '%%values.test' })
18+
.send()
19+
.then(() => {
20+
const stage = test.client.executePipeline.getCall(0).args[0][0];
21+
expect(stage).toHaveProperty('let');
22+
expect(stage.let).toEqual({ test: '%%values.test' });
23+
});
24+
});
25+
});
26+
});

Diff for: test/services/http.test.js

+24
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,30 @@ TestFixture.prototype.setup = function() {
99

1010
const test = new TestFixture();
1111
describe('HttpService', function() {
12+
describe('substages', () => {
13+
beforeEach(() => test.setup());
14+
it('should support a `let` argument', () => {
15+
return test.service
16+
.let({
17+
test: '%%values.test',
18+
auth: { $concat: [ 'Bearer ', '$$values.sg-api-key' ] }
19+
})
20+
.get('http://google.com')
21+
.then(() => {
22+
const stage = test.client.executePipeline.getCall(0).args[0][0];
23+
expect(stage).toEqual({
24+
service: 'testHttpService',
25+
action: 'get',
26+
args: { url: 'http://google.com' },
27+
'let': {
28+
test: '%%values.test',
29+
auth: { $concat: [ 'Bearer ', '$$values.sg-api-key' ] }
30+
}
31+
});
32+
});
33+
});
34+
});
35+
1236
['get', 'post', 'put', 'patch', 'delete', 'head'].forEach(method => {
1337
describe(method, () => {
1438
beforeEach(() => test.setup());

0 commit comments

Comments
 (0)