rubensworks / fetch-sparql-endpoint.js

A simple, lightweight module to send queries to SPARQL endpoints and retrieve their results in a streaming fashion.
MIT License
21 stars 12 forks source link

_a.cancel is not a function in v2.0.2 #35

Closed joachimvh closed 3 years ago

joachimvh commented 3 years ago

In https://github.com/solid/community-server/pull/791 I had to roll fetch-sparql-endpoint back to 2.0.1 because the sparql integration test failed with the following error:

FAIL test/integration/SparqlStorage.test.ts (5.307 s)
  ● A server with a SPARQL endpoint as storage › can add a Turtle file to the store.

    TypeError: _a.cancel is not a function

      314 |     this.logger.info(`Sending SPARQL UPDATE query to ${this.endpoint}: ${query}`);
      315 |     try {
    > 316 |       return await this.fetcher.fetchUpdate(this.endpoint, query);
          |              ^
      317 |     } catch (error: unknown) {
      318 |       this.logger.error(`SPARQL endpoint ${this.endpoint} error: ${createErrorMessage(error)}`);
      319 |       throw error;

      at SparqlEndpointFetcher.handleFetchCall (node_modules/fetch-sparql-endpoint/lib/SparqlEndpointFetcher.js:169:85)
          at runMicrotasks (<anonymous>)
      at SparqlEndpointFetcher.fetchUpdate (node_modules/fetch-sparql-endpoint/lib/SparqlEndpointFetcher.js:130:9)
      at SparqlDataAccessor.sendSparqlUpdate (src/storage/accessors/SparqlDataAccessor.ts:316:14)
      at DataAccessorBasedStore.writeData (src/storage/DataAccessorBasedStore.ts:320:5)
      at runWithTimeout (src/util/locking/WrappedExpiringReadWriteLocker.ts:67:16)
      at GreedyReadWriteLocker.withWriteLock (src/util/locking/GreedyReadWriteLocker.ts:62:14)
      at MonitoringStore.setRepresentation (src/storage/MonitoringStore.ts:45:29)
      at RootContainerInitializer.createRootContainer (src/init/RootContainerInitializer.ts:54:5)
      at RootContainerInitializer.handle (src/init/RootContainerInitializer.ts:33:7)
          at async Promise.all (index 0)
      at SequenceHandler.handle (src/util/handlers/SequenceHandler.ts:27:18)
      at App.start (src/init/App.ts:20:5)
      at test/integration/SparqlStorage.test.ts:31:5

This was the integration test on Node 16.0. The other ones didn't finish because of this so it could also be that this is because of a bug in 16.0, I did not test this in-depth yet.

rubensworks commented 3 years ago

That's interesting. Can you check manually if it also throws for Node 16? Because I'm running this without issues in Comunica.

In any case, it's part of the standard Fetch API: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/cancel

Could it be perhaps an issue with your test mocks?

joachimvh commented 3 years ago

Could it be perhaps an issue with your test mocks?

This is an integration test so it should be completely without mocks.

Note that this would not be the first time there's something wrong with a .0 Node version so it could also be that, can have a look later.

joachimvh commented 3 years ago

Did some small tests already. Using Node 14.15.1 doing a PUT request to a store with a sparql backend (same docker container as the CI tests). I get the same error on Ubuntu but on Windows it works. So that is going to make it interesting to find out what exactly the issue is.

rubensworks commented 3 years ago

Any way you could inspect the raw response body, to see what kind of object it is, and if cancel really does not exist?

joachimvh commented 3 years ago

I forgot to update the Windows setup to version 2.0.2 of course, explaining the discrepancy 🙄 . Fails on both now. Here's the full response object, no cancel function found.

<ref *1> Gunzip {
  _writeState: Uint32Array(2) [ 0, 0 ],
  _readableState: ReadableState {
    objectMode: false,
    highWaterMark: 16384,
    buffer: BufferList { head: null, tail: null, length: 0 },
    length: 0,
    pipes: [],
    flowing: null,
    ended: false,
    endEmitted: false,
    reading: false,
    sync: false,
    needReadable: false,
    emittedReadable: false,
    readableListening: false,
    resumeScheduled: false,
    errorEmitted: false,
    emitClose: true,
    autoDestroy: true,
    destroyed: false,
    errored: null,
    closed: false,
    closeEmitted: false,
    defaultEncoding: 'utf8',
    awaitDrainWriters: null,
    multiAwaitDrain: false,
    readingMore: false,
    decoder: null,
    encoding: null,
    [Symbol(kPaused)]: null
  },
  _events: [Object: null prototype] {
    prefinish: [Function: prefinish],
    unpipe: [Function: onunpipe],
    error: [ [Function: onerror], [Function (anonymous)] ],
    close: [Function: bound onceWrapper] { listener: [Function: onclose] },
    finish: [Function: bound onceWrapper] { listener: [Function: onfinish] }
  },
  _eventsCount: 5,
  _maxListeners: undefined,
  _writableState: WritableState {
    objectMode: false,
    highWaterMark: 16384,
    finalCalled: false,
    needDrain: false,
    ending: false,
    ended: false,
    finished: false,
    destroyed: false,
    decodeStrings: true,
    defaultEncoding: 'utf8',
    length: 0,
    writing: false,
    corked: 0,
    sync: true,
    bufferProcessing: false,
    onwrite: [Function: bound onwrite],
    writecb: null,
    writelen: 0,
    afterWriteTickInfo: null,
    buffered: [],
    bufferedIndex: 0,
    allBuffers: true,
    allNoop: true,
    pendingcb: 0,
    prefinished: false,
    errorEmitted: false,
    emitClose: true,
    autoDestroy: true,
    errored: null,
    closed: false
  },
  allowHalfOpen: true,
  bytesWritten: 0,
  _handle: Zlib {
    onerror: [Function: zlibOnError],
    [Symbol(owner_symbol)]: [Circular *1]
  },
  _outBuffer: <Buffer 00 27 db ff 49 ff 00 27 92 77 e8 42 00 02 00 80 a0 80 fd f3 24 02 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 a0 80 fd f3 24 02 00 00 28 42 ... 16334 more bytes>,
  _outOffset: 0,
  _chunkSize: 16384,
  _defaultFlushFlag: 2,
  _finishFlushFlag: 2,
  _defaultFullFlushFlag: 3,
  _info: undefined,
  _maxOutputLength: 4294967295,
  _level: -1,
  _strategy: 0,
  [Symbol(kCapture)]: false,
  [Symbol(kTransformState)]: {
    afterTransform: [Function: bound afterTransform],
    needTransform: false,
    transforming: false,
    writecb: null,
    writechunk: null,
    writeencoding: null
  },
  [Symbol(kError)]: null
}

I also tried passing our fetch function to the constructor since it's a later version of cross-fetch but it still has the same issue.

rubensworks commented 3 years ago

So it looks like cancel is indeed not supported by node-fetch, since it's apparently not strictly part of the fetch API (which indicates that there are problems with the TS typings).

One possible workaround would be to use the new abort controllers: https://github.com/node-fetch/node-fetch#request-cancellation-with-abortsignal

rubensworks commented 3 years ago

Fixed in 2.1.0. (also added some small integration tests to confirm this behaviour and avoid regressions)

rubensworks commented 3 years ago

For future reference, it looks like body (which is a ReadableStream) should in fact expose a .cancel() method according to the spec: