Skip to content

Commit

Permalink
feat: add js-kubo-rpc-client (#764)
Browse files Browse the repository at this point in the history
* feat: add js-kubo-rpc-client

see ipfs/kubo#9125

* chore: update kubo-rpc-client package name

* fix: use cross-env to set USE_KUBO_JS

* fix: Remove USE_KUBO_JS env var

* chore(deps): remove cross-env

* docs(readme): update npm install cmds for using kubo-rpc-client

* chore: log whether kubo-rpc-client or ipfs-http-client is in use

* chore(tests): use kubo-rpc-client for commmunication with go controllers

* chore(lint): fix lint failure

* fix: addCorrectRpcModule returns the additionalOpts object

* docs(readme): correct kubo-rpc-client name

Co-authored-by: Alex Potsides <alex@achingbrain.net>

* fix: use correct logger namespace convention

* fix: move kubo-rpc-client to devDeps

* fix: support generics for Controller type

* chore(deps): use kubo-rpc-client@1.0.1

* feat!: convert to typescript

Converts this module to typescript

* chore: fix linting

* chore: fix tests

* chore: apply suggestions from code review

* fix: resolve issues with js-kubo-rpc-client in typescript

* fix: use kuboRpcModule for go in factory.spec.ts

* fix: broken build from bad merge

* chore: address PR comments

Co-authored-by: Alex Potsides <alex@achingbrain.net>
  • Loading branch information
SgtPooki and achingbrain authored Oct 5, 2022
1 parent 536d683 commit 9538c1d
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 62 deletions.
8 changes: 5 additions & 3 deletions .aegir.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as ipfsModule from 'ipfs'
import * as ipfsHttpModule from 'ipfs-http-client'
import * as kuboRpcModule from 'kubo-rpc-client'
import * as goIpfsModule from 'go-ipfs'

/** @type {import('aegir').Options["build"]["config"]} */
Expand All @@ -13,13 +14,14 @@ const config = {

const server = createServer(undefined, {
ipfsModule,
ipfsHttpModule
}, {
go: {
ipfsBin: goIpfsModule.path()
ipfsBin: goIpfsModule.path(),
kuboRpcModule
},
js: {
ipfsBin: ipfsModule.path()
ipfsBin: ipfsModule.path(),
ipfsHttpModule
}
}
)
Expand Down
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,15 @@ $ npm i ipfsd-ctl

Version 1.0.0 changed a bit the api and the options methods take so please read the documentation below.

Please ensure your project also has dependencies on `ipfs`, `ipfs-http-client` and `go-ipfs`.
Please ensure your project also has dependencies on `ipfs`, `ipfs-http-client`, `kubo-rpc-client`, and `go-ipfs`.

```sh
npm install --save ipfs
npm install --save ipfs-http-client
npm install --save go-ipfs
npm install --save ipfs ipfs-http-client go-ipfs kubo-rpc-client
```

If you are only going to use the `go` implementation of IPFS, you can skip installing the `js` implementation and vice versa, though both will require the `ipfs-http-client` module.
If you are only going to use the `go` implementation of IPFS, you can skip installing the `js` implementation and `ipfs-http-client` module. (e.g. `npm i --save go-ipfs kubo-rpc-client`)

If you are only using the `proc` type in-process IPFS node, you can skip installing `go-ipfs` and `ipfs-http-client`.
If you are only using the `proc` type in-process IPFS node, you can skip installing `go-ipfs` and `ipfs-http-client`. (e.g. `npm i --save ipfs`)

> You also need to explicitly defined the options `ipfsBin`, `ipfsModule` and `ipfsHttpModule` according to your needs. Check [ControllerOptions](#controlleroptions) and [ControllerOptionsOverrides](#controlleroptionsoverrides) for more information.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
"ipfs-client": "^0.9.0",
"ipfs-core-types": "^0.12.0",
"ipfs-http-client": "^58.0.0",
"kubo-rpc-client": "^1.0.1",
"util": "^0.12.4"
},
"browser": {
Expand Down
4 changes: 2 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { isBrowser, isWebWorker } from 'wherearewe'
import type { NodeType } from './index.js'
import type { ControllerType } from './index.js'

export interface ConfigInit {
type?: NodeType
type?: ControllerType
}

export default (init: ConfigInit) => {
Expand Down
3 changes: 2 additions & 1 deletion src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ class DefaultFactory implements Factory {
remote: false,
ipfsBin: undefined,
ipfsModule: undefined,
ipfsHttpModule: undefined
ipfsHttpModule: undefined,
kuboRpcModule: undefined
}
}

Expand Down
36 changes: 20 additions & 16 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,30 @@ export interface PeerData {
addresses: Multiaddr[]
}

export interface Controller {
export type ControllerType = 'js' | 'go' | 'proc'

export interface Controller<Type extends ControllerType = 'go'> {
/**
* Initialize a repo
*/
init: (options?: InitOptions) => Promise<Controller>
init: (options?: InitOptions) => Promise<Controller<Type>>

/**
* Start the daemon
*/
start: () => Promise<Controller>
start: () => Promise<Controller<Type>>

/**
* Stop the daemon
*/
stop: () => Promise<Controller>
stop: () => Promise<Controller<Type>>

/**
* Delete the repo that was being used.
* If the node was marked as `disposable` this will be called
* automatically when the process is exited.
*/
cleanup: () => Promise<Controller>
cleanup: () => Promise<Controller<Type>>

/**
* Get the pid of the `ipfs daemon` process
Expand Down Expand Up @@ -65,8 +67,6 @@ export interface RemoteState {
grpcAddr: string
}

export type NodeType = 'js' | 'go' | 'proc'

export interface InitOptions {
pass?: string
bits?: number
Expand Down Expand Up @@ -156,7 +156,7 @@ export interface IPFSOptions {
repoAutoMigrate?: boolean
}

export interface ControllerOptions {
export interface ControllerOptions<Type extends ControllerType = ControllerType> {
/**
* Flag to activate custom config for tests
*/
Expand All @@ -176,7 +176,7 @@ export interface ControllerOptions {
/**
* The daemon type
*/
type?: NodeType
type?: Type
/**
* Additional environment variables, passed to executing shell. Only applies for Daemon controllers
*/
Expand All @@ -189,6 +189,10 @@ export interface ControllerOptions {
* Reference to an ipfs-http-client module
*/
ipfsHttpModule?: any
/**
* Reference to a kubo-rpc-client module
*/
kuboRpcModule?: any
/**
* Reference to an ipfs or ipfs-core module
*/
Expand Down Expand Up @@ -216,17 +220,17 @@ export interface ControllerOptions {
}

export interface ControllerOptionsOverrides {
js?: ControllerOptions
go?: ControllerOptions
proc?: ControllerOptions
js?: ControllerOptions<'js'>
go?: ControllerOptions<'go'>
proc?: ControllerOptions<'proc'>
}

export interface Factory {
export interface Factory<Type extends ControllerType = ControllerType> {
tmpDir: (options?: ControllerOptions) => Promise<string>
spawn: (options?: ControllerOptions) => Promise<Controller>
spawn: (options?: ControllerOptions) => Promise<Controller<Type>>
clean: () => Promise<void>
controllers: Controller[]
opts: ControllerOptions
controllers: Array<Controller<Type>>
opts: ControllerOptions<Type>
}

export interface CreateFactory { (): Factory | Promise<Factory> }
Expand Down
11 changes: 10 additions & 1 deletion src/ipfsd-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const daemonLog = {
info: logger('ipfsd-ctl:client:stdout'),
err: logger('ipfsd-ctl:client:stderr')
}
const rpcModuleLogger = logger('ipfsd-ctl:client')

/**
* Controller for remote nodes
Expand Down Expand Up @@ -84,7 +85,15 @@ class Client implements Controller {
http: this.apiAddr
})
} else if (this.apiAddr != null) {
this.api = this.opts.ipfsHttpModule.create(this.apiAddr)
if (this.opts.kuboRpcModule != null) {
rpcModuleLogger('Using kubo-rpc-client')
this.api = this.opts.kuboRpcModule.create(this.apiAddr)
} else if (this.opts.ipfsHttpModule != null) {
rpcModuleLogger('Using ipfs-http-client')
this.api = this.opts.ipfsHttpModule.create(this.apiAddr)
} else {
throw new Error('You must pass either a kuboRpcModule or ipfsHttpModule')
}
}

if (this.api != null) {
Expand Down
11 changes: 10 additions & 1 deletion src/ipfsd-daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const daemonLog = {
info: logger('ipfsd-ctl:daemon:stdout'),
err: logger('ipfsd-ctl:daemon:stderr')
}
const rpcModuleLogger = logger('ipfsd-ctl:daemon')

function translateError (err: Error & { stdout: string, stderr: string }) {
// get the actual error message to be the err.message
Expand Down Expand Up @@ -85,7 +86,15 @@ class Daemon implements Controller {
http: this.apiAddr
})
} else if (this.apiAddr != null) {
this.api = this.opts.ipfsHttpModule.create(this.apiAddr)
if (this.opts.kuboRpcModule != null) {
rpcModuleLogger('Using kubo-rpc-client')
this.api = this.opts.kuboRpcModule.create(this.apiAddr)
} else if (this.opts.ipfsHttpModule != null) {
rpcModuleLogger('Using ipfs-http-client')
this.api = this.opts.ipfsHttpModule.create(this.apiAddr)
} else {
throw new Error('You must pass either a kuboRpcModule or ipfsHttpModule')
}
}

if (this.api == null) {
Expand Down
13 changes: 12 additions & 1 deletion src/ipfsd-in-proc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const daemonLog = {
info: logger('ipfsd-ctl:proc:stdout'),
err: logger('ipfsd-ctl:proc:stderr')
}
const rpcModuleLogger = logger('ipfsd-ctl:proc')

/**
* Controller for in process nodes
Expand Down Expand Up @@ -67,7 +68,17 @@ class InProc implements Controller {

private _setApi (addr: string): void {
this.apiAddr = multiaddr(addr)
this.api = this.opts.ipfsHttpModule.create(addr)

if (this.opts.kuboRpcModule != null) {
rpcModuleLogger('Using kubo-rpc-client')
this.api = this.opts.kuboRpcModule.create(addr)
} else if (this.opts.ipfsHttpModule != null) {
rpcModuleLogger('Using ipfs-http-client')
this.api = this.opts.ipfsHttpModule.create(addr)
} else {
throw new Error('You must pass either a kuboRpcModule or ipfsHttpModule')
}

this.api.apiHost = this.apiAddr.nodeAddress().address
this.api.apiPort = this.apiAddr.nodeAddress().port
}
Expand Down
4 changes: 2 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import fs from 'fs'
import { logger } from '@libp2p/logger'
import { nanoid } from 'nanoid'
import tempWrite from 'temp-write'
import type { ControllerOptions, IPFSOptions, NodeType } from './index.js'
import type { ControllerOptions, IPFSOptions, ControllerType } from './index.js'

const log = logger('ipfsd-ctl:utils')

Expand All @@ -22,7 +22,7 @@ export const repoExists = async (repoPath: string): Promise<boolean> => {
return await Promise.resolve(fs.existsSync(path.join(repoPath, 'config')))
}

export const defaultRepo = (type?: NodeType): string => {
export const defaultRepo = (type?: ControllerType): string => {
if (process.env.IPFS_PATH !== undefined) {
return process.env.IPFS_PATH
}
Expand Down
35 changes: 25 additions & 10 deletions test/controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as ipfsModule from 'ipfs'
import * as ipfsHttpModule from 'ipfs-http-client'
// @ts-expect-error no types
import * as goIpfsModule from 'go-ipfs'
import * as kuboRpcModule from 'kubo-rpc-client'

const types: ControllerOptions[] = [{
type: 'js',
Expand All @@ -21,6 +22,7 @@ const types: ControllerOptions[] = [{
}
}, {
type: 'go',
kuboRpcModule,
ipfsOptions: {
init: false,
start: false
Expand All @@ -40,13 +42,27 @@ const types: ControllerOptions[] = [{
}
}, {
type: 'go',
kuboRpcModule,
remote: true,
ipfsOptions: {
init: false,
start: false
}
}]

/**
* Set the options object with the correct RPC module depending on the type
*/
function addCorrectRpcModule (opts: ControllerOptions, additionalOpts: ControllerOptions) {
if (opts.type === 'go') {
additionalOpts.kuboRpcModule = kuboRpcModule
} else {
additionalOpts.ipfsHttpModule = ipfsHttpModule
}

return additionalOpts
}

describe('Controller API', function () {
this.timeout(60000)

Expand All @@ -55,14 +71,15 @@ describe('Controller API', function () {
before(async () => {
factory = createFactory({
test: true,
ipfsHttpModule,
ipfsModule: (await import('ipfs'))
}, {
js: {
ipfsBin: isNode ? ipfsModule.path() : undefined
ipfsBin: isNode ? ipfsModule.path() : undefined,
ipfsHttpModule
},
go: {
ipfsBin: isNode ? goIpfsModule.path() : undefined
ipfsBin: isNode ? goIpfsModule.path() : undefined,
kuboRpcModule
}
})

Expand Down Expand Up @@ -191,13 +208,12 @@ describe('Controller API', function () {
// have to use createController so we don't try to shut down
// the node twice during test cleanup
const ctl = await createController(merge(
opts, {
ipfsHttpModule,
opts, addCorrectRpcModule(opts, {
ipfsModule,
ipfsOptions: {
repo: factory.controllers[0].path
}
}
})
))

await ctl.init()
Expand All @@ -219,7 +235,7 @@ describe('Controller API', function () {
const ctl1 = await createController(merge(
{
type: 'go',
ipfsHttpModule,
kuboRpcModule,
ipfsBin: goIpfsModule.path(),
test: true,
disposable: true,
Expand All @@ -232,16 +248,15 @@ describe('Controller API', function () {
expect(ctl1.started).to.be.true()

const ctl2 = await createController(merge(
opts, {
ipfsHttpModule,
opts, addCorrectRpcModule(opts, {
ipfsModule,
test: true,
disposable: true,
ipfsOptions: {
repo: ctl1.path,
start: true
}
}
})
))
expect(ctl2.started).to.be.true()

Expand Down
Loading

0 comments on commit 9538c1d

Please sign in to comment.