Skip to content

Commit f86d7ef

Browse files
authored
generate static schema (#76)
add a command to generate static schema
1 parent c75a391 commit f86d7ef

File tree

2 files changed

+191
-0
lines changed

2 files changed

+191
-0
lines changed

cmd/generate.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package cmd
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"os"
10+
"pb/pkg/common"
11+
internalHTTP "pb/pkg/http"
12+
13+
"github.com/spf13/cobra"
14+
)
15+
16+
const (
17+
generateStaticSchemaPath = "/logstream/schema/detect"
18+
)
19+
20+
var GenerateSchemaCmd = &cobra.Command{
21+
Use: "generate",
22+
Short: "Generate Schema for JSON",
23+
Example: "pb schema generate --file=test.json",
24+
RunE: func(cmd *cobra.Command, _ []string) error {
25+
// Get the file path from the `--file` flag
26+
filePath, err := cmd.Flags().GetString("file")
27+
if err != nil {
28+
return fmt.Errorf(common.Red+"failed to read file flag: %w"+common.Reset, err)
29+
}
30+
31+
if filePath == "" {
32+
return fmt.Errorf(common.Red + "file flag is required" + common.Reset)
33+
}
34+
35+
// Read the file content
36+
fileContent, err := os.ReadFile(filePath)
37+
if err != nil {
38+
return fmt.Errorf(common.Red+"failed to read file %s: %w"+common.Reset, filePath, err)
39+
}
40+
41+
// Initialize HTTP client
42+
client := internalHTTP.DefaultClient(&DefaultProfile)
43+
44+
// Create the HTTP request
45+
req, err := client.NewRequest(http.MethodPost, generateStaticSchemaPath, bytes.NewBuffer(fileContent))
46+
if err != nil {
47+
return fmt.Errorf(common.Red+"failed to create new request: %w"+common.Reset, err)
48+
}
49+
50+
// Set Content-Type header
51+
req.Header.Set("Content-Type", "application/json")
52+
53+
// Execute the request
54+
resp, err := client.Client.Do(req)
55+
if err != nil {
56+
return fmt.Errorf(common.Red+"request execution failed: %w"+common.Reset, err)
57+
}
58+
defer resp.Body.Close()
59+
60+
// Check for non-200 status codes
61+
if resp.StatusCode != http.StatusOK {
62+
body, _ := io.ReadAll(resp.Body)
63+
fmt.Printf(common.Red+"Error response: %s\n"+common.Reset, string(body))
64+
return fmt.Errorf(common.Red+"non-200 status code received: %s"+common.Reset, resp.Status)
65+
}
66+
67+
// Parse and print the response
68+
respBody, err := io.ReadAll(resp.Body)
69+
if err != nil {
70+
return fmt.Errorf(common.Red+"failed to read response body: %w"+common.Reset, err)
71+
}
72+
73+
var prettyJSON bytes.Buffer
74+
if err := json.Indent(&prettyJSON, respBody, "", " "); err != nil {
75+
return fmt.Errorf(common.Red+"failed to format response as JSON: %w"+common.Reset, err)
76+
}
77+
78+
fmt.Println(common.Green + prettyJSON.String() + common.Reset)
79+
return nil
80+
},
81+
}
82+
83+
var CreateSchemaCmd = &cobra.Command{
84+
Use: "create",
85+
Short: "Create Schema for a Parseable stream",
86+
Example: "pb schema create --stream=my_stream --file=schema.json",
87+
RunE: func(cmd *cobra.Command, _ []string) error {
88+
// Get the stream name from the `--stream` flag
89+
streamName, err := cmd.Flags().GetString("stream")
90+
if err != nil {
91+
return fmt.Errorf(common.Red+"failed to read stream flag: %w"+common.Reset, err)
92+
}
93+
94+
if streamName == "" {
95+
return fmt.Errorf(common.Red + "stream flag is required" + common.Reset)
96+
}
97+
98+
// Get the file path from the `--file` flag
99+
filePath, err := cmd.Flags().GetString("file")
100+
if err != nil {
101+
return fmt.Errorf(common.Red+"failed to read config flag: %w"+common.Reset, err)
102+
}
103+
104+
if filePath == "" {
105+
return fmt.Errorf(common.Red + "file path flag is required" + common.Reset)
106+
}
107+
108+
// Read the JSON schema file
109+
schemaContent, err := os.ReadFile(filePath)
110+
if err != nil {
111+
return fmt.Errorf(common.Red+"failed to read schema file %s: %w"+common.Reset, filePath, err)
112+
}
113+
114+
// Initialize HTTP client
115+
client := internalHTTP.DefaultClient(&DefaultProfile)
116+
117+
// Construct the API path
118+
apiPath := fmt.Sprintf("/logstream/%s", streamName)
119+
120+
// Create the HTTP PUT request
121+
req, err := client.NewRequest(http.MethodPut, apiPath, bytes.NewBuffer(schemaContent))
122+
if err != nil {
123+
return fmt.Errorf(common.Red+"failed to create new request: %w"+common.Reset, err)
124+
}
125+
126+
// Set custom headers
127+
req.Header.Set("Content-Type", "application/json")
128+
req.Header.Set("X-P-Static-Schema-Flag", "true")
129+
130+
// Execute the request
131+
resp, err := client.Client.Do(req)
132+
if err != nil {
133+
return fmt.Errorf(common.Red+"request execution failed: %w"+common.Reset, err)
134+
}
135+
defer resp.Body.Close()
136+
137+
// Check for non-200 status codes
138+
if resp.StatusCode != http.StatusOK {
139+
body, _ := io.ReadAll(resp.Body)
140+
fmt.Printf(common.Red+"Error response: %s\n"+common.Reset, string(body))
141+
return fmt.Errorf(common.Red+"non-200 status code received: %s"+common.Reset, resp.Status)
142+
}
143+
144+
// Parse and print the response
145+
respBody, err := io.ReadAll(resp.Body)
146+
if err != nil {
147+
return fmt.Errorf(common.Red+"failed to read response body: %w"+common.Reset, err)
148+
}
149+
150+
fmt.Println(common.Green + string(respBody) + common.Reset)
151+
return nil
152+
},
153+
}
154+
155+
func init() {
156+
// Add the `--file` flag to the command
157+
GenerateSchemaCmd.Flags().StringP("file", "f", "", "Path to the JSON file to generate schema")
158+
CreateSchemaCmd.Flags().StringP("stream", "s", "", "Name of the stream to associate with the schema")
159+
CreateSchemaCmd.Flags().StringP("file", "f", "", "Path to the JSON file to create schema")
160+
161+
}

