twskj / pretty-swag

Pretty UI for Swagger spec
MIT License
122 stars 20 forks source link

Allowing input to be provided directly as Json #41

Closed sundriver closed 7 years ago

sundriver commented 7 years ago

Hi There,

I wanted to embed pretty-swag in my frontend (react) which directly renders swagger spec on the browser.

To do that I need to be able to provide json object directly to your library.

At the moment there are references to "fs" that would break when the project is loaded in a browser.

Is there any plan to release a version that doesn't use "fs" and consumes json directly ?

Thanks

twskj commented 7 years ago

I think pretty-swag already handle that. So when you call

function parse(src, dst, config, callback)

You can pass in the input json as src. And if you set dst to null, you will get a call back with your result html content.

Let me know if this work out for you.

sundriver commented 7 years ago

Hi @twskj , I tried the following code using the parse method:

prettySwag.parse( this.props.spec, null, {
            format: "singleFile",
            markdown: true,
            fixedNav: true,
            autoTags: true,
            theme: {
                "default": "blue",
                "GET": "blue",
                "POST": "indigo",
                "DELETE": "red",
                "PUT": "amber"
            }
        }, ( html: string ) => {
            this.setState({prettySwag:html});
            console.log( html );
        } );

And I am getting the follow error re fs:

./~/pretty-swag/pretty-swag.js
Module not found: Error: Can't resolve 'fs' in '../node_modules/pretty-swag'
 @ ./~/pretty-swag/pretty-swag.js 1:9-22
 @ ./src/frontend/screens/app/screens/api/components/pretty-swag.render.component.tsx
 @ ./src/frontend/screens/app/screens/api/screens/api.screen.component.tsx
 @ ./src/frontend/screens/app/screens/app/app.tsx
 @ ./src/frontend/screens/app/app.layout.tsx
 @ ./src/frontend/index.tsx

Thanks

twskj commented 7 years ago

Oh I see what's going on. Wait a sec I'll make a patch.

twskj commented 7 years ago

I pushed it to master on github can you try it out and let me know if it helps?

sundriver commented 7 years ago

I am getting this error:

Module not found: Error: Can't resolve 'fs' in '/Users/ali/sf-projects/website/node_modules/pretty-swag'
 @ ./~/pretty-swag/pretty-swag.js 644:31-44
 @ ./src/frontend/screens/app/screens/api/components/pretty-swag.render.component.tsx
 @ ./src/frontend/screens/app/screens/api/screens/api.screen.component.tsx
 @ ./src/frontend/screens/app/screens/app/app.tsx
 @ ./src/frontend/screens/app/app.layout.tsx
 @ ./src/frontend/index.tsx
 @ multi (webpack)-dev-server/client?
twskj commented 7 years ago

I'm don't know much about react but why does it run line 644 ? Aren't it suppose to hit 642 and return?

twskj commented 7 years ago

Oh maybe it's the Webpack. Can you turn off Webpack just for now and see if things fall into place. If this is the case, we know we have to tell Webpack to leave fs alone somehow. It should be configurable.

twskj commented 7 years ago

Found it: add this to your Webpack config

node: {
  fs: "empty"
}
sundriver commented 7 years ago

Thank @twskj Now I am getting:

pretty-swag.render.component.tsx:15 Uncaught TypeError: prettySwag.parse is not a function
    at PrettySwagRenderComponent.componentDidMount (pretty-swag.render.component.tsx:15)
    at ReactCompositeComponent.js:264
    at measureLifeCyclePerf (ReactCompositeComponent.js:75)
    at ReactCompositeComponent.js:263
    at CallbackQueue.notifyAll (CallbackQueue.js:76)
    at ReactReconcileTransaction.close (ReactReconcileTransaction.js:80)
    at ReactReconcileTransaction.closeAll (Transaction.js:209)
    at ReactReconcileTransaction.perform (Transaction.js:156)
    at batchedMountComponentIntoNode (ReactMount.js:126)
    at ReactDefaultBatchingStrategyTransaction.perform (Transaction.js:143)

essentially this line:

