268 lines
7.5 KiB
Go
268 lines
7.5 KiB
Go
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"net/http"
|
||
|
|
)
|
||
|
|
|
||
|
|
// SDKFunction describes a function in the SDK
|
||
|
|
type SDKFunction struct {
|
||
|
|
Name string `json:"name"`
|
||
|
|
Signature string `json:"signature"`
|
||
|
|
InsertText string `json:"insertText"`
|
||
|
|
Documentation string `json:"documentation"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// SDKNamespace describes a namespace with functions
|
||
|
|
type SDKNamespace struct {
|
||
|
|
Functions []SDKFunction `json:"functions,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// SDKField describes a field in an event type
|
||
|
|
type SDKField struct {
|
||
|
|
Name string `json:"name"`
|
||
|
|
Type string `json:"type"`
|
||
|
|
Doc string `json:"doc,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// SDKEventType describes an event type with nested fields
|
||
|
|
type SDKEventType struct {
|
||
|
|
Fields []SDKField `json:"fields"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// SDKHookType maps hook names to their event types
|
||
|
|
type SDKHookTypes map[string]SDKEventType
|
||
|
|
|
||
|
|
// SDKSchema is the complete SDK schema for a language
|
||
|
|
type SDKSchema struct {
|
||
|
|
Runner SDKNamespace `json:"Runner"`
|
||
|
|
Events SDKHookTypes `json:"events"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// Event type definitions per hook (language-agnostic field names, language-specific in schema)
|
||
|
|
var hookEventFields = map[string][]SDKField{
|
||
|
|
"post.published": {
|
||
|
|
{Name: "post", Type: "Post", Doc: "The published post"},
|
||
|
|
{Name: "author", Type: "Author", Doc: "Post author"},
|
||
|
|
{Name: "blog", Type: "Blog", Doc: "Blog info"},
|
||
|
|
},
|
||
|
|
"post.updated": {
|
||
|
|
{Name: "post", Type: "Post", Doc: "The updated post"},
|
||
|
|
{Name: "author", Type: "Author", Doc: "Post author"},
|
||
|
|
{Name: "changes", Type: "Changes", Doc: "What changed"},
|
||
|
|
},
|
||
|
|
"comment.validate": {
|
||
|
|
{Name: "content", Type: "string", Doc: "Comment content"},
|
||
|
|
{Name: "authorName", Type: "string", Doc: "Author name"},
|
||
|
|
{Name: "authorEmail", Type: "string", Doc: "Author email"},
|
||
|
|
{Name: "postSlug", Type: "string", Doc: "Post slug"},
|
||
|
|
},
|
||
|
|
"comment.created": {
|
||
|
|
{Name: "comment", Type: "Comment", Doc: "The created comment"},
|
||
|
|
{Name: "post", Type: "Post", Doc: "The post"},
|
||
|
|
},
|
||
|
|
"member.subscribed": {
|
||
|
|
{Name: "member", Type: "Member", Doc: "The subscriber"},
|
||
|
|
{Name: "tier", Type: "Tier", Doc: "Subscription tier"},
|
||
|
|
},
|
||
|
|
"content.render": {
|
||
|
|
{Name: "html", Type: "string", Doc: "HTML content to transform"},
|
||
|
|
{Name: "post", Type: "Post", Doc: "Post metadata"},
|
||
|
|
},
|
||
|
|
"asset.uploaded": {
|
||
|
|
{Name: "id", Type: "string", Doc: "Asset ID"},
|
||
|
|
{Name: "url", Type: "string", Doc: "Asset URL"},
|
||
|
|
{Name: "contentType", Type: "string", Doc: "MIME type"},
|
||
|
|
{Name: "size", Type: "int", Doc: "Size in bytes"},
|
||
|
|
{Name: "width", Type: "int", Doc: "Image width (if image)"},
|
||
|
|
{Name: "height", Type: "int", Doc: "Image height (if image)"},
|
||
|
|
},
|
||
|
|
"analytics.sync": {
|
||
|
|
{Name: "period", Type: "Period", Doc: "Time period"},
|
||
|
|
{Name: "pageviews", Type: "int", Doc: "Total pageviews"},
|
||
|
|
{Name: "visitors", Type: "int", Doc: "Unique visitors"},
|
||
|
|
{Name: "topPages", Type: "[]PageView", Doc: "Top pages"},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
// Nested type definitions
|
||
|
|
var nestedTypes = map[string][]SDKField{
|
||
|
|
"Post": {
|
||
|
|
{Name: "slug", Type: "string"},
|
||
|
|
{Name: "title", Type: "string"},
|
||
|
|
{Name: "url", Type: "string"},
|
||
|
|
{Name: "excerpt", Type: "string"},
|
||
|
|
{Name: "publishedAt", Type: "string"},
|
||
|
|
{Name: "updatedAt", Type: "string"},
|
||
|
|
{Name: "tags", Type: "[]string"},
|
||
|
|
{Name: "readingTime", Type: "int"},
|
||
|
|
},
|
||
|
|
"Author": {
|
||
|
|
{Name: "name", Type: "string"},
|
||
|
|
{Name: "email", Type: "string"},
|
||
|
|
{Name: "avatar", Type: "string"},
|
||
|
|
},
|
||
|
|
"Blog": {
|
||
|
|
{Name: "name", Type: "string"},
|
||
|
|
{Name: "url", Type: "string"},
|
||
|
|
},
|
||
|
|
"Comment": {
|
||
|
|
{Name: "id", Type: "string"},
|
||
|
|
{Name: "content", Type: "string"},
|
||
|
|
{Name: "authorName", Type: "string"},
|
||
|
|
{Name: "authorEmail", Type: "string"},
|
||
|
|
{Name: "postSlug", Type: "string"},
|
||
|
|
{Name: "parentId", Type: "string"},
|
||
|
|
{Name: "createdAt", Type: "string"},
|
||
|
|
},
|
||
|
|
"Member": {
|
||
|
|
{Name: "email", Type: "string"},
|
||
|
|
{Name: "name", Type: "string"},
|
||
|
|
{Name: "subscribedAt", Type: "string"},
|
||
|
|
},
|
||
|
|
"Tier": {
|
||
|
|
{Name: "name", Type: "string"},
|
||
|
|
{Name: "price", Type: "int"},
|
||
|
|
},
|
||
|
|
"Changes": {
|
||
|
|
{Name: "title", Type: "TitleChange"},
|
||
|
|
{Name: "content", Type: "bool"},
|
||
|
|
{Name: "tags", Type: "TagChanges"},
|
||
|
|
},
|
||
|
|
"TitleChange": {
|
||
|
|
{Name: "old", Type: "string"},
|
||
|
|
{Name: "new", Type: "string"},
|
||
|
|
},
|
||
|
|
"TagChanges": {
|
||
|
|
{Name: "added", Type: "[]string"},
|
||
|
|
{Name: "removed", Type: "[]string"},
|
||
|
|
},
|
||
|
|
"Period": {
|
||
|
|
{Name: "start", Type: "string"},
|
||
|
|
{Name: "end", Type: "string"},
|
||
|
|
},
|
||
|
|
"PageView": {
|
||
|
|
{Name: "path", Type: "string"},
|
||
|
|
{Name: "views", Type: "int"},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
func buildEventTypes(lang string) SDKHookTypes {
|
||
|
|
events := make(SDKHookTypes)
|
||
|
|
for hook, fields := range hookEventFields {
|
||
|
|
// Convert field names for Go (PascalCase)
|
||
|
|
if lang == "go" {
|
||
|
|
converted := make([]SDKField, len(fields))
|
||
|
|
for i, f := range fields {
|
||
|
|
converted[i] = SDKField{
|
||
|
|
Name: toPascalCase(f.Name),
|
||
|
|
Type: f.Type,
|
||
|
|
Doc: f.Doc,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
events[hook] = SDKEventType{Fields: converted}
|
||
|
|
} else {
|
||
|
|
events[hook] = SDKEventType{Fields: fields}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return events
|
||
|
|
}
|
||
|
|
|
||
|
|
func toPascalCase(s string) string {
|
||
|
|
if len(s) == 0 {
|
||
|
|
return s
|
||
|
|
}
|
||
|
|
// Simple: just capitalize first letter
|
||
|
|
return string(s[0]-32) + s[1:]
|
||
|
|
}
|
||
|
|
|
||
|
|
// SDK schemas for each language
|
||
|
|
var sdkSchemas = map[string]SDKSchema{
|
||
|
|
"typescript": {
|
||
|
|
Runner: SDKNamespace{
|
||
|
|
Functions: []SDKFunction{
|
||
|
|
{
|
||
|
|
Name: "log",
|
||
|
|
Signature: "log(message: string): void",
|
||
|
|
InsertText: "log(${1:message})",
|
||
|
|
Documentation: "Log a message to the plugin console",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
Name: "httpRequest",
|
||
|
|
Signature: "httpRequest(options: HttpRequestOptions): HttpResponse",
|
||
|
|
InsertText: "httpRequest({\n url: ${1:url},\n method: \"${2:POST}\",\n headers: { \"Content-Type\": \"application/json\" },\n body: ${3:body}\n})",
|
||
|
|
Documentation: "Make an HTTP request to an external service",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
Events: buildEventTypes("typescript"),
|
||
|
|
},
|
||
|
|
"go": {
|
||
|
|
Runner: SDKNamespace{
|
||
|
|
Functions: []SDKFunction{
|
||
|
|
{
|
||
|
|
Name: "Log",
|
||
|
|
Signature: "Log(message string)",
|
||
|
|
InsertText: "Log(${1:message})",
|
||
|
|
Documentation: "Log a message to the plugin console",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
Name: "HttpRequest",
|
||
|
|
Signature: "HttpRequest(url string, method string, body []byte) (*Response, error)",
|
||
|
|
InsertText: "HttpRequest(${1:url}, \"${2:POST}\", []byte(${3:body}))",
|
||
|
|
Documentation: "Make an HTTP request to an external service",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
Events: buildEventTypes("go"),
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
// Also expose nested types
|
||
|
|
type FullSDKResponse struct {
|
||
|
|
Runner SDKNamespace `json:"Runner"`
|
||
|
|
Events SDKHookTypes `json:"events"`
|
||
|
|
NestedTypes map[string][]SDKField `json:"nestedTypes"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func sdkHandler(w http.ResponseWriter, r *http.Request) {
|
||
|
|
language := r.URL.Query().Get("language")
|
||
|
|
|
||
|
|
w.Header().Set("Content-Type", "application/json")
|
||
|
|
|
||
|
|
if language != "" {
|
||
|
|
if schema, ok := sdkSchemas[language]; ok {
|
||
|
|
// Convert nested types for the language
|
||
|
|
convertedNested := make(map[string][]SDKField)
|
||
|
|
for typeName, fields := range nestedTypes {
|
||
|
|
if language == "go" {
|
||
|
|
converted := make([]SDKField, len(fields))
|
||
|
|
for i, f := range fields {
|
||
|
|
converted[i] = SDKField{
|
||
|
|
Name: toPascalCase(f.Name),
|
||
|
|
Type: f.Type,
|
||
|
|
Doc: f.Doc,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
convertedNested[typeName] = converted
|
||
|
|
} else {
|
||
|
|
convertedNested[typeName] = fields
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
response := FullSDKResponse{
|
||
|
|
Runner: schema.Runner,
|
||
|
|
Events: schema.Events,
|
||
|
|
NestedTypes: convertedNested,
|
||
|
|
}
|
||
|
|
json.NewEncoder(w).Encode(response)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
http.Error(w, "Unknown language", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// Return all schemas (without nested types for brevity)
|
||
|
|
json.NewEncoder(w).Encode(sdkSchemas)
|
||
|
|
}
|