Skip to content

Commit 8189fe2

Browse files
committed
Address PR comments
1 parent 9a26ca9 commit 8189fe2

File tree

8 files changed

+117
-130
lines changed

8 files changed

+117
-130
lines changed

packages-exp/functions-exp/.eslintrc.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ module.exports = {
3030
'error',
3131
{
3232
'packageDir': [path.resolve(__dirname, '../../'), __dirname],
33-
devDependencies: true,
34-
peerDependencies: true
33+
devDependencies: true
3534
}
3635
]
3736
}

packages-exp/functions-exp/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"license": "Apache-2.0",
3434
"peerDependencies": {
3535
"@firebase/app-exp": "0.x",
36-
"@firebase/app-exp-types": "0.x"
36+
"@firebase/app-types-exp": "0.x"
3737
},
3838
"devDependencies": {
3939
"@firebase/app-exp": "0.0.800",

packages-exp/functions-exp/src/context.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
import { _FirebaseApp } from '@firebase/app-types/private';
1817
import {
1918
FirebaseMessaging,
2019
FirebaseMessagingName

packages-exp/functions-exp/src/error.ts

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { HttpsError, FunctionsErrorCode } from '@firebase/functions-types-exp';
19-
import { Serializer } from './serializer';
18+
import { FunctionsErrorCode } from '@firebase/functions-types-exp';
19+
import { decode } from './serializer';
2020
import { HttpResponseBody } from './service';
21+
import { FirebaseError } from '@firebase/util';
2122

2223
/**
2324
* Standard error codes for different ways a request can fail, as defined by:
@@ -50,28 +51,20 @@ const errorCodeMap: { [name: string]: FunctionsErrorCode } = {
5051
* An explicit error that can be thrown from a handler to send an error to the
5152
* client that called the function.
5253
*/
53-
export class HttpsErrorImpl extends Error implements HttpsError {
54-
/**
55-
* A standard error code that will be returned to the client. This also
56-
* determines the HTTP status code of the response, as defined in code.proto.
57-
*/
58-
readonly code: FunctionsErrorCode;
59-
60-
/**
61-
* Extra data to be converted to JSON and included in the error response.
62-
*/
63-
readonly details?: unknown;
64-
65-
constructor(code: FunctionsErrorCode, message?: string, details?: unknown) {
66-
super(message);
67-
68-
// This is a workaround for a bug in TypeScript when extending Error:
69-
// tslint:disable-next-line
70-
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
71-
Object.setPrototypeOf(this, HttpsErrorImpl.prototype);
72-
73-
this.code = code;
74-
this.details = details;
54+
export class FunctionsError extends FirebaseError {
55+
constructor(
56+
/**
57+
* A standard error code that will be returned to the client. This also
58+
* determines the HTTP status code of the response, as defined in code.proto.
59+
*/
60+
readonly code: FunctionsErrorCode,
61+
message?: string,
62+
/**
63+
* Extra data to be converted to JSON and included in the error response.
64+
*/
65+
readonly details?: unknown
66+
) {
67+
super(code, message || '');
7568
}
7669
}
7770

@@ -124,8 +117,7 @@ function codeForHTTPStatus(status: number): FunctionsErrorCode {
124117
*/
125118
export function _errorForResponse(
126119
status: number,
127-
bodyJSON: HttpResponseBody | null,
128-
serializer: Serializer
120+
bodyJSON: HttpResponseBody | null
129121
): Error | null {
130122
let code = codeForHTTPStatus(status);
131123

@@ -142,7 +134,7 @@ export function _errorForResponse(
142134
if (typeof status === 'string') {
143135
if (!errorCodeMap[status]) {
144136
// They must've included an unknown error code in the body.
145-
return new HttpsErrorImpl('internal', 'internal');
137+
return new FunctionsError('internal', 'internal');
146138
}
147139
code = errorCodeMap[status];
148140

@@ -158,7 +150,7 @@ export function _errorForResponse(
158150

159151
details = errorJSON.details;
160152
if (details !== undefined) {
161-
details = serializer.decode(details as {} | null);
153+
details = decode(details as {} | null);
162154
}
163155
}
164156
} catch (e) {
@@ -172,5 +164,5 @@ export function _errorForResponse(
172164
return null;
173165
}
174166

175-
return new HttpsErrorImpl(code, description, details);
167+
return new FunctionsError(code, description, details);
176168
}

packages-exp/functions-exp/src/serializer.test.ts

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,88 +15,82 @@
1515
* limitations under the License.
1616
*/
1717
import { expect } from 'chai';
18-
import { Serializer } from './serializer';
18+
import { encode, decode } from './serializer';
1919

2020
describe('Serializer', () => {
21-
const serializer = new Serializer();
22-
2321
it('encodes null', () => {
24-
expect(serializer.encode(null)).to.be.null;
25-
expect(serializer.encode(undefined)).to.be.null;
22+
expect(encode(null)).to.be.null;
23+
expect(encode(undefined)).to.be.null;
2624
});
2725

2826
it('decodes null', () => {
29-
expect(serializer.decode(null)).to.be.null;
27+
expect(decode(null)).to.be.null;
3028
});
3129

3230
it('encodes int', () => {
33-
expect(serializer.encode(1)).to.equal(1);
31+
expect(encode(1)).to.equal(1);
3432
// Number isn't allowed in our own codebase, but we need to test it, in case
3533
// a user passes one in. There's no reason not to support it, and we don't
3634
// want to unintentionally encode them as {}.
3735
// eslint-disable-next-line no-new-wrappers
38-
expect(serializer.encode(new Number(1))).to.equal(1);
36+
expect(encode(new Number(1))).to.equal(1);
3937
});
4038

4139
it('decodes int', () => {
42-
expect(serializer.decode(1)).to.equal(1);
40+
expect(decode(1)).to.equal(1);
4341
});
4442

4543
it('encodes long', () => {
46-
expect(serializer.encode(-9223372036854775000)).to.equal(
47-
-9223372036854775000
48-
);
44+
expect(encode(-9223372036854775000)).to.equal(-9223372036854775000);
4945
});
5046

5147
it('decodes long', () => {
5248
expect(
53-
serializer.decode({
49+
decode({
5450
'@type': 'type.googleapis.com/google.protobuf.Int64Value',
5551
value: '-9223372036854775000'
5652
})
5753
).to.equal(-9223372036854775000);
5854
});
5955

6056
it('encodes unsigned long', () => {
61-
expect(serializer.encode(9223372036854800000)).to.equal(
62-
9223372036854800000
63-
);
57+
expect(encode(9223372036854800000)).to.equal(9223372036854800000);
6458
});
6559

6660
it('decodes unsigned long', () => {
6761
expect(
68-
serializer.decode({
62+
decode({
6963
'@type': 'type.googleapis.com/google.protobuf.UInt64Value',
7064
value: '9223372036854800000'
7165
})
7266
).to.equal(9223372036854800000);
7367
});
7468

7569
it('encodes double', () => {
76-
expect(serializer.encode(1.2)).to.equal(1.2);
70+
expect(encode(1.2)).to.equal(1.2);
7771
});
7872

7973
it('decodes double', () => {
80-
expect(serializer.decode(1.2)).to.equal(1.2);
74+
expect(decode(1.2)).to.equal(1.2);
8175
});
8276

8377
it('encodes string', () => {
84-
expect(serializer.encode('hello')).to.equal('hello');
78+
expect(encode('hello')).to.equal('hello');
8579
});
8680

8781
it('decodes string', () => {
88-
expect(serializer.decode('hello')).to.equal('hello');
82+
expect(decode('hello')).to.equal('hello');
8983
});
9084

9185
// TODO(klimt): Make this test more interesting once we have a complex type
9286
// that can be created in JavaScript.
9387
it('encodes array', () => {
94-
expect(serializer.encode([1, '2', [3, 4]])).to.deep.equal([1, '2', [3, 4]]);
88+
expect(encode([1, '2', [3, 4]])).to.deep.equal([1, '2', [3, 4]]);
9589
});
9690

9791
it('decodes array', () => {
9892
expect(
99-
serializer.decode([
93+
decode([
10094
1,
10195
'2',
10296
[
@@ -114,7 +108,7 @@ describe('Serializer', () => {
114108
// that can be created in JavaScript.
115109
it('encodes object', () => {
116110
expect(
117-
serializer.encode({
111+
encode({
118112
foo: 1,
119113
bar: 'hello',
120114
baz: [1, 2, 3]
@@ -128,7 +122,7 @@ describe('Serializer', () => {
128122

129123
it('decodes object', () => {
130124
expect(
131-
serializer.decode({
125+
decode({
132126
foo: 1,
133127
bar: 'hello',
134128
baz: [
@@ -148,12 +142,12 @@ describe('Serializer', () => {
148142
});
149143

150144
it('fails to encode NaN', () => {
151-
expect(() => serializer.encode(NaN)).to.throw();
145+
expect(() => encode(NaN)).to.throw();
152146
});
153147

154148
it('fails to decode unknown type', () => {
155149
expect(() =>
156-
serializer.decode({
150+
decode({
157151
'@type': 'unknown',
158152
value: 'should be ignored'
159153
})

packages-exp/functions-exp/src/serializer.ts

Lines changed: 61 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -33,71 +33,74 @@ function mapValues(
3333
}
3434

3535
/**
36+
* Takes data and encodes it in a JSON-friendly way, such that types such as
37+
* Date are preserved.
3638
* @internal
39+
* @param data - Data to encode.
3740
*/
38-
export class Serializer {
39-
// Takes data and encodes it in a JSON-friendly way, such that types such as
40-
// Date are preserved.
41-
encode(data: unknown): unknown {
42-
if (data == null) {
43-
return null;
44-
}
45-
if (data instanceof Number) {
46-
data = data.valueOf();
47-
}
48-
if (typeof data === 'number' && isFinite(data)) {
49-
// Any number in JS is safe to put directly in JSON and parse as a double
50-
// without any loss of precision.
51-
return data;
52-
}
53-
if (data === true || data === false) {
54-
return data;
55-
}
56-
if (Object.prototype.toString.call(data) === '[object String]') {
57-
return data;
58-
}
59-
if (Array.isArray(data)) {
60-
return data.map(x => this.encode(x));
61-
}
62-
if (typeof data === 'function' || typeof data === 'object') {
63-
return mapValues(data as object, x => this.encode(x));
64-
}
65-
// If we got this far, the data is not encodable.
66-
throw new Error('Data cannot be encoded in JSON: ' + data);
41+
export function encode(data: unknown): unknown {
42+
if (data == null) {
43+
return null;
6744
}
45+
if (data instanceof Number) {
46+
data = data.valueOf();
47+
}
48+
if (typeof data === 'number' && isFinite(data)) {
49+
// Any number in JS is safe to put directly in JSON and parse as a double
50+
// without any loss of precision.
51+
return data;
52+
}
53+
if (data === true || data === false) {
54+
return data;
55+
}
56+
if (Object.prototype.toString.call(data) === '[object String]') {
57+
return data;
58+
}
59+
if (Array.isArray(data)) {
60+
return data.map(x => encode(x));
61+
}
62+
if (typeof data === 'function' || typeof data === 'object') {
63+
return mapValues(data as object, x => encode(x));
64+
}
65+
// If we got this far, the data is not encodable.
66+
throw new Error('Data cannot be encoded in JSON: ' + data);
67+
}
6868

69-
// Takes data that's been encoded in a JSON-friendly form and returns a form
70-
// with richer datatypes, such as Dates, etc.
71-
decode(json: unknown): unknown {
72-
if (json == null) {
73-
return json;
74-
}
75-
if ((json as { [key: string]: unknown })['@type']) {
76-
switch ((json as { [key: string]: unknown })['@type']) {
77-
case LONG_TYPE:
78-
// Fall through and handle this the same as unsigned.
79-
case UNSIGNED_LONG_TYPE: {
80-
// Technically, this could work return a valid number for malformed
81-
// data if there was a number followed by garbage. But it's just not
82-
// worth all the extra code to detect that case.
83-
const value = Number((json as { [key: string]: unknown })['value']);
84-
if (isNaN(value)) {
85-
throw new Error('Data cannot be decoded from JSON: ' + json);
86-
}
87-
return value;
88-
}
89-
default: {
69+
/**
70+
* Takes data that's been encoded in a JSON-friendly form and returns a form
71+
* with richer datatypes, such as Dates, etc.
72+
* @internal
73+
* @param json - JSON to convert.
74+
*/
75+
export function decode(json: unknown): unknown {
76+
if (json == null) {
77+
return json;
78+
}
79+
if ((json as { [key: string]: unknown })['@type']) {
80+
switch ((json as { [key: string]: unknown })['@type']) {
81+
case LONG_TYPE:
82+
// Fall through and handle this the same as unsigned.
83+
case UNSIGNED_LONG_TYPE: {
84+
// Technically, this could work return a valid number for malformed
85+
// data if there was a number followed by garbage. But it's just not
86+
// worth all the extra code to detect that case.
87+
const value = Number((json as { [key: string]: unknown })['value']);
88+
if (isNaN(value)) {
9089
throw new Error('Data cannot be decoded from JSON: ' + json);
9190
}
91+
return value;
92+
}
93+
default: {
94+
throw new Error('Data cannot be decoded from JSON: ' + json);
9295
}
9396
}
94-
if (Array.isArray(json)) {
95-
return json.map(x => this.decode(x));
96-
}
97-
if (typeof json === 'function' || typeof json === 'object') {
98-
return mapValues(json as object, x => this.decode(x as {} | null));
99-
}
100-
// Anything else is safe to return.
101-
return json;
10297
}
98+
if (Array.isArray(json)) {
99+
return json.map(x => decode(x));
100+
}
101+
if (typeof json === 'function' || typeof json === 'object') {
102+
return mapValues(json as object, x => decode(x as {} | null));
103+
}
104+
// Anything else is safe to return.
105+
return json;
103106
}

0 commit comments

Comments
 (0)