Skip to content

Commit 3158003

Browse files
committed
Release v1.17.0
1 parent 2baae94 commit 3158003

29 files changed

+1039
-37
lines changed

cli/blocks.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
} from '../sdk/studio';
2626
import { ips } from './get-ips';
2727
import { spawnHelper } from './spawn-helper';
28+
import { RequestDetailedFile } from '../sdk/studio/sdk/api';
2829

2930
const version = getCliVersion();
3031

@@ -73,14 +74,6 @@ type DSPChangedMsg = [string, {
7374
error?: string;
7475
}];
7576

76-
interface RequestDetailedFile {
77-
value: Buffer;
78-
options?: {
79-
filename?: string;
80-
contentType?: string;
81-
};
82-
}
83-
8477
export type MessageBlock = [
8578
string,
8679
{

cli/uploader.ts

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,17 @@ import asyncpool from 'tiny-async-pool';
77
import { upload, VALID_EXTENSIONS } from './make-image';
88
import { getCliVersion, initCliApp, setupCliApp } from './init-cli-app';
99
import { Config } from './config';
10-
import { ExportBoundingBoxesFile, parseBoundingBoxLabels } from '../shared/bounding-box-file-types';
10+
import { ExportBoundingBoxesFile, ExportInputBoundingBox, parseBoundingBoxLabels, parseUploaderInfo } from '../shared/bounding-box-file-types';
1111
import { FSHelpers } from './fs-helpers';
1212

1313
type UploaderFileType = {
1414
path: string,
1515
category: string,
1616
label: { type: 'unlabeled' } | { type: 'infer'} | { type: 'label', label: string },
1717
metadata: { [k: string]: string } | undefined,
18+
boundingBoxes: ExportInputBoundingBox[] | undefined,
1819
};
1920

20-
interface InfoFileV1 {
21-
version: 1;
22-
files: {
23-
path: string,
24-
category: string,
25-
label: { type: 'unlabeled' } | { type: 'label', label: string },
26-
metadata: { [k: string]: string } | undefined,
27-
}[];
28-
}
29-
3021
const versionArgv = process.argv.indexOf('--version') > -1;
3122
const cleanArgv = process.argv.indexOf('--clean') > -1;
3223
const labelArgvIx = process.argv.indexOf('--label');
@@ -55,6 +46,8 @@ const infoFileArgvIx = process.argv.indexOf('--info-file');
5546
const infoFileArgv = infoFileArgvIx !== -1 ? process.argv[infoFileArgvIx + 1] : undefined;
5647
const metadataArgvIx = process.argv.indexOf('--metadata');
5748
const metadataArgv = metadataArgvIx !== -1 ? process.argv[metadataArgvIx + 1] : undefined;
49+
const directoryArgvIx = process.argv.indexOf('--directory');
50+
const directoryArgv = directoryArgvIx !== -1 ? process.argv[directoryArgvIx + 1] : undefined;
5851

5952
let configFactory: Config;
6053

@@ -142,21 +135,19 @@ const cliOptions = {
142135

143136
if (infoFileArgv) {
144137
try {
145-
let infoFile = <InfoFileV1>JSON.parse(<string>await fs.promises.readFile(infoFileArgv, 'utf-8'));
146-
if (infoFile.version !== 1) {
147-
throw new Error('Invalid version, expected "1"');
148-
}
149-
if (!Array.isArray(infoFile.files)) {
150-
throw new Error('Invalid files, expected an array');
151-
}
152-
138+
let infoFile = parseUploaderInfo(<string>await fs.promises.readFile(infoFileArgv, 'utf-8'));
153139
files = [];
154140
for (let f of infoFile.files) {
141+
if (!Path.isAbsolute(f.path)) {
142+
f.path = Path.join(Path.dirname(infoFileArgv), f.path);
143+
}
144+
155145
files.push({
156146
category: f.category,
157147
label: f.label,
158148
path: f.path,
159149
metadata: f.metadata,
150+
boundingBoxes: f.boundingBoxes,
160151
});
161152
}
162153
}
@@ -165,6 +156,34 @@ const cliOptions = {
165156
process.exit(1);
166157
}
167158
}
159+
else if (directoryArgv) {
160+
const dir = await fs.promises.realpath(directoryArgv);
161+
let dirs = await fs.promises.readdir(dir);
162+
if (!dirs.find(x => x === 'training')) {
163+
throw new Error(`Cannot find a "training" directory in ${dir} (via --directory)`);
164+
}
165+
if (!dirs.find(x => x === 'testing')) {
166+
throw new Error(`Cannot find a "testing" directory in ${dir} (via --directory)`);
167+
}
168+
169+
files = [];
170+
171+
for (let c of [ 'training', 'testing' ]) {
172+
for (let f of await fs.promises.readdir(Path.join(dir, c))) {
173+
let fullPath = Path.join(dir, c, f);
174+
175+
if (f.startsWith('.')) continue;
176+
177+
files.push({
178+
category: c,
179+
label: { type: 'infer' },
180+
path: fullPath,
181+
metadata: { },
182+
boundingBoxes: [],
183+
});
184+
}
185+
}
186+
}
168187
else {
169188

170189
let fileArgs = process.argv.slice(argv);
@@ -234,6 +253,7 @@ const cliOptions = {
234253
category: 'split',
235254
label: { type: 'label', label: categoryFolder.replace('.class', '') },
236255
metadata: metadata,
256+
boundingBoxes: undefined,
237257
});
238258
}
239259
}
@@ -261,6 +281,7 @@ const cliOptions = {
261281
label: labelArgv
262282
} : { type: 'infer' },
263283
metadata: metadata,
284+
boundingBoxes: undefined,
264285
};
265286
});
266287
}
@@ -300,9 +321,12 @@ const cliOptions = {
300321

301322
const processFile = async (file: UploaderFileType) => {
302323
const boundingBoxesFile = boundingBoxCache[Path.resolve(Path.dirname(file.path))];
303-
const boundingBoxes = boundingBoxesFile ?
324+
let boundingBoxes = boundingBoxesFile ?
304325
(boundingBoxesFile.boundingBoxes[Path.basename(file.path)] || undefined) :
305326
undefined;
327+
if (!boundingBoxes && file.boundingBoxes) {
328+
boundingBoxes = file.boundingBoxes;
329+
}
306330

307331
try {
308332
let hrstart = Date.now();

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "edge-impulse-cli",
3-
"version": "1.16.4",
3+
"version": "1.17.0",
44
"description": "Command-line interface tools for Edge Impulse",
55
"preferGlobal": true,
66
"scripts": {

sdk/studio/sdk/api/adminApi.ts

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,170 @@ export class AdminApi {
165165
});
166166
});
167167
}
168+
/**
169+
* Admin-only API to delete an organization. If `fullDeletion` is set, it deletes the organization\'s identifiable information and files. Otherwise, it soft deletes the organization by setting its `delete_date` value.
170+
* @summary Delete an organization
171+
* @param organizationId Organization ID
172+
* @param fullDeletion Set to true for full deletion
173+
*/
174+
public async adminDeleteOrganization (organizationId: number, fullDeletion?: boolean, options: {headers: {[name: string]: string}} = {headers: {}}) : Promise<GenericApiResponse> {
175+
const localVarPath = this.basePath + '/api/admin/organizations/{organizationId}'
176+
.replace('{' + 'organizationId' + '}', encodeURIComponent(String(organizationId)));
177+
let localVarQueryParameters: any = {};
178+
let localVarHeaderParams: any = (<any>Object).assign({}, this.defaultHeaders);
179+
const produces = ['application/json'];
180+
// give precedence to 'application/json'
181+
if (produces.indexOf('application/json') >= 0) {
182+
localVarHeaderParams.Accept = 'application/json';
183+
} else {
184+
localVarHeaderParams.Accept = produces.join(',');
185+
}
186+
let localVarFormParams: any = {};
187+
188+
// verify required parameter 'organizationId' is not null or undefined
189+
if (organizationId === null || organizationId === undefined) {
190+
throw new Error('Required parameter organizationId was null or undefined when calling adminDeleteOrganization.');
191+
}
192+
193+
if (fullDeletion !== undefined) {
194+
localVarQueryParameters['fullDeletion'] = ObjectSerializer.serialize(fullDeletion, "boolean");
195+
}
196+
197+
(<any>Object).assign(localVarHeaderParams, options.headers);
198+
199+
let localVarUseFormData = false;
200+
201+
let localVarRequestOptions: localVarRequest.Options = {
202+
method: 'DELETE',
203+
qs: localVarQueryParameters,
204+
headers: localVarHeaderParams,
205+
uri: localVarPath,
206+
useQuerystring: this._useQuerystring,
207+
agentOptions: {keepAlive: false},
208+
json: true,
209+
};
210+
211+
let authenticationPromise = Promise.resolve();
212+
authenticationPromise = authenticationPromise.then(() => this.authentications.ApiKeyAuthentication.applyToRequest(localVarRequestOptions));
213+
214+
authenticationPromise = authenticationPromise.then(() => this.authentications.JWTAuthentication.applyToRequest(localVarRequestOptions));
215+
216+
authenticationPromise = authenticationPromise.then(() => this.authentications.JWTHttpHeaderAuthentication.applyToRequest(localVarRequestOptions));
217+
218+
authenticationPromise = authenticationPromise.then(() => this.authentications.default.applyToRequest(localVarRequestOptions));
219+
return authenticationPromise.then(() => {
220+
if (Object.keys(localVarFormParams).length) {
221+
if (localVarUseFormData) {
222+
(<any>localVarRequestOptions).formData = localVarFormParams;
223+
} else {
224+
localVarRequestOptions.form = localVarFormParams;
225+
}
226+
}
227+
return new Promise<GenericApiResponse>((resolve, reject) => {
228+
localVarRequest(localVarRequestOptions, (error, response, body) => {
229+
if (error) {
230+
reject(error);
231+
} else {
232+
body = ObjectSerializer.deserialize(body, "GenericApiResponse");
233+
234+
const errString = `Failed to call "${localVarPath}", returned ${response.statusCode}: ` + response.body;
235+
236+
if (typeof body.success === 'boolean' && !body.success) {
237+
reject(new Error(body.error || errString));
238+
}
239+
else if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) {
240+
resolve(body);
241+
}
242+
else {
243+
reject(errString);
244+
}
245+
}
246+
});
247+
});
248+
});
249+
}
250+
/**
251+
* Admin-only API to delete a user. If `fullDeletion` is set, it deletes the user\'s identifiable information and files. Otherwise, it soft deletes the user by setting it\'s `delete_date` value.
252+
* @summary Delete a user
253+
* @param userId User ID
254+
* @param fullDeletion Set to true for full deletion
255+
*/
256+
public async adminDeleteUser (userId: number, fullDeletion?: boolean, options: {headers: {[name: string]: string}} = {headers: {}}) : Promise<GenericApiResponse> {
257+
const localVarPath = this.basePath + '/api/admin/users/{userId}'
258+
.replace('{' + 'userId' + '}', encodeURIComponent(String(userId)));
259+
let localVarQueryParameters: any = {};
260+
let localVarHeaderParams: any = (<any>Object).assign({}, this.defaultHeaders);
261+
const produces = ['application/json'];
262+
// give precedence to 'application/json'
263+
if (produces.indexOf('application/json') >= 0) {
264+
localVarHeaderParams.Accept = 'application/json';
265+
} else {
266+
localVarHeaderParams.Accept = produces.join(',');
267+
}
268+
let localVarFormParams: any = {};
269+
270+
// verify required parameter 'userId' is not null or undefined
271+
if (userId === null || userId === undefined) {
272+
throw new Error('Required parameter userId was null or undefined when calling adminDeleteUser.');
273+
}
274+
275+
if (fullDeletion !== undefined) {
276+
localVarQueryParameters['fullDeletion'] = ObjectSerializer.serialize(fullDeletion, "boolean");
277+
}
278+
279+
(<any>Object).assign(localVarHeaderParams, options.headers);
280+
281+
let localVarUseFormData = false;
282+
283+
let localVarRequestOptions: localVarRequest.Options = {
284+
method: 'DELETE',
285+
qs: localVarQueryParameters,
286+
headers: localVarHeaderParams,
287+
uri: localVarPath,
288+
useQuerystring: this._useQuerystring,
289+
agentOptions: {keepAlive: false},
290+
json: true,
291+
};
292+
293+
let authenticationPromise = Promise.resolve();
294+
authenticationPromise = authenticationPromise.then(() => this.authentications.ApiKeyAuthentication.applyToRequest(localVarRequestOptions));
295+
296+
authenticationPromise = authenticationPromise.then(() => this.authentications.JWTAuthentication.applyToRequest(localVarRequestOptions));
297+
298+
authenticationPromise = authenticationPromise.then(() => this.authentications.JWTHttpHeaderAuthentication.applyToRequest(localVarRequestOptions));
299+
300+
authenticationPromise = authenticationPromise.then(() => this.authentications.default.applyToRequest(localVarRequestOptions));
301+
return authenticationPromise.then(() => {
302+
if (Object.keys(localVarFormParams).length) {
303+
if (localVarUseFormData) {
304+
(<any>localVarRequestOptions).formData = localVarFormParams;
305+
} else {
306+
localVarRequestOptions.form = localVarFormParams;
307+
}
308+
}
309+
return new Promise<GenericApiResponse>((resolve, reject) => {
310+
localVarRequest(localVarRequestOptions, (error, response, body) => {
311+
if (error) {
312+
reject(error);
313+
} else {
314+
body = ObjectSerializer.deserialize(body, "GenericApiResponse");
315+
316+
const errString = `Failed to call "${localVarPath}", returned ${response.statusCode}: ` + response.body;
317+
318+
if (typeof body.success === 'boolean' && !body.success) {
319+
reject(new Error(body.error || errString));
320+
}
321+
else if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) {
322+
resolve(body);
323+
}
324+
else {
325+
reject(errString);
326+
}
327+
}
328+
});
329+
});
330+
});
331+
}
168332
/**
169333
* Admin-only API to find a user by username or email address.
170334
* @summary Find a user

sdk/studio/sdk/api/apis.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,12 @@ export class HttpError extends Error {
8686

8787
export interface RequestDetailedFile {
8888
value: Buffer;
89-
options?: {
90-
filename?: string;
91-
contentType?: string;
89+
options: {
90+
filename: string;
91+
contentType: string;
9292
}
9393
}
9494

95-
export type RequestFile = string | Buffer | fs.ReadStream | RequestDetailedFile;
95+
export type RequestFile = RequestDetailedFile;
9696

9797
export const APIS = [AdminApi, AllowsReadOnlyApi, AuthApi, CDNApi, ClassifyApi, ContentDispositionInlineApi, DSPApi, DeploymentApi, DevicesApi, ExportApi, HealthApi, ImpulseApi, JobsApi, LearnApi, LoginApi, MetricsApi, OptimizationApi, OrganizationAllowDeveloperProfileApi, OrganizationAllowGuestAccessApi, OrganizationBlocksApi, OrganizationCreateProjectApi, OrganizationDataApi, OrganizationJobsApi, OrganizationPipelinesApi, OrganizationPortalsApi, OrganizationRequiresAdminApi, OrganizationsApi, PerformanceCalibrationApi, ProjectsApi, RawDataApi, RequiresSudoApi, RequiresThirdPartyAuthApiKeyApi, SupportsRangeApi, ThemesApi, ThirdPartyAuthApi, UploadPortalApi, UserApi, WhitelabelsApi];

0 commit comments

Comments
 (0)