Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
Expand Down
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

FROM golang:1.18-alpine AS builder

RUN apk add --no-cache --update go gcc g++

RUN mkdir /app
ADD . /app
WORKDIR /app

RUN CGO_ENABLED=0 GOOS=linux go build -o app cmd/server/main.go
RUN CGO_ENABLED=1 GOOS=linux go build -o app cmd/server/main.go
# RUN go install github.com/cosmtrek/air@latest
# RUN curl -sSfL https://raw.githubusercontent.com/cosmtrek/air/master/install.sh | sh -s

Expand Down
4 changes: 4 additions & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/nexentra/inteligpt/pkg/auth"
"github.com/nexentra/inteligpt/pkg/comments"
"github.com/nexentra/inteligpt/pkg/common/db"
"github.com/nexentra/inteligpt/middlware/jsight"
"github.com/spf13/viper"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
Expand Down Expand Up @@ -65,6 +66,9 @@ func setupRouter() *gin.Engine {
// fmt.Println(dsn)

r := gin.Default()

r.Use(jsight.Validator())

h := db.InitDatabase(dbUrl)

comments.RegisterRoutes(r, h)
Expand Down
108 changes: 108 additions & 0 deletions jsight/api-spec.jst
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
JSIGHT 0.3

INFO
Title "CloudNua: Comment Service"
Description
This is a simple CRUD HTTP service.

[Terms of service](http://swagger.io/terms/)
[CloudNua API Support - Website](http://www.swagger.io/support)
[Send email to CloudNua API Support](mailto:support@cloudnua.io)
[Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
Version 1.0

SERVER @local
BaseUrl "localhost:8080"

GET /ping
200
{
"message": "pong"
}

#------------------------ COMMENTS ---------------------------

GET /api/v1/comments
200
[@comment]
404
@error

POST /api/v1/comments
Request
@comment
201 // Created
@comment
400 // Bad request
@error
404 // Not Found
@error

URL /api/v1/comments/{id}
Path
{
"id": 123 // Comment ID
}

GET /api/v1/comments/{id} // Get specific comment
200
[@comment]
404 // Not found
@error

PUT /api/v1/comments/{id} // Update a comment
Request
@comment
200
@comment
400 // Bad request
@error
404 // Not Found
@error

DELETE /api/v1/comments/{id} // Delete a comment
200
@comment
400 // Bad request
@error
404 // Not Found
@error

#------------------------ COMMON --------------------------
GET /
200 any

GET /jsight // Getting service API specification
200 any
#------------------------ TYPES ---------------------------

TYPE @comment
{
"author": "johnsmith",
"slug" : "my-comment",
"title" : "My awesome comment"
}

TYPE @error
@appError | @validationError

TYPE @appError
{
"code": 400, // Http status code
"message": "status not found"
}

TYPE @validationError
{
"reportedBy": "HTTP Request validation",
"type": "http_body_error",
"code": 32001,
"title": "HTTP body error",
"detail": "Schema does not support key \"title1\"",
"position": { // {optional: true}
"filepath": "api-spec.jst", // {optional: true}
"index": 58, // {optional: true}
"line": 4, // {optional: true}
"col": 3 // {optional: true}
}
}
174 changes: 174 additions & 0 deletions middlware/jsight/jsight.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package jsight

import (
"plugin"
)

//------------------------- interfaces --------------------------------

func NewJSight(pluginPath string) JSight {
return newjsightPlugin(pluginPath)
}

type JSight interface {
ValidateHTTPRequest(apiSpecFilePath, requestMethod, requestURI string, requestHeaders map[string][]string, requestBody []byte) JSightValidationError
ValidateHTTPResponse(apiSpecFilePath, requestMethod, requestURI string, responseStatusCode int, responseHeaders map[string][]string, responseBody []byte) JSightValidationError
ClearCache()
Stat() string
}

type JSightValidationError interface {
ReportedBy() string
Type() string
Code() int
Title() string
Detail() string
Position() JSightPosition
Trace() []string
ToJSON() string
}

type JSightPosition interface {
Filepath() string
Index() int
Line() int
Col() int
}

//-------------------------- implementation ---------------------------

type jsightPlugin struct {
validateHTTPRequestSymbol func(
apiSpecFilePath, requestMethod, requestURI string,
requestHeaders map[string][]string,
requestBody []byte,
) error
validateHTTPResponseSymbol func(
apiSpecFilePath, requestMethod, requestURI string,
responseStatusCode int,
responseHeaders map[string][]string,
responseBody []byte,
) error
clearCacheSymbol func()
statSymbol func() string
}

type jsightValidationErrorStruct struct {
e jsightPluginValidationError
}

type jsightPluginValidationError interface {
ReportedBy() string
Type() string
Code() int
Title() string
Detail() string
Position() any
Trace() []string
ToJSON() string
}

func newjsightPlugin(pluginPath string) JSight {
j := jsightPlugin{}

p, err := plugin.Open(pluginPath)
if err != nil {
panic(err)
}

s, err := p.Lookup("JSightValidateHTTPRequest")
if err != nil {
panic(err)
}
j.validateHTTPRequestSymbol = s.(func(string, string, string, map[string][]string, []byte) error)

s, err = p.Lookup("JSightValidateHTTPResponse")
if err != nil {
panic(err)
}
j.validateHTTPResponseSymbol = s.(func(string, string, string, int, map[string][]string, []byte) error)

s, err = p.Lookup("JSightClearCache")
if err != nil {
panic(err)
}
j.clearCacheSymbol = s.(func())

s, err = p.Lookup("JSightStat")
if err != nil {
panic(err)
}
j.statSymbol = s.(func() string)

return j
}

func (j jsightPlugin) ValidateHTTPRequest(
apiSpecFilePath, requestMethod, requestURI string,
requestHeaders map[string][]string,
requestBody []byte) JSightValidationError {
e := j.validateHTTPRequestSymbol(apiSpecFilePath, requestMethod, requestURI, requestHeaders, requestBody)
if e == nil {
return nil
}
return newjsightValidationErrorStruct(e.(jsightPluginValidationError))
}

func (j jsightPlugin) ValidateHTTPResponse(
apiSpecFilePath, requestMethod, requestURI string,
responseCode int,
responseHeaders map[string][]string,
responseBody []byte) JSightValidationError {
e := j.validateHTTPResponseSymbol(apiSpecFilePath, requestMethod, requestURI, responseCode, responseHeaders, responseBody)
if e == nil {
return nil
}
return newjsightValidationErrorStruct(e.(jsightPluginValidationError))
}

func (j jsightPlugin) ClearCache() {
j.clearCacheSymbol()
}

func (j jsightPlugin) Stat() string {
return j.statSymbol()
}

func newjsightValidationErrorStruct(e jsightPluginValidationError) JSightValidationError {
return jsightValidationErrorStruct{e: e}
}

func (j jsightValidationErrorStruct) ReportedBy() string {
return j.e.ReportedBy()
}

func (j jsightValidationErrorStruct) Type() string {
return j.e.Type()
}

func (j jsightValidationErrorStruct) Code() int {
return j.e.Code()
}

func (j jsightValidationErrorStruct) Title() string {
return j.e.Title()
}

func (j jsightValidationErrorStruct) Detail() string {
return j.e.Detail()
}

func (j jsightValidationErrorStruct) Position() JSightPosition {
if j.e.Position() == nil {
return nil
}
return j.e.Position().(JSightPosition)
}

func (j jsightValidationErrorStruct) Trace() []string {
return j.e.Trace()
}

func (j jsightValidationErrorStruct) ToJSON() string {
return j.e.ToJSON()
}
Loading