mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
ed9a7ccd5a | |||
9451848ad4 | |||
6c0145b149 | |||
a94092e31c | |||
52744dbcd0 | |||
cc11226eab | |||
8d073aaef2 | |||
d2f078443e | |||
c6e1651d9e | |||
630090e38a | |||
5394349c73 | |||
5cd3bf83a6 | |||
13cf911edf | |||
fe0f41cecb | |||
265080453a | |||
2f9b8fbcfe |
13
.github/workflows/linux-build-on-release.yml
vendored
13
.github/workflows/linux-build-on-release.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Build Wakapi on Linux
|
||||
name: Linux
|
||||
|
||||
on:
|
||||
push:
|
||||
@ -10,7 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build-and-release:
|
||||
name: Build
|
||||
name: Linux - Build, Test & Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
@ -24,8 +24,15 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Get dependencies
|
||||
run: go get
|
||||
|
||||
- name: Unit Tests
|
||||
run: go test ./... -run ./...
|
||||
|
||||
- name: API Tests
|
||||
run: |
|
||||
go get
|
||||
npm -g install newman
|
||||
./testing/run_api_tests.sh
|
||||
|
||||
- name: Build
|
||||
run: GO111MODULE=on go build -v .
|
||||
|
4
.github/workflows/win-build-on-release.yml
vendored
4
.github/workflows/win-build-on-release.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Build Wakapi on Windows
|
||||
name: Windows
|
||||
|
||||
on:
|
||||
push:
|
||||
@ -10,7 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build-and-release:
|
||||
name: Build
|
||||
name: Windows - Build & Release
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
|
||||
|
6
.gitpod.yml
Normal file
6
.gitpod.yml
Normal file
@ -0,0 +1,6 @@
|
||||
# List the start up tasks. Learn more https://www.gitpod.io/docs/config-start-tasks/
|
||||
tasks:
|
||||
- before: printf "\n[settings]\napi_key = $WAKA_TIME_API_KEY\napi_url = $WAKA_TIME_API_URL\n" > ~/.wakatime.cfg
|
||||
ports:
|
||||
- port: 3000
|
||||
visibility: public
|
21
README.md
21
README.md
@ -4,14 +4,11 @@
|
||||
|
||||
<p align="center">
|
||||
<img src="https://badges.fw-web.space/github/license/muety/wakapi">
|
||||
<a href="#-treeware"><img src="https://badges.fw-web.space:/treeware/trees/muety/wakapi?color=%234EC820&label=%F0%9F%8C%B3%20trees"></a>
|
||||
<a href="https://liberapay.com/muety/"><img src="https://badges.fw-web.space/liberapay/receives/muety.svg?logo=liberapay"></a>
|
||||
<img src="https://badges.fw-web.space/endpoint?url=https://wakapi.dev/api/compat/shields/v1/n1try/interval:any/project:wakapi&color=blue&label=wakapi">
|
||||
<img src="https://badges.fw-web.space/github/languages/code-size/muety/wakapi">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://goreportcard.com/report/github.com/muety/wakapi"><img src="https://goreportcard.com/badge/github.com/muety/wakapi"></a>
|
||||
<a href="https://sonarcloud.io/dashboard?id=muety_wakapi"><img src="https://sonarcloud.io/api/project_badges/measure?project=muety_wakapi&metric=sqale_index"></a>
|
||||
<a href="https://sonarcloud.io/dashboard?id=muety_wakapi"><img src="https://sonarcloud.io/api/project_badges/measure?project=muety_wakapi&metric=ncloc"></a>
|
||||
</p>
|
||||
|
||||
@ -39,7 +36,7 @@
|
||||
* [User Survey](#-user-survey)
|
||||
* [Features](#-features)
|
||||
* [Roadmap](#-roadmap)
|
||||
* [How to use](#-how-to-use)
|
||||
* [How to use](#%EF%B8%8F-how-to-use)
|
||||
* [Configuration Options](#-configuration-options)
|
||||
* [API Endpoints](#-api-endpoints)
|
||||
* [Integrations](#-integrations)
|
||||
@ -306,10 +303,7 @@ To get a predictable environment, tests are run against a fresh and clean Wakapi
|
||||
# 1. sqlite (cli)
|
||||
$ sudo apt install sqlite # Fedora: sudo dnf install sqlite
|
||||
|
||||
# 2. screen
|
||||
$ sudo apt install screen # Fedora: sudo dnf install screen
|
||||
|
||||
# 3. newman
|
||||
# 2. newman
|
||||
$ npm install -g newman
|
||||
```
|
||||
|
||||
@ -335,9 +329,6 @@ $ node scripts/bundle_icons.js
|
||||
|
||||
New icons can be added by editing the `icons` array in [scripts/bundle_icons.js](scripts/bundle_icons.js).
|
||||
|
||||
## 🙏 Support
|
||||
If you like this project, please consider supporting it 🙂. You can donate either through [buying me a coffee](https://buymeacoff.ee/n1try) or becoming a GitHub sponsor. Every little donation is highly appreciated and boosts the developers' motivation to keep improving Wakapi!
|
||||
|
||||
## ❔ FAQs
|
||||
Since Wakapi heavily relies on the concepts provided by WakaTime, [their FAQs](https://wakatime.com/faq) apply to Wakapi for large parts as well. You might find answers there.
|
||||
|
||||
@ -411,6 +402,12 @@ It is unclear how to handle the three minutes in between. Did the developer do a
|
||||
Wakapi adds a "padding" of two minutes before the third heartbeat. This is why total times will slightly vary between Wakapi and WakaTime.
|
||||
</details>
|
||||
|
||||
## 🌳 Treeware
|
||||
This package is [Treeware](https://treeware.earth). If you use it in production, then we ask that you [**buy the world a tree**](https://plant.treeware.earth/muety/wakapi) to thank us for our work. By contributing to the Treeware forest you’ll be creating employment for local families and restoring wildlife habitats.
|
||||
|
||||
## 👏 Support
|
||||
Coding in open source is my passion and I would love to do it on a full-time basis and make a living from it one day. So if you like this project, please consider supporting it 🙂. You can donate either through [buying me a coffee](https://buymeacoff.ee/n1try) or becoming a GitHub sponsor. Every little donation is highly appreciated and boosts my motivation to keep improving Wakapi!
|
||||
|
||||
## 🙏 Thanks
|
||||
I highly appreciate the efforts of **[@alanhamlett](https://github.com/alanhamlett)** and the WakaTime team and am thankful for their software being open source.
|
||||
|
||||
|
@ -39,6 +39,7 @@ security:
|
||||
cookie_max_age: 172800
|
||||
allow_signup: true
|
||||
expose_metrics: false
|
||||
enable_proxy: false # only intended for production instance at wakapi.dev
|
||||
|
||||
sentry:
|
||||
dsn: # leave blank to disable sentry integration
|
||||
|
@ -75,6 +75,7 @@ type appConfig struct {
|
||||
type securityConfig struct {
|
||||
AllowSignup bool `yaml:"allow_signup" default:"true" env:"WAKAPI_ALLOW_SIGNUP"`
|
||||
ExposeMetrics bool `yaml:"expose_metrics" default:"false" env:"WAKAPI_EXPOSE_METRICS"`
|
||||
EnableProxy bool `yaml:"enable_proxy" default:"false" env:"WAKAPI_ENABLE_PROXY"` // only intended for production instance at wakapi.dev
|
||||
// this is actually a pepper (https://en.wikipedia.org/wiki/Pepper_(cryptography))
|
||||
PasswordSalt string `yaml:"password_salt" default:"" env:"WAKAPI_PASSWORD_SALT"`
|
||||
InsecureCookies bool `yaml:"insecure_cookies" default:"false" env:"WAKAPI_INSECURE_COOKIES"`
|
||||
|
File diff suppressed because it is too large
Load Diff
5
main.go
5
main.go
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"embed"
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/muety/wakapi/routes/relay"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net"
|
||||
@ -191,6 +192,9 @@ func main() {
|
||||
loginHandler := routes.NewLoginHandler(userService, mailService)
|
||||
imprintHandler := routes.NewImprintHandler(keyValueService)
|
||||
|
||||
// Other Handlers
|
||||
relayHandler := relay.NewRelayHandler()
|
||||
|
||||
// Setup Routers
|
||||
router := mux.NewRouter()
|
||||
rootRouter := router.PathPrefix("/").Subrouter()
|
||||
@ -219,6 +223,7 @@ func main() {
|
||||
imprintHandler.RegisterRoutes(rootRouter)
|
||||
summaryHandler.RegisterRoutes(rootRouter)
|
||||
settingsHandler.RegisterRoutes(rootRouter)
|
||||
relayHandler.RegisterRoutes(rootRouter)
|
||||
|
||||
// API route registrations
|
||||
summaryApiHandler.RegisterRoutes(apiRouter)
|
||||
|
@ -1,12 +1,23 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
conf "github.com/muety/wakapi/config"
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/muety/wakapi/services"
|
||||
"github.com/muety/wakapi/utils"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// queryApiKey is the query parameter name for api key.
|
||||
queryApiKey = "api_key"
|
||||
)
|
||||
|
||||
var (
|
||||
errEmptyKey = fmt.Errorf("the api_key is empty")
|
||||
)
|
||||
|
||||
type AuthenticateMiddleware struct {
|
||||
@ -45,7 +56,10 @@ func (m *AuthenticateMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Reques
|
||||
user, err := m.tryGetUserByCookie(r)
|
||||
|
||||
if err != nil {
|
||||
user, err = m.tryGetUserByApiKey(r)
|
||||
user, err = m.tryGetUserByApiKeyHeader(r)
|
||||
}
|
||||
if err != nil {
|
||||
user, err = m.tryGetUserByApiKeyQuery(r)
|
||||
}
|
||||
|
||||
if err != nil || user == nil {
|
||||
@ -77,7 +91,7 @@ func (m *AuthenticateMiddleware) isOptional(requestPath string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *AuthenticateMiddleware) tryGetUserByApiKey(r *http.Request) (*models.User, error) {
|
||||
func (m *AuthenticateMiddleware) tryGetUserByApiKeyHeader(r *http.Request) (*models.User, error) {
|
||||
key, err := utils.ExtractBearerAuth(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -92,6 +106,20 @@ func (m *AuthenticateMiddleware) tryGetUserByApiKey(r *http.Request) (*models.Us
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (m *AuthenticateMiddleware) tryGetUserByApiKeyQuery(r *http.Request) (*models.User, error) {
|
||||
key := r.URL.Query().Get(queryApiKey)
|
||||
var user *models.User
|
||||
userKey := strings.TrimSpace(key)
|
||||
if userKey == "" {
|
||||
return nil, errEmptyKey
|
||||
}
|
||||
user, err := m.userSrvc.GetUserByKey(userKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (m *AuthenticateMiddleware) tryGetUserByCookie(r *http.Request) (*models.User, error) {
|
||||
username, err := utils.ExtractCookieAuth(r, m.config)
|
||||
if err != nil {
|
||||
|
@ -3,14 +3,16 @@ package middlewares
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/muety/wakapi/mocks"
|
||||
"github.com/muety/wakapi/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAuthenticateMiddleware_tryGetUserByApiKey_Success(t *testing.T) {
|
||||
func TestAuthenticateMiddleware_tryGetUserByApiKeyHeader_Success(t *testing.T) {
|
||||
testApiKey := "z5uig69cn9ut93n"
|
||||
testToken := base64.StdEncoding.EncodeToString([]byte(testApiKey))
|
||||
testUser := &models.User{ApiKey: testApiKey}
|
||||
@ -26,13 +28,13 @@ func TestAuthenticateMiddleware_tryGetUserByApiKey_Success(t *testing.T) {
|
||||
|
||||
sut := NewAuthenticateMiddleware(userServiceMock)
|
||||
|
||||
result, err := sut.tryGetUserByApiKey(mockRequest)
|
||||
result, err := sut.tryGetUserByApiKeyHeader(mockRequest)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, testUser, result)
|
||||
}
|
||||
|
||||
func TestAuthenticateMiddleware_tryGetUserByApiKey_InvalidHeader(t *testing.T) {
|
||||
func TestAuthenticateMiddleware_tryGetUserByApiKeyHeader_Invalid(t *testing.T) {
|
||||
testApiKey := "z5uig69cn9ut93n"
|
||||
testToken := base64.StdEncoding.EncodeToString([]byte(testApiKey))
|
||||
|
||||
@ -47,10 +49,55 @@ func TestAuthenticateMiddleware_tryGetUserByApiKey_InvalidHeader(t *testing.T) {
|
||||
|
||||
sut := NewAuthenticateMiddleware(userServiceMock)
|
||||
|
||||
result, err := sut.tryGetUserByApiKey(mockRequest)
|
||||
result, err := sut.tryGetUserByApiKeyHeader(mockRequest)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestAuthenticateMiddleware_tryGetUserByApiKeyQuery_Success(t *testing.T) {
|
||||
testApiKey := "z5uig69cn9ut93n"
|
||||
testUser := &models.User{ApiKey: testApiKey}
|
||||
|
||||
params := url.Values{}
|
||||
params.Add("api_key", testApiKey)
|
||||
mockRequest := &http.Request{
|
||||
URL: &url.URL{
|
||||
RawQuery: params.Encode(),
|
||||
},
|
||||
}
|
||||
|
||||
userServiceMock := new(mocks.UserServiceMock)
|
||||
userServiceMock.On("GetUserByKey", testApiKey).Return(testUser, nil)
|
||||
|
||||
sut := NewAuthenticateMiddleware(userServiceMock)
|
||||
|
||||
result, err := sut.tryGetUserByApiKeyQuery(mockRequest)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, testUser, result)
|
||||
}
|
||||
|
||||
func TestAuthenticateMiddleware_tryGetUserByApiKeyQuery_Invalid(t *testing.T) {
|
||||
testApiKey := "z5uig69cn9ut93n"
|
||||
|
||||
params := url.Values{}
|
||||
params.Add("token", testApiKey)
|
||||
mockRequest := &http.Request{
|
||||
URL: &url.URL{
|
||||
RawQuery: params.Encode(),
|
||||
},
|
||||
}
|
||||
|
||||
userServiceMock := new(mocks.UserServiceMock)
|
||||
|
||||
sut := NewAuthenticateMiddleware(userServiceMock)
|
||||
|
||||
result, actualErr := sut.tryGetUserByApiKeyQuery(mockRequest)
|
||||
|
||||
assert.Error(t, actualErr)
|
||||
assert.Equal(t, errEmptyKey, actualErr)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
// TODO: somehow test cookie auth function
|
||||
|
@ -24,6 +24,9 @@ func (r *AliasRepository) GetAll() ([]*models.Alias, error) {
|
||||
|
||||
func (r *AliasRepository) GetByUser(userId string) ([]*models.Alias, error) {
|
||||
var aliases []*models.Alias
|
||||
if userId == "" {
|
||||
return aliases, nil
|
||||
}
|
||||
if err := r.db.
|
||||
Where(&models.Alias{UserID: userId}).
|
||||
Find(&aliases).Error; err != nil {
|
||||
@ -34,6 +37,9 @@ func (r *AliasRepository) GetByUser(userId string) ([]*models.Alias, error) {
|
||||
|
||||
func (r *AliasRepository) GetByUserAndKey(userId, key string) ([]*models.Alias, error) {
|
||||
var aliases []*models.Alias
|
||||
if userId == "" {
|
||||
return aliases, nil
|
||||
}
|
||||
if err := r.db.
|
||||
Where(&models.Alias{
|
||||
UserID: userId,
|
||||
@ -47,6 +53,9 @@ func (r *AliasRepository) GetByUserAndKey(userId, key string) ([]*models.Alias,
|
||||
|
||||
func (r *AliasRepository) GetByUserAndKeyAndType(userId, key string, summaryType uint8) ([]*models.Alias, error) {
|
||||
var aliases []*models.Alias
|
||||
if userId == "" {
|
||||
return aliases, nil
|
||||
}
|
||||
if err := r.db.
|
||||
Where(&models.Alias{
|
||||
UserID: userId,
|
||||
@ -61,6 +70,9 @@ func (r *AliasRepository) GetByUserAndKeyAndType(userId, key string, summaryType
|
||||
|
||||
func (r *AliasRepository) GetByUserAndTypeAndValue(userId string, summaryType uint8, value string) (*models.Alias, error) {
|
||||
alias := &models.Alias{}
|
||||
if userId == "" {
|
||||
return nil, errors.New("invalid input")
|
||||
}
|
||||
if err := r.db.
|
||||
Where(&models.Alias{
|
||||
UserID: userId,
|
||||
|
@ -34,6 +34,9 @@ func (r *LanguageMappingRepository) GetById(id uint) (*models.LanguageMapping, e
|
||||
|
||||
func (r *LanguageMappingRepository) GetByUser(userId string) ([]*models.LanguageMapping, error) {
|
||||
var mappings []*models.LanguageMapping
|
||||
if userId == "" {
|
||||
return mappings, nil
|
||||
}
|
||||
if err := r.db.
|
||||
Where(&models.LanguageMapping{UserID: userId}).
|
||||
Find(&mappings).Error; err != nil {
|
||||
|
@ -33,6 +33,9 @@ func (r *ProjectLabelRepository) GetById(id uint) (*models.ProjectLabel, error)
|
||||
}
|
||||
|
||||
func (r *ProjectLabelRepository) GetByUser(userId string) ([]*models.ProjectLabel, error) {
|
||||
if userId == "" {
|
||||
return []*models.ProjectLabel{}, nil
|
||||
}
|
||||
var labels []*models.ProjectLabel
|
||||
if err := r.db.
|
||||
Where(&models.ProjectLabel{UserID: userId}).
|
||||
|
@ -35,6 +35,9 @@ func (r *UserRepository) GetByIds(userIds []string) ([]*models.User, error) {
|
||||
}
|
||||
|
||||
func (r *UserRepository) GetByApiKey(key string) (*models.User, error) {
|
||||
if key == "" {
|
||||
return nil, errors.New("invalid input")
|
||||
}
|
||||
u := &models.User{}
|
||||
if err := r.db.Where(&models.User{ApiKey: key}).First(u).Error; err != nil {
|
||||
return u, err
|
||||
|
75
routes/relay/relay.go
Normal file
75
routes/relay/relay.go
Normal file
@ -0,0 +1,75 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
conf "github.com/muety/wakapi/config"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const targetUrlHeader = "X-Target-URL"
|
||||
const pathMatcherPattern = `^/api/(heartbeat|heartbeats|summary|users|v1/users|compat/wakatime)`
|
||||
|
||||
type RelayHandler struct {
|
||||
config *conf.Config
|
||||
}
|
||||
|
||||
func NewRelayHandler() *RelayHandler {
|
||||
return &RelayHandler{
|
||||
config: conf.Get(),
|
||||
}
|
||||
}
|
||||
|
||||
type filteringMiddleware struct {
|
||||
handler http.Handler
|
||||
pathMatcher *regexp.Regexp
|
||||
}
|
||||
|
||||
func newFilteringMiddleware() func(http.Handler) http.Handler {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return &filteringMiddleware{
|
||||
handler: h,
|
||||
pathMatcher: regexp.MustCompile(pathMatcherPattern),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *filteringMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
targetUrl, err := url.Parse(r.Header.Get(targetUrlHeader))
|
||||
if err != nil || !m.pathMatcher.MatchString(targetUrl.Path) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
w.Write([]byte{})
|
||||
return
|
||||
}
|
||||
m.handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (h *RelayHandler) RegisterRoutes(router *mux.Router) {
|
||||
if !h.config.Security.EnableProxy {
|
||||
return
|
||||
}
|
||||
|
||||
r := router.PathPrefix("/relay").Subrouter()
|
||||
r.Use(newFilteringMiddleware())
|
||||
r.Path("").HandlerFunc(h.Any)
|
||||
}
|
||||
|
||||
func (h *RelayHandler) Any(w http.ResponseWriter, r *http.Request) {
|
||||
targetUrl, err := url.Parse(r.Header.Get(targetUrlHeader))
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte{})
|
||||
return
|
||||
}
|
||||
|
||||
p := httputil.ReverseProxy{
|
||||
Director: func(r *http.Request) {
|
||||
r.URL = targetUrl
|
||||
r.Host = targetUrl.Host
|
||||
},
|
||||
}
|
||||
|
||||
p.ServeHTTP(w, r)
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"info": {
|
||||
"_postman_id": "36595622-81dc-4f4a-826e-345ae63fc83b",
|
||||
"_postman_id": "da93a75e-e931-4f00-80b8-428f0e7ae824",
|
||||
"name": "Wakapi API Tests",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
@ -251,6 +251,105 @@
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Authenticate (header)",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"exec": [
|
||||
"pm.test(\"Status code is 200\", function () {",
|
||||
" pm.response.to.have.status(200);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"protocolProfileBehavior": {
|
||||
"disableCookies": true,
|
||||
"followRedirects": false
|
||||
},
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"bearer": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{WRITEUSER_TOKEN}}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{BASE_URL}}/api/summary?interval=today",
|
||||
"host": [
|
||||
"{{BASE_URL}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"summary"
|
||||
],
|
||||
"query": [
|
||||
{
|
||||
"key": "interval",
|
||||
"value": "today"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Authenticate (query param)",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"exec": [
|
||||
"pm.test(\"Status code is 200\", function () {",
|
||||
" pm.response.to.have.status(200);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"protocolProfileBehavior": {
|
||||
"disableCookies": true,
|
||||
"followRedirects": false
|
||||
},
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "noauth"
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{BASE_URL}}/api/summary?interval=today&api_key={{WRITEUSER_API_KEY}}",
|
||||
"host": [
|
||||
"{{BASE_URL}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"summary"
|
||||
],
|
||||
"query": [
|
||||
{
|
||||
"key": "interval",
|
||||
"value": "today"
|
||||
},
|
||||
{
|
||||
"key": "api_key",
|
||||
"value": "{{WRITEUSER_API_KEY}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1,9 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -f "wakapi" ]; then
|
||||
echo "Wakapi executable not found. Compiling."
|
||||
go build
|
||||
fi
|
||||
echo "Compiling."
|
||||
go build
|
||||
|
||||
if ! command -v newman &> /dev/null
|
||||
then
|
||||
@ -20,7 +18,8 @@ echo "Importing seed data ..."
|
||||
sqlite3 wakapi_testing.db < data.sql
|
||||
|
||||
echo "Running Wakapi testing instance in background ..."
|
||||
screen -S wakapi_testing -dm bash -c "../wakapi -config config.testing.yml"
|
||||
../wakapi -config config.testing.yml &
|
||||
pid=$!
|
||||
|
||||
echo "Waiting for Wakapi to come up ..."
|
||||
until $(curl --output /dev/null --silent --get --fail http://localhost:3000/api/health); do
|
||||
@ -32,9 +31,12 @@ echo ""
|
||||
|
||||
echo "Running test collection ..."
|
||||
newman run "Wakapi API Tests.postman_collection.json"
|
||||
exit_code=$?
|
||||
|
||||
echo "Shutting down Wakapi ..."
|
||||
screen -S wakapi_testing -X quit
|
||||
kill -TERM $pid
|
||||
|
||||
echo "Deleting database ..."
|
||||
rm wakapi_testing.db
|
||||
rm wakapi_testing.db
|
||||
|
||||
exit $exit_code
|
@ -1 +1 @@
|
||||
1.29.6
|
||||
1.30.0
|
||||
|
Reference in New Issue
Block a user