Skip to content

Commit d05d4e5

Browse files
committed
Fixing the GRPC toError() bug with ErrorValidation
1 parent a54598e commit d05d4e5

8 files changed

Lines changed: 141 additions & 13 deletions

File tree

src/validation/errors.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,45 @@ import { CustomError } from 'ts-custom-error';
22
import { ErrorPolykey, sysexits } from '../errors';
33

44
/**
5-
* This packages the `ErrorParse` array into the `data` property
6-
* This is to allow encoding to and decoding from GRPC errors
5+
* Generic error containing all parsing errors that occurred during
6+
* execution.
77
*/
88
class ErrorValidation extends ErrorPolykey {
9-
public readonly errors: Array<ErrorParse>;
10-
constructor(errors: Array<ErrorParse>) {
9+
description = 'Input data failed validation';
10+
exitCode = sysexits.DATAERR;
11+
public errors: Array<ErrorParse>;
12+
constructor(message, data) {
13+
super(message, data);
14+
if (data.errors != null) {
15+
const errors: Array<ErrorParse> = [];
16+
for (const eData of data.errors) {
17+
const errorParse = new ErrorParse(eData.message);
18+
errorParse.keyPath = eData.keyPath;
19+
errorParse.value = eData.value;
20+
errorParse.context = eData.context;
21+
errors.push(errorParse);
22+
}
23+
this.errors = errors;
24+
}
25+
}
26+
27+
/**
28+
* This packages an `ErrorParse` array into the `data` property
29+
* This is to allow encoding to and decoding from GRPC errors
30+
*/
31+
static createFromErrors(errors: Array<ErrorParse>): ErrorValidation {
1132
const message = errors.map((e) => e.message).join('; ');
1233
const data = {
1334
errors: errors.map((e) => ({
1435
message: e.message,
1536
keyPath: e.keyPath,
1637
value: e.value.valueOf(),
38+
context: e.context,
1739
})),
1840
};
19-
super(message, data);
20-
this.description = 'Input data failed validation';
21-
this.exitCode = sysexits.DATAERR;
22-
this.errors = errors;
41+
const e = new ErrorValidation(message, data);
42+
e.errors = errors;
43+
return e;
2344
}
2445
}
2546

src/validation/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ async function validate(
4141
data = await parse_([], data, { undefined: data });
4242
} catch (e) {
4343
if (e instanceof validationErrors.ErrorParse) {
44-
throw new validationErrors.ErrorValidation(errors);
44+
throw validationErrors.ErrorValidation.createFromErrors(errors);
4545
} else {
4646
throw e;
4747
}
4848
}
4949
if (errors.length > 0) {
50-
throw new validationErrors.ErrorValidation(errors);
50+
throw validationErrors.ErrorValidation.createFromErrors(errors);
5151
}
5252
return data;
5353
}
@@ -88,13 +88,13 @@ function validateSync(
8888
data = parse_([], data, { undefined: data });
8989
} catch (e) {
9090
if (e instanceof validationErrors.ErrorParse) {
91-
throw new validationErrors.ErrorValidation(errors);
91+
throw validationErrors.ErrorValidation.createFromErrors(errors);
9292
} else {
9393
throw e;
9494
}
9595
}
9696
if (errors.length > 0) {
97-
throw new validationErrors.ErrorValidation(errors);
97+
throw validationErrors.ErrorValidation.createFromErrors(errors);
9898
}
9999
return data;
100100
}

tests/client/service/identitiesAuthenticate.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
import identitiesAuthenticate from '@/client/service/identitiesAuthenticate';
1717
import * as identitiesPB from '@/proto/js/polykey/v1/identities/identities_pb';
1818
import { utils as nodesUtils } from '@/nodes';
19+
import * as validationErrors from '@/validation/errors';
1920
import TestProvider from '../../identities/TestProvider';
2021

