@@ -4,7 +4,10 @@ import { useNotify } from "@/composables/useNotify"
44import { useTypeAccess } from "@/composables/useTypeAccess"
55import { getObjectType } from "@/utils/objectTypes"
66import {
7+ ACCEPTED_FILE_TYPES_HINT ,
78 IMPORTABLE_TYPES ,
9+ SCHEME_MODES ,
10+ SSSOM_CONTENT_TYPE ,
811 detectFormat ,
912 extractFirstObject ,
1013 guessType ,
@@ -52,6 +55,7 @@ export function useImport() {
5255
5356 const source = ref ( null )
5457 const isBulk = ref ( false )
58+ const schemeMode = ref ( SCHEME_MODES [ 0 ] . value )
5559 const selectedType = ref ( null )
5660
5761 let abortController = null
@@ -103,6 +107,12 @@ export function useImport() {
103107
104108 const isUploading = computed ( ( ) => source . value ?. status === "uploading" )
105109
110+ /**
111+ * Whether the source is an SSSOM/TSV file, the only format whose import the
112+ * scheme handling applies to.
113+ */
114+ const hasSchemeOptions = computed ( ( ) => source . value ?. format === "sssom" )
115+
106116 /**
107117 * What a finished import wrote, or null while none succeeded.
108118 */
@@ -143,14 +153,19 @@ export function useImport() {
143153 if ( inspected . kind !== "file" ) {
144154 return
145155 }
146- try {
147- const sample = await readSample ( inspected . file , inspected . format )
148- inspected . detectedType = guessType (
149- extractFirstObject ( sample , inspected . format ) ,
150- )
151- } catch ( error ) {
152- notify ( `Could not read ${ inspected . name } : ${ error . message } ` , "warning" )
153- return
156+ if ( inspected . format === "sssom" ) {
157+ // SSSOM/TSV holds mappings by definition, and is not JSON to sample.
158+ inspected . detectedType = "mappings"
159+ } else {
160+ try {
161+ const sample = await readSample ( inspected . file , inspected . format )
162+ inspected . detectedType = guessType (
163+ extractFirstObject ( sample , inspected . format ) ,
164+ )
165+ } catch ( error ) {
166+ notify ( `Could not read ${ inspected . name } : ${ error . message } ` , "warning" )
167+ return
168+ }
154169 }
155170 if (
156171 inspected . detectedType &&
@@ -185,6 +200,13 @@ export function useImport() {
185200 if ( ! trimmed ) {
186201 return
187202 }
203+ if ( detectFormat ( trimmed ) === "sssom" ) {
204+ notify (
205+ `Cannot import ${ trimmed } . The server cannot fetch SSSOM/TSV, so it has to be imported as a file.` ,
206+ "warning" ,
207+ )
208+ return
209+ }
188210 setSource ( createSource ( { kind : "url" , url : trimmed } ) )
189211 }
190212
@@ -216,7 +238,7 @@ export function useImport() {
216238 */
217239 function reportRejectedFile ( name ) {
218240 notify (
219- `Cannot import ${ name } — only JSON and NDJSON are supported.` ,
241+ `Cannot import ${ name } . Only ${ ACCEPTED_FILE_TYPES_HINT } are supported.` ,
220242 "warning" ,
221243 )
222244 }
@@ -231,8 +253,8 @@ export function useImport() {
231253 /**
232254 * Builds the request parameters.
233255 *
234- * A URL is handed to the server, which fetches it itself.
235- * A file is streamed as multipart form data .
256+ * A URL is handed to the server, which fetches it itself. A file is sent as
257+ * the request body, see { @link buildBody} .
236258 *
237259 * @param {!ImportSource } imported The source to import.
238260 * @returns {!Object } The request parameters.
@@ -242,6 +264,9 @@ export function useImport() {
242264 if ( isBulk . value ) {
243265 params . bulk = true
244266 }
267+ if ( hasSchemeOptions . value ) {
268+ params . scheme = schemeMode . value
269+ }
245270 if ( imported . kind === "url" ) {
246271 params . url = imported . url
247272 if ( imported . format ) {
@@ -254,18 +279,40 @@ export function useImport() {
254279 /**
255280 * Builds the request body.
256281 *
282+ * SSSOM/TSV is sent raw, because the server recognizes it by the request's
283+ * content type. Other formats are streamed as multipart form data.
284+ *
257285 * @param {!ImportSource } imported The source to import.
258- * @returns {?FormData } The body, or null when the server fetches the data.
286+ * @returns {?FormData|!File } The body, or null when the server fetches the
287+ * data.
259288 */
260289 function buildBody ( imported ) {
261290 if ( imported . kind !== "file" ) {
262291 return null
263292 }
293+ if ( imported . format === "sssom" ) {
294+ return imported . file
295+ }
264296 const body = new FormData ( )
265297 body . append ( "data" , imported . file , imported . file . name )
266298 return body
267299 }
268300
301+ /**
302+ * Builds the request headers.
303+ *
304+ * @param {!ImportSource } imported The source to import.
305+ * @returns {!Object<string, string> } The headers, empty unless the format has
306+ * to be declared. Multipart bodies are left to axios, which adds the
307+ * boundary to their content type.
308+ */
309+ function buildHeaders ( imported ) {
310+ if ( imported . kind !== "file" || imported . format !== "sssom" ) {
311+ return { }
312+ }
313+ return { "Content-Type" : SSSOM_CONTENT_TYPE }
314+ }
315+
269316 /**
270317 * Aborts a running import.
271318 */
@@ -304,6 +351,7 @@ export function useImport() {
304351 method : "post" ,
305352 url : resolveImportUrl ( store . activeUrl , type ) ,
306353 params : buildParams ( imported ) ,
354+ headers : buildHeaders ( imported ) ,
307355 data : buildBody ( imported ) ,
308356 signal : abortController . signal ,
309357 } )
@@ -321,7 +369,7 @@ export function useImport() {
321369 imported . errorKind = "canceled"
322370 return
323371 }
324- const { kind, message } = describeImportError ( error )
372+ const { kind, message } = describeImportError ( error , imported . format )
325373 imported . status = "failed"
326374 imported . errorKind = kind
327375 imported . error = message
@@ -354,12 +402,14 @@ export function useImport() {
354402 return {
355403 source,
356404 isBulk,
405+ schemeMode,
357406 selectedType,
358407 canImport,
359408 typeAccess,
360409 unavailableReason,
361410 blockedReason,
362411 hasTypeMismatch,
412+ hasSchemeOptions,
363413 isUploading,
364414 result,
365415 addFile,
0 commit comments