Skip to content

Commit

Permalink
feat: refactor for formatdataform cli ( beta )
Browse files Browse the repository at this point in the history
  • Loading branch information
ashish10alex committed Jun 28, 2024
0 parents commit 2ca23db
Show file tree
Hide file tree
Showing 11 changed files with 767 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test/test_dataform/*
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright © 2024 Ashish Alex ashish.alex10@gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
114 changes: 114 additions & 0 deletions assets/.sqlfluff
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
[sqlfluff]
templater = placeholder
dialect = bigquery
output_line_length = 120

# EXCLUDED RULES
# ==============
# AL07 - Avoid table aliases in from clauses and join conditions.
# ST06 - Select wildcards then simple targets before calculations and aggregates.
# ST07 - Prefer specifying join keys instead of using USING.
# AM03 - Ambiguous ordering directions for columns in order by clause.
# ST04 - Dont mess up nested CASE statement
# LT05 - Line is too long
# AL05 - Tables should not be aliased if that alias is not used.
exclude_rules = AL07, ST06, ST07, AM03, ST04, LT05, AL05

[sqlfluff:rules]
allow_scalar = False

[sqlfluff:indentation]
tab_space_size = 2
indented_joins = False
indented_using_on = True
indented_then = False
indented_on_contents = False
allow_implicit_indents = True
template_blocks_indent = True

[sqlfluff:layout:type:where_clause]
line_position = alone:strict

[sqlfluff:layout:type:binary_operator]
line_position = leading

[sqlfluff:layout:type:comparison_operator]
line_position = trailing

[sqlfluff:layout:type:alias_expression]
# We want non-default spacing _before_ the alias expressions.
spacing_before = align
align_within = select_clause
align_scope = bracketed

[sqlfluff:rules:capitalisation.keywords]
# Keywords must be capitalised
capitalisation_policy = upper

[sqlfluff:rules:capitalisation.literals]
# Null & Boolean Literals eg: NULL, TRUE, FALSE
capitalisation_policy = upper

[sqlfluff:rules:capitalisation.types]
# Data Types eg: INT, STR
extended_capitalisation_policy = upper

[sqlfluff:rules:capitalisation.identifiers]
# Unquoted identifiers
extended_capitalisation_policy = upper
unquoted_identifiers_policy=all

[sqlfluff:rules:capitalisation.functions]
# Function names
capitalisation_policy = upper
extended_capitalisation_policy = upper

[sqlfluff:rules:ambiguous.join]
# Fully qualify JOIN clause
fully_qualify_join_types = inner

[sqlfluff:rules:aliasing.length]
# Minimum string length when creating an alias
min_alias_length = 3

[sqlfluff:rules:aliasing.table]
# Aliasing preference for tables, ie needs an AS
aliasing = explicit

[sqlfluff:rules:aliasing.column]
# Aliasing preference for columns, ie needs an AS
aliasing = explicit

[sqlfluff:rules:layout.commas]
# Leading or trailing commas
line_position = leading

[sqlfluff:layout:type:comma]
line_position = leading


[sqlfluff:rules:convention.select_trailing_comma]
# No trailing comma at end of SELECT, ie before FROM (after last column name)
select_clause_trailing_comma = forbid

[sqlfluff:rules:ambiguous.column_references]
# GROUP BY/ORDER BY column references (i.e. implicit by position or explicit by name)
group_by_and_order_by_style = explicit

[sqlfluff:rules:references.special_chars]
# Special characters in identifiers
unquoted_identifiers_policy = all
quoted_identifiers_policy = all
allow_space_in_identifier = False
additional_allowed_characters = ["", $]

[sqlfluff:rules:references.keywords]
# Keywords should not be used as identifiers.
unquoted_identifiers_policy = all
quoted_identifiers_policy = none



[sqlfluff:templater:placeholder]
param_regex = \${ref\(\"\d*(?P<param_name>[\w]+)"\)\}|\${(?P<param_name>[^\s]+)}

132 changes: 132 additions & 0 deletions cmd/format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
*/
package cmd

import (
"fmt"
"log"
"log/slog"
"os"
"path/filepath"
"sync"

"github.com/fatih/color"
"github.com/spf13/cobra"
)