2122
describe('identitiesAuthenticate', () => {
@@ -87,7 +88,6 @@ describe('identitiesAuthenticate', () => {
8788
test('authenticates identity', async () => {
8889
const request = new identitiesPB.Provider();
8990
request.setProviderId(testToken.providerId);
90-
request.setIdentityId(testToken.identityId);
9191
const response = grpcClient.identitiesAuthenticate(
9292
request,
9393
clientUtils.encodeAuthFromPassword(password),
@@ -123,4 +123,16 @@ describe('identitiesAuthenticate', () => {
123123
testToken.identityId,
124124
);
125125
});
126+
test('cannot authenticate invalid provider', async () => {
127+
const request = new identitiesPB.Provider();
128+
request.setProviderId('');
129+
await expect(
130+
grpcClient
131+
.identitiesAuthenticate(
132+
request,
133+
clientUtils.encodeAuthFromPassword(password),
134+
)
135+
.next(),
136+
).rejects.toThrow(validationErrors.ErrorValidation);
137+
});
126138
});

tests/client/service/identitiesClaim.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
import identitiesClaim from '@/client/service/identitiesClaim';
2323
import * as identitiesPB from '@/proto/js/polykey/v1/identities/identities_pb';
2424
import * as claimsUtils from '@/claims/utils';
25+
import * as validationErrors from '@/validation/errors';
2526
import TestProvider from '../../identities/TestProvider';
2627
import * as testUtils from '../../utils';
2728

@@ -199,4 +200,31 @@ describe('identitiesClaim', () => {
199200
testToken.identityId,
200201
);
201202
});
203+
test('cannot claim invalid identity', async () => {
204+
const request = new identitiesPB.Provider();
205+
request.setIdentityId('');
206+
request.setProviderId(testToken.providerId);
207+
await expect(
208+
grpcClient.identitiesClaim(
209+
request,
210+
clientUtils.encodeAuthFromPassword(password),
211+
),
212+
).rejects.toThrow(validationErrors.ErrorValidation);
213+
request.setIdentityId(testToken.identityId);
214+
request.setProviderId('');
215+
await expect(
216+
grpcClient.identitiesClaim(
217+
request,
218+
clientUtils.encodeAuthFromPassword(password),
219+
),
220+
).rejects.toThrow(validationErrors.ErrorValidation);
221+
request.setIdentityId('');
222+
request.setProviderId('');
223+
await expect(
224+
grpcClient.identitiesClaim(
225+
request,
226+
clientUtils.encodeAuthFromPassword(password),
227+
),
228+
).rejects.toThrow(validationErrors.ErrorValidation);
229+
});
202230
});

tests/client/service/nodesAdd.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import nodesAdd from '@/client/service/nodesAdd';
1919
import * as nodesPB from '@/proto/js/polykey/v1/nodes/nodes_pb';
2020
import * as utilsPB from '@/proto/js/polykey/v1/utils/utils_pb';
2121
import { utils as nodesUtils } from '@/nodes';
22+
import * as validationErrors from '@/validation/errors';
2223
import * as testUtils from '../../utils';
2324

2425
describe('nodesAdd', () => {
@@ -155,4 +156,37 @@ describe('nodesAdd', () => {
155156
expect(result!.host).toBe('127.0.0.1');
156157
expect(result!.port).toBe(11111);
157158
});
159+
test('cannot add invalid node', async () => {
160+
// Invalid host
161+
const addressMessage = new nodesPB.Address();
162+
addressMessage.setHost('');
163+
addressMessage.setPort(11111);
164+
const request = new nodesPB.NodeAddress();
165+
request.setNodeId('vrsc24a1er424epq77dtoveo93meij0pc8ig4uvs9jbeld78n9nl0');
166+
request.setAddress(addressMessage);
167+
await expect(
168+
grpcClient.nodesAdd(
169+
request,
170+
clientUtils.encodeAuthFromPassword(password),
171+
),
172+
).rejects.toThrow(validationErrors.ErrorValidation);
173+
// Invalid port
174+
addressMessage.setHost('127.0.0.1');
175+
addressMessage.setPort(111111);
176+
await expect(
177+
grpcClient.nodesAdd(
178+
request,
179+
clientUtils.encodeAuthFromPassword(password),
180+
),
181+
).rejects.toThrow(validationErrors.ErrorValidation);
182+
// Invalid nodeid
183+
addressMessage.setPort(11111);
184+
request.setNodeId('nodeId');
185+
await expect(
186+
grpcClient.nodesAdd(
187+
request,
188+
clientUtils.encodeAuthFromPassword(password),
189+
),
190+
).rejects.toThrow(validationErrors.ErrorValidation);
191+
});
158192
});

tests/client/service/nodesClaim.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
import nodesClaim from '@/client/service/nodesClaim';
2323
import * as nodesPB from '@/proto/js/polykey/v1/nodes/nodes_pb';
2424
import * as utilsPB from '@/proto/js/polykey/v1/utils/utils_pb';
25+
import * as validationErrors from '@/validation/errors';
2526
import * as testUtils from '../../utils';
2627

2728
describe('nodesClaim', () => {
@@ -212,4 +213,14 @@ describe('nodesClaim', () => {
212213
expect(response).toBeInstanceOf(utilsPB.StatusMessage);
213214
expect(response.getSuccess()).toBeTruthy();
214215
});
216+
test('cannot claim an invalid node', async () => {
217+
const request = new nodesPB.Claim();
218+
request.setNodeId('nodeId');
219+
await expect(
220+
grpcClient.nodesClaim(
221+
request,
222+
clientUtils.encodeAuthFromPassword(password),
223+
),
224+
).rejects.toThrow(validationErrors.ErrorValidation);
225+
});
215226
});

tests/client/service/nodesFind.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from '@/client';
1818
import nodesFind from '@/client/service/nodesFind';
1919
import * as nodesPB from '@/proto/js/polykey/v1/nodes/nodes_pb';
20+
import * as validationErrors from '@/validation/errors';
2021
import * as testUtils from '../../utils';
2122

2223
describe('nodesFind', () => {
@@ -153,4 +154,14 @@ describe('nodesFind', () => {
153154
expect(response.getAddress()!.getHost()).toBe('127.0.0.1');
154155
expect(response.getAddress()!.getPort()).toBe(11111);
155156
});
157+
test('cannot find an invalid node', async () => {
158+
const request = new nodesPB.Node();
159+
request.setNodeId('nodeId');
160+
await expect(
161+
grpcClient.nodesFind(
162+
request,
163+
clientUtils.encodeAuthFromPassword(password),
164+
),
165+
).rejects.toThrow(validationErrors.ErrorValidation);
166+
});
156167
});

tests/client/service/nodesPing.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
import nodesPing from '@/client/service/nodesPing';
1919
import * as nodesPB from '@/proto/js/polykey/v1/nodes/nodes_pb';
2020
import * as utilsPB from '@/proto/js/polykey/v1/utils/utils_pb';
21+
import * as validationErrors from '@/validation/errors';
2122
import * as testUtils from '../../utils';
2223

2324
describe('nodesPing', () => {
@@ -158,4 +159,14 @@ describe('nodesPing', () => {
158159
expect(response).toBeInstanceOf(utilsPB.StatusMessage);
159160
expect(response.getSuccess()).toBeTruthy();
160161
});
162+
test('cannot ping an invalid node', async () => {
163+
const request = new nodesPB.Node();
164+
request.setNodeId('nodeId');
165+
await expect(
166+
grpcClient.nodesPing(
167+
request,
168+
clientUtils.encodeAuthFromPassword(password),
169+
),
170+
).rejects.toThrow(validationErrors.ErrorValidation);
171+
});
161172
});

0 commit comments

Comments
 (0)