diff --git a/index.ts b/index.ts index ae3002a51..5d69b1f92 100644 --- a/index.ts +++ b/index.ts @@ -18,6 +18,7 @@ export * from './src/api/GraphEdge'; export * from './src/api/GraphNode'; export * from './src/api/GraphQuery'; export * from './src/api/GraphSchema'; +export * from './src/api/import'; export * from './src/api/License'; export * from './src/api/Linkurious'; export * from './src/api/Plugin'; diff --git a/src/api/GraphEdge/index.ts b/src/api/GraphEdge/index.ts index a823569a6..a1f76e482 100644 --- a/src/api/GraphEdge/index.ts +++ b/src/api/GraphEdge/index.ts @@ -9,7 +9,13 @@ import {LkErrorKey} from '../../http/response'; import {LkEdge, LkSubGraph} from '../graphItemTypes'; import {IDataSourceParams} from '../commonTypes'; -import {ICreateEdgeParams, IDeleteEdgeParams, IGetEdgeParams, IUpdateEdgeParams} from './types'; +import { + BulkCreateEdgesParams, + ICreateEdgeParams, + IDeleteEdgeParams, + IGetEdgeParams, + IUpdateEdgeParams +} from './types'; export * from './types'; @@ -56,6 +62,19 @@ export class GraphEdgeAPI extends Request { }); } + /** + * Add a chunk of edges to the graph. Optionally accepts an import ID parameter. If it is + * provided, the edges are linked to this import (via a hidden property). Otherwise, the + * edges are created without any link. + */ + public bulkCreateEdges(params: BulkCreateEdgesParams) { + return this.request({ + errors: [UNAUTHORIZED, DATA_SOURCE_UNAVAILABLE, FORBIDDEN], + url: '/:sourceKey/graph/edges/bulk', + method: 'POST', + params: params + }); + } /** * Delete an edge from the graph. */ diff --git a/src/api/GraphEdge/types.ts b/src/api/GraphEdge/types.ts index 725b3b633..1b39f52c4 100644 --- a/src/api/GraphEdge/types.ts +++ b/src/api/GraphEdge/types.ts @@ -27,3 +27,8 @@ export interface IUpdateEdgeParams extends IDataSourceParams { export interface IDeleteEdgeParams extends IDataSourceParams { id: string; } + +export interface BulkCreateEdgesParams extends IDataSourceParams { + edges: Omit; + importId?: number; +} diff --git a/src/api/GraphNode/index.ts b/src/api/GraphNode/index.ts index 48b6778e3..c31e62e3c 100644 --- a/src/api/GraphNode/index.ts +++ b/src/api/GraphNode/index.ts @@ -10,6 +10,7 @@ import {LkNode, LkNodeStatistics, LkSubGraph} from '../graphItemTypes'; import {IDataSourceParams} from '../commonTypes'; import { + BulkCreateNodesParams, ICreateNodeParams, IDeleteNodeParams, IGetAdjacentNodesParams, @@ -98,6 +99,26 @@ export class GraphNodeAPI extends Request { }); } + /** + * Add a chunk of nodes to the graph. Optionally accepts an import ID parameter. If it is + * provided, the nodes are linked to this import (via a hidden property). Otherwise, the + * nodes are created without any link. + */ + public bulkCreateNodes(params: BulkCreateNodesParams) { + return this.request({ + errors: [ + UNAUTHORIZED, + DATA_SOURCE_UNAVAILABLE, + FORBIDDEN, + INVALID_PARAMETER, + CONSTRAINT_VIOLATION + ], + url: '/:sourceKey/graph/nodes/bulk', + method: 'POST', + params: params + }); + } + /** * Get the number of nodes in the graph. */ diff --git a/src/api/GraphNode/types.ts b/src/api/GraphNode/types.ts index 7b75e0fb2..8cdafd2f0 100644 --- a/src/api/GraphNode/types.ts +++ b/src/api/GraphNode/types.ts @@ -29,6 +29,11 @@ export interface IDeleteNodeParams extends IDataSourceParams { id: string; } +export interface BulkCreateNodesParams extends IDataSourceParams { + nodes: Omit; + importId?: number; +} + export interface IGetStatisticsParams extends IDataSourceParams { ids: string[]; withDigest?: boolean; diff --git a/src/api/import/index.ts b/src/api/import/index.ts new file mode 100644 index 000000000..487e9581c --- /dev/null +++ b/src/api/import/index.ts @@ -0,0 +1,109 @@ +/** + * LINKURIOUS CONFIDENTIAL + * Copyright Linkurious SAS 2012 - 2025 + * + * - Created on 2025-11-18. + */ +import {Request} from '../../http/request'; +import {LkErrorKey} from '../../http/response'; + +import { + CreateImportParams, + CreateImportTemplateParams, + DeleteImportDataParams, + DeleteImportTemplateParams, + GetImportTemplatesParams, + Import, + ImportTemplate, + UpdateImportTemplateParams +} from './types'; + +export * from './types'; + +const {UNAUTHORIZED, DATA_SOURCE_UNAVAILABLE, FORBIDDEN, NOT_FOUND} = LkErrorKey; + +export class ImportAPI extends Request { + /** + * Create a new import template. + */ + createImportTemplate(this: Request, params: CreateImportTemplateParams) { + return this.request({ + errors: [UNAUTHORIZED, FORBIDDEN, DATA_SOURCE_UNAVAILABLE], + url: '/:sourceKey/imports/templates', + method: 'POST', + params: params + }); + } + + /** + * Update an existing import template. + */ + updateImportTemplate(this: Request, params: UpdateImportTemplateParams) { + return this.request({ + errors: [UNAUTHORIZED, FORBIDDEN, DATA_SOURCE_UNAVAILABLE, NOT_FOUND], + url: '/:sourceKey/imports/templates/:id', + method: 'PATCH', + params: params + }); + } + + /** + * Delete an existing import template. + */ + deleteImportTemplate(params: DeleteImportTemplateParams) { + return this.request({ + errors: [UNAUTHORIZED, FORBIDDEN, DATA_SOURCE_UNAVAILABLE, NOT_FOUND], + url: '/:sourceKey/imports/templates/:id', + method: 'DELETE', + params: params + }); + } + + /** + * List all the import templates (the publicly shared ones and the private ones owned by the user). + */ + getImportTemplates(this: Request<{items: ImportTemplate[]}>, params: GetImportTemplatesParams) { + return this.request({ + errors: [UNAUTHORIZED, FORBIDDEN, DATA_SOURCE_UNAVAILABLE], + url: '/:sourceKey/imports/templates', + method: 'GET', + params: params + }); + } + + /** + * Create a new import. + */ + createImport(this: Request, params: CreateImportParams) { + return this.request({ + errors: [UNAUTHORIZED, FORBIDDEN, DATA_SOURCE_UNAVAILABLE], + url: '/:sourceKey/imports', + method: 'POST', + params: params + }); + } + + /** + * List all the existing imports (for the current user if they are not admin, or for all users + * if the current user is an admin). + */ + getImports(this: Request<{items: Import[]}>) { + return this.request({ + errors: [UNAUTHORIZED, FORBIDDEN, DATA_SOURCE_UNAVAILABLE], + url: '/:sourceKey/imports', + method: 'GET' + }); + } + + /** + * Delete all the nodes/edges uploaded as part of an existing import. + */ + deleteImportData(params: DeleteImportDataParams) { + return this.request({ + errors: [UNAUTHORIZED, FORBIDDEN, DATA_SOURCE_UNAVAILABLE], + url: '/:sourceKey/imports/:id/data', + method: 'DELETE', + params: params + }); + } +} diff --git a/src/api/import/types.ts b/src/api/import/types.ts new file mode 100644 index 000000000..0da7c68a1 --- /dev/null +++ b/src/api/import/types.ts @@ -0,0 +1,106 @@ +/** + * LINKURIOUS CONFIDENTIAL + * Copyright Linkurious SAS 2012 - 2025 + * + * - Created on 2025-11-18. + */ +import {DeletableUser, IDataSourceParams, SharingMode} from '../commonTypes'; +import {EntityType} from '../GraphSchema'; + +export type CreateImportTemplateParams = + | CreateNodeImportTemplateParams + | CreateEdgeImportTemplateParams; + +export interface CreateNodeImportTemplateParams extends CreateBaseImportTemplateParams { + entityType: EntityType.NODE; +} + +export interface CreateEdgeImportTemplateParams extends CreateBaseImportTemplateParams { + entityType: EntityType.EDGE; + sourceNode: NodeReference; + targetNode: NodeReference; +} + +interface CreateBaseImportTemplateParams extends IDataSourceParams { + name: string; + description?: string; + sharing?: SharingMode.PRIVATE | SharingMode.SOURCE; + /** + * The target node category / edge type. + */ + itemType: string; + /** + * How to map imported fields to node/edge properties. + */ + properties: PropertyMapping[]; +} + +interface PropertyMapping { + /** + * The field in the imported file. + */ + sourceField: string; + /** + * The destination property key on the node/edge. + */ + targetProperty: string; +} + +interface NodeReference { + /** + * The field in the imported file. + */ + sourceField: string; + /** + * The destination node category. + */ + targetCategory: string; + /** + * The destination property key on the node. If it is undefined, the destination is the native + * ID of the node. + */ + targetProperty?: string; +} + +export type UpdateImportTemplateParams = CreateImportTemplateParams & { + id: number; +}; + +export interface DeleteImportTemplateParams extends IDataSourceParams { + id: number; +} + +export interface GetImportTemplatesParams extends IDataSourceParams { + entityType?: EntityType; +} + +export type ImportTemplate = CreateImportTemplateParams & { + id: number; + sourceKey: string; +}; + +export interface CreateImportParams extends IDataSourceParams { + /** + * Filename of the uploaded file (including its extension). + */ + filename: string; +} + +export interface Import extends CreateImportParams { + id: number; + sourceKey: string; + /** + * Who created this import. + */ + createdBy: DeletableUser; + /** + * When was this import handle created (so before actually uploading nodes/edges). + * + * It's a date-time formatted as a ISO 8601 string, for instance "2025-01-31T09:32:07.508Z". + */ + createdAt: string; +} + +export interface DeleteImportDataParams extends IDataSourceParams { + id: number; +} diff --git a/src/index.ts b/src/index.ts index 2dcd7edcd..5185908b1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,6 +21,7 @@ import {GraphEdgeAPI} from './api/GraphEdge'; import {GraphNodeAPI} from './api/GraphNode'; import {GraphQueryAPI} from './api/GraphQuery'; import {GraphSchemaAPI} from './api/GraphSchema'; +import {ImportAPI} from './api/import'; import {LicenseAPI} from './api/License'; import {LinkuriousAPI} from './api/Linkurious'; import {PluginAPI} from './api/Plugin'; @@ -50,6 +51,7 @@ export class RestClient extends ErrorListener { public readonly graphNode: GraphNodeAPI; public readonly graphQuery: GraphQueryAPI; public readonly graphSchema: GraphSchemaAPI; + public readonly import: ImportAPI; public readonly license: LicenseAPI; public readonly linkurious: LinkuriousAPI; public readonly plugin: PluginAPI; @@ -97,6 +99,7 @@ export class RestClient extends ErrorListener { this.graphNode = new GraphNodeAPI(moduleProps); this.graphQuery = new GraphQueryAPI(moduleProps); this.graphSchema = new GraphSchemaAPI(moduleProps); + this.import = new ImportAPI(moduleProps); this.license = new LicenseAPI(moduleProps); this.linkurious = new LinkuriousAPI(moduleProps); this.plugin = new PluginAPI(moduleProps);