Skip to content

Commit 54f210d

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
fix: try fetch the sketch by path if not in the cache
The sketch cache might be empty, when trying to generate the secrets include in the main sketch file from the `secrets` property. Closes #1999 Signed-off-by: Akos Kitta <[email protected]>
1 parent 5540170 commit 54f210d

File tree

3 files changed

+102
-5
lines changed

3 files changed

+102
-5
lines changed

arduino-ide-extension/src/browser/create/create-api.ts

+58-3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,30 @@ export class CreateApi {
5757
return result;
5858
}
5959

60+
/**
61+
* `sketchPath` is not the POSIX path but the path with the user UUID, username, etc.
62+
* See [Create.Resource#path](./typings.ts). If `cache` is `true` and a sketch exists with the path,
63+
* the cache will be updated with the new state of the sketch.
64+
*/
65+
// TODO: no nulls in API
66+
async sketchByPath(
67+
sketchPath: string,
68+
cache = false
69+
): Promise<Create.Sketch | null> {
70+
const url = new URL(`${this.domain()}/sketches/byPath/${sketchPath}`);
71+
const headers = await this.headers();
72+
const sketch = await this.run<Create.Sketch>(url, {
73+
method: 'GET',
74+
headers,
75+
});
76+
if (sketch && cache) {
77+
this.sketchCache.addSketch(sketch);
78+
const posixPath = createPaths.toPosixPath(sketch.path);
79+
this.sketchCache.purgeByPath(posixPath);
80+
}
81+
return sketch;
82+
}
83+
6084
async sketches(limit = 50): Promise<Create.Sketch[]> {
6185
const url = new URL(`${this.domain()}/sketches`);
6286
url.searchParams.set('user_id', 'me');
@@ -86,7 +110,11 @@ export class CreateApi {
86110

87111
async createSketch(
88112
posixPath: string,
89-
contentProvider: MaybePromise<string> = this.sketchesService.defaultInoContent()
113+
contentProvider: MaybePromise<string> = this.sketchesService.defaultInoContent(),
114+
payloadOverride: Record<
115+
string,
116+
string | boolean | number | Record<string, unknown>
117+
> = {}
90118
): Promise<Create.Sketch> {
91119
const url = new URL(`${this.domain()}/sketches`);
92120
const [headers, content] = await Promise.all([
@@ -97,6 +125,7 @@ export class CreateApi {
97125
ino: btoa(content),
98126
path: posixPath,
99127
user_id: 'me',
128+
...payloadOverride,
100129
};
101130
const init = {
102131
method: 'PUT',
@@ -212,7 +241,17 @@ export class CreateApi {
212241
return data;
213242
}
214243

215-
const sketch = this.sketchCache.getSketch(createPaths.parentPosix(path));
244+
const posixPath = createPaths.parentPosix(path);
245+
let sketch = this.sketchCache.getSketch(posixPath);
246+
// Workaround for https://github.com/arduino/arduino-ide/issues/1999.
247+
if (!sketch) {
248+
// Convert the ordinary sketch POSIX path to the Create path.
249+
// For example, `/sketch_apr6a` will be transformed to `8a694e4b83878cc53472bd75ee928053:kittaakos/sketches_v2/sketch_apr6a`.
250+
const createPathPrefix = this.sketchCache.createPathPrefix;
251+
if (createPathPrefix) {
252+
sketch = await this.sketchByPath(createPathPrefix + posixPath, true);
253+
}
254+
}
216255

217256
if (
218257
sketch &&
@@ -448,13 +487,18 @@ export class CreateApi {
448487
await this.run(url, init, ResponseResultProvider.NOOP);
449488
}
450489

490+
private fetchCounter = 0;
451491
private async run<T>(
452492
requestInfo: URL,
453493
init: RequestInit | undefined,
454494
resultProvider: ResponseResultProvider = ResponseResultProvider.JSON
455495
): Promise<T> {
456-
console.debug(`HTTP ${init?.method}: ${requestInfo.toString()}`);
496+
const fetchCount = `[${++this.fetchCounter}]`;
497+
const fetchStart = performance.now();
498+
const method = init?.method ? `${init.method}: ` : '';
499+
const url = requestInfo.toString();
457500
const response = await fetch(requestInfo.toString(), init);
501+
const fetchEnd = performance.now();
458502
if (!response.ok) {
459503
let details: string | undefined = undefined;
460504
try {
@@ -465,7 +509,18 @@ export class CreateApi {
465509
const { statusText, status } = response;
466510
throw new CreateError(statusText, status, details);
467511
}
512+
const parseStart = performance.now();
468513
const result = await resultProvider(response);
514+
const parseEnd = performance.now();
515+
console.debug(
516+
`HTTP ${fetchCount} ${method} ${url} [fetch: ${(
517+
fetchEnd - fetchStart
518+
).toFixed(2)} ms, parse: ${(parseEnd - parseStart).toFixed(
519+
2
520+
)} ms] body: ${
521+
typeof result === 'string' ? result : JSON.stringify(result)
522+
}`
523+
);
469524
return result;
470525
}
471526

arduino-ide-extension/src/browser/widgets/cloud-sketchbook/cloud-sketch-cache.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { FileStat } from '@theia/filesystem/lib/common/files';
22
import { injectable } from '@theia/core/shared/inversify';
3-
import { toPosixPath } from '../../create/create-paths';
3+
import { splitSketchPath } from '../../create/create-paths';
44
import { Create } from '../../create/typings';
55

66
@injectable()
77
export class SketchCache {
88
sketches: Record<string, Create.Sketch> = {};
99
fileStats: Record<string, FileStat> = {};
10+
private _createPathPrefix: string | undefined;
1011

1112
init(): void {
1213
// reset the data
@@ -32,14 +33,21 @@ export class SketchCache {
3233

3334
addSketch(sketch: Create.Sketch): void {
3435
const { path } = sketch;
35-
const posixPath = toPosixPath(path);
36+
const [pathPrefix, posixPath] = splitSketchPath(path);
37+
if (pathPrefix !== this._createPathPrefix) {
38+
this._createPathPrefix = pathPrefix;
39+
}
3640
this.sketches[posixPath] = sketch;
3741
}
3842

3943
getSketch(path: string): Create.Sketch | null {
4044
return this.sketches[path] || null;
4145
}
4246

47+
get createPathPrefix(): string | undefined {
48+
return this._createPathPrefix;
49+
}
50+
4351
toString(): string {
4452
return JSON.stringify({
4553
sketches: this.sketches,

arduino-ide-extension/src/test/browser/create-api.test.ts

+34
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Container, ContainerModule } from '@theia/core/shared/inversify';
22
import { assert, expect } from 'chai';
33
import fetch from 'cross-fetch';
4+
import { posix } from 'path';
45
import { v4 } from 'uuid';
56
import { ArduinoPreferences } from '../../browser/arduino-preferences';
67
import { AuthenticationClientService } from '../../browser/auth/authentication-client-service';
@@ -251,6 +252,39 @@ describe('create-api', () => {
251252
expect(sketch).to.be.undefined;
252253
});
253254
});
255+
256+
it("should fetch the sketch when transforming the 'secrets' into '#include' and the sketch is not in the cache", async () => {
257+
const name = v4();
258+
const posixPath = toPosix(name);
259+
const newSketch = await createApi.createSketch(
260+
posixPath,
261+
'void setup(){} void loop(){}',
262+
{
263+
secrets: {
264+
data: [
265+
{
266+
name: 'SECRET_THING',
267+
value: '❤︎',
268+
},
269+
],
270+
},
271+
}
272+
);
273+
expect(newSketch).to.be.not.undefined;
274+
expect(newSketch.secrets).to.be.not.undefined;
275+
expect(Array.isArray(newSketch.secrets)).to.be.true;
276+
expect(newSketch.secrets?.length).to.be.equal(1);
277+
expect(newSketch.secrets?.[0]).to.be.deep.equal({
278+
name: 'SECRET_THING',
279+
value: '❤︎',
280+
});
281+
createApi.sketchCache.init(); // invalidate the cache
282+
const content = await createApi.readFile(
283+
posix.join(posixPath, `${name}.ino`)
284+
);
285+
expect(content.includes(`#include "${Create.arduino_secrets_file}"`)).to.be
286+
.true;
287+
});
254288
});
255289

256290
// Using environment variables is recommended for testing but you can modify the module too.

0 commit comments

Comments
 (0)