mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
7a339a1ffe
This is the second PR for moving connection events out of the audit log.
This PR:
- Adds the `/api/v2/connectionlog` endpoint
- Adds filtering for `GetAuthorizedConnectionLogsOffset` and thus the endpoint.
There's quite a few, but I was aiming for feature parity with the audit log.
1. `organization:<id|name>`
2. `workspace_owner:<username>`
3. `workspace_owner_email:<email>`
4. `type:<ssh|vscode|jetbrains|reconnecting_pty|workspace_app|port_forwarding>`
5. `username:<username>`
- Only includes web-based connection events (workspace apps, web port forwarding) as only those include user metadata.
6. `user_email:<email>`
7. `connected_after:<time>`
8. `connected_before:<time>`
9. `workspace_id:<id>`
10. `connection_id:<id>`
- If you have one snapshot of the connection log, and some sessions are ongoing in that snapshot, you could use this filter to check if they've been closed since.
11. `status:<connected|disconnected>`
- If `connected` only sessions with a null `close_time` are returned, if `disconnected`, only those with a non-null `close_time`. If filter is omitted, both are returned.
Future PRs:
- Populate `count` on `ConnectionLogResponse` using a seperate query (to preemptively mitigate the issue described in #17689)
- Implement a table in the Web UI for viewing connection logs.
- Write a query to delete old events from the audit log, call it from dbpurge.
- Write documentation for the endpoint / feature (including these filters)
142 lines
3.2 KiB
Go
142 lines
3.2 KiB
Go
package coderd_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/coder/coder/v2/coderd"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
)
|
|
|
|
func TestPagination(t *testing.T) {
|
|
t.Parallel()
|
|
const invalidValues = "Query parameters have invalid values"
|
|
testCases := []struct {
|
|
Name string
|
|
|
|
AfterID string
|
|
Limit string
|
|
Offset string
|
|
|
|
ExpectedError string
|
|
ExpectedParams codersdk.Pagination
|
|
}{
|
|
{
|
|
Name: "BadAfterID",
|
|
AfterID: "bogus",
|
|
ExpectedError: invalidValues,
|
|
},
|
|
{
|
|
Name: "ShortAfterID",
|
|
AfterID: "ff22a7b-bb6f-43d8-83e1-eefe0a1f5197",
|
|
ExpectedError: invalidValues,
|
|
},
|
|
{
|
|
Name: "LongAfterID",
|
|
AfterID: "cff22a7b-bb6f-43d8-83e1-eefe0a1f51972",
|
|
ExpectedError: invalidValues,
|
|
},
|
|
{
|
|
Name: "BadLimit",
|
|
Limit: "bogus",
|
|
ExpectedError: invalidValues,
|
|
},
|
|
{
|
|
Name: "TooHighLimit",
|
|
Limit: "2147483648",
|
|
ExpectedError: invalidValues,
|
|
},
|
|
{
|
|
Name: "NegativeLimit",
|
|
Limit: "-1",
|
|
ExpectedError: invalidValues,
|
|
},
|
|
{
|
|
Name: "BadOffset",
|
|
Offset: "bogus",
|
|
ExpectedError: invalidValues,
|
|
},
|
|
{
|
|
Name: "TooHighOffset",
|
|
Offset: "2147483648",
|
|
ExpectedError: invalidValues,
|
|
},
|
|
{
|
|
Name: "NegativeOffset",
|
|
Offset: "-1",
|
|
ExpectedError: invalidValues,
|
|
},
|
|
|
|
// Valid values
|
|
{
|
|
Name: "ValidAllParams",
|
|
AfterID: "d6c1c331-bfc8-44ef-a0d2-d2294be6195a",
|
|
Offset: "100",
|
|
Limit: "50",
|
|
ExpectedParams: codersdk.Pagination{
|
|
AfterID: uuid.MustParse("d6c1c331-bfc8-44ef-a0d2-d2294be6195a"),
|
|
Limit: 50,
|
|
Offset: 100,
|
|
},
|
|
},
|
|
{
|
|
Name: "ValidLimit",
|
|
Limit: "50",
|
|
ExpectedParams: codersdk.Pagination{
|
|
AfterID: uuid.Nil,
|
|
Limit: 50,
|
|
},
|
|
},
|
|
{
|
|
Name: "ValidOffset",
|
|
Offset: "150",
|
|
ExpectedParams: codersdk.Pagination{
|
|
AfterID: uuid.Nil,
|
|
Offset: 150,
|
|
},
|
|
},
|
|
{
|
|
Name: "ValidAfterID",
|
|
AfterID: "5f2005fc-acc4-4e5e-a7fa-be017359c60b",
|
|
ExpectedParams: codersdk.Pagination{
|
|
AfterID: uuid.MustParse("5f2005fc-acc4-4e5e-a7fa-be017359c60b"),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, c := range testCases {
|
|
t.Run(c.Name, func(t *testing.T) {
|
|
t.Parallel()
|
|
rw := httptest.NewRecorder()
|
|
r, err := http.NewRequestWithContext(context.Background(), "GET", "https://example.com", nil)
|
|
require.NoError(t, err, "new request")
|
|
|
|
// Set query params
|
|
query := r.URL.Query()
|
|
query.Set("after_id", c.AfterID)
|
|
query.Set("limit", c.Limit)
|
|
query.Set("offset", c.Offset)
|
|
r.URL.RawQuery = query.Encode()
|
|
|
|
params, ok := coderd.ParsePagination(rw, r)
|
|
if c.ExpectedError == "" {
|
|
require.True(t, ok, "expect ok")
|
|
require.Equal(t, c.ExpectedParams, params, "expected params")
|
|
} else {
|
|
require.False(t, ok, "expect !ok")
|
|
require.Equal(t, http.StatusBadRequest, rw.Code, "bad request status code")
|
|
var apiError codersdk.Error
|
|
err := json.NewDecoder(rw.Body).Decode(&apiError)
|
|
require.NoError(t, err, "decode response")
|
|
require.Contains(t, apiError.Message, c.ExpectedError, "expected error")
|
|
}
|
|
})
|
|
}
|
|
}
|