-
Notifications
You must be signed in to change notification settings - Fork 109
Conversation
Generated by 🚫 dangerJS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks so good! It will be a huge value add for people using OpenAPI!
bin/restful-react-import.js
Outdated
|
||
const program = require("commander"); | ||
const { join } = require("path"); | ||
const importOpenApi = require("../dist/import-open-api"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is in bin
and a part of the restful-react build, so he take the generated .js
file from dist
;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is quite confusing. Can we think of a clearer way to express this in code?
scripts/import-open-api.ts
Outdated
const componentName = pascal(operation.operationId!); | ||
|
||
const isOk = ([statusCode]: [string, ResponseObject | ReferenceObject]) => statusCode.toString().startsWith("2"); | ||
const isError = ([statusCode]: [string, ResponseObject | ReferenceObject]) => !statusCode.toString().startsWith("2"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Careful, .startsWith("3")
is not an error.
scripts/import-open-api.ts
Outdated
const schema = importSpecs(path); | ||
|
||
if (!schema.openapi.startsWith("3.0")) { | ||
throw new Error("This tools can only parse open-api 3.0.x specifications"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should support older versions too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since a lot of tools can make the swagger -> open-api migration, it's not really a problem 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, sorry for chiming in. Talked to @TejasQ about contract validation and found that I'm also interested in the outcome of this work. Ping if I can help somehow
scripts/import-open-api.ts
Outdated
} | ||
`; | ||
case "array": | ||
return `export type ${pascal(name)} = ${getArray(schema)}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't be covered by getScalar
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really need to add a bunch of integration tests to be sure about this, but it's probably true 👍
scripts/import-open-api.ts
Outdated
} else { | ||
if (res.content && res.content["application/json"]) { | ||
const schema = res.content["application/json"].schema!; | ||
return isReference(schema) ? getRef(schema.$ref) : getScalar(schema); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems that's a common pattern and could be extracted
function resolveValue(schema){
return isReference(schema) ? getRef(schema.$ref) : getScalar(schema);
}
scripts/import-open-api.ts
Outdated
${Object.entries(schema.properties || {}) | ||
.map( | ||
([key, properties]) => | ||
isReference(properties) ? ` ${key}: ${getRef(properties.$ref)}` : `${key}: ${getScalar(properties)}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
couldn't be transformed into ${key}: ${resolveValue(property)}
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably ^^ I introduce getScalar
lately and totally forget everything about this implementation
scripts/import-open-api.ts
Outdated
const importSpecs = (path: string): OpenAPIObject => { | ||
const data = readFileSync(path, "utf-8"); | ||
const { ext } = parse(path); | ||
return ext === ".yaml" ? YAML.parse(data) : JSON.parse(data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yml
seems to be also a valid extension https://en.wikipedia.org/wiki/YAML
scripts/import-open-api.ts
Outdated
* @param responses reponses object from open-api specs | ||
* @param componentName name of the current component | ||
*/ | ||
const getResponseTypes = (responses: Array<[string, ResponseObject | ReferenceObject]>, componentName: string) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
componentName
is not used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My old me probably have a plan about this, but now… 😄 Good catch!
scripts/import-open-api.ts
Outdated
* | ||
* @param operation | ||
*/ | ||
const generateGetComponent = (operation: OperationObject, verb: string, route: string) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
file is called import-open-api
and generateGetComponent
consumes it. so consumer shouldn't be declared here. am I wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The entry point is the main
function on bottom, and yes I will split this file in smaller, and also add a lot of unit tests! It was just more conveniant like this to iterate 😉 (it's a wip branch)
@restrry Thanks for your feedbacks 💯 This PR is totally work in progress, and sadly I don't have any time to work on it… 😞 Ideally I will also split this huge file in smaller one, and add some unit tests for every small parts (when it make sense). One of the big part missing where you can maybe help, is about the type of the body. Let me add a bit more context: <Mutation verb="POST">{
(post) => <Button onClick={() => post({/* the body can't be typesafe for now */})}/>
}</Mutation> To deal with this I would like to add more generic into Mutation to be able to specify the type of the body. The goal of course is to have this type generated from open-api specs (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#request-body-object) |
@restrry to echo what @fabien0102 said, your contributions here would be super helpful – unfortunately we're quite busy developing the product so we need all the help we can get on this project. 😉 |
np. |
@fabien0102 has all the info. He'll respond today. |
@restrry Logically everything in the PR description (#28 (comment)), the idea is to have the best as possible type safe component generated from open-api specs. My current implementation only have the I really trying to keep the PR todo list and examples up to date (I also add this query params on the todolist). To be very transparent, the trello card attached: Really, all relevant information are in the description, the trello card is just for a planning purpose 😉 |
7223a19
to
019a147
Compare
@fabien0102 This is incredibly cool. How about adding optional frontend validation of the responses in the frontend (maybe using https://www.npmjs.com/package/yup) as a second step? We could set it up so that you can enable/disable this validation and choose whether validation errors should throw or just console warn. These could be options to the generator script so that you only add this overhead to your code if you really want this. @peterszerzo You have experience with |
I've had a very good time working with (loosely related, I've proposed some |
@micha-f Very good idea for adding yup! It can be a very nice second step for this generator 💯 We already have talked about this validation layer with @peterszerzo 😉 But first of all, I need to fix the build and test everything in a real project to be sure everything is well following. |
I tried to approach the problem in a bit different way. in the chain |
@restrry thank you for your contribution! This is definitely not something you need to say sorry for. Ever. We actually looked into off the shelf solutions as well and, unfortunately could not find much that we could comfortable work with. I think it might be easier to process JSON swagger docs (both, v2 and OpenAPI) using Babel, which we have some experience with for the transformation layer (this is what Babel is for). However, this doesn't quite apply for
This link doesn't quite work as expected. Do you have an alternative? 😅 About yup I think this extra layer of safety is a welcome addition to the feature! Great idea, @micha-f! |
updated the link. |
We seem to be mixing up concepts a bit, my apologies. 😅 I was talking more about the static domain of transforming Also, I'm not sure how moustache (or any templating at all) fits into the equation. Could you help me out a bit there? I think I'm missing something... |
@fabien0102 Might make sense to check out the |
@TejasQ |
Oh man I'd love to see a PR from you into this PR 👼 |
Every solutions can work in this case, I must admit that I don't really see the advantage to use mustache or handlebar since we have built-in template string in JavaScript, but this also works. Just to compare:
restful-react/src/scripts/import-open-api.ts Lines 281 to 292 in 019a147
The main difference between theses libraries and my implementation is that nobody use yet OpenAPI 3.x and the generation code is not it typescript. I personally really prefer use typescript for this kind of task (autocompletion on OpenAPI specs thanks to Regarding the edge cases, I'm quite confident, I had the OpenAPI specs opened everytime when I develop this and a lot of unit tests. But I will discover the remaining one during my integration tests with our products 😃 But we can borrow some ideas from these generators:
Bref, this implementation is working well for now. We have types on every layer and it's quite cool 😎 The only missing piece is the build step, for now you need to update the @restrry If you can try locally this script with your own projects to have a feedback, this can be very helpful. Procedure to test:
For now it's OpenAPI 3 only, so you will probably need to convert your specs (I will try to put the convertor directly inside the script if it's possible) |
Exactly this. I have to say I completely agree and was wondering what it would look like in code, hence my suggesting a PR. I much prefer the latter approach here. |
@fabien0102 unfortunately, cannot help with testing, as I don't use restful-react :) just sharing my thoughts about implementation, as I'm working on the same problem for my project (on my free time), so most likely I continue with 3rd part library (swagger-typescript-codegen, for example) so other people could reuse it for their needs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks amazing so far. I will update the docs a bit when everything's ready to merge. 🚀
README.md
Outdated
@@ -1,5 +1,6 @@ | |||
# RESTful React | |||
[![Greenkeeper badge](https://badges.greenkeeper.io/contiamo/restful-react.svg)](https://greenkeeper.io/) | |||
|
|||
[![Greenkeeper badge](https://badges.greenkeeper.io/contiamo/restful-react.svg)](https://greenkeeper.io/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[![Greenkeeper badge](https://badges.greenkeeper.io/contiamo/restful-react.svg)](https://greenkeeper.io/) |
src/Mutate.tsx
Outdated
@@ -15,7 +15,7 @@ export interface States<TData, TError> { | |||
error?: GetState<TData, TError>["error"]; | |||
} | |||
|
|||
export type MutateMethod<TData> = (data?: string | {}) => Promise<TData>; | |||
export type MutateMethod<TData, TBody> = (data?: string | TBody) => Promise<TData>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TData
represents a response body so I find TBody
a little confusing in this case. Can we name it TRequestBody
instead?
src/scripts/import-open-api.ts
Outdated
SchemaObject, | ||
} from "openapi3-ts"; | ||
|
||
// @ts-ignore - no type definition here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we polyfill with a .d.ts
file and declare module "swagger2openapi"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried… It was just not working, but I will give another try
src/scripts/import-open-api.ts
Outdated
}; | ||
|
||
/** | ||
* Generate a restful-react compoment from openapi operation specs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Generate a restful-react compoment from openapi operation specs | |
* Generate a restful-react component from openapi operation specs |
src/scripts/import-open-api.ts
Outdated
@@ -267,7 +272,7 @@ export const generateRestfulComponent = ( | |||
const needAResponseComponent = responseTypes.includes("{"); | |||
|
|||
// We ignore last param of DELETE action, the last params should be the `id` and it's given after in restful-react |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this mean the last params should be the
id and it's given after in restful-react
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In restful-react, for the delete action, we can do:
<Mutate verb="DELETE" path="myresource">
{deleteMyRes => <button onClick={() => deleteMyRes("myid")}>delete this</button>}
</Mutate>
And this will call {base}/myresources/myid
on click => the id
is injected after, not directly in the path
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a lot of English changes to come to this PR. My plan is to add a commit with updated docs/comments/etc.
IMO we can resolve this for now and then add docs at the end as a final touch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TejasQ As long as we don't forget. This is not docs, this is a code comment. In my opinion, comments should be fixed with the code they comment on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My plan is to add a commit with updated docs/comments/etc.
We won't forget. 😄
1 similar comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, but I have some thoughts.
Let's merge this after we confirm the prerelease works with IdP. |
Why
The idea is to be able to give any open-api specs file (in json or yaml) and generate fully typed restful-react components 🎉
Usage
Output (current)
Usage inside the client project (current)
Todo
As the above example show, everything open-api definition pattern are not following yet. I will update this example during the API evolution 😉
allOf
patternin: "path"
andin: "query"
)Mutation
component to be able to typebody
Mutation
component"module": "commonjs"
)verb
is missing inMutate