// formatCmd represents the format command
var formatCmd = &cobra.Command{
Use: "format",
Short: "formats a file a directory depending on the next argument",
Long: `formats a file a directory depending on the next argument
To format a file: formatdataform format path/to/file.sqlx
To format a directory: formatdataform format /dir/to/format
`,
Run: func(cmd *cobra.Command, args []string) {

inplace, _ := cmd.Flags().GetBool("inplace")
sqlfluffConfigPath := cmd.Flag("sqlfluff_config_path").Value.String()


// make sure the .formatdataform directory exists if not create it
os.Mkdir(".formatdataform", 0755)
logFile, err := os.OpenFile(".formatdataform/formatdataform_logs.json", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Fatalf("error opening file: %v", err)
}
defer logFile.Close()

logger := slog.New(slog.NewJSONHandler(logFile, nil))

logger.Info("Formatting config",
slog.String("sqlfluffConfigPath", sqlfluffConfigPath),
slog.Bool("inplace", inplace),
)


green := color.New(color.FgGreen).SprintFunc()
yellow := color.New(color.FgYellow).SprintFunc()
red := color.New(color.FgRed).SprintFunc()

if sqlfluffConfigPath == ""{
fmt.Printf(red("sqlfluff config file path is required \n"))
return
}

if fileExists(sqlfluffConfigPath) == false {
fmt.Printf(red("sqlfluff config file does not exist at: %v \n"), sqlfluffConfigPath)
return
}

if fileExists(".formatdataform/sqlfluff_formatter.py") == false {
fmt.Print(yellow("sqlfluff_formatter.py file does not exist at: ", ".formatdataform/sqlfluff_formatter.py. Run: "))
fmt.Printf("formatdataform setup \n")
return
}

// If not file or directory path is supplied
if len(args) == 0 {
log.Fatalf(yellow(`No file or directory path supplied to command: `, red(` formatdataform format <path>
^^^^^`)))
return
}

// If more than one file or directory path is supplied
// TODO: Add support for multiple files or directories
if len(args) > 1 {
color.Set(color.FgYellow)
fmt.Printf("Only supports one file or directory path at a time, you passed %v \n", len(args))
color.Unset()
return
}

fileOrDirPath := args[0]

fileInfo, err := os.Stat(fileOrDirPath)
if err != nil {
log.Fatalf("Error opening file: %v", err)
return
}

if fileInfo.IsDir() {
fmt.Println("Directory to format: ", green(fileOrDirPath))
var sqlxFiles = findSqlxFiles(fileOrDirPath) // TODO: specify directory and depth to search for sql files here ?
fmt.Println("Number of sqlx files found: ", green(len(*sqlxFiles))+"\n")

if len(*sqlxFiles) == 0 {
fmt.Println("No .sqlx files found in the directory: ", yellow(fileOrDirPath))
return
}

var wg sync.WaitGroup
for i := 0; i < len(*sqlxFiles); i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
formatSqlxFile((*sqlxFiles)[i], inplace, sqlfluffConfigPath, logger)
}(i)
}
wg.Wait()

} else if !fileInfo.IsDir() {
fmt.Println("File to format: ", green(fileOrDirPath))
if filepath.Ext(fileOrDirPath) != ".sqlx" {
fmt.Printf(red("Only .sqlx files are supported for formatting \n"))
return
}
formatSqlxFile(fileOrDirPath, inplace, sqlfluffConfigPath, logger)
} else {
cmd.Help()
}
},
}

func init() {
rootCmd.AddCommand(formatCmd)

formatCmd.Flags().StringP("file", "f", "", "file to format")
formatCmd.Flags().StringP("dir", "d", "", "directory to format")
formatCmd.Flags().BoolP("inplace", "i", true, "format the file in place")

}
67 changes: 67 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright © 2024 Ashish Alex ashish.alex10@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd

import (
"os"

"github.com/spf13/cobra"
)

var sqlfluffConfigPath string



// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "formatdataform",
Short: "Format .sqlx files",
Long: ``,
// Uncomment the following line if your bare application
// has an action associated with it:
//TODO: might add version information here
// Run: func(cmd *cobra.Command, args []string) { },
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}

func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.

// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.formatdataform.yaml)")

rootCmd.PersistentFlags().StringP("sqlfluff_config_path", "c", "", "Path to the sqlfluff config file")
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}


Loading

0 comments on commit 2ca23db

Please sign in to comment.