const prettySwag = require( 'pretty-swag' );
prettySwag.parse( this.props.spec, null, {
            format: "singleFile",
            markdown: true,
            fixedNav: true,
            autoTags: true,
            theme: {
                "default": "blue",
                "GET": "blue",
                "POST": "indigo",
                "DELETE": "red",
                "PUT": "amber"
            }
        }, ( html: string ) => {
            this.setState( { prettySwag: html } );
            console.log( html );
        } );

says parse is not a method

sundriver commented 7 years ago

@twskj looks like parse method is exported at run

I changed the prettySwag like below:

 prettySwag.run( this.props.spec, null, {
            format: "singleFile",
            markdown: true,
            fixedNav: true,
            autoTags: true,
            theme: {
                "default": "blue",
                "GET": "blue",
                "POST": "indigo",
                "DELETE": "red",
                "PUT": "amber"
            }
        }, ( html: string ) => {
            this.setState( { prettySwag: html } );
            console.log( html );
        } );

Getting

TypeError: fs.readFileSync is not a function
    at makeSingleFile (livedoc.js:1106)
    at Object.generateHTML (livedoc.js:1260)
    at pretty-swag.js:640
    at index.js:11
    at run (setImmediate.js:40)
    at runIfPresent (setImmediate.js:69)
    at onGlobalMessage (setImmediate.js:109)
twskj commented 7 years ago

try .run

sundriver commented 7 years ago

I tried run and getting the error above

twskj commented 7 years ago

I'm looking at it.

twskj commented 7 years ago

I see what happened here. Let's do a work around. I'll add that in tomorrow. run with format lite should give you a working one for now

twskj commented 7 years ago

rolled out on v0.1.125. Can you try it out and let me know?

sundriver commented 7 years ago

Thanks @twskj After upgrading to latest version I don't get any error but the resulting string is null, any ideas ?

My code:

 prettySwag.run( this.props.spec, null, {
      format: "singleFile",
      markdown: true,
      fixedNav: true,
      autoTags: true,
      theme: {
        "default": "blue",
        "GET": "blue",
        "POST": "indigo",
        "DELETE": "red",
        "PUT": "amber"
      }
    }, ( html: string ) => {
      console.log( html ); // prints Null
    } );

Here is my input

