├── .gitignore ├── .flowconfig ├── .babelrc ├── src ├── index.js ├── reducers │ ├── index.js │ ├── api.js │ ├── domain.js │ └── network.js ├── common │ └── actions │ │ └── index.js ├── actions │ └── index.js ├── helpers │ └── api.js └── swagger.js ├── __tests__ └── index.js ├── html └── index-dev.html ├── lib ├── reducers │ ├── index.js │ ├── api.js │ ├── domain.js │ └── network.js ├── index.js ├── common │ └── actions │ │ └── index.js ├── actions │ └── index.js ├── helpers │ └── api.js └── swagger.js ├── .eslintrc ├── LICENSE ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build/app 3 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/.* 3 | [include] 4 | 5 | [libs] 6 | 7 | [options] 8 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "transform-runtime", 4 | "transform-object-rest-spread", 5 | ["babel-plugin-root-import", { "rootPathSuffix": "src" }] 6 | ], 7 | "presets": ["env"] 8 | } 9 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import * as Action from '~/actions'; 2 | import * as Helper from '~/helpers/api'; 3 | import Reducer from '~/reducers'; 4 | export default { 5 | Action, 6 | Helper, 7 | Reducer, 8 | }; 9 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import network from './network'; 3 | import domain from './domain'; 4 | 5 | export default { 6 | domain, 7 | api, 8 | network, 9 | }; 10 | -------------------------------------------------------------------------------- /__tests__/index.js: -------------------------------------------------------------------------------- 1 | import {double} from 'helpers/weightCalculation'; 2 | import {total} from 'helpers/priceCalculation'; 3 | test('total', () => { 4 | expect(total(2 * 100, 0.8)).toBe(160); 5 | expect(double(100)).toBe(200); 6 | }); 7 | -------------------------------------------------------------------------------- /html/index-dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | babel-react-rollup-starter 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/reducers/api.js: -------------------------------------------------------------------------------- 1 | export default function api( 2 | state = { protocol: "http", contentType: "", headers: {} }, 3 | action 4 | ) { 5 | switch (action.type) { 6 | case "API:SET_BASE_HEADERS": 7 | return { ...state, headers: action.payload.headers }; 8 | case "API:SET_SWAGGER": 9 | return { ...state, ...action.payload }; 10 | case "API:SET_PROTOCOL": 11 | return { ...state, protocol: action.payload }; 12 | } 13 | return state; 14 | } 15 | -------------------------------------------------------------------------------- /src/reducers/domain.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | export default function domain(state = {}, action) { 3 | let idsRegexResult = /^(.+)S_BY_ID$/.exec(_.get(action, 'meta.data')); 4 | 5 | switch (true) { 6 | case idsRegexResult != null: 7 | let relatedIdsName = _.camelCase(idsRegexResult[1]) + 'sById'; 8 | let newObjectWithIds = {}; 9 | newObjectWithIds[relatedIdsName] = action.payload; 10 | 11 | return { ...state, ...newObjectWithIds }; 12 | } 13 | return state; 14 | } 15 | -------------------------------------------------------------------------------- /src/reducers/network.js: -------------------------------------------------------------------------------- 1 | export default function network(state = {}, action) { 2 | switch (true) { 3 | case action.type == 'SUCCESS': 4 | console.log('action', action); 5 | return { 6 | ...state, 7 | [action.meta.name]: 'SUCCESS', 8 | }; 9 | 10 | case action.type == 'FAILURE': 11 | return { 12 | ...state, 13 | [action.meta.name]: 'FAILURE', 14 | }; 15 | 16 | case action.type == 'REQUEST': 17 | return { 18 | ...state, 19 | [action.meta.name]: 'REQUEST', 20 | }; 21 | } 22 | return state; 23 | } 24 | -------------------------------------------------------------------------------- /lib/reducers/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _api = require('./api'); 8 | 9 | var _api2 = _interopRequireDefault(_api); 10 | 11 | var _network = require('./network'); 12 | 13 | var _network2 = _interopRequireDefault(_network); 14 | 15 | var _domain = require('./domain'); 16 | 17 | var _domain2 = _interopRequireDefault(_domain); 18 | 19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 20 | 21 | exports.default = { 22 | domain: _domain2.default, 23 | api: _api2.default, 24 | network: _network2.default 25 | }; -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "plugins": [ 4 | "babel", 5 | "prettier" 6 | ], 7 | "env": { 8 | "browser": true, 9 | "es6": true 10 | }, 11 | "globals": { 12 | "it": true, 13 | "test": true, 14 | "describe": true, 15 | "expect": true, 16 | "grecaptcha": true 17 | }, 18 | "parserOptions": { 19 | "ecmaFeatures": { 20 | "experimentalObjectRestSpread": true, 21 | "modules": true 22 | } 23 | }, 24 | "root": true, 25 | "rules": { 26 | "no-unused-vars": [ 27 | "warn", 28 | { 29 | "vars": "all", 30 | "args": "after-used" 31 | } 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _actions = require('./actions'); 8 | 9 | var Action = _interopRequireWildcard(_actions); 10 | 11 | var _api = require('./helpers/api'); 12 | 13 | var Helper = _interopRequireWildcard(_api); 14 | 15 | var _reducers = require('./reducers'); 16 | 17 | var _reducers2 = _interopRequireDefault(_reducers); 18 | 19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 20 | 21 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } 22 | 23 | exports.default = { 24 | Action: Action, 25 | Helper: Helper, 26 | Reducer: _reducers2.default 27 | }; -------------------------------------------------------------------------------- /lib/reducers/api.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _extends2 = require("babel-runtime/helpers/extends"); 8 | 9 | var _extends3 = _interopRequireDefault(_extends2); 10 | 11 | exports.default = api; 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 14 | 15 | function api() { 16 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { protocol: "http", contentType: "", headers: {} }; 17 | var action = arguments[1]; 18 | 19 | switch (action.type) { 20 | case "API:SET_BASE_HEADERS": 21 | return (0, _extends3.default)({}, state, { headers: action.payload.headers }); 22 | case "API:SET_SWAGGER": 23 | return (0, _extends3.default)({}, state, action.payload); 24 | case "API:SET_PROTOCOL": 25 | return (0, _extends3.default)({}, state, { protocol: action.payload }); 26 | } 27 | return state; 28 | } -------------------------------------------------------------------------------- /lib/reducers/domain.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _extends2 = require('babel-runtime/helpers/extends'); 8 | 9 | var _extends3 = _interopRequireDefault(_extends2); 10 | 11 | exports.default = domain; 12 | 13 | var _lodash = require('lodash'); 14 | 15 | var _lodash2 = _interopRequireDefault(_lodash); 16 | 17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 18 | 19 | function domain() { 20 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 21 | var action = arguments[1]; 22 | 23 | var idsRegexResult = /^(.+)S_BY_ID$/.exec(_lodash2.default.get(action, 'meta.data')); 24 | 25 | switch (true) { 26 | case idsRegexResult != null: 27 | var relatedIdsName = _lodash2.default.camelCase(idsRegexResult[1]) + 'sById'; 28 | var newObjectWithIds = {}; 29 | newObjectWithIds[relatedIdsName] = action.payload; 30 | 31 | return (0, _extends3.default)({}, state, newObjectWithIds); 32 | } 33 | return state; 34 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Davy Duperron 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/reducers/network.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _defineProperty2 = require('babel-runtime/helpers/defineProperty'); 8 | 9 | var _defineProperty3 = _interopRequireDefault(_defineProperty2); 10 | 11 | var _extends5 = require('babel-runtime/helpers/extends'); 12 | 13 | var _extends6 = _interopRequireDefault(_extends5); 14 | 15 | exports.default = network; 16 | 17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 18 | 19 | function network() { 20 | var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 21 | var action = arguments[1]; 22 | 23 | switch (true) { 24 | case action.type == 'SUCCESS': 25 | console.log('action', action); 26 | return (0, _extends6.default)({}, state, (0, _defineProperty3.default)({}, action.meta.name, 'SUCCESS')); 27 | 28 | case action.type == 'FAILURE': 29 | return (0, _extends6.default)({}, state, (0, _defineProperty3.default)({}, action.meta.name, 'FAILURE')); 30 | 31 | case action.type == 'REQUEST': 32 | return (0, _extends6.default)({}, state, (0, _defineProperty3.default)({}, action.meta.name, 'REQUEST')); 33 | } 34 | return state; 35 | } -------------------------------------------------------------------------------- /src/common/actions/index.js: -------------------------------------------------------------------------------- 1 | import URI from "urijs"; 2 | import { CALL_API } from "redux-api-middleware"; 3 | import { stringify } from "query-string"; 4 | import _ from "lodash"; 5 | import { subsituteUrl, formData, processType } from "~/helpers/api"; 6 | export function setProtocol(protocol) { 7 | return { 8 | type: "API:SET_PROTOCOL", 9 | payload: protocol 10 | }; 11 | } 12 | 13 | export function setSwagger(swagger) { 14 | return { 15 | type: "API:SET_SWAGGER", 16 | payload: swagger 17 | }; 18 | } 19 | export function setHeaders(headers) { 20 | return { 21 | type: "API:SET_BASE_HEADERS", 22 | payload: { 23 | headers: headers 24 | } 25 | }; 26 | } 27 | export function request(pathName, { method, data, subst }, types) { 28 | const pathEntity = _.get(getState().api.paths, pathName); 29 | 30 | let entityPath = pathName; 31 | if (subst) { 32 | entityPath = subsituteUrl(pathName, subst); 33 | } 34 | let realPath = entityPath; 35 | 36 | // //TODO Header should be create by Header constructor instead of plain object 37 | let headers = { 38 | ...getState().api.headers, 39 | ...pathEntity.headers 40 | }; 41 | let body; 42 | if (method == "get" && data) { 43 | realPath = realPath + "?" + stringify(data); 44 | } else { 45 | body = formData(data, headers["Content-Type"]); 46 | } 47 | 48 | if (headers["Content-Type"] === "multipart/form-data") { 49 | delete headers["Content-Type"]; 50 | delete headers["content-type"]; 51 | } 52 | // 53 | const result = { 54 | endpoint: 55 | getState().api.protocol + 56 | "://" + 57 | getState().api.host + 58 | getState().api.basePath + 59 | realPath, 60 | method: method, 61 | 62 | headers: headers, 63 | body: body, 64 | types: processType(types, pathName, method) 65 | }; 66 | return { 67 | [CALL_API]: result 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /src/actions/index.js: -------------------------------------------------------------------------------- 1 | import URI from "urijs"; 2 | import { CALL_API } from "redux-api-middleware"; 3 | import { stringify } from "query-string"; 4 | import _ from "lodash"; 5 | import { subsituteUrl, formData, processType } from "~/helpers/api"; 6 | export function setProtocol(protocol) { 7 | return { 8 | type: "API:SET_PROTOCOL", 9 | payload: protocol 10 | }; 11 | } 12 | 13 | export function setSwagger(swagger) { 14 | return { 15 | type: "API:SET_SWAGGER", 16 | payload: swagger 17 | }; 18 | } 19 | export function setHeaders(headers) { 20 | return { 21 | type: "API:SET_BASE_HEADERS", 22 | payload: { 23 | headers: headers 24 | } 25 | }; 26 | } 27 | export function request(pathName, { method, data, subst }, types) { 28 | return (dispatch, getState) => { 29 | const pathEntity = _.get(getState().api.paths, pathName); 30 | 31 | let entityPath = pathName; 32 | if (subst) { 33 | entityPath = subsituteUrl(pathName, subst); 34 | } 35 | let realPath = entityPath; 36 | 37 | // //TODO Header should be create by Header constructor instead of plain object 38 | let headers = { 39 | ...getState().api.headers, 40 | ...pathEntity.headers 41 | }; 42 | let body; 43 | if (method == "get" && data) { 44 | realPath = realPath + "?" + stringify(data); 45 | } else { 46 | body = formData(data, headers["Content-Type"]); 47 | } 48 | 49 | if (headers["Content-Type"] === "multipart/form-data") { 50 | delete headers["Content-Type"]; 51 | delete headers["content-type"]; 52 | } 53 | // 54 | const result = { 55 | endpoint: 56 | getState().api.protocol + 57 | "://" + 58 | getState().api.host + 59 | getState().api.basePath + 60 | realPath, 61 | method: method, 62 | 63 | headers: headers, 64 | body: body, 65 | types: processType(types, pathName, method) 66 | }; 67 | 68 | return dispatch({ 69 | [CALL_API]: result 70 | }); 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-api-middleware-addon", 3 | "version": "1.0.0", 4 | "title": "redux-api-middleware-addon", 5 | "main": "lib/index.js", 6 | "description": "A opinioned boilerplate for develop react component(with styled-components) and redux action,reducer with proper development tools", 7 | "keywords": [ 8 | "Babel", 9 | "redux", 10 | "redux api middleware addon", 11 | "Library" 12 | ], 13 | "homepage": "https://github.com/chungchi300/redux-api-middleware-addon", 14 | "author": { 15 | "name": "Jeff Chung", 16 | "url": "https://github.com/chungchi300" 17 | }, 18 | "license": "MIT", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/chungchi300/redux-api-middleware-addon" 22 | }, 23 | "engines": { 24 | "npm": ">=3.0.0", 25 | "node": ">=6.0.0" 26 | }, 27 | "dependencies": { 28 | "babel-plugin-transform-runtime": "^6.23.0", 29 | "babel-runtime": "^6.25.0", 30 | "form-data": "^2.3.1", 31 | "isomorphic-fetch": "^2.2.1", 32 | "lodash": "^4.17.4", 33 | "query-string": "^5.0.1", 34 | "redux-api-middleware": "^2.0.1", 35 | "urijs": "^1.18.4" 36 | }, 37 | "devDependencies": { 38 | "babel-cli": "^6.26.0", 39 | "babel-eslint": "^8.0.2", 40 | "babel-istanbul": "^0.12.2", 41 | "babel-plugin-root-import": "^5.1.0", 42 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 43 | "babel-preset-env": "^1.6.0", 44 | "coveralls": "^3.0.0", 45 | "eslint": "^4.3.0", 46 | "eslint-plugin-babel": "^4.1.2", 47 | "eslint-plugin-prettier": "^2.1.2", 48 | "isomorphic-fetch": "^2.1.1", 49 | "nock": "^9.1.0", 50 | "prettier": "^1.5.3", 51 | "rimraf": "^2.6.2", 52 | "tap-spec": "^4.1.1 ", 53 | "tape": "^4.8.0" 54 | }, 55 | "scripts": { 56 | "build": "babel src --out-dir lib", 57 | "clean": "rimraf lib coverage", 58 | "cover": "babel-node ./node_modules/.bin/babel-istanbul cover test/index.js | tap-spec", 59 | "lint": "eslint src test", 60 | "prepublish": "npm run lint && npm test && npm run clean && npm run build", 61 | "test": "" 62 | }, 63 | "jest": { 64 | "modulePaths": [ 65 | "./node_modules", 66 | "./src/common" 67 | ] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Features 2 | 1. **Predictable and Ready-to-go api reducer to handle common network result for Rest API** ,E.g **Domain,Network,Api** Reducer 3 | 2. Support **swagger.json**,**documentation as code** 4 | 3. Support **file upload** 5 | 6 | ## Installation 7 | ``` 8 | npm install redux-api-middleware-addon --save 9 | ``` 10 | ## Example 11 | 12 | ### Swagger definitions 13 | https://github.com/chungchi300/redux-api-middleware-addon/blob/master/src/common/swagger.js 14 | 15 | ### Code Usage 16 | ``` 17 | import SWAGGER from 'swagger.js'; 18 | 19 | store.dispatch(Action.setProtocol('https')); 20 | 21 | store.dispatch(Action.setSwagger(SWAGGER)); 22 | store.dispatch( 23 | Action.setHeaders({ 24 | // 'X-Token': 'base64TokenForApiCall', 25 | Accept: 'application/json', 26 | ['Content-Type']: 'application/json', 27 | }) 28 | ); 29 | store 30 | .dispatch( 31 | Action.request( 32 | '/pet/findByStatus', 33 | { 34 | method: 'get', 35 | data: { status: 'available' }, 36 | subst: null, 37 | }, 38 | BASIC 39 | ) 40 | ) 41 | .then(res => console.log(res)); 42 | store 43 | .dispatch( 44 | Action.request( 45 | '/pet', 46 | { 47 | method: 'post', 48 | data: { 49 | name: 'ronald', 50 | id: 3, 51 | photoUrls: [ 52 | 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/26/Letter_d.svg/1200px-Letter_d.svg.png', 53 | ], 54 | }, 55 | }, 56 | BASIC 57 | ) 58 | ) 59 | .then(res => console.log(res)); 60 | store 61 | .dispatch( 62 | Action.request( 63 | '/pet/findByStatus', 64 | { 65 | method: 'get', 66 | data: { status: 'available' }, 67 | subst: null, 68 | }, 69 | entity('PETS_BY_ID') 70 | ) 71 | ) 72 | .then(res => console.log(res)); 73 | ``` 74 | ### Result 75 | ![Result](http://jeff-chung.com/blog_accessary/blog_images/repo/redux-middleware-api-addon.png) 76 | 77 | ## Components 78 | 79 | ### Action 80 | 1. SET_SWAGGER 81 | 2. Request 82 | 3. SET_HEADER(default empty) 83 | 4. SET_PROTOCOL(default http) 84 | 85 | ### helpers 86 | 1. Generate Basic types 87 | 2. Generate Get Entity types 88 | 89 | //TODO 90 | Generate update,delete,create which help network reducer record which api has api result that failed 91 | 92 | ### Reducer 93 | 1. network (reducer,where is network result located) 94 | 2. domain (reducer,where is CRUD resource result located) 95 | 3. api (reducer,where is the api located) 96 | -------------------------------------------------------------------------------- /lib/common/actions/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _defineProperty2 = require("babel-runtime/helpers/defineProperty"); 8 | 9 | var _defineProperty3 = _interopRequireDefault(_defineProperty2); 10 | 11 | var _extends2 = require("babel-runtime/helpers/extends"); 12 | 13 | var _extends3 = _interopRequireDefault(_extends2); 14 | 15 | exports.setProtocol = setProtocol; 16 | exports.setSwagger = setSwagger; 17 | exports.setHeaders = setHeaders; 18 | exports.request = request; 19 | 20 | var _urijs = require("urijs"); 21 | 22 | var _urijs2 = _interopRequireDefault(_urijs); 23 | 24 | var _reduxApiMiddleware = require("redux-api-middleware"); 25 | 26 | var _queryString = require("query-string"); 27 | 28 | var _lodash = require("lodash"); 29 | 30 | var _lodash2 = _interopRequireDefault(_lodash); 31 | 32 | var _api = require("../../helpers/api"); 33 | 34 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 35 | 36 | function setProtocol(protocol) { 37 | return { 38 | type: "API:SET_PROTOCOL", 39 | payload: protocol 40 | }; 41 | } 42 | 43 | function setSwagger(swagger) { 44 | return { 45 | type: "API:SET_SWAGGER", 46 | payload: swagger 47 | }; 48 | } 49 | function setHeaders(headers) { 50 | return { 51 | type: "API:SET_BASE_HEADERS", 52 | payload: { 53 | headers: headers 54 | } 55 | }; 56 | } 57 | function request(pathName, _ref, types) { 58 | var method = _ref.method, 59 | data = _ref.data, 60 | subst = _ref.subst; 61 | 62 | var pathEntity = _lodash2.default.get(getState().api.paths, pathName); 63 | 64 | var entityPath = pathName; 65 | if (subst) { 66 | entityPath = (0, _api.subsituteUrl)(pathName, subst); 67 | } 68 | var realPath = entityPath; 69 | 70 | // //TODO Header should be create by Header constructor instead of plain object 71 | var headers = (0, _extends3.default)({}, getState().api.headers, pathEntity.headers); 72 | var body = void 0; 73 | if (method == "get" && data) { 74 | realPath = realPath + "?" + (0, _queryString.stringify)(data); 75 | } else { 76 | body = (0, _api.formData)(data, headers["Content-Type"]); 77 | } 78 | 79 | if (headers["Content-Type"] === "multipart/form-data") { 80 | delete headers["Content-Type"]; 81 | delete headers["content-type"]; 82 | } 83 | // 84 | var result = { 85 | endpoint: getState().api.protocol + "://" + getState().api.host + getState().api.basePath + realPath, 86 | method: method, 87 | 88 | headers: headers, 89 | body: body, 90 | types: (0, _api.processType)(types, pathName, method) 91 | }; 92 | return (0, _defineProperty3.default)({}, _reduxApiMiddleware.CALL_API, result); 93 | } -------------------------------------------------------------------------------- /lib/actions/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _defineProperty2 = require("babel-runtime/helpers/defineProperty"); 8 | 9 | var _defineProperty3 = _interopRequireDefault(_defineProperty2); 10 | 11 | var _extends2 = require("babel-runtime/helpers/extends"); 12 | 13 | var _extends3 = _interopRequireDefault(_extends2); 14 | 15 | exports.setProtocol = setProtocol; 16 | exports.setSwagger = setSwagger; 17 | exports.setHeaders = setHeaders; 18 | exports.request = request; 19 | 20 | var _urijs = require("urijs"); 21 | 22 | var _urijs2 = _interopRequireDefault(_urijs); 23 | 24 | var _reduxApiMiddleware = require("redux-api-middleware"); 25 | 26 | var _queryString = require("query-string"); 27 | 28 | var _lodash = require("lodash"); 29 | 30 | var _lodash2 = _interopRequireDefault(_lodash); 31 | 32 | var _api = require("../helpers/api"); 33 | 34 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 35 | 36 | function setProtocol(protocol) { 37 | return { 38 | type: "API:SET_PROTOCOL", 39 | payload: protocol 40 | }; 41 | } 42 | 43 | function setSwagger(swagger) { 44 | return { 45 | type: "API:SET_SWAGGER", 46 | payload: swagger 47 | }; 48 | } 49 | function setHeaders(headers) { 50 | return { 51 | type: "API:SET_BASE_HEADERS", 52 | payload: { 53 | headers: headers 54 | } 55 | }; 56 | } 57 | function request(pathName, _ref, types) { 58 | var method = _ref.method, 59 | data = _ref.data, 60 | subst = _ref.subst; 61 | 62 | return function (dispatch, getState) { 63 | var pathEntity = _lodash2.default.get(getState().api.paths, pathName); 64 | 65 | var entityPath = pathName; 66 | if (subst) { 67 | entityPath = (0, _api.subsituteUrl)(pathName, subst); 68 | } 69 | var realPath = entityPath; 70 | 71 | // //TODO Header should be create by Header constructor instead of plain object 72 | var headers = (0, _extends3.default)({}, getState().api.headers, pathEntity.headers); 73 | var body = void 0; 74 | if (method == "get" && data) { 75 | realPath = realPath + "?" + (0, _queryString.stringify)(data); 76 | } else { 77 | body = (0, _api.formData)(data, headers["Content-Type"]); 78 | } 79 | 80 | if (headers["Content-Type"] === "multipart/form-data") { 81 | delete headers["Content-Type"]; 82 | delete headers["content-type"]; 83 | } 84 | // 85 | var result = { 86 | endpoint: getState().api.protocol + "://" + getState().api.host + getState().api.basePath + realPath, 87 | method: method, 88 | 89 | headers: headers, 90 | body: body, 91 | types: (0, _api.processType)(types, pathName, method) 92 | }; 93 | 94 | return dispatch((0, _defineProperty3.default)({}, _reduxApiMiddleware.CALL_API, result)); 95 | }; 96 | } -------------------------------------------------------------------------------- /src/helpers/api.js: -------------------------------------------------------------------------------- 1 | import FormData from "form-data"; 2 | import { stringify } from "query-string"; 3 | 4 | // ['REQUEST', 'SUCESS', 'FAILURE']; 5 | import _ from "lodash"; 6 | // mapById(['entity']); 7 | // defaultGuessing by name; 8 | export const BASIC = ["REQUEST", "SUCCESS", "FAILURE"]; 9 | export function sleep(ms) { 10 | return new Promise(resolve => setTimeout(resolve, ms)); 11 | } 12 | export async function waitForAWhile() { 13 | return await sleep(3000); 14 | } 15 | export function entity(name) { 16 | return [ 17 | "REQUEST", 18 | { 19 | type: "SUCCESS", 20 | meta: { data: name }, 21 | payload: (action, state, res) => { 22 | return res.json(); 23 | } 24 | }, 25 | "FAILURE" 26 | ]; 27 | } 28 | export function formData(dataMap, contentType) { 29 | if (!dataMap) return null; 30 | if (contentType === "multipart/form-data") { 31 | let formData = new FormData(); 32 | 33 | Object.keys(dataMap).forEach(key => formData.append(key, dataMap[key])); 34 | 35 | return formData; 36 | } else if (contentType === "application/x-www-form-urlencoded") { 37 | return stringify(dataMap); 38 | } else if (contentType === "application/json") { 39 | return JSON.stringify(dataMap); 40 | } else { 41 | throw new Error("unknown contentType for " + contentType); 42 | } 43 | } 44 | function getConventionalName(pathName, method) { 45 | return ( 46 | method + 47 | pathName 48 | .replace(/\{.*\}/g, "") 49 | .replace(/\/(.)/g, function($1) { 50 | return $1.toUpperCase(); 51 | }) 52 | .replace(/\//g, "") 53 | ); 54 | } 55 | export function processType(types, pathName, method) { 56 | let originalTypes = _.cloneDeep(types); 57 | let processedTypes = []; 58 | 59 | originalTypes = originalTypes.map(originalType => { 60 | if (typeof originalType == "string") { 61 | return { type: originalType }; 62 | } else { 63 | return originalType; 64 | } 65 | }); 66 | console.log("types", types); 67 | processedTypes[0] = { 68 | ...originalTypes[0], 69 | meta: { 70 | path: pathName, 71 | name: getConventionalName(pathName, method), 72 | method: method, 73 | ...originalTypes[0].meta 74 | } 75 | }; 76 | processedTypes[1] = { 77 | ...originalTypes[1], 78 | meta: { 79 | path: pathName, 80 | name: getConventionalName(pathName, method), 81 | method: method, 82 | ...originalTypes[1].meta 83 | } 84 | }; 85 | processedTypes[2] = { 86 | ...originalTypes[2], 87 | meta: { 88 | path: pathName, 89 | name: getConventionalName(pathName, method), 90 | method: method, 91 | ...originalTypes[2].meta 92 | } 93 | }; 94 | 95 | return processedTypes; 96 | } 97 | export function subsituteUrl(url, substitues) { 98 | let finalUrl = url; 99 | 100 | Object.keys(substitues).forEach((key, index) => { 101 | finalUrl = finalUrl.replace("{" + key + "}", substitues[key]); 102 | }); 103 | return finalUrl; 104 | } 105 | -------------------------------------------------------------------------------- /lib/helpers/api.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.waitForAWhile = exports.BASIC = undefined; 7 | 8 | var _extends2 = require("babel-runtime/helpers/extends"); 9 | 10 | var _extends3 = _interopRequireDefault(_extends2); 11 | 12 | var _stringify = require("babel-runtime/core-js/json/stringify"); 13 | 14 | var _stringify2 = _interopRequireDefault(_stringify); 15 | 16 | var _keys = require("babel-runtime/core-js/object/keys"); 17 | 18 | var _keys2 = _interopRequireDefault(_keys); 19 | 20 | var _regenerator = require("babel-runtime/regenerator"); 21 | 22 | var _regenerator2 = _interopRequireDefault(_regenerator); 23 | 24 | var _asyncToGenerator2 = require("babel-runtime/helpers/asyncToGenerator"); 25 | 26 | var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); 27 | 28 | var _promise = require("babel-runtime/core-js/promise"); 29 | 30 | var _promise2 = _interopRequireDefault(_promise); 31 | 32 | var waitForAWhile = exports.waitForAWhile = function () { 33 | var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee() { 34 | return _regenerator2.default.wrap(function _callee$(_context) { 35 | while (1) { 36 | switch (_context.prev = _context.next) { 37 | case 0: 38 | _context.next = 2; 39 | return sleep(3000); 40 | 41 | case 2: 42 | return _context.abrupt("return", _context.sent); 43 | 44 | case 3: 45 | case "end": 46 | return _context.stop(); 47 | } 48 | } 49 | }, _callee, this); 50 | })); 51 | 52 | return function waitForAWhile() { 53 | return _ref.apply(this, arguments); 54 | }; 55 | }(); 56 | 57 | exports.sleep = sleep; 58 | exports.entity = entity; 59 | exports.formData = formData; 60 | exports.processType = processType; 61 | exports.subsituteUrl = subsituteUrl; 62 | 63 | var _formData2 = require("form-data"); 64 | 65 | var _formData3 = _interopRequireDefault(_formData2); 66 | 67 | var _queryString = require("query-string"); 68 | 69 | var _lodash = require("lodash"); 70 | 71 | var _lodash2 = _interopRequireDefault(_lodash); 72 | 73 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 74 | 75 | // mapById(['entity']); 76 | // defaultGuessing by name; 77 | var BASIC = exports.BASIC = ["REQUEST", "SUCCESS", "FAILURE"]; 78 | 79 | // ['REQUEST', 'SUCESS', 'FAILURE']; 80 | function sleep(ms) { 81 | return new _promise2.default(function (resolve) { 82 | return setTimeout(resolve, ms); 83 | }); 84 | } 85 | function entity(name) { 86 | return ["REQUEST", { 87 | type: "SUCCESS", 88 | meta: { data: name }, 89 | payload: function payload(action, state, res) { 90 | return res.json(); 91 | } 92 | }, "FAILURE"]; 93 | } 94 | function formData(dataMap, contentType) { 95 | if (!dataMap) return null; 96 | if (contentType === "multipart/form-data") { 97 | var _formData = new _formData3.default(); 98 | 99 | (0, _keys2.default)(dataMap).forEach(function (key) { 100 | return _formData.append(key, dataMap[key]); 101 | }); 102 | 103 | return _formData; 104 | } else if (contentType === "application/x-www-form-urlencoded") { 105 | return (0, _queryString.stringify)(dataMap); 106 | } else if (contentType === "application/json") { 107 | return (0, _stringify2.default)(dataMap); 108 | } else { 109 | throw new Error("unknown contentType for " + contentType); 110 | } 111 | } 112 | function getConventionalName(pathName, method) { 113 | return method + pathName.replace(/\{.*\}/g, "").replace(/\/(.)/g, function ($1) { 114 | return $1.toUpperCase(); 115 | }).replace(/\//g, ""); 116 | } 117 | function processType(types, pathName, method) { 118 | var originalTypes = _lodash2.default.cloneDeep(types); 119 | var processedTypes = []; 120 | 121 | originalTypes = originalTypes.map(function (originalType) { 122 | if (typeof originalType == "string") { 123 | return { type: originalType }; 124 | } else { 125 | return originalType; 126 | } 127 | }); 128 | console.log("types", types); 129 | processedTypes[0] = (0, _extends3.default)({}, originalTypes[0], { 130 | meta: (0, _extends3.default)({ 131 | path: pathName, 132 | name: getConventionalName(pathName, method), 133 | method: method 134 | }, originalTypes[0].meta) 135 | }); 136 | processedTypes[1] = (0, _extends3.default)({}, originalTypes[1], { 137 | meta: (0, _extends3.default)({ 138 | path: pathName, 139 | name: getConventionalName(pathName, method), 140 | method: method 141 | }, originalTypes[1].meta) 142 | }); 143 | processedTypes[2] = (0, _extends3.default)({}, originalTypes[2], { 144 | meta: (0, _extends3.default)({ 145 | path: pathName, 146 | name: getConventionalName(pathName, method), 147 | method: method 148 | }, originalTypes[2].meta) 149 | }); 150 | 151 | return processedTypes; 152 | } 153 | function subsituteUrl(url, substitues) { 154 | var finalUrl = url; 155 | 156 | (0, _keys2.default)(substitues).forEach(function (key, index) { 157 | finalUrl = finalUrl.replace("{" + key + "}", substitues[key]); 158 | }); 159 | return finalUrl; 160 | } -------------------------------------------------------------------------------- /lib/swagger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = { 7 | swagger: '2.0', 8 | info: { 9 | description: 'This is a sample Petstore server. You can find \nout more about Swagger at \n[http://swagger.io](http://swagger.io) or on \n[irc.freenode.net, #swagger](http://swagger.io/irc/).\n', 10 | version: '1.0.0', 11 | title: 'Swagger Petstore', 12 | termsOfService: 'http://swagger.io/terms/', 13 | contact: { 14 | email: 'apiteam@swagger.io' 15 | }, 16 | license: { 17 | name: 'Apache 2.0', 18 | url: 'http://www.apache.org/licenses/LICENSE-2.0.html' 19 | } 20 | }, 21 | host: 'virtserver.swaggerhub.com', 22 | basePath: '/chungchi300/Petstore-redux-api-middleware-addon/1.0.0', 23 | tags: [{ 24 | name: 'pet', 25 | description: 'Everything about your Pets', 26 | externalDocs: { 27 | description: 'Find out more', 28 | url: 'http://swagger.io' 29 | } 30 | }, { 31 | name: 'store', 32 | description: 'Access to Petstore orders' 33 | }, { 34 | name: 'user', 35 | description: 'Operations about user', 36 | externalDocs: { 37 | description: 'Find out more about our store', 38 | url: 'http://swagger.io' 39 | } 40 | }], 41 | schemes: ['https', 'http'], 42 | paths: { 43 | '/pet': { 44 | post: { 45 | tags: ['pet'], 46 | summary: 'Add a new pet to the store', 47 | operationId: 'addPet', 48 | consumes: ['application/json', 'application/xml'], 49 | produces: ['application/json', 'application/xml'], 50 | parameters: [{ 51 | in: 'body', 52 | name: 'body', 53 | description: 'Pet object that needs to be added to the store', 54 | required: true, 55 | schema: { 56 | $ref: '#/definitions/Pet' 57 | } 58 | }], 59 | responses: { 60 | '405': { 61 | description: 'Invalid input' 62 | } 63 | }, 64 | security: [{ 65 | petstore_auth: ['write:pets', 'read:pets'] 66 | }] 67 | }, 68 | put: { 69 | tags: ['pet'], 70 | summary: 'Update an existing pet', 71 | operationId: 'updatePet', 72 | consumes: ['application/json', 'application/xml'], 73 | produces: ['application/json', 'application/xml'], 74 | parameters: [{ 75 | in: 'body', 76 | name: 'body', 77 | description: 'Pet object that needs to be added to the store', 78 | required: true, 79 | schema: { 80 | $ref: '#/definitions/Pet' 81 | } 82 | }], 83 | responses: { 84 | '400': { 85 | description: 'Invalid ID supplied' 86 | }, 87 | '404': { 88 | description: 'Pet not found' 89 | }, 90 | '405': { 91 | description: 'Validation exception' 92 | } 93 | }, 94 | security: [{ 95 | petstore_auth: ['write:pets', 'read:pets'] 96 | }] 97 | } 98 | }, 99 | '/pet/findByStatus': { 100 | get: { 101 | tags: ['pet'], 102 | summary: 'Finds Pets by status', 103 | description: 'Multiple status values can be provided with comma separated strings', 104 | operationId: 'findPetsByStatus', 105 | produces: ['application/json', 'application/xml'], 106 | parameters: [{ 107 | name: 'status', 108 | in: 'query', 109 | description: 'Status values that need to be considered for filter', 110 | required: true, 111 | type: 'array', 112 | items: { 113 | type: 'string', 114 | enum: ['available', 'pending', 'sold'], 115 | default: 'available' 116 | }, 117 | collectionFormat: 'multi' 118 | }], 119 | responses: { 120 | '200': { 121 | description: 'successful operation', 122 | schema: { 123 | type: 'array', 124 | items: { 125 | $ref: '#/definitions/Pet' 126 | } 127 | } 128 | }, 129 | '400': { 130 | description: 'Invalid status value' 131 | } 132 | }, 133 | security: [{ 134 | petstore_auth: ['write:pets', 'read:pets'] 135 | }] 136 | } 137 | }, 138 | '/pet/{id}': { 139 | get: { 140 | tags: ['pet'], 141 | summary: 'Find pet by ID', 142 | description: 'Returns a single pet', 143 | operationId: 'getPetById', 144 | produces: ['application/json', 'application/xml'], 145 | parameters: [{ 146 | name: 'petId', 147 | in: 'path', 148 | description: 'ID of pet to return', 149 | required: true, 150 | type: 'integer', 151 | format: 'int64' 152 | }], 153 | responses: { 154 | '200': { 155 | description: 'successful operation', 156 | schema: { 157 | $ref: '#/definitions/Pet' 158 | } 159 | }, 160 | '400': { 161 | description: 'Invalid ID supplied' 162 | }, 163 | '404': { 164 | description: 'Pet not found' 165 | } 166 | }, 167 | security: [{ 168 | api_key: [] 169 | }] 170 | }, 171 | post: { 172 | tags: ['pet'], 173 | summary: 'Updates a pet in the store with form data', 174 | operationId: 'updatePetWithForm', 175 | consumes: ['application/x-www-form-urlencoded'], 176 | produces: ['application/json', 'application/xml'], 177 | parameters: [{ 178 | name: 'petId', 179 | in: 'path', 180 | description: 'ID of pet that needs to be updated', 181 | required: true, 182 | type: 'integer', 183 | format: 'int64' 184 | }, { 185 | name: 'name', 186 | in: 'formData', 187 | description: 'Updated name of the pet', 188 | required: false, 189 | type: 'string' 190 | }, { 191 | name: 'status', 192 | in: 'formData', 193 | description: 'Updated status of the pet', 194 | required: false, 195 | type: 'string' 196 | }], 197 | responses: { 198 | '405': { 199 | description: 'Invalid input' 200 | } 201 | }, 202 | security: [{ 203 | petstore_auth: ['write:pets', 'read:pets'] 204 | }] 205 | }, 206 | delete: { 207 | tags: ['pet'], 208 | summary: 'Deletes a pet', 209 | operationId: 'deletePet', 210 | produces: ['application/json', 'application/xml'], 211 | parameters: [{ 212 | name: 'api_key', 213 | in: 'header', 214 | required: false, 215 | type: 'string' 216 | }, { 217 | name: 'petId', 218 | in: 'path', 219 | description: 'Pet id to delete', 220 | required: true, 221 | type: 'integer', 222 | format: 'int64' 223 | }], 224 | responses: { 225 | '400': { 226 | description: 'Invalid ID supplied' 227 | }, 228 | '404': { 229 | description: 'Pet not found' 230 | } 231 | }, 232 | security: [{ 233 | petstore_auth: ['write:pets', 'read:pets'] 234 | }] 235 | } 236 | } 237 | }, 238 | securityDefinitions: { 239 | petstore_auth: { 240 | type: 'oauth2', 241 | authorizationUrl: 'http://petstore.swagger.io/oauth/dialog', 242 | flow: 'implicit', 243 | scopes: { 244 | 'write:pets': 'modify pets in your account', 245 | 'read:pets': 'read your pets' 246 | } 247 | }, 248 | api_key: { 249 | type: 'apiKey', 250 | name: 'api_key', 251 | in: 'header' 252 | } 253 | }, 254 | definitions: { 255 | Pet: { 256 | type: 'object', 257 | required: ['name', 'photoUrls'], 258 | properties: { 259 | id: { 260 | type: 'integer', 261 | format: 'int64' 262 | }, 263 | category: { 264 | $ref: '#/definitions/Category' 265 | }, 266 | name: { 267 | type: 'string', 268 | example: 'doggie' 269 | }, 270 | photoUrls: { 271 | type: 'array', 272 | xml: { 273 | name: 'photoUrl', 274 | wrapped: true 275 | }, 276 | items: { 277 | type: 'string' 278 | } 279 | }, 280 | tags: { 281 | type: 'array', 282 | xml: { 283 | name: 'tag', 284 | wrapped: true 285 | }, 286 | items: { 287 | $ref: '#/definitions/Tag' 288 | } 289 | }, 290 | status: { 291 | type: 'string', 292 | description: 'pet status in the store', 293 | enum: ['available', 'pending', 'sold'] 294 | } 295 | }, 296 | example: { 297 | photoUrls: ['photoUrls', 'photoUrls'], 298 | name: 'doggie', 299 | id: 0, 300 | category: { 301 | name: 'name', 302 | id: 6 303 | }, 304 | tags: [{ 305 | name: 'name', 306 | id: 1 307 | }, { 308 | name: 'name', 309 | id: 1 310 | }], 311 | status: 'available' 312 | }, 313 | xml: { 314 | name: 'Pet' 315 | } 316 | }, 317 | ApiResponse: { 318 | type: 'object', 319 | properties: { 320 | code: { 321 | type: 'integer', 322 | format: 'int32' 323 | }, 324 | type: { 325 | type: 'string' 326 | }, 327 | message: { 328 | type: 'string' 329 | } 330 | }, 331 | example: { 332 | code: 0, 333 | type: 'type', 334 | message: 'message' 335 | } 336 | } 337 | }, 338 | externalDocs: { 339 | description: 'Find out more about Swagger', 340 | url: 'http://swagger.io' 341 | } 342 | }; -------------------------------------------------------------------------------- /src/swagger.js: -------------------------------------------------------------------------------- 1 | export default { 2 | swagger: '2.0', 3 | info: { 4 | description: 5 | 'This is a sample Petstore server. You can find \nout more about Swagger at \n[http://swagger.io](http://swagger.io) or on \n[irc.freenode.net, #swagger](http://swagger.io/irc/).\n', 6 | version: '1.0.0', 7 | title: 'Swagger Petstore', 8 | termsOfService: 'http://swagger.io/terms/', 9 | contact: { 10 | email: 'apiteam@swagger.io', 11 | }, 12 | license: { 13 | name: 'Apache 2.0', 14 | url: 'http://www.apache.org/licenses/LICENSE-2.0.html', 15 | }, 16 | }, 17 | host: 'virtserver.swaggerhub.com', 18 | basePath: '/chungchi300/Petstore-redux-api-middleware-addon/1.0.0', 19 | tags: [ 20 | { 21 | name: 'pet', 22 | description: 'Everything about your Pets', 23 | externalDocs: { 24 | description: 'Find out more', 25 | url: 'http://swagger.io', 26 | }, 27 | }, 28 | { 29 | name: 'store', 30 | description: 'Access to Petstore orders', 31 | }, 32 | { 33 | name: 'user', 34 | description: 'Operations about user', 35 | externalDocs: { 36 | description: 'Find out more about our store', 37 | url: 'http://swagger.io', 38 | }, 39 | }, 40 | ], 41 | schemes: ['https', 'http'], 42 | paths: { 43 | '/pet': { 44 | post: { 45 | tags: ['pet'], 46 | summary: 'Add a new pet to the store', 47 | operationId: 'addPet', 48 | consumes: ['application/json', 'application/xml'], 49 | produces: ['application/json', 'application/xml'], 50 | parameters: [ 51 | { 52 | in: 'body', 53 | name: 'body', 54 | description: 'Pet object that needs to be added to the store', 55 | required: true, 56 | schema: { 57 | $ref: '#/definitions/Pet', 58 | }, 59 | }, 60 | ], 61 | responses: { 62 | '405': { 63 | description: 'Invalid input', 64 | }, 65 | }, 66 | security: [ 67 | { 68 | petstore_auth: ['write:pets', 'read:pets'], 69 | }, 70 | ], 71 | }, 72 | put: { 73 | tags: ['pet'], 74 | summary: 'Update an existing pet', 75 | operationId: 'updatePet', 76 | consumes: ['application/json', 'application/xml'], 77 | produces: ['application/json', 'application/xml'], 78 | parameters: [ 79 | { 80 | in: 'body', 81 | name: 'body', 82 | description: 'Pet object that needs to be added to the store', 83 | required: true, 84 | schema: { 85 | $ref: '#/definitions/Pet', 86 | }, 87 | }, 88 | ], 89 | responses: { 90 | '400': { 91 | description: 'Invalid ID supplied', 92 | }, 93 | '404': { 94 | description: 'Pet not found', 95 | }, 96 | '405': { 97 | description: 'Validation exception', 98 | }, 99 | }, 100 | security: [ 101 | { 102 | petstore_auth: ['write:pets', 'read:pets'], 103 | }, 104 | ], 105 | }, 106 | }, 107 | '/pet/findByStatus': { 108 | get: { 109 | tags: ['pet'], 110 | summary: 'Finds Pets by status', 111 | description: 112 | 'Multiple status values can be provided with comma separated strings', 113 | operationId: 'findPetsByStatus', 114 | produces: ['application/json', 'application/xml'], 115 | parameters: [ 116 | { 117 | name: 'status', 118 | in: 'query', 119 | description: 'Status values that need to be considered for filter', 120 | required: true, 121 | type: 'array', 122 | items: { 123 | type: 'string', 124 | enum: ['available', 'pending', 'sold'], 125 | default: 'available', 126 | }, 127 | collectionFormat: 'multi', 128 | }, 129 | ], 130 | responses: { 131 | '200': { 132 | description: 'successful operation', 133 | schema: { 134 | type: 'array', 135 | items: { 136 | $ref: '#/definitions/Pet', 137 | }, 138 | }, 139 | }, 140 | '400': { 141 | description: 'Invalid status value', 142 | }, 143 | }, 144 | security: [ 145 | { 146 | petstore_auth: ['write:pets', 'read:pets'], 147 | }, 148 | ], 149 | }, 150 | }, 151 | '/pet/{id}': { 152 | get: { 153 | tags: ['pet'], 154 | summary: 'Find pet by ID', 155 | description: 'Returns a single pet', 156 | operationId: 'getPetById', 157 | produces: ['application/json', 'application/xml'], 158 | parameters: [ 159 | { 160 | name: 'petId', 161 | in: 'path', 162 | description: 'ID of pet to return', 163 | required: true, 164 | type: 'integer', 165 | format: 'int64', 166 | }, 167 | ], 168 | responses: { 169 | '200': { 170 | description: 'successful operation', 171 | schema: { 172 | $ref: '#/definitions/Pet', 173 | }, 174 | }, 175 | '400': { 176 | description: 'Invalid ID supplied', 177 | }, 178 | '404': { 179 | description: 'Pet not found', 180 | }, 181 | }, 182 | security: [ 183 | { 184 | api_key: [], 185 | }, 186 | ], 187 | }, 188 | post: { 189 | tags: ['pet'], 190 | summary: 'Updates a pet in the store with form data', 191 | operationId: 'updatePetWithForm', 192 | consumes: ['application/x-www-form-urlencoded'], 193 | produces: ['application/json', 'application/xml'], 194 | parameters: [ 195 | { 196 | name: 'petId', 197 | in: 'path', 198 | description: 'ID of pet that needs to be updated', 199 | required: true, 200 | type: 'integer', 201 | format: 'int64', 202 | }, 203 | { 204 | name: 'name', 205 | in: 'formData', 206 | description: 'Updated name of the pet', 207 | required: false, 208 | type: 'string', 209 | }, 210 | { 211 | name: 'status', 212 | in: 'formData', 213 | description: 'Updated status of the pet', 214 | required: false, 215 | type: 'string', 216 | }, 217 | ], 218 | responses: { 219 | '405': { 220 | description: 'Invalid input', 221 | }, 222 | }, 223 | security: [ 224 | { 225 | petstore_auth: ['write:pets', 'read:pets'], 226 | }, 227 | ], 228 | }, 229 | delete: { 230 | tags: ['pet'], 231 | summary: 'Deletes a pet', 232 | operationId: 'deletePet', 233 | produces: ['application/json', 'application/xml'], 234 | parameters: [ 235 | { 236 | name: 'api_key', 237 | in: 'header', 238 | required: false, 239 | type: 'string', 240 | }, 241 | { 242 | name: 'petId', 243 | in: 'path', 244 | description: 'Pet id to delete', 245 | required: true, 246 | type: 'integer', 247 | format: 'int64', 248 | }, 249 | ], 250 | responses: { 251 | '400': { 252 | description: 'Invalid ID supplied', 253 | }, 254 | '404': { 255 | description: 'Pet not found', 256 | }, 257 | }, 258 | security: [ 259 | { 260 | petstore_auth: ['write:pets', 'read:pets'], 261 | }, 262 | ], 263 | }, 264 | }, 265 | }, 266 | securityDefinitions: { 267 | petstore_auth: { 268 | type: 'oauth2', 269 | authorizationUrl: 'http://petstore.swagger.io/oauth/dialog', 270 | flow: 'implicit', 271 | scopes: { 272 | 'write:pets': 'modify pets in your account', 273 | 'read:pets': 'read your pets', 274 | }, 275 | }, 276 | api_key: { 277 | type: 'apiKey', 278 | name: 'api_key', 279 | in: 'header', 280 | }, 281 | }, 282 | definitions: { 283 | Pet: { 284 | type: 'object', 285 | required: ['name', 'photoUrls'], 286 | properties: { 287 | id: { 288 | type: 'integer', 289 | format: 'int64', 290 | }, 291 | category: { 292 | $ref: '#/definitions/Category', 293 | }, 294 | name: { 295 | type: 'string', 296 | example: 'doggie', 297 | }, 298 | photoUrls: { 299 | type: 'array', 300 | xml: { 301 | name: 'photoUrl', 302 | wrapped: true, 303 | }, 304 | items: { 305 | type: 'string', 306 | }, 307 | }, 308 | tags: { 309 | type: 'array', 310 | xml: { 311 | name: 'tag', 312 | wrapped: true, 313 | }, 314 | items: { 315 | $ref: '#/definitions/Tag', 316 | }, 317 | }, 318 | status: { 319 | type: 'string', 320 | description: 'pet status in the store', 321 | enum: ['available', 'pending', 'sold'], 322 | }, 323 | }, 324 | example: { 325 | photoUrls: ['photoUrls', 'photoUrls'], 326 | name: 'doggie', 327 | id: 0, 328 | category: { 329 | name: 'name', 330 | id: 6, 331 | }, 332 | tags: [ 333 | { 334 | name: 'name', 335 | id: 1, 336 | }, 337 | { 338 | name: 'name', 339 | id: 1, 340 | }, 341 | ], 342 | status: 'available', 343 | }, 344 | xml: { 345 | name: 'Pet', 346 | }, 347 | }, 348 | ApiResponse: { 349 | type: 'object', 350 | properties: { 351 | code: { 352 | type: 'integer', 353 | format: 'int32', 354 | }, 355 | type: { 356 | type: 'string', 357 | }, 358 | message: { 359 | type: 'string', 360 | }, 361 | }, 362 | example: { 363 | code: 0, 364 | type: 'type', 365 | message: 'message', 366 | }, 367 | }, 368 | }, 369 | externalDocs: { 370 | description: 'Find out more about Swagger', 371 | url: 'http://swagger.io', 372 | }, 373 | }; 374 | --------------------------------------------------------------------------------