Skip to content

Commit

Permalink
🔥 Add run api (#870)
Browse files Browse the repository at this point in the history
* 🚧 checker api

* 🚧 single check

* 🚧 single check

* 🧪 fix test

* 🧪 fix tests

* 🧪 fix tests

* 🧪

* 🧪 fix tests

* 🧪 fix tests

* 🧪 add tests

* 🛂 pr

* 📝 format
  • Loading branch information
thibaultleouay authored Jun 13, 2024
1 parent 82ec6bf commit 5d867bb
Show file tree
Hide file tree
Showing 25 changed files with 3,026 additions and 60 deletions.
30 changes: 9 additions & 21 deletions apps/checker/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"github.com/openstatushq/openstatus/apps/checker/request"
"github.com/rs/zerolog/log"

unkey "github.com/WilfredAlmeida/unkey-go/features"
backoff "github.com/cenkalti/backoff/v4"
)

Expand Down Expand Up @@ -61,7 +60,7 @@ func main() {
router := gin.New()
router.POST("/checker", func(c *gin.Context) {
ctx := c.Request.Context()

dataSourceName := "ping_response__v8"
if c.GetHeader("Authorization") != fmt.Sprintf("Basic %s", cronSecret) {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return
Expand Down Expand Up @@ -112,7 +111,6 @@ func main() {
if err != nil {
// handle error
return fmt.Errorf("unable to unmarshal assertion: %w", err)

}
switch assert.AssertionType {
case request.AssertionHeader:
Expand Down Expand Up @@ -183,7 +181,7 @@ func main() {
})
}

if err := tinybirdClient.SendEvent(ctx, res); err != nil {
if err := tinybirdClient.SendEvent(ctx, res, dataSourceName); err != nil {
log.Ctx(ctx).Error().Err(err).Msg("failed to send event to tinybird")
}

Expand All @@ -202,7 +200,7 @@ func main() {
Error: 1,
Assertions: assertionAsString,
Body: "",
}); err != nil {
}, dataSourceName); err != nil {
log.Ctx(ctx).Error().Err(err).Msg("failed to send event to tinybird")
}

Expand All @@ -226,32 +224,18 @@ func main() {
})

router.POST("/ping/:region", func(c *gin.Context) {
dataSourceName := "check_response__v1"
region := c.Param("region")
if region == "" {
c.String(http.StatusBadRequest, "region is required")
return
}
fmt.Printf("Start of /ping/%s\n", region)

apiKey := c.GetHeader("x-openstatus-key")

if c.GetHeader("Authorization") != fmt.Sprintf("Basic %s", cronSecret) && apiKey == "" {
if c.GetHeader("Authorization") != fmt.Sprintf("Basic %s", cronSecret) {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return
}
if apiKey != "" {
response, err := unkey.KeyVerify(apiKey)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return
}

if !response.Valid {
fmt.Println("Key is not valid valid")
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})

}
}

if region != flyRegion {
c.Header("fly-replay", fmt.Sprintf("region=%s", region))
Expand All @@ -276,6 +260,10 @@ func main() {
if err != nil {
return fmt.Errorf("unable to ping: %w", err)
}
r.Region = flyRegion
if err := tinybirdClient.SendEvent(ctx, res, dataSourceName); err != nil {
log.Ctx(ctx).Error().Err(err).Msg("failed to send event to tinybird")
}
res = r
return nil
}
Expand Down
1 change: 1 addition & 0 deletions apps/checker/ping.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type Response struct {
Timing Timing `json:"timing"`
Error string `json:"error,omitempty"`
Tags []string `json:"tags,omitempty"`
Region string `json:"region"`
}

func Ping(ctx context.Context, client *http.Client, inputData request.CheckerRequest) (PingData, error) {
Expand Down
6 changes: 3 additions & 3 deletions apps/checker/pkg/tinybird/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
const baseURL = "https://api.tinybird.co/v0/events"

type Client interface {
SendEvent(ctx context.Context, event any) error
SendEvent(ctx context.Context, event any, dataSourceName string) error
}

type client struct {
Expand All @@ -29,15 +29,15 @@ func NewClient(httpClient *http.Client, apiKey string) Client {
}
}

func (c client) SendEvent(ctx context.Context, event any) error {
func (c client) SendEvent(ctx context.Context, event any, dataSourceName string) error {
requestURL, err := url.Parse(baseURL)
if err != nil {
log.Ctx(ctx).Error().Err(err).Msg("unable to parse url")
return fmt.Errorf("unable to parse url: %w", err)
}

q := requestURL.Query()
q.Add("name", "ping_response__v8")
q.Add("name", dataSourceName)
requestURL.RawQuery = q.Encode()

var payload bytes.Buffer
Expand Down
8 changes: 4 additions & 4 deletions apps/checker/pkg/tinybird/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestSendEvent(t *testing.T) {

client := tinybird.NewClient(interceptor.GetHTTPClient(), "apiKey")

err := client.SendEvent(ctx, "event")
err := client.SendEvent(ctx, "event", "test")
require.Error(t, err)
})

Expand All @@ -54,7 +54,7 @@ func TestSendEvent(t *testing.T) {

client := tinybird.NewClient(interceptor.GetHTTPClient(), "apiKey")

err := client.SendEvent(ctx, "event")
err := client.SendEvent(ctx, "event", "test")
require.Error(t, err)
})

Expand All @@ -71,8 +71,8 @@ func TestSendEvent(t *testing.T) {

client := tinybird.NewClient(interceptor.GetHTTPClient(), "apiKey")

err := client.SendEvent(ctx, "event")
err := client.SendEvent(ctx, "event", "test")
require.NoError(t, err)
require.Equal(t, "https://api.tinybird.co/v0/events?name=ping_response__v8", url)
require.Equal(t, "https://api.tinybird.co/v0/events?name=test", url)
})
}
1 change: 1 addition & 0 deletions apps/checker/request/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type CheckerRequest struct {
}

type PingRequest struct {
ID string `json:"id"`
URL string `json:"url"`
Method string `json:"method"`
Body string `json:"body"`
Expand Down
7 changes: 4 additions & 3 deletions apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
},
"dependencies": {
"@hono/sentry": "1.1.0",
"@hono/zod-openapi": "0.13.0",
"@hono/zod-validator": "0.2.1",
"@hono/zod-openapi": "0.14.2",
"@hono/zod-validator": "0.2.2",
"@openstatus/analytics": "workspace:^",
"@openstatus/db": "workspace:*",
"@openstatus/emails": "workspace:*",
Expand All @@ -29,8 +29,9 @@
"@t3-oss/env-core": "0.7.1",
"@unkey/api": "0.16.0",
"@upstash/qstash": "2.1.8",
"hono": "4.0.0",
"hono": "4.4.5",
"nanoid": "5.0.2",
"percentile": "^1.6.0",
"validator": "13.11.0",
"zod": "3.22.4"
},
Expand Down
14 changes: 14 additions & 0 deletions apps/server/src/v1/check/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { OpenAPIHono } from "@hono/zod-openapi";

import type { Variables } from "../index";

import { handleZodError } from "../../libs/errors";
import { registerPostCheck } from "./post";

const checkAPI = new OpenAPIHono<{ Variables: Variables }>({
defaultHook: handleZodError,
});

registerPostCheck(checkAPI);

export { checkAPI };
152 changes: 152 additions & 0 deletions apps/server/src/v1/check/post.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { expect, test } from "bun:test";

import { api } from "../index";

import { afterEach, mock } from "bun:test";

const mockFetch = mock();

global.fetch = mockFetch;
mock.module("node-fetch", () => mockFetch);

afterEach(() => {
mockFetch.mockReset();
});

test("Create a single check ", async () => {
const data = {
url: "https://www.openstatus.dev",
regions: ["ams"],
method: "POST",
body: '{"hello":"world"}',
headers: [{ key: "key", value: "value" }],
};
mockFetch.mockReturnValue(
Promise.resolve(
new Response(
'{"status":200,"latency":100,"body":"Hello World","headers":{"Content-Type":"application/json"},"timestamp":1234567890,"timing":{"dnsStart":1,"dnsDone":2,"connectStart":3,"connectDone":4,"tlsHandshakeStart":5,"tlsHandshakeDone":6,"firstByteStart":7,"firstByteDone":8,"transferStart":9,"transferDone":10},"region":"ams"}',
{ status: 200, headers: { "content-type": "application/json" } },
),
),
);

const res = await api.request("/check", {
method: "POST",
headers: {
"x-openstatus-key": "1",
"content-type": "application/json",
},
body: JSON.stringify(data),
});

expect(res.status).toBe(200);

expect(await res.json()).toMatchObject({
id: expect.any(Number),
raw: [
{
connectDone: 4,
connectStart: 3,
dnsDone: 2,
dnsStart: 1,
firstByteDone: 8,
firstByteStart: 7,
tlsHandshakeDone: 6,
tlsHandshakeStart: 5,
transferDone: 10,
transferStart: 9,
},
],
response: {
body: "Hello World",
headers: {
"Content-Type": "application/json",
},
latency: 100,
region: "ams",
status: 200,
timestamp: 1234567890,
timing: {
connectDone: 4,
connectStart: 3,
dnsDone: 2,
dnsStart: 1,
firstByteDone: 8,
firstByteStart: 7,
tlsHandshakeDone: 6,
tlsHandshakeStart: 5,
transferDone: 10,
transferStart: 9,
},
},
});
});

test.todo("Create a multiple check ", async () => {
const data = {
url: "https://www.openstatus.dev",
regions: ["ams", "gru"],
method: "POST",
body: '{"hello":"world"}',
headers: [{ key: "key", value: "value" }],
};
mockFetch.mockReturnValue(
Promise.resolve(
new Response(
'{"status":200,"latency":100,"body":"Hello World","headers":{"Content-Type":"application/json"},"timestamp":1234567890,"timing":{"dnsStart":1,"dnsDone":2,"connectStart":3,"connectDone":4,"tlsHandshakeStart":5,"tlsHandshakeDone":6,"firstByteStart":7,"firstByteDone":8,"transferStart":9,"transferDone":10},"region":"ams"}',
{ status: 200, headers: { "content-type": "application/json" } },
),
),
);

const res = await api.request("/check", {
method: "POST",
headers: {
"x-openstatus-key": "1",
"content-type": "application/json",
},
body: JSON.stringify(data),
});

expect(res.status).toBe(200);

expect(await res.json()).toMatchObject({
id: expect.any(Number),
raw: [
{
connectDone: 4,
connectStart: 3,
dnsDone: 2,
dnsStart: 1,
firstByteDone: 8,
firstByteStart: 7,
tlsHandshakeDone: 6,
tlsHandshakeStart: 5,
transferDone: 10,
transferStart: 9,
},
],
response: {
body: "Hello World",
headers: {
"Content-Type": "application/json",
},
latency: 100,
region: "ams",
status: 200,
timestamp: 1234567890,
timing: {
connectDone: 4,
connectStart: 3,
dnsDone: 2,
dnsStart: 1,
firstByteDone: 8,
firstByteStart: 7,
tlsHandshakeDone: 6,
tlsHandshakeStart: 5,
transferDone: 10,
transferStart: 9,
},
},
});
});
Loading

0 comments on commit 5d867bb

Please sign in to comment.