1
- import { injectable } from 'inversify' ;
1
+ import { injectable , inject } from 'inversify' ;
2
2
import * as createPaths from './create-paths' ;
3
- import { posix , splitSketchPath } from './create-paths' ;
3
+ import { posix } from './create-paths' ;
4
4
import { AuthenticationClientService } from '../auth/authentication-client-service' ;
5
5
import { ArduinoPreferences } from '../arduino-preferences' ;
6
+ import { SketchCache } from '../widgets/cloud-sketchbook/cloud-sketch-cache' ;
7
+ import { Create , CreateError } from './typings' ;
6
8
7
9
export interface ResponseResultProvider {
8
10
( response : Response ) : Promise < any > ;
@@ -15,10 +17,11 @@ export namespace ResponseResultProvider {
15
17
16
18
type ResourceType = 'f' | 'd' ;
17
19
18
- export let sketchCache : Create . Sketch [ ] = [ ] ;
19
-
20
20
@injectable ( )
21
21
export class CreateApi {
22
+ @inject ( SketchCache )
23
+ protected sketchCache : SketchCache ;
24
+
22
25
protected authenticationService : AuthenticationClientService ;
23
26
protected arduinoPreferences : ArduinoPreferences ;
24
27
@@ -32,48 +35,20 @@ export class CreateApi {
32
35
return this ;
33
36
}
34
37
35
- public sketchCompareByPath = ( param : string ) => {
36
- return ( sketch : Create . Sketch ) => {
37
- const [ , spath ] = splitSketchPath ( sketch . path ) ;
38
- return param === spath ;
39
- } ;
40
- } ;
41
-
42
- async findSketchInCache (
43
- compareFn : ( sketch : Create . Sketch ) => boolean ,
44
- trustCache = true
45
- ) : Promise < Create . Sketch | undefined > {
46
- const sketch = sketchCache . find ( ( sketch ) => compareFn ( sketch ) ) ;
47
- if ( trustCache ) {
48
- return Promise . resolve ( sketch ) ;
49
- }
50
- return await this . sketch ( { id : sketch ?. id } ) ;
51
- }
52
-
53
38
getSketchSecretStat ( sketch : Create . Sketch ) : Create . Resource {
54
39
return {
55
40
href : `${ sketch . href } ${ posix . sep } ${ Create . arduino_secrets_file } ` ,
56
41
modified_at : sketch . modified_at ,
42
+ created_at : sketch . created_at ,
57
43
name : `${ Create . arduino_secrets_file } ` ,
58
44
path : `${ sketch . path } ${ posix . sep } ${ Create . arduino_secrets_file } ` ,
59
45
mimetype : 'text/x-c++src; charset=utf-8' ,
60
46
type : 'file' ,
61
- sketchId : sketch . id ,
62
47
} ;
63
48
}
64
49
65
- async sketch ( opt : {
66
- id ?: string ;
67
- path ?: string ;
68
- } ) : Promise < Create . Sketch | undefined > {
69
- let url ;
70
- if ( opt . id ) {
71
- url = new URL ( `${ this . domain ( ) } /sketches/byID/${ opt . id } ` ) ;
72
- } else if ( opt . path ) {
73
- url = new URL ( `${ this . domain ( ) } /sketches/byPath${ opt . path } ` ) ;
74
- } else {
75
- return ;
76
- }
50
+ async sketch ( id : string ) : Promise < Create . Sketch > {
51
+ const url = new URL ( `${ this . domain ( ) } /sketches/byID/${ id } ` ) ;
77
52
78
53
url . searchParams . set ( 'user_id' , 'me' ) ;
79
54
const headers = await this . headers ( ) ;
@@ -92,7 +67,7 @@ export class CreateApi {
92
67
method : 'GET' ,
93
68
headers,
94
69
} ) ;
95
- sketchCache = result . sketches ;
70
+ result . sketches . forEach ( ( sketch ) => this . sketchCache . addSketch ( sketch ) ) ;
96
71
return result . sketches ;
97
72
}
98
73
@@ -118,7 +93,7 @@ export class CreateApi {
118
93
119
94
async readDirectory (
120
95
posixPath : string ,
121
- options : { recursive ?: boolean ; match ?: string ; secrets ?: boolean } = { }
96
+ options : { recursive ?: boolean ; match ?: string } = { }
122
97
) : Promise < Create . Resource [ ] > {
123
98
const url = new URL (
124
99
`${ this . domain ( ) } /files/d/$HOME/sketches_v2${ posixPath } `
@@ -131,58 +106,21 @@ export class CreateApi {
131
106
}
132
107
const headers = await this . headers ( ) ;
133
108
134
- const sketchProm = options . secrets
135
- ? this . sketches ( )
136
- : Promise . resolve ( sketchCache ) ;
137
-
138
- return Promise . all ( [
139
- this . run < Create . RawResource [ ] > ( url , {
140
- method : 'GET' ,
141
- headers,
142
- } ) ,
143
- sketchProm ,
144
- ] )
145
- . then ( async ( [ result , sketches ] ) => {
146
- if ( options . secrets ) {
147
- // for every sketch with secrets, create a fake arduino_secrets.h
148
- result . forEach ( async ( res ) => {
149
- if ( res . type !== 'sketch' ) {
150
- return ;
151
- }
152
-
153
- const [ , spath ] = createPaths . splitSketchPath ( res . path ) ;
154
- const sketch = await this . findSketchInCache (
155
- this . sketchCompareByPath ( spath )
156
- ) ;
157
- if ( sketch && sketch . secrets && sketch . secrets . length > 0 ) {
158
- result . push ( this . getSketchSecretStat ( sketch ) ) ;
159
- }
160
- } ) ;
161
-
162
- if ( posixPath !== posix . sep ) {
163
- const sketch = await this . findSketchInCache (
164
- this . sketchCompareByPath ( posixPath )
165
- ) ;
166
- if ( sketch && sketch . secrets && sketch . secrets . length > 0 ) {
167
- result . push ( this . getSketchSecretStat ( sketch ) ) ;
168
- }
109
+ return this . run < Create . RawResource [ ] > ( url , {
110
+ method : 'GET' ,
111
+ headers,
112
+ } )
113
+ . then ( async ( result ) => {
114
+ // add arduino_secrets.h to the results, when reading a sketch main folder
115
+ if ( posixPath . length && posixPath !== posix . sep ) {
116
+ const sketch = this . sketchCache . getSketch ( posixPath ) ;
117
+
118
+ if ( sketch && sketch . secrets && sketch . secrets . length > 0 ) {
119
+ result . push ( this . getSketchSecretStat ( sketch ) ) ;
169
120
}
170
121
}
171
- const sketchesMap : Record < string , Create . Sketch > = sketches . reduce (
172
- ( prev , curr ) => {
173
- return { ...prev , [ curr . path ] : curr } ;
174
- } ,
175
- { }
176
- ) ;
177
122
178
- // add the sketch id and isPublic to the resource
179
- return result . map ( ( resource ) => {
180
- return {
181
- ...resource ,
182
- sketchId : sketchesMap [ resource . path ] ?. id || '' ,
183
- isPublic : sketchesMap [ resource . path ] ?. is_public || false ,
184
- } ;
185
- } ) ;
123
+ return result ;
186
124
} )
187
125
. catch ( ( reason ) => {
188
126
if ( reason ?. status === 404 ) return [ ] as Create . Resource [ ] ;
@@ -214,18 +152,16 @@ export class CreateApi {
214
152
215
153
let resources ;
216
154
if ( basename === Create . arduino_secrets_file ) {
217
- const sketch = await this . findSketchInCache (
218
- this . sketchCompareByPath ( parentPosixPath )
219
- ) ;
155
+ const sketch = this . sketchCache . getSketch ( parentPosixPath ) ;
220
156
resources = sketch ? [ this . getSketchSecretStat ( sketch ) ] : [ ] ;
221
157
} else {
222
158
resources = await this . readDirectory ( parentPosixPath , {
223
159
match : basename ,
224
160
} ) ;
225
161
}
226
-
227
- resources . sort ( ( left , right ) => left . path . length - right . path . length ) ;
228
- const resource = resources . find ( ( { name } ) => name === basename ) ;
162
+ const resource = resources . find (
163
+ ( { path } ) => createPaths . splitSketchPath ( path ) [ 1 ] === posixPath
164
+ ) ;
229
165
if ( ! resource ) {
230
166
throw new CreateError ( `Not found: ${ posixPath } .` , 404 ) ;
231
167
}
@@ -248,10 +184,7 @@ export class CreateApi {
248
184
return data ;
249
185
}
250
186
251
- const sketch = await this . findSketchInCache ( ( sketch ) => {
252
- const [ , spath ] = splitSketchPath ( sketch . path ) ;
253
- return spath === createPaths . parentPosix ( path ) ;
254
- } , true ) ;
187
+ const sketch = this . sketchCache . getSketch ( createPaths . parentPosix ( path ) ) ;
255
188
256
189
if (
257
190
sketch &&
@@ -273,14 +206,25 @@ export class CreateApi {
273
206
274
207
if ( basename === Create . arduino_secrets_file ) {
275
208
const parentPosixPath = createPaths . parentPosix ( posixPath ) ;
276
- const sketch = await this . findSketchInCache (
277
- this . sketchCompareByPath ( parentPosixPath ) ,
278
- false
279
- ) ;
209
+
210
+ //retrieve the sketch id from the cache
211
+ const cacheSketch = this . sketchCache . getSketch ( parentPosixPath ) ;
212
+ if ( ! cacheSketch ) {
213
+ throw new Error ( `Unable to find sketch ${ parentPosixPath } in cache` ) ;
214
+ }
215
+
216
+ // get a fresh copy of the sketch in order to guarantee fresh secrets
217
+ const sketch = await this . sketch ( cacheSketch . id ) ;
218
+ if ( ! sketch ) {
219
+ throw new Error (
220
+ `Unable to get a fresh copy of the sketch ${ cacheSketch . id } `
221
+ ) ;
222
+ }
223
+ this . sketchCache . addSketch ( sketch ) ;
280
224
281
225
let file = '' ;
282
226
if ( sketch && sketch . secrets ) {
283
- for ( const item of sketch ? .secrets ) {
227
+ for ( const item of sketch . secrets ) {
284
228
file += `#define ${ item . name } "${ item . value } "\r\n` ;
285
229
}
286
230
}
@@ -310,9 +254,9 @@ export class CreateApi {
310
254
311
255
if ( basename === Create . arduino_secrets_file ) {
312
256
const parentPosixPath = createPaths . parentPosix ( posixPath ) ;
313
- const sketch = await this . findSketchInCache (
314
- this . sketchCompareByPath ( parentPosixPath )
315
- ) ;
257
+
258
+ const sketch = this . sketchCache . getSketch ( parentPosixPath ) ;
259
+
316
260
if ( sketch ) {
317
261
const url = new URL ( `${ this . domain ( ) } /sketches/${ sketch . id } ` ) ;
318
262
const headers = await this . headers ( ) ;
@@ -356,9 +300,10 @@ export class CreateApi {
356
300
secrets : { data : secrets } ,
357
301
} ;
358
302
359
- // replace the sketch in the cache, so other calls will not overwrite each other
360
- sketchCache = sketchCache . filter ( ( skt ) => skt . id !== sketch . id ) ;
361
- sketchCache . push ( { ...sketch , secrets } ) ;
303
+ // replace the sketch in the cache with the one we are pushing
304
+ // TODO: we should do a get after the POST, in order to be sure the cache
305
+ // is updated the most recent metadata
306
+ this . sketchCache . addSketch ( sketch ) ;
362
307
363
308
const init = {
364
309
method : 'POST' ,
@@ -370,6 +315,14 @@ export class CreateApi {
370
315
return ;
371
316
}
372
317
318
+ // do not upload "do_not_sync" files/directoris and their descendants
319
+ const segments = posixPath . split ( posix . sep ) || [ ] ;
320
+ if (
321
+ segments . some ( ( segment ) => Create . do_not_sync_files . includes ( segment ) )
322
+ ) {
323
+ return ;
324
+ }
325
+
373
326
const url = new URL (
374
327
`${ this . domain ( ) } /files/f/$HOME/sketches_v2${ posixPath } `
375
328
) ;
@@ -512,75 +465,3 @@ void loop() {
512
465
513
466
` ;
514
467
}
515
-
516
- export namespace Create {
517
- export interface Sketch {
518
- readonly name : string ;
519
- readonly path : string ;
520
- readonly modified_at : string ;
521
- readonly created_at : string ;
522
-
523
- readonly secrets ?: { name : string ; value : string } [ ] ;
524
-
525
- readonly id : string ;
526
- readonly is_public : boolean ;
527
- // readonly board_fqbn: '',
528
- // readonly board_name: '',
529
- // readonly board_type: 'serial' | 'network' | 'cloud' | '',
530
- readonly href ?: string ;
531
- readonly libraries : string [ ] ;
532
- // readonly tutorials: string[] | null;
533
- // readonly types: string[] | null;
534
- // readonly user_id: string;
535
- }
536
-
537
- export type ResourceType = 'sketch' | 'folder' | 'file' ;
538
- export const arduino_secrets_file = 'arduino_secrets.h' ;
539
- export interface Resource {
540
- readonly name : string ;
541
- /**
542
- * Note: this path is **not** the POSIX path we use. It has the leading segments with the `user_id`.
543
- */
544
- readonly path : string ;
545
- readonly type : ResourceType ;
546
- readonly sketchId : string ;
547
- readonly modified_at : string ; // As an ISO-8601 formatted string: `YYYY-MM-DDTHH:mm:ss.sssZ`
548
- readonly children ?: number ; // For 'sketch' and 'folder' types.
549
- readonly size ?: number ; // For 'sketch' type only.
550
- readonly isPublic ?: boolean ; // For 'sketch' type only.
551
-
552
- readonly mimetype ?: string ; // For 'file' type.
553
- readonly href ?: string ;
554
- }
555
- export namespace Resource {
556
- export function is ( arg : any ) : arg is Resource {
557
- return (
558
- ! ! arg &&
559
- 'name' in arg &&
560
- typeof arg [ 'name' ] === 'string' &&
561
- 'path' in arg &&
562
- typeof arg [ 'path' ] === 'string' &&
563
- 'type' in arg &&
564
- typeof arg [ 'type' ] === 'string' &&
565
- 'modified_at' in arg &&
566
- typeof arg [ 'modified_at' ] === 'string' &&
567
- ( arg [ 'type' ] === 'sketch' ||
568
- arg [ 'type' ] === 'folder' ||
569
- arg [ 'type' ] === 'file' )
570
- ) ;
571
- }
572
- }
573
-
574
- export type RawResource = Omit < Resource , 'sketchId' | 'isPublic' > ;
575
- }
576
-
577
- export class CreateError extends Error {
578
- constructor (
579
- message : string ,
580
- readonly status : number ,
581
- readonly details ?: string
582
- ) {
583
- super ( message ) ;
584
- Object . setPrototypeOf ( this , CreateError . prototype ) ;
585
- }
586
- }
0 commit comments