浏览代码

storage-node-v2: Stat adding web API.

- add API spec
- change joystream types version
- add uploading command stub
Shamil Gadelshin 3 年之前
父节点
当前提交
8b5e0cac22

+ 56 - 34
storage-node-v2/README.md

@@ -28,40 +28,40 @@ USAGE
 <!-- usagestop -->
 # Commands
 <!-- commands -->
-* [`storage-node dev:init [FILE]`](#storage-node-devinit-file)
-* [`storage-node dev:upload [FILE]`](#storage-node-devupload-file)
+* [`storage-node dev:init`](#storage-node-devinit)
+* [`storage-node dev:upload`](#storage-node-devupload)
 * [`storage-node help [COMMAND]`](#storage-node-help-command)
 * [`storage-node leader:create-bucket`](#storage-node-leadercreate-bucket)
-* [`storage-node leader:update-bag [FILE]`](#storage-node-leaderupdate-bag-file)
-* [`storage-node operator:accept-invitation [FILE]`](#storage-node-operatoraccept-invitation-file)
+* [`storage-node leader:update-bag`](#storage-node-leaderupdate-bag)
+* [`storage-node operator:accept-invitation`](#storage-node-operatoraccept-invitation)
+* [`storage-node server [FILE]`](#storage-node-server-file)
 
-## `storage-node dev:init [FILE]`
+## `storage-node dev:init`
 
-describe the command here
+Initialize development environment. Sets Alice as storage working group leader.
 
 ```
 USAGE
-  $ storage-node dev:init [FILE]
+  $ storage-node dev:init
 
 OPTIONS
-  -f, --force
-  -h, --help       show CLI help
-  -n, --name=name  name to print
+  -h, --help  show CLI help
 ```
 
 _See code: [src/commands/dev/init.ts](https://github.com/shamil-gadelshin/storage-node-v2/blob/v0.1.0/src/commands/dev/init.ts)_
 
-## `storage-node dev:upload [FILE]`
+## `storage-node dev:upload`
 
-describe the command here
+Upload data object (development mode only).
 
 ```
 USAGE
-  $ storage-node dev:upload [FILE]
+  $ storage-node dev:upload
 
 OPTIONS
-  -d, --dev   Use development mode
-  -h, --help  show CLI help
+  -c, --cid=cid    (required) Data object IPFS content ID.
+  -h, --help       show CLI help
+  -s, --size=size  (required) Data object size.
 ```
 
 _See code: [src/commands/dev/upload.ts](https://github.com/shamil-gadelshin/storage-node-v2/blob/v0.1.0/src/commands/dev/upload.ts)_
@@ -81,7 +81,7 @@ OPTIONS
   --all  see all commands in CLI
 ```
 
-_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.2.2/src/commands/help.ts)_
+_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.0.1/src/commands/help.ts)_
 
 ## `storage-node leader:create-bucket`
 
@@ -92,47 +92,69 @@ USAGE
   $ storage-node leader:create-bucket
 
 OPTIONS
-  -a, --allow            Accepts new bags
-  -d, --dev              Use development mode
-  -h, --help             show CLI help
-  -i, --invited=invited  Invited storage operator ID (storage WG worker ID)
-  -n, --number=number    Storage bucket max total objects number
-  -s, --size=size        Storage bucket max total objects size
+  -a, --allow              Accepts new bags
+  -d, --dev                Use development mode
+  -h, --help               show CLI help
+  -i, --invited=invited    Invited storage operator ID (storage WG worker ID)
+  -k, --keyfile=keyfile    Key file for the account. Mandatory in non-dev environment.
+  -n, --number=number      Storage bucket max total objects number
+  -p, --password=password  Key file password (optional).
+  -s, --size=size          Storage bucket max total objects size
 ```
 
 _See code: [src/commands/leader/create-bucket.ts](https://github.com/shamil-gadelshin/storage-node-v2/blob/v0.1.0/src/commands/leader/create-bucket.ts)_
 
-## `storage-node leader:update-bag [FILE]`
+## `storage-node leader:update-bag`
 
 Add/remove a storage bucket from a bag (adds by default).
 
 ```
 USAGE
-  $ storage-node leader:update-bag [FILE]
+  $ storage-node leader:update-bag
 
 OPTIONS
-  -b, --bucket=bucket  (required) Storage bucket ID
-  -d, --dev            Use development mode
-  -h, --help           show CLI help
-  -r, --remove         Remove a bucket from the bag
+  -b, --bucket=bucket      (required) Storage bucket ID
+  -d, --dev                Use development mode
+  -h, --help               show CLI help
+  -k, --keyfile=keyfile    Key file for the account. Mandatory in non-dev environment.
+  -p, --password=password  Key file password (optional).
+  -r, --remove             Remove a bucket from the bag
 ```
 
 _See code: [src/commands/leader/update-bag.ts](https://github.com/shamil-gadelshin/storage-node-v2/blob/v0.1.0/src/commands/leader/update-bag.ts)_
 
-## `storage-node operator:accept-invitation [FILE]`
+## `storage-node operator:accept-invitation`
 
 Accept pending storage bucket invitation.
 
 ```
 USAGE
-  $ storage-node operator:accept-invitation [FILE]
+  $ storage-node operator:accept-invitation
 
 OPTIONS
-  -b, --bucket=bucket  (required) Storage bucket ID
-  -d, --dev            Use development mode
-  -h, --help           show CLI help
-  -w, --worker=worker  (required) Storage operator worker ID
+  -b, --bucket=bucket      (required) Storage bucket ID
+  -d, --dev                Use development mode
+  -h, --help               show CLI help
+  -k, --keyfile=keyfile    Key file for the account. Mandatory in non-dev environment.
+  -p, --password=password  Key file password (optional).
+  -w, --worker=worker      (required) Storage operator worker ID
 ```
 
 _See code: [src/commands/operator/accept-invitation.ts](https://github.com/shamil-gadelshin/storage-node-v2/blob/v0.1.0/src/commands/operator/accept-invitation.ts)_
+
+## `storage-node server [FILE]`
+
+describe the command here
+
+```
+USAGE
+  $ storage-node server [FILE]
+
+OPTIONS
+  -f, --force
+  -h, --help       show CLI help
+  -n, --name=name  name to print
+```
+
+_See code: [src/commands/server.ts](https://github.com/shamil-gadelshin/storage-node-v2/blob/v0.1.0/src/commands/server.ts)_
 <!-- commandsstop -->

+ 6 - 2
storage-node-v2/package.json

@@ -8,13 +8,16 @@
   },
   "bugs": "https://github.com/shamil-gadelshin/storage-node-v2/issues",
   "dependencies": {
-    "@joystream/types": "link:../types",
+    "@joystream/types": "0.17.0",
     "@oclif/command": "^1",
     "@oclif/config": "^1",
     "@oclif/plugin-help": "^3",
     "@polkadot/api": "4.2.1",
     "storage-node-v2": "file:.",
-    "tslib": "^1"
+    "tslib": "^1",
+    "@types/express": "^4.17.11",
+    "express": "^4.17.1",
+    "express-openapi-validator": "^4.12.4"
   },
   "devDependencies": {
     "@oclif/dev-cli": "^1",
@@ -50,6 +53,7 @@
   "main": "lib/index.js",
   "oclif": {
     "commands": "./lib/commands",
+    "defaultCommand": "server",
     "bin": "storage-node",
     "plugins": [
       "@oclif/plugin-help"

+ 49 - 0
storage-node-v2/src/api-spec/openapi.yaml

@@ -0,0 +1,49 @@
+openapi: 3.0.3
+info:
+  title: Storage node API
+  description: Storage node API
+  contact:
+    email: info@joystream.org
+  license:
+    name: MIT
+    url: https://opensource.org/licenses/MIT
+  version: 0.1.0
+externalDocs:
+  description: Storage node API
+  url: joystream.org
+servers:
+- url: /api/v1
+ 
+tags:
+  - name: storage
+    description: Storage node APIs
+ 
+paths:
+  /upload:
+    post:
+      description: Upload data
+      operationId: publicApi.upload
+      requestBody:
+        content:
+          multipart/form-data:
+            schema:
+              # $ref: '#/components/schemas/NewPhoto'
+              type: object
+              required:
+                - recfile
+              properties:
+                file:
+                  description: Data file
+                  type: string
+                  format: binary
+        required: true
+      responses:
+        200:
+          description: Created
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  success:
+                    type: boolean

+ 1 - 1
storage-node-v2/src/command-base/ApiCommandBase.ts

@@ -1,5 +1,5 @@
 import { Command, flags } from '@oclif/command'
-import { createApi, getAlicePair } from './../services/api'
+import { createApi, getAlicePair } from '../services/runtimeApi'
 import { getAccountFromJsonFile } from './../services/accounts'
 import { KeyringPair } from '@polkadot/keyring/types'
 

+ 40 - 0
storage-node-v2/src/commands/server/index.ts

@@ -0,0 +1,40 @@
+import {Command, flags} from '@oclif/command'
+import {createServer} from '../../services/api/server'
+
+// TODO: fix command not found error (error handling)
+// TODO: custom IP address? 
+
+export default class Server extends Command {
+  static description = 'Starts the storage node server.'
+
+  static flags = {
+    help: flags.help({char: 'h'}),
+    dev: flags.boolean({ char: 'd', description: 'Use development mode' }),
+    uploads: flags.string({
+      char: 'u',
+      required: true,
+      description: 'Data uploading directory.',
+    }),
+    port: flags.integer({
+      char: 'p',
+      required: true,
+      description: 'Server port.',
+    }),
+  }
+
+  static args = [{name: 'file'}]
+
+  async run() {
+    const {flags} = this.parse(Server)
+    
+    try {
+      const port = flags.port
+      const server = await createServer(flags.dev, flags.uploads)
+      console.info(`Listening on http://localhost:${port}`)
+      server.listen(port)
+    }
+    catch(err) {
+      console.error(`Error: ${err}`)
+    }
+  }
+}

+ 10 - 0
storage-node-v2/src/services/api/controllers/publicApi.ts

@@ -0,0 +1,10 @@
+import * as express from 'express'
+ 
+export function upload(req: express.Request, res: express.Response): void {
+  console.log(req.body);
+  console.log(req.files);
+  
+  res.json({
+      file: "received"
+  });
+}

+ 45 - 0
storage-node-v2/src/services/api/server.ts

@@ -0,0 +1,45 @@
+import express from 'express'
+import path from 'path'
+import {Express} from 'express-serve-static-core'
+import * as OpenApiValidator from 'express-openapi-validator'
+
+//TODO: add swagger UI
+//TODO: custom errors (including validation errors)
+
+
+export async function createServer(devMode: boolean, uploadsDir: string): Promise<Express> {
+  const server = express()
+  const spec = path.join(__dirname, './../../api-spec/openapi.yaml');
+
+  if (devMode) {
+    // Form for the upload testing.
+    server.get('/', function (_, res) {
+      res.send('<html><head></head><body>\
+                <form method="POST" enctype="multipart/form-data" action="/api/v1/upload">\
+                  <h2>Api development form (upload)</h2>\
+                  <input type="file" name="recfile"><br />\
+                  <input type="submit">\
+                </form>\
+              </body></html>');
+      res.end();
+    });
+    // TODO: localhost only?
+    server.use('/spec', express.static(spec));
+  }
+
+  server.use(
+    OpenApiValidator.middleware({
+      apiSpec: spec,
+      validateApiSpec: true,
+      validateResponses: true,
+      validateRequests: true,
+      operationHandlers: {
+          basePath: path.join(__dirname, './controllers'),
+          resolver: OpenApiValidator.resolvers.modulePathResolver
+      },
+      fileUploader : { dest: uploadsDir },
+    }),
+  );
+
+  return server
+}

+ 1 - 1
storage-node-v2/src/services/extrinsics.ts

@@ -3,7 +3,7 @@ import {
   sendAndFollowSudoNamedTx,
   sendAndFollowNamedTx,
   getAlicePair,
-} from './api'
+} from './runtimeApi'
 import { KeyringPair } from '@polkadot/keyring/types'
 import { CodecArg } from '@polkadot/types/types'
 

+ 1 - 1
storage-node-v2/src/services/hireLead.ts

@@ -3,7 +3,7 @@ import {
   getAlicePair,
   sendAndFollowSudoNamedTx,
   sendAndFollowNamedTx,
-} from './api'
+} from './runtimeApi'
 import { CodecArg } from '@polkadot/types/types'
 import { Option, Vec } from '@polkadot/types'
 import {

+ 0 - 0
storage-node-v2/src/services/api.ts → storage-node-v2/src/services/runtimeApi.ts


+ 17 - 0
storage-node-v2/test/commands/server.test.ts

@@ -0,0 +1,17 @@
+import {expect, test} from '@oclif/test'
+
+describe('server', () => {
+  test
+  .stdout()
+  .command(['server'])
+  .it('runs hello', ctx => {
+    expect(ctx.stdout).to.contain('hello world')
+  })
+
+  test
+  .stdout()
+  .command(['server', '--name', 'jeff'])
+  .it('runs hello --name jeff', ctx => {
+    expect(ctx.stdout).to.contain('hello jeff')
+  })
+})

+ 1 - 1
types/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@joystream/types",
-  "version": "0.15.0",
+  "version": "0.17.0",
   "description": "Types for Joystream Substrate Runtime - Antioch release",
   "main": "index.js",
   "types": "index.d.ts",

+ 118 - 18
yarn.lock

@@ -23,6 +23,15 @@
     call-me-maybe "^1.0.1"
     js-yaml "^3.13.1"
 
+"@apidevtools/json-schema-ref-parser@9.0.7":
+  version "9.0.7"
+  resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.7.tgz#64aa7f5b34e43d74ea9e408b90ddfba02050dde3"
+  integrity sha512-QdwOGF1+eeyFh+17v2Tz626WX0nucd1iKOm6JUTUvCZdbolblCOOQCxGrQPY0f7jEhn36PiAWqZnsC2r5vmUWg==
+  dependencies:
+    "@jsdevtools/ono" "^7.1.3"
+    call-me-maybe "^1.0.1"
+    js-yaml "^3.13.1"
+
 "@apollo/client@^3.2.5":
   version "3.2.5"
   resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.2.5.tgz#24e0a6faa1d231ab44807af237c6227410c75c4d"
@@ -2358,8 +2367,22 @@
     "@types/yargs" "^15.0.0"
     chalk "^4.0.0"
 
-"@joystream/types@link:types":
+"@joystream/types@^0.15.0":
   version "0.15.0"
+  resolved "https://registry.yarnpkg.com/@joystream/types/-/types-0.15.0.tgz#7493fbd39bd3b0de7d110e25710a4797de4c302f"
+  integrity sha512-UjfHH3MlRUt2yRnzqsynoXcifWLGuzXm0VFBZIpgZo7IJQpZMbY6bmORYsxpAuwAebm4ogSqvWAzrSSJa5s6bQ==
+  dependencies:
+    "@polkadot/api" "4.2.1"
+    "@polkadot/keyring" "^6.0.5"
+    "@polkadot/types" "4.2.1"
+    "@types/lodash" "^4.14.157"
+    "@types/vfile" "^4.0.0"
+    ajv "^6.11.0"
+    lodash "^4.17.15"
+    moment "^2.24.0"
+
+"@joystream/types@link:types":
+  version "0.17.0"
   dependencies:
     "@polkadot/api" "4.2.1"
     "@polkadot/keyring" "^6.0.5"
@@ -2370,7 +2393,7 @@
     lodash "^4.17.15"
     moment "^2.24.0"
 
-"@jsdevtools/ono@^7.1.3":
+"@jsdevtools/ono@7.1.3", "@jsdevtools/ono@^7.1.3":
   version "7.1.3"
   resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796"
   integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==
@@ -4880,6 +4903,15 @@
     "@types/qs" "*"
     "@types/range-parser" "*"
 
+"@types/express-serve-static-core@^4.17.18":
+  version "4.17.20"
+  resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.20.tgz#44caee029f2c26c46711da5e845cdc12167ad72d"
+  integrity sha512-8qqFN4W53IEWa9bdmuVrUcVkFemQWnt5DKPQ/oa8xKDYgtjCr2OO6NX5TIK49NLFr3mPYU2cLh92DQquC3oWWQ==
+  dependencies:
+    "@types/node" "*"
+    "@types/qs" "*"
+    "@types/range-parser" "*"
+
 "@types/express@*", "@types/express@^4.17.2":
   version "4.17.8"
   resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.8.tgz#3df4293293317e61c60137d273a2e96cd8d5f27a"
@@ -4900,6 +4932,16 @@
     "@types/qs" "*"
     "@types/serve-static" "*"
 
+"@types/express@^4.17.11":
+  version "4.17.12"
+  resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.12.tgz#4bc1bf3cd0cfe6d3f6f2853648b40db7d54de350"
+  integrity sha512-pTYas6FrP15B1Oa0bkN5tQMNqOcVXa9j4FTFtO8DWI9kppKib+6NJtfTOOLcwxuuYvcX2+dVG6et1SxW/Kc17Q==
+  dependencies:
+    "@types/body-parser" "*"
+    "@types/express-serve-static-core" "^4.17.18"
+    "@types/qs" "*"
+    "@types/serve-static" "*"
+
 "@types/faker@^4.1.8":
   version "4.1.12"
   resolved "https://registry.yarnpkg.com/@types/faker/-/faker-4.1.12.tgz#065d37343677df1aa757c622650bd14666c42602"
@@ -5239,6 +5281,13 @@
   resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea"
   integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==
 
+"@types/multer@^1.4.5":
+  version "1.4.5"
+  resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.5.tgz#db0557562307e9adb6661a9500c334cd7ddd0cd9"
+  integrity sha512-9b/0a8JyrR0r2nQhL73JR86obWL7cogfX12augvlrvcpciCo/hkvEsgu80Z4S2g2DHGVXHr8pUIi1VhqFJ8Ufw==
+  dependencies:
+    "@types/express" "*"
+
 "@types/mustache@^4.0.1":
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.1.1.tgz#fcfa2db0cee6261e66f2437dc2fe71e26c7856b4"
@@ -5270,11 +5319,6 @@
   resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.14.tgz#1c1d6e3c75dba466e0326948d56e8bd72a1903d2"
   integrity sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA==
 
-"@types/node@^10":
-  version "10.17.60"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b"
-  integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==
-
 "@types/node@^10.1.0":
   version "10.17.44"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.44.tgz#3945e6b702cb6403f22b779c8ea9e5c3f44ead40"
@@ -5300,6 +5344,11 @@
   resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.3.tgz#a6e252973214079155f749e8bef99cc80af182fa"
   integrity sha512-8Jduo8wvvwDzEVJCOvS/G6sgilOLvvhn1eMmK3TW8/T217O7u1jdrK6ImKLv80tVryaPSVeKu6sjDEiFjd4/eg==
 
+"@types/node@^14.14.33":
+  version "14.17.1"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.1.tgz#5e07e0cb2ff793aa7a1b41deae76221e6166049f"
+  integrity sha512-/tpUyFD7meeooTRwl3sYlihx2BrJE7q9XF71EguPFIySj9B7qgnRtHsHTho+0AUm4m1SvWGm6uSncrR94q6Vtw==
+
 "@types/node@^9.6.4":
   version "9.6.61"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.61.tgz#29f124eddd41c4c74281bd0b455d689109fc2a2d"
@@ -6539,20 +6588,20 @@ ajv@^6.11.0:
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
-ajv@^6.5.2, ajv@^6.5.4:
-  version "6.12.2"
-  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
-  integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==
+ajv@^6.12.6, ajv@^6.9.1:
+  version "6.12.6"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+  integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
   dependencies:
     fast-deep-equal "^3.1.1"
     fast-json-stable-stringify "^2.0.0"
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
-ajv@^6.9.1:
-  version "6.12.6"
-  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
-  integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ajv@^6.5.2, ajv@^6.5.4:
+  version "6.12.2"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
+  integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==
   dependencies:
     fast-deep-equal "^3.1.1"
     fast-json-stable-stringify "^2.0.0"
@@ -12831,6 +12880,24 @@ express-normalize-query-params-middleware@^0.5.0:
   resolved "https://registry.yarnpkg.com/express-normalize-query-params-middleware/-/express-normalize-query-params-middleware-0.5.1.tgz#dbe1e8139aecb234fb6adb5c0059c75db9733d2a"
   integrity sha1-2+HoE5rssjT7attcAFnHXblzPSo=
 
+express-openapi-validator@^4.12.4:
+  version "4.12.11"
+  resolved "https://registry.yarnpkg.com/express-openapi-validator/-/express-openapi-validator-4.12.11.tgz#0d84b7f9c9c7cc64c916258e2e06c6d9fd1a2d7d"
+  integrity sha512-j9nQYa69F191BJi2gQc47HYA7hLMfhj9K9T6zr6bn0GiQKJUtRr80XKP+baGRNeAb2e0CfL3OpUXTsf1pDkYvg==
+  dependencies:
+    "@types/multer" "^1.4.5"
+    ajv "^6.12.6"
+    content-type "^1.0.4"
+    json-schema-ref-parser "^9.0.7"
+    lodash.clonedeep "^4.5.0"
+    lodash.get "^4.4.2"
+    lodash.uniq "^4.5.0"
+    lodash.zipobject "^4.1.3"
+    media-typer "^1.1.0"
+    multer "^1.4.2"
+    ono "^7.1.3"
+    path-to-regexp "^6.2.0"
+
 express-openapi@^4.6.1:
   version "4.6.5"
   resolved "https://registry.yarnpkg.com/express-openapi/-/express-openapi-4.6.5.tgz#a4b607925c71d5f54caa098430683a89da3424ad"
@@ -18149,6 +18216,13 @@ json-schema-ref-parser@^9.0.1:
   dependencies:
     "@apidevtools/json-schema-ref-parser" "9.0.6"
 
+json-schema-ref-parser@^9.0.7:
+  version "9.0.7"
+  resolved "https://registry.yarnpkg.com/json-schema-ref-parser/-/json-schema-ref-parser-9.0.7.tgz#c0ccc5aaee34844f0865889b67e0b67d616f7375"
+  integrity sha512-uxU9Ix+MVszvCTvBucQiIcNEny3oAEFg7EQHSZw2bquCCuqUqEPEczIdv/Uqo1Zv4/wDPZqOI+ulrMk1ncMtjQ==
+  dependencies:
+    "@apidevtools/json-schema-ref-parser" "9.0.7"
+
 json-schema-to-typescript@^9.1.1:
   version "9.1.1"
   resolved "https://registry.yarnpkg.com/json-schema-to-typescript/-/json-schema-to-typescript-9.1.1.tgz#572c1eb8b7ca82d6534c023c4651f3fe925171c0"
@@ -19104,6 +19178,11 @@ lodash.zip@^4.2.0:
   resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020"
   integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=
 
+lodash.zipobject@^4.1.3:
+  version "4.1.3"
+  resolved "https://registry.yarnpkg.com/lodash.zipobject/-/lodash.zipobject-4.1.3.tgz#b399f5aba8ff62a746f6979bf20b214f964dbef8"
+  integrity sha1-s5n1q6j/YqdG9peb8gshT5ZNvvg=
+
 lodash@^4.0.0, lodash@^4.0.1, lodash@^4.17.10, lodash@^4.17.19, lodash@^4.17.3, lodash@^4.17.5, lodash@^4.2.1, lodash@~4.17.10:
   version "4.17.19"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
@@ -19595,6 +19674,11 @@ media-typer@0.3.0:
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
   integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
 
+media-typer@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561"
+  integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==
+
 mem@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
@@ -20193,7 +20277,7 @@ ms@2.1.2, ms@^2.0.0, ms@^2.1.1:
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
   integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
 
-multer@^1.4.1:
+multer@^1.4.1, multer@^1.4.2:
   version "1.4.2"
   resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.2.tgz#2f1f4d12dbaeeba74cb37e623f234bf4d3d2057a"
   integrity sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==
@@ -21332,6 +21416,13 @@ only@~0.0.2:
   resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4"
   integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q=
 
+ono@^7.1.3:
+  version "7.1.3"
+  resolved "https://registry.yarnpkg.com/ono/-/ono-7.1.3.tgz#a054e96a388f566a6c4c95e1e92b9b253722d286"
+  integrity sha512-9jnfVriq7uJM4o5ganUY54ntUm+5EK21EGaQ5NWnkWg3zz5ywbbonlBguRcnmF1/HDiIe3zxNxXcO1YPBmPcQQ==
+  dependencies:
+    "@jsdevtools/ono" "7.1.3"
+
 open@*:
   version "7.3.0"
   resolved "https://registry.yarnpkg.com/open/-/open-7.3.0.tgz#45461fdee46444f3645b6e14eb3ca94b82e1be69"
@@ -22080,6 +22171,11 @@ path-to-regexp@^1.2.0, path-to-regexp@^1.7.0:
   dependencies:
     isarray "0.0.1"
 
+path-to-regexp@^6.2.0:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38"
+  integrity sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg==
+
 path-type@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
@@ -26154,12 +26250,16 @@ stealthy-require@^1.1.1:
 "storage-node-v2@file:./storage-node-v2":
   version "0.1.0"
   dependencies:
-    "@joystream/types" "link:../../../Library/Caches/Yarn/v6/npm-storage-node-v2-0.1.0-fc41cc8a-ca81-4ab0-ad18-930cad74d30f-1622040106242/node_modules/types"
+    "@joystream/types" "0.17.0"
     "@oclif/command" "^1"
     "@oclif/config" "^1"
     "@oclif/plugin-help" "^3"
     "@polkadot/api" "4.2.1"
-    storage-node-v2 "file:../../../Library/Caches/Yarn/v6/npm-storage-node-v2-0.1.0-fc41cc8a-ca81-4ab0-ad18-930cad74d30f-1622040106242/node_modules/storage-node-v2"
+    "@types/express" "^4.17.11"
+    "@types/node" "^14.14.33"
+    express "^4.17.1"
+    express-openapi-validator "^4.12.4"
+    storage-node-v2 "file:../../../Library/Caches/Yarn/v6/npm-storage-node-v2-0.1.0-e2edaf3b-51ee-451b-a882-cc21ea587ddb-1622471201601/node_modules/storage-node-v2"
     tslib "^1"
 
 store2@^2.7.1: