Skip to content

feat: Add rudimentary plugin support#1

Merged
simse merged 2 commits into
mainfrom
feat/plugin-support
Sep 13, 2024
Merged

feat: Add rudimentary plugin support#1
simse merged 2 commits into
mainfrom
feat/plugin-support

Conversation

@simse
Copy link
Copy Markdown
Owner

@simse simse commented Sep 13, 2024

Summary by CodeRabbit

  • New Features

    • Introduced introspection capabilities for better GraphQL schema management.
    • Added support for generating TypeScript definitions from GraphQL schemas.
  • Bug Fixes

    • Enhanced handling of project schemas to ensure clarity and consistency.
  • Refactor

    • Improved modularity and organization of the codebase, particularly in plugin management and schema generation.
    • Streamlined the main function for better flow and user feedback during project code generation.
  • Chores

    • Updated configuration to support new output specifications and plugin verification.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Sep 13, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The pull request introduces substantial updates to the codebase, particularly within the feature-account package of a monorepo. Key modifications include the addition of an introspection JSON output specification, restructuring of configuration types, and the implementation of a plugin management system. Several functions responsible for schema conversion have been removed, indicating a change in the approach to handling GraphQL schemas. New files have been created to facilitate TypeScript generation from GraphQL schemas and to define plugin tasks, enhancing modularity and organization within the code.

Changes

Files Change Summary
examples/projects/monorepo/packages/feature-account/codegen.yml Added output specification for introspection.json using the introspection plugin.
examples/projects/monorepo/packages/feature-account/introspection.json Introduced introspection.json file indicating that the introspection plugin is not implemented.
examples/projects/monorepo/.gitignore Added introspection.json to the .gitignore file to prevent tracking by Git.
go.mod Added several indirect dependencies to enhance project functionality.
internal/config.go Restructured Config type; renamed Schema to Schemas and introduced Generates type for better clarity and maintainability. Updated GetConfig method to reflect these changes.
internal/converter.go Removed the entire implementation of the ConvertSchema function and associated helper functions, eliminating schema-to-TypeScript conversion functionality.
internal/plugins/casing.go Changed package declaration from internal to plugins, indicating a reorganization of code structure.
internal/plugins/introspection.go Added Introspect method for PluginTask, currently serving as a placeholder for introspection functionality.
internal/plugins/plugins.go Defined PluginTask type and VerifyPlugin function for managing and validating plugins.
internal/plugins/typescript.go Implemented functionality for converting GraphQL schemas into TypeScript definitions, including various utility functions for handling different schema types.
internal/project.go Introduced ExecuteDestinationTasks method for executing plugin tasks related to schema generation, enhancing project configuration handling.
main.go Refactored the main function and its logic for project code generation into modular functions for better organization and readability.

Tip

OpenAI O1 model for chat
  • We have deployed OpenAI's latest O1 model for chat.
  • OpenAI claims that this model has superior reasoning capabilities than their GPT-4o model.
  • Please share any feedback with us in the discussions post.

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE

Commits

Files that changed from the base of the PR and between a51ac1b and 416c68b.

Files selected for processing (9)
  • examples/projects/monorepo/packages/feature-account/codegen.yml (1 hunks)
  • examples/projects/monorepo/packages/feature-account/introspection.json (1 hunks)
  • internal/config.go (1 hunks)
  • internal/converter.go (0 hunks)
  • internal/plugins/casing.go (1 hunks)
  • internal/plugins/introspection.go (1 hunks)
  • internal/plugins/plugins.go (1 hunks)
  • internal/plugins/typescript.go (1 hunks)
  • internal/project.go (5 hunks)
Files not reviewed due to no reviewable changes (1)
  • internal/converter.go
Additional context used
yamllint
examples/projects/monorepo/packages/feature-account/codegen.yml

[error] 8-8: no new line character at the end of file

(new-line-at-end-of-file)

Additional comments not posted (22)
examples/projects/monorepo/packages/feature-account/codegen.yml (1)

7-8: LGTM! The new output specification looks good.

Adding the introspection.json output using the introspection plugin is a great enhancement. It will generate introspection data which is very useful for tools that rely on GraphQL schema introspection.

Tools
yamllint

[error] 8-8: no new line character at the end of file

(new-line-at-end-of-file)

internal/plugins/casing.go (3)

1-1: Package name change looks good!

The package name has been changed from internal to plugins. This change is acceptable if it aligns with the overall project structure and naming conventions.


Line range hint 8-10: ToUpper function looks good!

The ToUpper function converts a string to uppercase using the standard library strings package. The implementation is correct and follows best practices.


Line range hint 12-14: ToCamel function looks good!

The ToCamel function converts a string to camel case using the iancoleman/strcase package. The implementation is correct and follows best practices.

internal/config.go (4)

11-11: Good naming choice!

Renaming the Schema field to Schemas improves clarity and suggests that the configuration can accommodate multiple schemas. This change enhances the readability and maintainability of the code.


14-14: Nice refactoring!

Modifying the Generates field from an inline struct to a separate type is a great refactoring decision. It improves code readability, maintainability, and allows for better reuse and potential extension of the Generates structure in the future.


17-20: Well-structured type!

Introducing the Generates type with Plugins and Preset fields is a great way to encapsulate related configuration options. It provides a clear and organized structure for managing the generation settings.


22-22: Consistent update!

Updating the logic in the GetConfig method to check for p.config.Schemas instead of p.config.Schema ensures consistency with the changes made to the Config struct. This change maintains the correct behavior of the method in light of the updated configuration structure.

internal/project.go (6)

132-138: Great use of goroutines to parallelize task execution! 👍

Launching a goroutine for each destination to execute the generation tasks concurrently is an efficient approach. It will help speed up the overall execution time, especially for projects with multiple destinations.


145-148: Nice work with the strings.Builder and ExecuteDestinationTasks! 🙌

Building the output in memory using strings.Builder is a smart move. It helps optimize performance by minimizing file I/O operations.

Also, extracting the task execution logic into a separate ExecuteDestinationTasks method improves code readability and maintainability. Good job!


150-164: Solid file handling! 💪

The code for creating the output file, writing the contents, and closing the file looks good. Handling potential errors and panicking on failures is the right approach here, as we want to halt the execution if something goes wrong with the file operations.


174-202: Excellent work on the ExecuteDestinationTasks method! 🎉

The implementation looks clean and well-structured. Here are a few highlights:

  1. The method signature and parameters are well-defined and appropriate for the task at hand.
  2. Creating a PluginTask struct to encapsulate the task data is a good design choice. It keeps the method focused and avoids passing around too many parameters.
  3. The plugin execution logic is clear and easy to follow. The use of VerifyPlugin to validate the plugin before execution is a nice touch.
  4. The method is extensible and can easily support more plugins in the future by adding new conditions.

Overall, great job on this method! It enhances the modularity and organization of the codebase.


4-7: Appropriate import statements! 👌

The new import statements look good and are necessary for the changes made in this file.

Using the project's internal plugins package is the right choice here, as it keeps the code modular and encapsulated.

Also, importing slog for logging is a great practice to improve the observability and debuggability of the codebase.


35-35: Good schema assignment! 👍

Assigning the Schemas field from the project config to the Schemas field of the Project struct is the right thing to do here. It ensures that the Project struct has access to the schemas defined in the config, which is necessary for further processing.

The assignment is consistent with the rest of the code and maintains the integrity of the Project struct.

internal/plugins/typescript.go (8)

11-13: LGTM!

The Typescript function is correctly invoking ConvertSchema with the appropriate parameters.


72-75: LGTM!

The AddBaseTypes function correctly writes the necessary base TypeScript types to the output.


77-95: LGTM!

The ConvertDefinition function provides a clean and extensible way to handle different GraphQL definition kinds using a switch statement. It calls the appropriate conversion functions based on the definition kind and returns an error for unknown kinds, which is a good practice for error handling.


117-123: LGTM!

The WriteComment function correctly writes the comment to the output if it exists and handles the case when the comment is empty.


125-131: LGTM!

The WriteFieldComment function correctly writes the field comment to the output if it exists and handles the case when the comment is empty.


133-139: LGTM!

The WriteArgumentComment function correctly writes the argument comment to the output if it exists and handles the case when the comment is empty.


215-230: LGTM!

The WriteFieldArguments function correctly writes the field arguments to the output. It effectively uses the WriteArgumentComment and AddArgumentType functions to handle argument comments and types, promoting code reuse.


232-248: LGTM!

The ConvertInputObject function correctly converts the GraphQL input object definition to TypeScript syntax. It effectively uses the WriteFieldComment and AddFieldType functions to handle field comments and types, promoting code reuse.

@@ -0,0 +1 @@
{"message":"introspection plugin is not implemented"}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarify the purpose of this file and improve the message format.

It's unclear if this file is intended to be committed to the repository or if it's a temporary file.

If it's a temporary file:

  • Please add it to the .gitignore file to prevent accidental commits.

If it's intended to be committed:

  • Please provide a more descriptive message that follows a standard format for introspection output.
  • Consider using a structured format like JSON or YAML for the output, with clear fields for the plugin name, version, status, and any relevant details.

Comment on lines +3 to +5
func (p* PluginTask) Introspect() {
p.Output.WriteString("{\"message\":\"introspection plugin is not implemented\"}\n")
} No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve the placeholder implementation.

The placeholder implementation can be improved in the following ways:

  1. Rename the JSON key from message to Message to follow the Go naming conventions.
  2. Update the JSON message to be more descriptive, such as "Introspection plugin is not implemented yet."
  3. Return an error to indicate that the plugin is not implemented.

Apply this diff to improve the placeholder implementation:

-func (p* PluginTask) Introspect() {
-    p.Output.WriteString("{\"message\":\"introspection plugin is not implemented\"}\n")
+func (p* PluginTask) Introspect() error {
+    p.Output.WriteString("{\"Message\":\"Introspection plugin is not implemented yet.\"}\n")
+    return errors.New("introspection plugin is not implemented")
}

Committable suggestion was skipped due to low confidence.

plugins: [typescript] No newline at end of file
plugins: [typescript]
'introspection.json':
plugins: [introspection] No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a new line at the end of the file.

The static analysis tool has flagged that there is no new line character at the end of the file. While this is a minor issue, it's considered a best practice to always have a new line at the end.

You can fix it by simply adding a new line at the end of the file.

Tools
yamllint

[error] 8-8: no new line character at the end of file

(new-line-at-end-of-file)

Comment on lines +9 to +13
type PluginTask struct {
Schema *ast.Schema
Output *strings.Builder
Config interface{}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a more specific type for the Config field.

The Config field is currently an empty interface, which could lead to type safety issues and make the code harder to understand and maintain.

Consider defining a specific type for the configuration, such as a struct or a map, to improve type safety and provide better documentation about the expected structure of the configuration.

Comment on lines +18 to +24
func VerifyPlugin(pluginName string, config interface{}) error {
if pluginName != "typescript" && pluginName != "introspection" {
return errors.New("unknown plugin")
}

return nil
} No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve error message and consider using a map or slice for supported plugins.

The VerifyPlugin function could be improved in a couple of ways:

  1. Include the plugin name in the error message for better debugging. For example:
return fmt.Errorf("unknown plugin: %s", pluginName)
  1. Consider using a map or slice of supported plugins for better extensibility. This would make it easier to add or remove supported plugins in the future without modifying the function logic. For example:
supportedPlugins := map[string]bool{
    "typescript":    true,
    "introspection": true,
}

if !supportedPlugins[pluginName] {
    return fmt.Errorf("unknown plugin: %s", pluginName)
}

Comment on lines +97 to +115
func ConvertEnum(definition *ast.Definition, output *strings.Builder) {
enumName := ToCamel(definition.Name)

WriteComment(definition, output)

output.WriteString("export enum " + enumName + " {\n")

for i, enumValue := range definition.EnumValues {
enumName := enumValue.Name
enumKey := ToUpper(enumValue.Name)
output.WriteString("\t" + enumKey + " = '" + enumName + "'")

if i != len(definition.EnumValues)-1 {
output.WriteString(",")
}
output.WriteString("\n")
}
output.WriteString("}\n")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting comment writing logic for reusability.

The ConvertEnum function correctly converts the GraphQL enum definition to TypeScript syntax. However, consider extracting the comment writing logic (line 100) into a separate function, such as WriteComment, for better reusability across different conversion functions.

Comment on lines +141 to +157
func ConvertUnion(definition *ast.Definition, output *strings.Builder) {
unionName := ToCamel(definition.Name)

WriteComment(definition, output)

output.WriteString("export type " + unionName + " = ")
for i, alias := range definition.Types {
output.WriteString(alias)

if i != len(definition.Types)-1 {
output.WriteString(" | ")
} else {
output.WriteString(";")
}
}
output.WriteString("\n")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting union types writing logic for better readability.

The ConvertUnion function correctly converts the GraphQL union definition to TypeScript syntax. However, consider extracting the logic for writing union types (lines 147-155) into a separate function, such as WriteUnionTypes, for better readability and maintainability.

Comment on lines +159 to +179
func ConvertInterface(definition *ast.Definition, output *strings.Builder, knownScalars []*ast.Definition) {
interfaceName := ToCamel(definition.Name)

WriteComment(definition, output)

output.WriteString("export type " + interfaceName + " = {\n")
for _, field := range definition.Fields {
WriteFieldComment(field, output)

fieldName := field.Name

output.WriteString("\t" + fieldName)
AddFieldType(field, output, "Maybe", knownScalars)
}

output.WriteString("}\n")

for _, field := range definition.Fields {
WriteFieldArguments(field, output, knownScalars, interfaceName)
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting fields writing logic for better readability.

The ConvertInterface function correctly converts the GraphQL interface definition to TypeScript syntax. It effectively uses the WriteFieldComment and WriteFieldArguments functions to handle field comments and arguments, promoting code reuse.

However, consider extracting the logic for writing fields (lines 165-172) into a separate function, such as WriteInterfaceFields, for better readability and maintainability.

Comment on lines +181 to +213
func ConvertObject(definition *ast.Definition, output *strings.Builder, knownScalars []*ast.Definition) {
interfaceName := ToCamel(definition.Name)

WriteComment(definition, output)

output.WriteString("export type " + interfaceName + " = ")

// check implements
for _, implementsInterface := range definition.Interfaces {
output.WriteString(implementsInterface + " & ")
}
output.WriteString("{\n")

output.WriteString("\t__typename: '" + interfaceName + "';\n")
for _, field := range definition.Fields {
if field.Name == "__type" || field.Name == "__schema" {
continue
}

WriteFieldComment(field, output)

fieldName := field.Name

output.WriteString("\t" + fieldName)
AddFieldType(field, output, "Maybe", knownScalars)
}

output.WriteString("}\n")

for _, field := range definition.Fields {
WriteFieldArguments(field, output, knownScalars, interfaceName)
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting implemented interfaces writing logic for better readability.

The ConvertObject function correctly converts the GraphQL object definition to TypeScript syntax. It effectively uses the WriteFieldComment and WriteFieldArguments functions to handle field comments and arguments, promoting code reuse.

However, consider extracting the logic for writing implemented interfaces (lines 189-191) into a separate function, such as WriteImplementedInterfaces, for better readability and maintainability.

Comment on lines +250 to +299
func AddFieldType(definition *ast.FieldDefinition, output *strings.Builder, maybeType string, knownScalars []*ast.Definition) {
isNullable := !definition.Type.NonNull

if isNullable {
output.WriteString("?: " + maybeType + "<")
} else {
output.WriteString(": ")
}

isArray := definition.Type.Elem != nil
isElemNullable := true

if isArray {
output.WriteString("Array<")

isElemNullable = !definition.Type.Elem.NonNull
}

if isArray && isElemNullable {
output.WriteString(maybeType + "<")
}

// check if scalar is known
isScalarKnown := false
for _, scalar := range knownScalars {
if scalar.Name == definition.Type.Name() {
isScalarKnown = true
}
}

if isScalarKnown {
output.WriteString("Scalars['" + definition.Type.Name() + "']")
} else {
output.WriteString(ToCamel(definition.Type.Name()))
}

if isNullable {
output.WriteString(">")
}

if isArray {
output.WriteString(">")
}

if isArray && isElemNullable {
output.WriteString(">")
}

output.WriteString(";\n")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting array type handling logic for better readability.

The AddFieldType function correctly adds field type information to the output based on nullability, array type, and scalar type. It effectively uses the knownScalars parameter to handle custom scalar types.

However, consider extracting the logic for handling array types (lines 259-296) into a separate function, such as HandleArrayType, for better readability and maintainability.

@simse simse merged commit f033e86 into main Sep 13, 2024
@simse simse deleted the feat/plugin-support branch September 14, 2024 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant