16
16
17
17
import * as chai from 'chai' ;
18
18
import * as chaiAsPromised from 'chai-as-promised' ;
19
- import { getDataConnect , ConnectorConfig } from '../../lib/data-connect/index' ;
19
+ import { getDataConnect , ConnectorConfig , GraphqlOptions } from '../../lib/data-connect/index' ;
20
20
21
21
chai . should ( ) ;
22
22
chai . use ( chaiAsPromised ) ;
@@ -26,61 +26,65 @@ const expect = chai.expect;
26
26
/*
27
27
// Schema
28
28
type User @table (key: "uid") {
29
- uid: String!
30
- name: String!
31
- address: String!
29
+ uid: String!
30
+ name: String!
31
+ address: String!
32
32
}
33
+ */
34
+ type User = {
35
+ uid ?: string ;
36
+ name ?: string ;
37
+ address ?: string ;
38
+ // Generated
39
+ emails_on_from ?: Email [ ] ;
40
+ } ;
33
41
42
+ /*
43
+ // Schema
34
44
type Email @table {
35
- subject: String!
36
- date: Date!
37
- text: String!
38
- from: User!
45
+ subject: String!
46
+ date: Date!
47
+ text: String!
48
+ from: User!
39
49
}
40
50
*/
51
+ type Email = {
52
+ subject ?: string ;
53
+ date ?: string ;
54
+ text ?: string ;
55
+ from ?: User ;
56
+ // Generated
57
+ id ?: string ;
58
+ } ;
41
59
42
60
interface UserResponse {
43
- user : {
44
- name : string ;
45
- uid : string ;
46
- }
61
+ user : User ;
47
62
}
48
63
49
64
interface UsersResponse {
50
- users : [
51
- user : {
52
- id : string ;
53
- name : string ;
54
- address : string ;
55
- }
56
- ] ;
65
+ users : User [ ] ;
66
+ }
67
+
68
+ interface UserUpsertResponse {
69
+ user_upsert : { uid : string ; } ;
57
70
}
58
71
59
72
interface UserUpdateResponse {
60
- user_upsert : { uid : string }
73
+ user_update : { uid : string ; } ;
61
74
}
62
75
63
76
interface EmailsResponse {
64
- emails : [
65
- email : {
66
- text : string ;
67
- subject : string ;
68
- id : string ;
69
- date : string ;
70
- from : {
71
- name : string ;
72
- }
73
- }
74
- ] ;
77
+ emails : Email [ ] ;
75
78
}
76
79
77
80
interface UserVariables {
78
- id : { uid : string } ;
81
+ id : { uid : string ; } ;
79
82
}
80
83
81
84
const connectorConfig : ConnectorConfig = {
82
85
location : 'us-west2' ,
83
86
serviceId : 'my-service' ,
87
+ // serviceId: 'je-test-fdc',
84
88
} ;
85
89
86
90
const userId = 'QVBJcy5ndXJ3' ;
@@ -90,17 +94,30 @@ describe('getDataConnect()', () => {
90
94
const queryListUsers = 'query ListUsers @auth(level: PUBLIC) { users { uid, name, address } }' ;
91
95
const queryListEmails = 'query ListEmails @auth(level: NO_ACCESS) { emails { id subject text date from { name } } }' ;
92
96
const queryGetUserById = 'query GetUser($id: User_Key!) { user(key: $id) { uid name } }' ;
97
+
98
+ const queryListUsersImpersonation = `
99
+ query ListUsers @auth(level: USER) {
100
+ users(where: { uid: { eq_expr: "auth.uid" } }) { uid, name, address }
101
+ }` ;
102
+
93
103
const multipleQueries = `
94
104
${ queryListUsers }
95
105
${ queryListEmails }
96
106
` ;
107
+
97
108
const mutation = `mutation user { user_insert(data: {uid: "${ userId } ", address: "32 St", name: "Fred Car"}) }` ;
109
+
110
+ const updateImpersonatedUser = `
111
+ mutation UpdateUser @auth(level: USER) {
112
+ user_update(key: { uid_expr: "auth.uid" }, data: { address: "32 Elm St.", name: "Fredrick" })
113
+ }` ;
114
+
98
115
const upsertUser = `mutation UpsertUser($id: String) {
99
116
user_upsert(data: { uid: $id, address: "32 St.", name: "Fred" }) }` ;
100
117
101
118
describe ( 'executeGraphql()' , ( ) => {
102
119
it ( 'executeGraphql() successfully executes a GraphQL mutation' , async ( ) => {
103
- const resp = await getDataConnect ( connectorConfig ) . executeGraphql < UserUpdateResponse , unknown > (
120
+ const resp = await getDataConnect ( connectorConfig ) . executeGraphql < UserUpsertResponse , unknown > (
104
121
upsertUser , { variables : { id : userId } }
105
122
) ;
106
123
//{ data: { user_insert: { uid: 'QVBJcy5ndXJ3' } } }
@@ -111,18 +128,20 @@ describe('getDataConnect()', () => {
111
128
it ( 'executeGraphql() successfully executes a GraphQL' , async ( ) => {
112
129
const resp = await getDataConnect ( connectorConfig ) . executeGraphql < UsersResponse , UserVariables > ( queryListUsers ) ;
113
130
expect ( resp . data . users ) . to . be . not . empty ;
114
- expect ( resp . data . users [ 0 ] . name ) . to . be . not . undefined ;
115
- expect ( resp . data . users [ 0 ] . address ) . to . be . not . undefined ;
131
+ expect ( resp . data . users . length ) . to . be . greaterThan ( 1 ) ;
132
+ expect ( resp . data . users [ 0 ] . uid ) . to . not . be . undefined ;
133
+ expect ( resp . data . users [ 0 ] . name ) . to . not . be . undefined ;
134
+ expect ( resp . data . users [ 0 ] . address ) . to . not . be . undefined ;
116
135
} ) ;
117
136
118
137
it ( 'executeGraphql() use the operationName when multiple queries are provided' , async ( ) => {
119
138
const resp = await getDataConnect ( connectorConfig ) . executeGraphql < EmailsResponse , unknown > (
120
- multipleQueries ,
139
+ multipleQueries ,
121
140
{ operationName : 'ListEmails' }
122
141
) ;
123
142
expect ( resp . data . emails ) . to . be . not . empty ;
124
143
expect ( resp . data . emails [ 0 ] . id ) . to . be . not . undefined ;
125
- expect ( resp . data . emails [ 0 ] . from . name ) . to . be . not . undefined ;
144
+ expect ( resp . data . emails [ 0 ] . from ? .name ) . to . be . not . undefined ;
126
145
} ) ;
127
146
128
147
it ( 'executeGraphql() should throw for a query error' , async ( ) => {
@@ -135,8 +154,9 @@ describe('getDataConnect()', () => {
135
154
queryGetUserById ,
136
155
{ variables : { id : { uid : userId } } }
137
156
) ;
138
- expect ( resp . data . user . name ) . to . be . not . undefined ;
139
157
expect ( resp . data . user . uid ) . equals ( userId ) ;
158
+ expect ( resp . data . user . name ) . to . not . be . undefined ;
159
+ expect ( resp . data . user . address ) . to . be . undefined ;
140
160
} ) ;
141
161
} ) ;
142
162
@@ -145,13 +165,154 @@ describe('getDataConnect()', () => {
145
165
const resp =
146
166
await getDataConnect ( connectorConfig ) . executeGraphqlRead < UsersResponse , UserVariables > ( queryListUsers ) ;
147
167
expect ( resp . data . users ) . to . be . not . empty ;
168
+ expect ( resp . data . users . length ) . to . be . greaterThan ( 1 ) ;
169
+ expect ( resp . data . users [ 0 ] . uid ) . to . not . be . undefined ;
148
170
expect ( resp . data . users [ 0 ] . name ) . to . be . not . undefined ;
149
171
expect ( resp . data . users [ 0 ] . address ) . to . be . not . undefined ;
150
172
} ) ;
151
173
152
174
it ( 'executeGraphqlRead() should throw for a GraphQL mutation' , async ( ) => {
153
175
return getDataConnect ( connectorConfig ) . executeGraphqlRead ( mutation )
154
- . should . eventually . be . rejected ;
176
+ . should . eventually . be . rejected . and . have . property ( 'code' , 'data-connect/permission-denied' ) ;
177
+ } ) ;
178
+ } ) ;
179
+
180
+ describe ( 'Impersonation' , ( ) => {
181
+ const optsAuthorizedClaims : GraphqlOptions < undefined > = {
182
+ impersonate : {
183
+ authClaims : {
184
+ sub : userId ,
185
+ email_verified : true
186
+ }
187
+ }
188
+ } ;
189
+
190
+ const optsNonExistingClaims : GraphqlOptions < undefined > = {
191
+ impersonate : {
192
+ authClaims : {
193
+ sub : 'non-exisiting-id' ,
194
+ email_verified : true
195
+ }
196
+ }
197
+ } ;
198
+
199
+ const optsUnauthorizedClaims : GraphqlOptions < undefined > = {
200
+ impersonate : {
201
+ unauthenticated : true
202
+ }
203
+ } ;
204
+
205
+ describe ( 'USER Auth Policy' , ( ) => {
206
+ it ( 'executeGraphqlRead() successfully executes an impersonated query with authenticated claims' , async ( ) => {
207
+ const resp =
208
+ await getDataConnect ( connectorConfig ) . executeGraphqlRead < UsersResponse , undefined > (
209
+ queryListUsersImpersonation , optsAuthorizedClaims ) ;
210
+ expect ( resp . data . users ) . to . be . not . empty ;
211
+ expect ( resp . data . users . length ) . equals ( 1 ) ;
212
+ expect ( resp . data . users [ 0 ] . uid ) . equals ( userId ) ;
213
+ expect ( resp . data . users [ 0 ] . name ) . to . not . be . undefined ;
214
+ expect ( resp . data . users [ 0 ] . address ) . to . not . be . undefined ;
215
+ } ) ;
216
+
217
+ it ( 'executeGraphqlRead() should throw for impersonated query with unauthenticated claims' , async ( ) => {
218
+ return getDataConnect ( connectorConfig ) . executeGraphqlRead ( queryListUsersImpersonation , optsUnauthorizedClaims )
219
+ . should . eventually . be . rejected . and . have . property ( 'code' , 'data-connect/unauthenticated' ) ;
220
+ } ) ;
221
+
222
+ it ( 'executeGraphql() successfully executes an impersonated query with authenticated claims' , async ( ) => {
223
+ const resp =
224
+ await getDataConnect ( connectorConfig ) . executeGraphqlRead < UsersResponse , undefined > (
225
+ queryListUsersImpersonation , optsAuthorizedClaims ) ;
226
+ expect ( resp . data . users ) . to . be . not . empty ;
227
+ expect ( resp . data . users . length ) . equals ( 1 ) ;
228
+ expect ( resp . data . users [ 0 ] . uid ) . equals ( userId ) ;
229
+ expect ( resp . data . users [ 0 ] . name ) . to . be . not . undefined ;
230
+ expect ( resp . data . users [ 0 ] . address ) . to . be . not . undefined ;
231
+ } ) ;
232
+
233
+ it ( 'executeGraphql() should throw for impersonated query with unauthenticated claims' , async ( ) => {
234
+ return getDataConnect ( connectorConfig ) . executeGraphql ( queryListUsersImpersonation , optsUnauthorizedClaims )
235
+ . should . eventually . be . rejected . and . has . property ( 'code' , 'data-connect/unauthenticated' ) ;
236
+ } ) ;
237
+
238
+ it ( 'executeGraphql() successfully executes an impersonated query with non-existing authenticated claims' ,
239
+ async ( ) => {
240
+ const resp = await getDataConnect ( connectorConfig ) . executeGraphql < UsersResponse , undefined > (
241
+ queryListUsersImpersonation , optsNonExistingClaims ) ;
242
+ // Should find no data
243
+ expect ( resp . data . users ) . to . be . empty ;
244
+ } ) ;
245
+
246
+ it ( 'executeGraphql() successfully executes an impersonated mutation with authenticated claims' ,
247
+ async ( ) => {
248
+ const resp = await getDataConnect ( connectorConfig ) . executeGraphql < UserUpdateResponse , undefined > (
249
+ updateImpersonatedUser , optsAuthorizedClaims ) ;
250
+ expect ( resp . data . user_update . uid ) . equals ( userId ) ;
251
+ } ) ;
252
+
253
+ it ( 'executeGraphql() should throw for impersonated mutation with unauthenticated claims' , async ( ) => {
254
+ return getDataConnect ( connectorConfig ) . executeGraphql ( updateImpersonatedUser , optsUnauthorizedClaims )
255
+ . should . eventually . be . rejected . and . has . property ( 'code' , 'data-connect/unauthenticated' ) ;
256
+ } ) ;
257
+
258
+ it ( 'executeGraphql() successfully executes an impersonated mutation with non-existing authenticated claims' ,
259
+ async ( ) => {
260
+ const resp = await getDataConnect ( connectorConfig ) . executeGraphql < UserUpdateResponse , undefined > (
261
+ updateImpersonatedUser , optsNonExistingClaims ) ;
262
+ // Should find no data
263
+ expect ( resp . data . user_update ) . to . be . null ;
264
+ } ) ;
265
+ } ) ;
266
+
267
+ describe ( 'PUBLIC Auth Policy' , ( ) => {
268
+ it ( 'executeGraphql() successfully executes an impersonated query with authenticated claims' , async ( ) => {
269
+ const resp = await getDataConnect ( connectorConfig ) . executeGraphql < UsersResponse , undefined > (
270
+ queryListUsers , optsAuthorizedClaims ) ;
271
+ expect ( resp . data . users ) . to . be . not . empty ;
272
+ expect ( resp . data . users . length ) . to . be . greaterThan ( 1 ) ;
273
+ expect ( resp . data . users [ 0 ] . uid ) . to . not . be . undefined ;
274
+ expect ( resp . data . users [ 0 ] . name ) . to . not . be . undefined ;
275
+ expect ( resp . data . users [ 0 ] . address ) . to . not . be . undefined ;
276
+ } ) ;
277
+
278
+ it ( 'executeGraphql() successfully executes an impersonated query with unauthenticated claims' , async ( ) => {
279
+ const resp = await getDataConnect ( connectorConfig ) . executeGraphql < UsersResponse , undefined > (
280
+ queryListUsers , optsUnauthorizedClaims ) ;
281
+ expect ( resp . data . users ) . to . be . not . empty ;
282
+ expect ( resp . data . users . length ) . to . be . greaterThan ( 1 ) ;
283
+ expect ( resp . data . users [ 0 ] . uid ) . to . not . be . undefined ;
284
+ expect ( resp . data . users [ 0 ] . name ) . to . not . be . undefined ;
285
+ expect ( resp . data . users [ 0 ] . address ) . to . not . be . undefined ;
286
+ } ) ;
287
+
288
+ it ( 'executeGraphql() successfully executes an impersonated query with non-existing authenticated claims' ,
289
+ async ( ) => {
290
+ const resp = await getDataConnect ( connectorConfig ) . executeGraphql < UsersResponse , undefined > (
291
+ queryListUsers , optsNonExistingClaims ) ;
292
+ expect ( resp . data . users ) . to . be . not . empty ;
293
+ expect ( resp . data . users . length ) . to . be . greaterThan ( 1 ) ;
294
+ expect ( resp . data . users [ 0 ] . uid ) . to . not . be . undefined ;
295
+ expect ( resp . data . users [ 0 ] . name ) . to . not . be . undefined ;
296
+ expect ( resp . data . users [ 0 ] . address ) . to . not . be . undefined ;
297
+ } ) ;
298
+ } ) ;
299
+
300
+ describe ( 'NO_ACCESS Auth Policy' , ( ) => {
301
+ it ( 'executeGraphql() should throw for an impersonated query with authenticated claims' , async ( ) => {
302
+ return await getDataConnect ( connectorConfig ) . executeGraphql ( queryListEmails , optsAuthorizedClaims )
303
+ . should . eventually . be . rejected . and . has . property ( 'code' , 'data-connect/permission-denied' ) ;
304
+ } ) ;
305
+
306
+ it ( 'executeGraphql() should throw for an impersonated query with unauthenticated claims' , async ( ) => {
307
+ return await getDataConnect ( connectorConfig ) . executeGraphql ( queryListEmails , optsUnauthorizedClaims )
308
+ . should . eventually . be . rejected . and . has . property ( 'code' , 'data-connect/permission-denied' ) ;
309
+ } ) ;
310
+
311
+ it ( 'executeGraphql() should throw for an impersonated query with non-existing authenticated claims' ,
312
+ async ( ) => {
313
+ return await getDataConnect ( connectorConfig ) . executeGraphql ( queryListEmails , optsNonExistingClaims )
314
+ . should . eventually . be . rejected . and . has . property ( 'code' , 'data-connect/permission-denied' ) ;
315
+ } ) ;
155
316
} ) ;
156
317
} ) ;
157
318
} ) ;
0 commit comments