{
    "swagger": "2.0",
    "info": {
        "title": "Test title",
        "description": "Test descripiton",
        "version": "1.0.0",
        "contact": {
            "name": "No Contact Name provided",
            "url": "http://www.test.com",
            "email": "test@test.com"
        }
    },
    "host": "localhost:3000",
    "schemes": ["http"],
    "basePath": "/api/v1.0",
    "produces": ["application/json"],
    "paths": {
        "/user": {
            "post": {
                "operationId": "createUser",
                "summary": "Create a new User",
                "description": "This call will create a new User and returns it back to the callee.\n",
                "tags": ["User"],
                "parameters": [{
                    "in": "body",
                    "name": "body",
                    "required": true,
                    "schema": {
                        "$ref": "#/definitions/UserCreateRequest"
                    }
                }],
                "responses": {
                    "201": {
                        "description": "The new User, in detailed view.",
                        "schema": {
                            "$ref": "#/definitions/UserDetailResponse"
                        }
                    },
                    "422": {
                        "$ref": "#/responses/ErrorResponse422"
                    }
                }
            }
        }
    },
    "parameters": {
        "userId": {
            "name": "userId",
            "in": "path",
            "description": "User id",
            "required": true,
            "type": "string"
        }
    },
    "definitions": {
        "AppSummaryResponse": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "string",
                    "description": "Id of entity"
                },
                "url": {
                    "type": "string",
                    "description": "URL pointing to this entity for getting detailed view"
                },
                "deleted_at": {
                    "type": "string",
                    "description": "When this entry was marked to be deleted"
                }
            }
        },
        "AppCreateRequest": {
            "type": "object",
            "properties": {}
        },
        "AppDetailResponse": {
            "type": "object",
            "properties": {
                "created_by": {
                    "type": "string",
                    "description": "The user who created the entry"
                },
                "created_at": {
                    "type": "string",
                    "description": "The timestamp when this entry was created"
                },
                "updated_by": {
                    "type": "string",
                    "description": "The user who most recently updated the entry"
                },
                "updated_at": {
                    "type": "string",
                    "description": "When was the entry was most recently updated"
                },
                "deleted_by": {
                    "type": "string",
                    "description": "The user who deleted the entry"
                }
            }
        },
        "UserCommonFields": {
            "type": "object",
            "properties": {}
        },
        "UserCreateRequest": {
            "allOf": [{
                "$ref": "#/definitions/AppCreateRequest"
            }, {
                "$ref": "#/definitions/UserCommonFields"
            }, {
                "type": "object",
                "required": ["name", "email", "password"],
                "properties": {
                    "name": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 255
                    },
                    "description": {
                        "type": "string",
                        "maxLength": 1024
                    },
                    "external_uri": {
                        "type": "string",
                        "format": "url"
                    },
                    "email": {
                        "type": "string",
                        "format": "email"
                    },
                    "password": {
                        "type": "string",
                        "minLength": 8,
                        "maxLength": 20
                    }
                }
            }]
        },
        "AppUpdateRequest": {
            "type": "object"
        },
        "UserSummaryResponse": {
            "allOf": [{
                "$ref": "#/definitions/AppSummaryResponse"
            }, {
                "$ref": "#/definitions/UserCommonFields"
            }, {
                "type": "object",
                "properties": {
                    "name": {
                        "type": "string"
                    }
                }
            }]
        },
        "UserUpdateRequest": {
            "allOf": [{
                "$ref": "#/definitions/AppUpdateRequest"
            }, {
                "$ref": "#/definitions/UserCommonFields"
            }, {
                "type": "object",
                "properties": {
                    "name": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 255
                    },
                    "description": {
                        "type": "string",
                        "maxLength": 1024
                    },
                    "external_uri": {
                        "type": "string",
                        "format": "url"
                    },
                    "email": {
                        "type": "string",
                        "format": "email"
                    },
                    "password": {
                        "type": "string",
                        "minLength": 8,
                        "maxLength": 20
                    }
                }
            }]
        },
        "UserDetailResponse": {
            "allOf": [{
                "$ref": "#/definitions/AppDetailResponse"
            }, {
                "$ref": "#/definitions/UserSummaryResponse"
            }, {
                "type": "object",
                "properties": {
                    "name": {
                        "type": "string"
                    },
                    "description": {
                        "type": "string"
                    },
                    "external_uri": {
                        "type": "string"
                    },
                    "email": {
                        "type": "string"
                    }
                }
            }]
        },
        "Error": {
            "type": "object",
            "properties": {
                "error_code": {
                    "type": "string"
                },
                "resource_name": {
                    "type": "string"
                }
            }
        },
        "ErrorMessage": {
            "type": "object",
            "properties": {
                "message": {
                    "type": "string"
                },
                "errors": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/Error"
                    }
                }
            }
        },
        "SingleErrorMessage": {
            "type": "object",
            "properties": {
                "message": {
                    "type": "string"
                }
            }
        }
    },
    "responses": {
        "ErrorResponse400": {
            "description": "Bad request, problems parsing JSON",
            "schema": {
                "$ref": "#/definitions/SingleErrorMessage"
            }
        },
        "ErrorResponse401": {
            "description": "Request failed because user is not authenticated",
            "schema": {
                "$ref": "#/definitions/SingleErrorMessage"
            }
        },
        "ErrorResponse403": {
            "description": "Request failed because user does not have authorization to access a specific resource",
            "schema": {
                "$ref": "#/definitions/SingleErrorMessage"
            }
        },
        "ErrorResponse422": {
            "description": "Your request was understood, but contained invalid parameters",
            "schema": {
                "$ref": "#/definitions/ErrorMessage"
            }
        },
        "CreateResponse": {
            "description": "Entry created successfully",
            "headers": {
                "location": {
                    "type": "string",
                    "description": "Link to newly created entry."
                }
            }
        }
    }
}
twskj commented 7 years ago

The call back signature is the normal Nodejs style: callback(err, data)

twskj commented 7 years ago

Do you have it running ok now ?

sundriver commented 7 years ago

Thanks @twskj