main.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,32 @@ var profile = &cobra.Command{
9292
},
9393
}
9494

95+
var schema = &cobra.Command{
96+
Use: "schema",
97+
Short: "Generate or create schemas for JSON data or Parseable streams",
98+
Long: `The "schema" command allows you to either:
99+
- Generate a schema automatically from a JSON file for analysis or integration.
100+
- Create a custom schema for Parseable streams (PB streams) to structure and process your data.
101+
102+
Examples:
103+
- To generate a schema from a JSON file:
104+
pb schema generate --file=data.json
105+
- To create a schema for a PB stream:
106+
pb schema create --stream-name=my_stream --config=data.json
107+
`,
108+
PersistentPreRunE: combinedPreRun,
109+
PersistentPostRun: func(cmd *cobra.Command, args []string) {
110+
if os.Getenv("PB_ANALYTICS") == "disable" {
111+
return
112+
}
113+
wg.Add(1)
114+
go func() {
115+
defer wg.Done()
116+
analytics.PostRunAnalytics(cmd, "generate", args)
117+
}()
118+
},
119+
}
120+
95121
var user = &cobra.Command{
96122
Use: "user",
97123
Short: "Manage users",
@@ -200,6 +226,9 @@ func main() {
200226
query.AddCommand(pb.QueryCmd)
201227
query.AddCommand(pb.SavedQueryList)
202228

229+
schema.AddCommand(pb.GenerateSchemaCmd)
230+
schema.AddCommand(pb.CreateSchemaCmd)
231+
203232
install.AddCommand(pb.InstallOssCmd)
204233

205234
cli.AddCommand(profile)
@@ -211,6 +240,7 @@ func main() {
211240

212241
cli.AddCommand(pb.AutocompleteCmd)
213242
cli.AddCommand(install)
243+
cli.AddCommand(schema)
214244

215245
// Set as command
216246
pb.VersionCmd.Run = func(_ *cobra.Command, _ []string) {

0 commit comments

Comments
 (0)