mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-05-11 02:32:39 +00:00
Compare commits
12 commits
4c4ce2ef74
...
5fbb304080
Author | SHA1 | Date | |
---|---|---|---|
5fbb304080 | |||
2d66cfcce2 | |||
ae1cfd2ecd | |||
b3f163f15c | |||
dfb59b92b9 | |||
5ee4a0f7d3 | |||
ed9be3e7c8 | |||
3880844a79 | |||
d316e3c5cb | |||
db5a384390 | |||
8ce3461289 | |||
cacb16c0f1 |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -13,7 +13,7 @@
|
|||
*.so
|
||||
*.dylib
|
||||
vendor/
|
||||
__debug_bin
|
||||
__debug_bin*
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
</p>
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="https://ci.woodpecker-ci.org/repos/3780" title="Build Status">
|
||||
<img src="https://ci.woodpecker-ci.org/api/badges/3780/status.svg" alt="Build Status">
|
||||
<a href="https://ci.woodpecker-ci.org/repos/3780" title="Pipeline Status">
|
||||
<img src="https://ci.woodpecker-ci.org/api/badges/3780/status.svg" alt="Pipeline Status">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/woodpecker-ci/woodpecker">
|
||||
<img src="https://codecov.io/gh/woodpecker-ci/woodpecker/branch/main/graph/badge.svg" alt="Code coverage">
|
||||
|
|
|
@ -169,12 +169,17 @@ func apiRoutes(e *gin.RouterGroup) {
|
|||
queue.GET("/norunningpipelines", api.BlockTilQueueHasRunningItem)
|
||||
}
|
||||
|
||||
// global secrets can be read without actual values by any user
|
||||
readGlobalSecrets := apiBase.Group("/secrets")
|
||||
{
|
||||
readGlobalSecrets.Use(session.MustUser())
|
||||
readGlobalSecrets.GET("", api.GetGlobalSecretList)
|
||||
readGlobalSecrets.GET("/:secret", api.GetGlobalSecret)
|
||||
}
|
||||
secrets := apiBase.Group("/secrets")
|
||||
{
|
||||
secrets.Use(session.MustAdmin())
|
||||
secrets.GET("", api.GetGlobalSecretList)
|
||||
secrets.POST("", api.PostGlobalSecret)
|
||||
secrets.GET("/:secret", api.GetGlobalSecret)
|
||||
secrets.PATCH("/:secret", api.PatchGlobalSecret)
|
||||
secrets.DELETE("/:secret", api.DeleteGlobalSecret)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"format": "prettier --write .",
|
||||
"format:check": "prettier -c .",
|
||||
"typecheck": "vue-tsc --noEmit",
|
||||
"test": "echo 'No tests configured' && exit 0"
|
||||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||
|
@ -44,6 +44,7 @@
|
|||
"@typescript-eslint/parser": "^7.0.0",
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"@vue/compiler-sfc": "^3.4.15",
|
||||
"@vue/test-utils": "^2.4.5",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||
|
@ -54,6 +55,7 @@
|
|||
"eslint-plugin-simple-import-sort": "^12.0.0",
|
||||
"eslint-plugin-vue": "^9.20.1",
|
||||
"eslint-plugin-vue-scoped-css": "^2.7.2",
|
||||
"jsdom": "^24.0.0",
|
||||
"prettier": "^3.2.4",
|
||||
"replace-in-file": "^7.1.0",
|
||||
"tinycolor2": "^1.6.0",
|
||||
|
@ -64,6 +66,7 @@
|
|||
"vite-plugin-prismjs": "^0.0.11",
|
||||
"vite-plugin-windicss": "^1.9.3",
|
||||
"vite-svg-loader": "^5.1.0",
|
||||
"vitest": "^1.5.0",
|
||||
"vue-eslint-parser": "^9.4.0",
|
||||
"vue-tsc": "^2.0.0",
|
||||
"windicss": "^3.5.6"
|
||||
|
|
6033
web/pnpm-lock.yaml
6033
web/pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -83,6 +83,7 @@ async function loadSecrets(page: number, level: 'repo' | 'org' | 'global'): Prom
|
|||
|
||||
const { resetPage, data: _secrets } = usePagination(loadSecrets, () => !selectedSecret.value, {
|
||||
each: ['repo', 'org', 'global'],
|
||||
pageSize: 50,
|
||||
});
|
||||
const secrets = computed(() => {
|
||||
const secretsList: Record<string, Secret & { edit?: boolean; level: 'repo' | 'org' | 'global' }> = {};
|
||||
|
|
155
web/src/compositions/usePaginate.test.ts
Normal file
155
web/src/compositions/usePaginate.test.ts
Normal file
|
@ -0,0 +1,155 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { type Ref, watch } from 'vue';
|
||||
|
||||
import { usePagination } from './usePaginate';
|
||||
|
||||
async function waitForState<T>(ref: Ref<T>, expected: T): Promise<void> {
|
||||
await new Promise<void>((resolve) => {
|
||||
watch(
|
||||
ref,
|
||||
(value) => {
|
||||
if (value === expected) {
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
export const mountComposition = (cb: () => void) => {
|
||||
const wrapper = shallowMount({
|
||||
setup() {
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
cb();
|
||||
return {};
|
||||
},
|
||||
template: '<div />',
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
describe('usePaginate', () => {
|
||||
const repoSecrets = [
|
||||
[{ name: 'repo1' }, { name: 'repo2' }, { name: 'repo3' }],
|
||||
[{ name: 'repo4' }, { name: 'repo5' }, { name: 'repo6' }],
|
||||
];
|
||||
const orgSecrets = [
|
||||
[{ name: 'org1' }, { name: 'org2' }, { name: 'org3' }],
|
||||
[{ name: 'org4' }, { name: 'org5' }, { name: 'org6' }],
|
||||
];
|
||||
|
||||
it('should get first page', async () => {
|
||||
let usePaginationComposition = null as unknown as ReturnType<typeof usePagination>;
|
||||
mountComposition(() => {
|
||||
usePaginationComposition = usePagination<{ name: string }>(
|
||||
async (page) => repoSecrets[page - 1],
|
||||
() => true,
|
||||
);
|
||||
});
|
||||
await waitForState(usePaginationComposition.loading, true);
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
expect(usePaginationComposition.data.value.length).toBe(3);
|
||||
expect(usePaginationComposition.data.value[0]).toStrictEqual(repoSecrets[0][0]);
|
||||
});
|
||||
|
||||
it('should get first & second page', async () => {
|
||||
let usePaginationComposition = null as unknown as ReturnType<typeof usePagination>;
|
||||
mountComposition(() => {
|
||||
usePaginationComposition = usePagination<{ name: string }>(
|
||||
async (page) => repoSecrets[page - 1],
|
||||
() => true,
|
||||
);
|
||||
});
|
||||
await waitForState(usePaginationComposition.loading, true);
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
usePaginationComposition.nextPage();
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
expect(usePaginationComposition.data.value.length).toBe(6);
|
||||
expect(usePaginationComposition.data.value.at(-1)).toStrictEqual(repoSecrets[1][2]);
|
||||
});
|
||||
|
||||
it('should get first page for each category', async () => {
|
||||
let usePaginationComposition = null as unknown as ReturnType<typeof usePagination>;
|
||||
mountComposition(() => {
|
||||
usePaginationComposition = usePagination<{ name: string }>(
|
||||
async (page, level) => {
|
||||
if (level === 'repo') {
|
||||
return repoSecrets[page - 1];
|
||||
}
|
||||
return orgSecrets[page - 1];
|
||||
},
|
||||
() => true,
|
||||
{ each: ['repo', 'org'] },
|
||||
);
|
||||
});
|
||||
await waitForState(usePaginationComposition.loading, true);
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
usePaginationComposition.nextPage();
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
usePaginationComposition.nextPage();
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
usePaginationComposition.nextPage();
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
console.log(usePaginationComposition.data.value);
|
||||
|
||||
expect(usePaginationComposition.data.value.length).toBe(9);
|
||||
expect(usePaginationComposition.data.value.at(-1)).toStrictEqual(orgSecrets[0][2]);
|
||||
});
|
||||
|
||||
it('should reset page and get first page again', async () => {
|
||||
let usePaginationComposition = null as unknown as ReturnType<typeof usePagination>;
|
||||
mountComposition(() => {
|
||||
usePaginationComposition = usePagination<{ name: string }>(
|
||||
async (page) => repoSecrets[page - 1],
|
||||
() => true,
|
||||
);
|
||||
});
|
||||
await waitForState(usePaginationComposition.loading, true);
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
usePaginationComposition.nextPage();
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
usePaginationComposition.resetPage();
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
expect(usePaginationComposition.data.value.length).toBe(3);
|
||||
expect(usePaginationComposition.data.value[0]).toStrictEqual(repoSecrets[0][0]);
|
||||
});
|
||||
|
||||
it('should not hasMore when no data is left', async () => {
|
||||
let usePaginationComposition = null as unknown as ReturnType<typeof usePagination>;
|
||||
mountComposition(() => {
|
||||
usePaginationComposition = usePagination<{ name: string }>(
|
||||
async (page) => repoSecrets[page - 1],
|
||||
() => true,
|
||||
);
|
||||
});
|
||||
await waitForState(usePaginationComposition.loading, true);
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
|
||||
expect(usePaginationComposition.hasMore.value).toBe(true);
|
||||
expect(usePaginationComposition.data.value.length).toBe(3);
|
||||
|
||||
usePaginationComposition.nextPage();
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
expect(usePaginationComposition.hasMore.value).toBe(true);
|
||||
expect(usePaginationComposition.data.value.length).toBe(6);
|
||||
|
||||
usePaginationComposition.nextPage();
|
||||
await waitForState(usePaginationComposition.loading, false);
|
||||
expect(usePaginationComposition.hasMore.value).toBe(false);
|
||||
expect(usePaginationComposition.data.value.length).toBe(6);
|
||||
});
|
||||
});
|
|
@ -18,15 +18,19 @@ export async function usePaginate<T>(getSingle: (page: number) => Promise<T[]>):
|
|||
export function usePagination<T, S = unknown>(
|
||||
_loadData: (page: number, arg: S) => Promise<T[] | null>,
|
||||
isActive: () => boolean = () => true,
|
||||
{ scrollElement: _scrollElement, each: _each }: { scrollElement?: Ref<HTMLElement | null>; each?: S[] } = {},
|
||||
{
|
||||
scrollElement: _scrollElement,
|
||||
each: _each,
|
||||
pageSize: _pageSize,
|
||||
}: { scrollElement?: Ref<HTMLElement | null> | null; each?: S[]; pageSize?: number } = {},
|
||||
) {
|
||||
const scrollElement = _scrollElement ?? ref(document.getElementById('scroll-component'));
|
||||
const scrollElement = _scrollElement === null ? null : ref(document.getElementById('scroll-component'));
|
||||
const page = ref(1);
|
||||
const pageSize = ref(0);
|
||||
const pageSize = ref(_pageSize ?? 0);
|
||||
const hasMore = ref(true);
|
||||
const data = ref<T[]>([]) as Ref<T[]>;
|
||||
const loading = ref(false);
|
||||
const each = ref(_each ?? []);
|
||||
const each = ref([...(_each ?? [])]);
|
||||
|
||||
async function loadData() {
|
||||
if (loading.value === true || hasMore.value === false) {
|
||||
|
@ -45,7 +49,7 @@ export function usePagination<T, S = unknown>(
|
|||
// use next each element
|
||||
each.value.shift();
|
||||
page.value = 1;
|
||||
pageSize.value = 0;
|
||||
pageSize.value = _pageSize ?? 0;
|
||||
hasMore.value = each.value.length > 0;
|
||||
if (hasMore.value) {
|
||||
loading.value = false;
|
||||
|
@ -65,15 +69,19 @@ export function usePagination<T, S = unknown>(
|
|||
}
|
||||
}
|
||||
|
||||
useInfiniteScroll(scrollElement, nextPage, { distance: 10 });
|
||||
if (scrollElement !== null) {
|
||||
useInfiniteScroll(scrollElement, nextPage, { distance: 10 });
|
||||
}
|
||||
|
||||
async function resetPage() {
|
||||
const _page = page.value;
|
||||
|
||||
page.value = 1;
|
||||
pageSize.value = _pageSize ?? 0;
|
||||
hasMore.value = true;
|
||||
data.value = [];
|
||||
each.value = (_each ?? []) as UnwrapRef<S[]>;
|
||||
page.value = 1;
|
||||
loading.value = false;
|
||||
each.value = [...(_each ?? [])] as UnwrapRef<S[]>;
|
||||
|
||||
if (_page === 1) {
|
||||
// we need to reload manually as the page is already 1, so changing won't trigger watcher
|
||||
|
|
|
@ -7,10 +7,10 @@ import replace from 'replace-in-file';
|
|||
import IconsResolver from 'unplugin-icons/resolver';
|
||||
import Icons from 'unplugin-icons/vite';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import prismjs from 'vite-plugin-prismjs';
|
||||
import WindiCSS from 'vite-plugin-windicss';
|
||||
import svgLoader from 'vite-svg-loader';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
function woodpeckerInfoPlugin() {
|
||||
return {
|
||||
|
@ -133,4 +133,8 @@ export default defineConfig({
|
|||
host: process.env.VITE_DEV_SERVER_HOST || '127.0.0.1',
|
||||
port: 8010,
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
},
|
||||
});
|
||||
|
|
50
woodpecker-go/woodpecker/agent.go
Normal file
50
woodpecker-go/woodpecker/agent.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package woodpecker
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
pathAgents = "%s/api/agents"
|
||||
pathAgent = "%s/api/agents/%d"
|
||||
pathAgentTasks = "%s/api/agents/%d/tasks"
|
||||
)
|
||||
|
||||
// AgentCreate creates a new agent.
|
||||
func (c *client) AgentCreate(in *Agent) (*Agent, error) {
|
||||
out := new(Agent)
|
||||
uri := fmt.Sprintf(pathAgents, c.addr)
|
||||
return out, c.post(uri, in, out)
|
||||
}
|
||||
|
||||
// AgentList returns a list of all registered agents.
|
||||
func (c *client) AgentList() ([]*Agent, error) {
|
||||
out := make([]*Agent, 0, 5)
|
||||
uri := fmt.Sprintf(pathAgents, c.addr)
|
||||
return out, c.get(uri, &out)
|
||||
}
|
||||
|
||||
// Agent returns an agent by id.
|
||||
func (c *client) Agent(agentID int64) (*Agent, error) {
|
||||
out := new(Agent)
|
||||
uri := fmt.Sprintf(pathAgent, c.addr, agentID)
|
||||
return out, c.get(uri, out)
|
||||
}
|
||||
|
||||
// AgentUpdate updates the agent with the provided Agent struct.
|
||||
func (c *client) AgentUpdate(in *Agent) (*Agent, error) {
|
||||
out := new(Agent)
|
||||
uri := fmt.Sprintf(pathAgent, c.addr, in.ID)
|
||||
return out, c.patch(uri, in, out)
|
||||
}
|
||||
|
||||
// AgentDelete deletes the agent with the given id.
|
||||
func (c *client) AgentDelete(agentID int64) error {
|
||||
uri := fmt.Sprintf(pathAgent, c.addr, agentID)
|
||||
return c.delete(uri)
|
||||
}
|
||||
|
||||
// AgentTasksList returns a list of all tasks for the agent with the given id.
|
||||
func (c *client) AgentTasksList(agentID int64) ([]*Task, error) {
|
||||
out := make([]*Task, 0, 5)
|
||||
uri := fmt.Sprintf(pathAgentTasks, c.addr, agentID)
|
||||
return out, c.get(uri, &out)
|
||||
}
|
511
woodpecker-go/woodpecker/agent_test.go
Normal file
511
woodpecker-go/woodpecker/agent_test.go
Normal file
|
@ -0,0 +1,511 @@
|
|||
package woodpecker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestClient_AgentCreate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
input *Agent
|
||||
expected *Agent
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_, err := fmt.Fprint(w, `{"id":1,"name":"new_agent","backend":"local","capacity":2,"version":"1.0.0"}`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
input: &Agent{Name: "new_agent", Backend: "local", Capacity: 2, Version: "1.0.0"},
|
||||
expected: &Agent{ID: 1, Name: "new_agent", Backend: "local", Capacity: 2, Version: "1.0.0"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid input",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
},
|
||||
input: &Agent{},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "server error",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
},
|
||||
input: &Agent{Name: "new_agent", Backend: "local", Capacity: 2, Version: "1.0.0"},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ts := httptest.NewServer(tt.handler)
|
||||
defer ts.Close()
|
||||
|
||||
client := NewClient(ts.URL, http.DefaultClient)
|
||||
agent, err := client.AgentCreate(tt.input)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, agent, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_AgentList(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
expected []*Agent
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprint(w, `[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "agent-1",
|
||||
"backend": "local",
|
||||
"capacity": 2,
|
||||
"version": "1.0.0"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "agent-2",
|
||||
"backend": "kubernetes",
|
||||
"capacity": 4,
|
||||
"version": "1.0.0"
|
||||
}
|
||||
]`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
expected: []*Agent{
|
||||
{
|
||||
ID: 1,
|
||||
Name: "agent-1",
|
||||
Backend: "local",
|
||||
Capacity: 2,
|
||||
Version: "1.0.0",
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Name: "agent-2",
|
||||
Backend: "kubernetes",
|
||||
Capacity: 4,
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "server error",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid response",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprint(w, `invalid json`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ts := httptest.NewServer(tt.handler)
|
||||
defer ts.Close()
|
||||
|
||||
client := NewClient(ts.URL, http.DefaultClient)
|
||||
agents, err := client.AgentList()
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, agents)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Agent(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
agentID int64
|
||||
expected *Agent
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprint(w, `{"id":1,"name":"agent-1","backend":"local","capacity":2,"version":"1.0.0"}`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
agentID: 1,
|
||||
expected: &Agent{ID: 1, Name: "agent-1", Backend: "local", Capacity: 2, Version: "1.0.0"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
},
|
||||
agentID: 999,
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "server error",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
},
|
||||
agentID: 1,
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid response",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprint(w, `invalid json`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
agentID: 1,
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ts := httptest.NewServer(tt.handler)
|
||||
defer ts.Close()
|
||||
|
||||
client := NewClient(ts.URL, http.DefaultClient)
|
||||
agent, err := client.Agent(tt.agentID)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, agent)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_AgentUpdate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
input *Agent
|
||||
expected *Agent
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPatch {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprint(w, `{"id":1,"name":"updated_agent"}`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
input: &Agent{ID: 1, Name: "existing_agent"},
|
||||
expected: &Agent{ID: 1, Name: "updated_agent"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPatch {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
},
|
||||
input: &Agent{ID: 999, Name: "nonexistent_agent"},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid input",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPatch {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
},
|
||||
input: &Agent{},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "server error",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPatch {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
},
|
||||
input: &Agent{ID: 1, Name: "existing_agent"},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ts := httptest.NewServer(tt.handler)
|
||||
defer ts.Close()
|
||||
|
||||
client := NewClient(ts.URL, http.DefaultClient)
|
||||
agent, err := client.AgentUpdate(tt.input)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, agent, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_AgentDelete(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
agentID int64
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
},
|
||||
agentID: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
},
|
||||
agentID: 999,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "server error",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
},
|
||||
agentID: 1,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ts := httptest.NewServer(tt.handler)
|
||||
defer ts.Close()
|
||||
|
||||
client := NewClient(ts.URL, http.DefaultClient)
|
||||
err := client.AgentDelete(tt.agentID)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_AgentTasksList(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
agentID int64
|
||||
expected []*Task
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprint(w, `[
|
||||
{
|
||||
"id": "4696",
|
||||
"data": "",
|
||||
"labels": {
|
||||
"platform": "linux/amd64",
|
||||
"repo": "woodpecker-ci/woodpecker"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "4697",
|
||||
"data": "",
|
||||
"labels": {
|
||||
"platform": "linux/arm64",
|
||||
"repo": "woodpecker-ci/woodpecker"
|
||||
}
|
||||
}
|
||||
]`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
agentID: 1,
|
||||
expected: []*Task{
|
||||
{
|
||||
ID: "4696",
|
||||
Data: []byte{},
|
||||
Labels: map[string]string{
|
||||
"platform": "linux/amd64",
|
||||
"repo": "woodpecker-ci/woodpecker",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "4697",
|
||||
Data: []byte{},
|
||||
Labels: map[string]string{
|
||||
"platform": "linux/arm64",
|
||||
"repo": "woodpecker-ci/woodpecker",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
},
|
||||
agentID: 999,
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "server error",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
},
|
||||
agentID: 1,
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid response",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprint(w, `invalid json`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
agentID: 1,
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ts := httptest.NewServer(tt.handler)
|
||||
defer ts.Close()
|
||||
|
||||
client := NewClient(ts.URL, http.DefaultClient)
|
||||
tasks, err := client.AgentTasksList(tt.agentID)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, tasks)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -26,41 +26,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
pathSelf = "%s/api/user"
|
||||
pathRepos = "%s/api/user/repos"
|
||||
pathRepoPost = "%s/api/repos?forge_remote_id=%d"
|
||||
pathRepo = "%s/api/repos/%d"
|
||||
pathRepoLookup = "%s/api/repos/lookup/%s"
|
||||
pathRepoMove = "%s/api/repos/%d/move?to=%s"
|
||||
pathChown = "%s/api/repos/%d/chown"
|
||||
pathRepair = "%s/api/repos/%d/repair"
|
||||
pathPipelines = "%s/api/repos/%d/pipelines"
|
||||
pathPipeline = "%s/api/repos/%d/pipelines/%v"
|
||||
pathPipelineLogs = "%s/api/repos/%d/logs/%d"
|
||||
pathStepLogs = "%s/api/repos/%d/logs/%d/%d"
|
||||
pathApprove = "%s/api/repos/%d/pipelines/%d/approve"
|
||||
pathDecline = "%s/api/repos/%d/pipelines/%d/decline"
|
||||
pathStop = "%s/api/repos/%d/pipelines/%d/cancel"
|
||||
pathRepoSecrets = "%s/api/repos/%d/secrets"
|
||||
pathRepoSecret = "%s/api/repos/%d/secrets/%s"
|
||||
pathRepoRegistries = "%s/api/repos/%d/registry"
|
||||
pathRepoRegistry = "%s/api/repos/%d/registry/%s"
|
||||
pathRepoCrons = "%s/api/repos/%d/cron"
|
||||
pathRepoCron = "%s/api/repos/%d/cron/%d"
|
||||
pathOrg = "%s/api/orgs/%d"
|
||||
pathOrgLookup = "%s/api/orgs/lookup/%s"
|
||||
pathOrgSecrets = "%s/api/orgs/%d/secrets"
|
||||
pathOrgSecret = "%s/api/orgs/%d/secrets/%s"
|
||||
pathGlobalSecrets = "%s/api/secrets"
|
||||
pathGlobalSecret = "%s/api/secrets/%s"
|
||||
pathUsers = "%s/api/users"
|
||||
pathUser = "%s/api/users/%s"
|
||||
pathPipelineQueue = "%s/api/pipelines"
|
||||
pathQueue = "%s/api/queue"
|
||||
pathLogLevel = "%s/api/log-level"
|
||||
pathAgents = "%s/api/agents"
|
||||
pathAgent = "%s/api/agents/%d"
|
||||
pathAgentTasks = "%s/api/agents/%d/tasks"
|
||||
pathLogLevel = "%s/api/log-level"
|
||||
|
||||
// TODO: implement endpoints
|
||||
// pathFeed = "%s/api/user/feed"
|
||||
// pathVersion = "%s/version"
|
||||
|
@ -91,422 +58,6 @@ func (c *client) SetAddress(addr string) {
|
|||
c.addr = addr
|
||||
}
|
||||
|
||||
// Self returns the currently authenticated user.
|
||||
func (c *client) Self() (*User, error) {
|
||||
out := new(User)
|
||||
uri := fmt.Sprintf(pathSelf, c.addr)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// User returns a user by login.
|
||||
func (c *client) User(login string) (*User, error) {
|
||||
out := new(User)
|
||||
uri := fmt.Sprintf(pathUser, c.addr, login)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// UserList returns a list of all registered users.
|
||||
func (c *client) UserList() ([]*User, error) {
|
||||
var out []*User
|
||||
uri := fmt.Sprintf(pathUsers, c.addr)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// UserPost creates a new user account.
|
||||
func (c *client) UserPost(in *User) (*User, error) {
|
||||
out := new(User)
|
||||
uri := fmt.Sprintf(pathUsers, c.addr)
|
||||
err := c.post(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// UserPatch updates a user account.
|
||||
func (c *client) UserPatch(in *User) (*User, error) {
|
||||
out := new(User)
|
||||
uri := fmt.Sprintf(pathUser, c.addr, in.Login)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// UserDel deletes a user account.
|
||||
func (c *client) UserDel(login string) error {
|
||||
uri := fmt.Sprintf(pathUser, c.addr, login)
|
||||
err := c.delete(uri)
|
||||
return err
|
||||
}
|
||||
|
||||
// Repo returns a repository by id.
|
||||
func (c *client) Repo(repoID int64) (*Repo, error) {
|
||||
out := new(Repo)
|
||||
uri := fmt.Sprintf(pathRepo, c.addr, repoID)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoLookup returns a repository by name.
|
||||
func (c *client) RepoLookup(fullName string) (*Repo, error) {
|
||||
out := new(Repo)
|
||||
uri := fmt.Sprintf(pathRepoLookup, c.addr, fullName)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoList returns a list of all repositories to which
|
||||
// the user has explicit access in the host system.
|
||||
func (c *client) RepoList() ([]*Repo, error) {
|
||||
var out []*Repo
|
||||
uri := fmt.Sprintf(pathRepos, c.addr)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoListOpts returns a list of all repositories to which
|
||||
// the user has explicit access in the host system.
|
||||
func (c *client) RepoListOpts(all bool) ([]*Repo, error) {
|
||||
var out []*Repo
|
||||
uri := fmt.Sprintf(pathRepos+"?all=%v", c.addr, all)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoPost activates a repository.
|
||||
func (c *client) RepoPost(forgeRemoteID int64) (*Repo, error) {
|
||||
out := new(Repo)
|
||||
uri := fmt.Sprintf(pathRepoPost, c.addr, forgeRemoteID)
|
||||
err := c.post(uri, nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoChown updates a repository owner.
|
||||
func (c *client) RepoChown(repoID int64) (*Repo, error) {
|
||||
out := new(Repo)
|
||||
uri := fmt.Sprintf(pathChown, c.addr, repoID)
|
||||
err := c.post(uri, nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoRepair repairs the repository hooks.
|
||||
func (c *client) RepoRepair(repoID int64) error {
|
||||
uri := fmt.Sprintf(pathRepair, c.addr, repoID)
|
||||
return c.post(uri, nil, nil)
|
||||
}
|
||||
|
||||
// RepoPatch updates a repository.
|
||||
func (c *client) RepoPatch(repoID int64, in *RepoPatch) (*Repo, error) {
|
||||
out := new(Repo)
|
||||
uri := fmt.Sprintf(pathRepo, c.addr, repoID)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoDel deletes a repository.
|
||||
func (c *client) RepoDel(repoID int64) error {
|
||||
uri := fmt.Sprintf(pathRepo, c.addr, repoID)
|
||||
err := c.delete(uri)
|
||||
return err
|
||||
}
|
||||
|
||||
// RepoMove moves a repository
|
||||
func (c *client) RepoMove(repoID int64, newFullName string) error {
|
||||
uri := fmt.Sprintf(pathRepoMove, c.addr, repoID, newFullName)
|
||||
return c.post(uri, nil, nil)
|
||||
}
|
||||
|
||||
// Pipeline returns a repository pipeline by pipeline-id.
|
||||
func (c *client) Pipeline(repoID, pipeline int64) (*Pipeline, error) {
|
||||
out := new(Pipeline)
|
||||
uri := fmt.Sprintf(pathPipeline, c.addr, repoID, pipeline)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// Pipeline returns the latest repository pipeline by branch.
|
||||
func (c *client) PipelineLast(repoID int64, branch string) (*Pipeline, error) {
|
||||
out := new(Pipeline)
|
||||
uri := fmt.Sprintf(pathPipeline, c.addr, repoID, "latest")
|
||||
if len(branch) != 0 {
|
||||
uri += "?branch=" + branch
|
||||
}
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// PipelineList returns a list of recent pipelines for the
|
||||
// the specified repository.
|
||||
func (c *client) PipelineList(repoID int64) ([]*Pipeline, error) {
|
||||
var out []*Pipeline
|
||||
uri := fmt.Sprintf(pathPipelines, c.addr, repoID)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (c *client) PipelineCreate(repoID int64, options *PipelineOptions) (*Pipeline, error) {
|
||||
var out *Pipeline
|
||||
uri := fmt.Sprintf(pathPipelines, c.addr, repoID)
|
||||
err := c.post(uri, options, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// PipelineQueue returns a list of enqueued pipelines.
|
||||
func (c *client) PipelineQueue() ([]*Feed, error) {
|
||||
var out []*Feed
|
||||
uri := fmt.Sprintf(pathPipelineQueue, c.addr)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// PipelineStart re-starts a stopped pipeline.
|
||||
func (c *client) PipelineStart(repoID, pipeline int64, params map[string]string) (*Pipeline, error) {
|
||||
out := new(Pipeline)
|
||||
val := mapValues(params)
|
||||
uri := fmt.Sprintf(pathPipeline, c.addr, repoID, pipeline)
|
||||
err := c.post(uri+"?"+val.Encode(), nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// PipelineStop cancels the running step.
|
||||
func (c *client) PipelineStop(repoID, pipeline int64) error {
|
||||
uri := fmt.Sprintf(pathStop, c.addr, repoID, pipeline)
|
||||
err := c.post(uri, nil, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// PipelineApprove approves a blocked pipeline.
|
||||
func (c *client) PipelineApprove(repoID, pipeline int64) (*Pipeline, error) {
|
||||
out := new(Pipeline)
|
||||
uri := fmt.Sprintf(pathApprove, c.addr, repoID, pipeline)
|
||||
err := c.post(uri, nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// PipelineDecline declines a blocked pipeline.
|
||||
func (c *client) PipelineDecline(repoID, pipeline int64) (*Pipeline, error) {
|
||||
out := new(Pipeline)
|
||||
uri := fmt.Sprintf(pathDecline, c.addr, repoID, pipeline)
|
||||
err := c.post(uri, nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// PipelineKill force kills the running pipeline.
|
||||
func (c *client) PipelineKill(repoID, pipeline int64) error {
|
||||
uri := fmt.Sprintf(pathPipeline, c.addr, repoID, pipeline)
|
||||
err := c.delete(uri)
|
||||
return err
|
||||
}
|
||||
|
||||
// LogsPurge purges the pipeline all steps logs for the specified pipeline.
|
||||
func (c *client) LogsPurge(repoID, pipeline int64) error {
|
||||
uri := fmt.Sprintf(pathPipelineLogs, c.addr, repoID, pipeline)
|
||||
err := c.delete(uri)
|
||||
return err
|
||||
}
|
||||
|
||||
// StepLogEntries returns the pipeline logs for the specified step.
|
||||
func (c *client) StepLogEntries(repoID, num, step int64) ([]*LogEntry, error) {
|
||||
uri := fmt.Sprintf(pathStepLogs, c.addr, repoID, num, step)
|
||||
var out []*LogEntry
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// StepLogsPurge purges the pipeline logs for the specified step.
|
||||
func (c *client) StepLogsPurge(repoID, pipelineNumber, stepID int64) error {
|
||||
uri := fmt.Sprintf(pathStepLogs, c.addr, repoID, pipelineNumber, stepID)
|
||||
err := c.delete(uri)
|
||||
return err
|
||||
}
|
||||
|
||||
// Deploy triggers a deployment for an existing pipeline using the
|
||||
// specified target environment.
|
||||
func (c *client) Deploy(repoID, pipeline int64, env string, params map[string]string) (*Pipeline, error) {
|
||||
out := new(Pipeline)
|
||||
val := mapValues(params)
|
||||
val.Set("event", EventDeploy)
|
||||
val.Set("deploy_to", env)
|
||||
uri := fmt.Sprintf(pathPipeline, c.addr, repoID, pipeline)
|
||||
err := c.post(uri+"?"+val.Encode(), nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// Registry returns a registry by hostname.
|
||||
func (c *client) Registry(repoID int64, hostname string) (*Registry, error) {
|
||||
out := new(Registry)
|
||||
uri := fmt.Sprintf(pathRepoRegistry, c.addr, repoID, hostname)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RegistryList returns a list of all repository registries.
|
||||
func (c *client) RegistryList(repoID int64) ([]*Registry, error) {
|
||||
var out []*Registry
|
||||
uri := fmt.Sprintf(pathRepoRegistries, c.addr, repoID)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RegistryCreate creates a registry.
|
||||
func (c *client) RegistryCreate(repoID int64, in *Registry) (*Registry, error) {
|
||||
out := new(Registry)
|
||||
uri := fmt.Sprintf(pathRepoRegistries, c.addr, repoID)
|
||||
err := c.post(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RegistryUpdate updates a registry.
|
||||
func (c *client) RegistryUpdate(repoID int64, in *Registry) (*Registry, error) {
|
||||
out := new(Registry)
|
||||
uri := fmt.Sprintf(pathRepoRegistry, c.addr, repoID, in.Address)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RegistryDelete deletes a registry.
|
||||
func (c *client) RegistryDelete(repoID int64, hostname string) error {
|
||||
uri := fmt.Sprintf(pathRepoRegistry, c.addr, repoID, hostname)
|
||||
return c.delete(uri)
|
||||
}
|
||||
|
||||
// Secret returns a secret by name.
|
||||
func (c *client) Secret(repoID int64, secret string) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathRepoSecret, c.addr, repoID, secret)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// SecretList returns a list of all repository secrets.
|
||||
func (c *client) SecretList(repoID int64) ([]*Secret, error) {
|
||||
var out []*Secret
|
||||
uri := fmt.Sprintf(pathRepoSecrets, c.addr, repoID)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// SecretCreate creates a secret.
|
||||
func (c *client) SecretCreate(repoID int64, in *Secret) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathRepoSecrets, c.addr, repoID)
|
||||
err := c.post(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// SecretUpdate updates a secret.
|
||||
func (c *client) SecretUpdate(repoID int64, in *Secret) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathRepoSecret, c.addr, repoID, in.Name)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// SecretDelete deletes a secret.
|
||||
func (c *client) SecretDelete(repoID int64, secret string) error {
|
||||
uri := fmt.Sprintf(pathRepoSecret, c.addr, repoID, secret)
|
||||
return c.delete(uri)
|
||||
}
|
||||
|
||||
// Org returns an organization by id.
|
||||
func (c *client) Org(orgID int64) (*Org, error) {
|
||||
out := new(Org)
|
||||
uri := fmt.Sprintf(pathOrg, c.addr, orgID)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OrgLookup returns a organization by its name.
|
||||
func (c *client) OrgLookup(name string) (*Org, error) {
|
||||
out := new(Org)
|
||||
uri := fmt.Sprintf(pathOrgLookup, c.addr, name)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OrgSecret returns an organization secret by name.
|
||||
func (c *client) OrgSecret(orgID int64, secret string) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathOrgSecret, c.addr, orgID, secret)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OrgSecretList returns a list of all organization secrets.
|
||||
func (c *client) OrgSecretList(orgID int64) ([]*Secret, error) {
|
||||
var out []*Secret
|
||||
uri := fmt.Sprintf(pathOrgSecrets, c.addr, orgID)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OrgSecretCreate creates an organization secret.
|
||||
func (c *client) OrgSecretCreate(orgID int64, in *Secret) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathOrgSecrets, c.addr, orgID)
|
||||
err := c.post(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OrgSecretUpdate updates an organization secret.
|
||||
func (c *client) OrgSecretUpdate(orgID int64, in *Secret) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathOrgSecret, c.addr, orgID, in.Name)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OrgSecretDelete deletes an organization secret.
|
||||
func (c *client) OrgSecretDelete(orgID int64, secret string) error {
|
||||
uri := fmt.Sprintf(pathOrgSecret, c.addr, orgID, secret)
|
||||
return c.delete(uri)
|
||||
}
|
||||
|
||||
// GlobalOrgSecret returns an global secret by name.
|
||||
func (c *client) GlobalSecret(secret string) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathGlobalSecret, c.addr, secret)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// GlobalSecretList returns a list of all global secrets.
|
||||
func (c *client) GlobalSecretList() ([]*Secret, error) {
|
||||
var out []*Secret
|
||||
uri := fmt.Sprintf(pathGlobalSecrets, c.addr)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// GlobalSecretCreate creates a global secret.
|
||||
func (c *client) GlobalSecretCreate(in *Secret) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathGlobalSecrets, c.addr)
|
||||
err := c.post(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// GlobalSecretUpdate updates a global secret.
|
||||
func (c *client) GlobalSecretUpdate(in *Secret) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathGlobalSecret, c.addr, in.Name)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// GlobalSecretDelete deletes a global secret.
|
||||
func (c *client) GlobalSecretDelete(secret string) error {
|
||||
uri := fmt.Sprintf(pathGlobalSecret, c.addr, secret)
|
||||
return c.delete(uri)
|
||||
}
|
||||
|
||||
// QueueInfo returns queue info
|
||||
func (c *client) QueueInfo() (*Info, error) {
|
||||
out := new(Info)
|
||||
uri := fmt.Sprintf(pathQueue+"/info", c.addr)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// LogLevel returns the current logging level
|
||||
func (c *client) LogLevel() (*LogLevel, error) {
|
||||
out := new(LogLevel)
|
||||
|
@ -523,96 +74,31 @@ func (c *client) SetLogLevel(in *LogLevel) (*LogLevel, error) {
|
|||
return out, err
|
||||
}
|
||||
|
||||
func (c *client) CronList(repoID int64) ([]*Cron, error) {
|
||||
out := make([]*Cron, 0, 5)
|
||||
uri := fmt.Sprintf(pathRepoCrons, c.addr, repoID)
|
||||
return out, c.get(uri, &out)
|
||||
}
|
||||
|
||||
func (c *client) CronCreate(repoID int64, in *Cron) (*Cron, error) {
|
||||
out := new(Cron)
|
||||
uri := fmt.Sprintf(pathRepoCrons, c.addr, repoID)
|
||||
return out, c.post(uri, in, out)
|
||||
}
|
||||
|
||||
func (c *client) CronUpdate(repoID int64, in *Cron) (*Cron, error) {
|
||||
out := new(Cron)
|
||||
uri := fmt.Sprintf(pathRepoCron, c.addr, repoID, in.ID)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (c *client) CronDelete(repoID, cronID int64) error {
|
||||
uri := fmt.Sprintf(pathRepoCron, c.addr, repoID, cronID)
|
||||
return c.delete(uri)
|
||||
}
|
||||
|
||||
func (c *client) CronGet(repoID, cronID int64) (*Cron, error) {
|
||||
out := new(Cron)
|
||||
uri := fmt.Sprintf(pathRepoCron, c.addr, repoID, cronID)
|
||||
return out, c.get(uri, out)
|
||||
}
|
||||
|
||||
func (c *client) AgentList() ([]*Agent, error) {
|
||||
out := make([]*Agent, 0, 5)
|
||||
uri := fmt.Sprintf(pathAgents, c.addr)
|
||||
return out, c.get(uri, &out)
|
||||
}
|
||||
|
||||
func (c *client) Agent(agentID int64) (*Agent, error) {
|
||||
out := new(Agent)
|
||||
uri := fmt.Sprintf(pathAgent, c.addr, agentID)
|
||||
return out, c.get(uri, out)
|
||||
}
|
||||
|
||||
func (c *client) AgentCreate(in *Agent) (*Agent, error) {
|
||||
out := new(Agent)
|
||||
uri := fmt.Sprintf(pathAgents, c.addr)
|
||||
return out, c.post(uri, in, out)
|
||||
}
|
||||
|
||||
func (c *client) AgentUpdate(in *Agent) (*Agent, error) {
|
||||
out := new(Agent)
|
||||
uri := fmt.Sprintf(pathAgent, c.addr, in.ID)
|
||||
return out, c.patch(uri, in, out)
|
||||
}
|
||||
|
||||
func (c *client) AgentDelete(agentID int64) error {
|
||||
uri := fmt.Sprintf(pathAgent, c.addr, agentID)
|
||||
return c.delete(uri)
|
||||
}
|
||||
|
||||
func (c *client) AgentTasksList(agentID int64) ([]*Task, error) {
|
||||
out := make([]*Task, 0, 5)
|
||||
uri := fmt.Sprintf(pathAgentTasks, c.addr, agentID)
|
||||
return out, c.get(uri, &out)
|
||||
}
|
||||
|
||||
//
|
||||
// http request helper functions
|
||||
//
|
||||
|
||||
// helper function for making an http GET request.
|
||||
// Helper function for making an http GET request.
|
||||
func (c *client) get(rawurl string, out any) error {
|
||||
return c.do(rawurl, http.MethodGet, nil, out)
|
||||
}
|
||||
|
||||
// helper function for making an http POST request.
|
||||
// Helper function for making an http POST request.
|
||||
func (c *client) post(rawurl string, in, out any) error {
|
||||
return c.do(rawurl, http.MethodPost, in, out)
|
||||
}
|
||||
|
||||
// helper function for making an http PATCH request.
|
||||
// Helper function for making an http PATCH request.
|
||||
func (c *client) patch(rawurl string, in, out any) error {
|
||||
return c.do(rawurl, http.MethodPatch, in, out)
|
||||
}
|
||||
|
||||
// helper function for making an http DELETE request.
|
||||
// Helper function for making an http DELETE request.
|
||||
func (c *client) delete(rawurl string) error {
|
||||
return c.do(rawurl, http.MethodDelete, nil, nil)
|
||||
}
|
||||
|
||||
// helper function to make an http request
|
||||
// Helper function to make an http request.
|
||||
func (c *client) do(rawurl, method string, in, out any) error {
|
||||
body, err := c.open(rawurl, method, in)
|
||||
if err != nil {
|
||||
|
@ -625,7 +111,7 @@ func (c *client) do(rawurl, method string, in, out any) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// helper function to open an http request
|
||||
// Helper function to open an http request.
|
||||
func (c *client) open(rawurl, method string, in any) (io.ReadCloser, error) {
|
||||
uri, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
|
|
|
@ -25,44 +25,6 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_QueueInfo(t *testing.T) {
|
||||
fixtureHandler := func(w http.ResponseWriter, _ *http.Request) {
|
||||
fmt.Fprint(w, `{
|
||||
"pending": null,
|
||||
"running": [
|
||||
{
|
||||
"id": "4696",
|
||||
"data": "",
|
||||
"labels": {
|
||||
"platform": "linux/amd64",
|
||||
"repo": "woodpecker-ci/woodpecker"
|
||||
},
|
||||
"Dependencies": [],
|
||||
"DepStatus": {},
|
||||
"RunOn": null
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"worker_count": 3,
|
||||
"pending_count": 0,
|
||||
"waiting_on_deps_count": 0,
|
||||
"running_count": 1,
|
||||
"completed_count": 0
|
||||
},
|
||||
"Paused": false
|
||||
}`)
|
||||
}
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(fixtureHandler))
|
||||
defer ts.Close()
|
||||
|
||||
client := NewClient(ts.URL, http.DefaultClient)
|
||||
|
||||
info, err := client.QueueInfo()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, info.Stats.Workers)
|
||||
}
|
||||
|
||||
func Test_LogLevel(t *testing.T) {
|
||||
logLevel := "warn"
|
||||
fixtureHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -49,7 +49,7 @@ const (
|
|||
LogEntryProgress
|
||||
)
|
||||
|
||||
// StepType identifies the type of step
|
||||
// StepType identifies the type of step.
|
||||
type StepType string
|
||||
|
||||
const (
|
||||
|
|
46
woodpecker-go/woodpecker/global_secret.go
Normal file
46
woodpecker-go/woodpecker/global_secret.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package woodpecker
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
pathGlobalSecrets = "%s/api/secrets"
|
||||
pathGlobalSecret = "%s/api/secrets/%s"
|
||||
)
|
||||
|
||||
// GlobalOrgSecret returns an global secret by name.
|
||||
func (c *client) GlobalSecret(secret string) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathGlobalSecret, c.addr, secret)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// GlobalSecretList returns a list of all global secrets.
|
||||
func (c *client) GlobalSecretList() ([]*Secret, error) {
|
||||
var out []*Secret
|
||||
uri := fmt.Sprintf(pathGlobalSecrets, c.addr)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// GlobalSecretCreate creates a global secret.
|
||||
func (c *client) GlobalSecretCreate(in *Secret) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathGlobalSecrets, c.addr)
|
||||
err := c.post(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// GlobalSecretUpdate updates a global secret.
|
||||
func (c *client) GlobalSecretUpdate(in *Secret) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathGlobalSecret, c.addr, in.Name)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// GlobalSecretDelete deletes a global secret.
|
||||
func (c *client) GlobalSecretDelete(secret string) error {
|
||||
uri := fmt.Sprintf(pathGlobalSecret, c.addr, secret)
|
||||
return c.delete(uri)
|
||||
}
|
|
@ -190,42 +190,42 @@ type Client interface {
|
|||
// QueueInfo returns the queue state.
|
||||
QueueInfo() (*Info, error)
|
||||
|
||||
// LogLevel returns the current logging level
|
||||
// LogLevel returns the current logging level.
|
||||
LogLevel() (*LogLevel, error)
|
||||
|
||||
// SetLogLevel sets the server's logging level
|
||||
// SetLogLevel sets the server's logging level.
|
||||
SetLogLevel(logLevel *LogLevel) (*LogLevel, error)
|
||||
|
||||
// CronList list all cron jobs of a repo
|
||||
// CronList list all cron jobs of a repo.
|
||||
CronList(repoID int64) ([]*Cron, error)
|
||||
|
||||
// CronGet get a specific cron job of a repo by id
|
||||
// CronGet get a specific cron job of a repo by id.
|
||||
CronGet(repoID, cronID int64) (*Cron, error)
|
||||
|
||||
// CronDelete delete a specific cron job of a repo by id
|
||||
// CronDelete delete a specific cron job of a repo by id.
|
||||
CronDelete(repoID, cronID int64) error
|
||||
|
||||
// CronCreate create a new cron job in a repo
|
||||
// CronCreate create a new cron job in a repo.
|
||||
CronCreate(repoID int64, cron *Cron) (*Cron, error)
|
||||
|
||||
// CronUpdate update an existing cron job of a repo
|
||||
// CronUpdate update an existing cron job of a repo.
|
||||
CronUpdate(repoID int64, cron *Cron) (*Cron, error)
|
||||
|
||||
// AgentList returns a list of all registered agents
|
||||
// AgentList returns a list of all registered agents.
|
||||
AgentList() ([]*Agent, error)
|
||||
|
||||
// Agent returns an agent by id
|
||||
// Agent returns an agent by id.
|
||||
Agent(int64) (*Agent, error)
|
||||
|
||||
// AgentCreate creates a new agent
|
||||
// AgentCreate creates a new agent.
|
||||
AgentCreate(*Agent) (*Agent, error)
|
||||
|
||||
// AgentUpdate updates an existing agent
|
||||
// AgentUpdate updates an existing agent.
|
||||
AgentUpdate(*Agent) (*Agent, error)
|
||||
|
||||
// AgentDelete deletes an agent
|
||||
// AgentDelete deletes an agent.
|
||||
AgentDelete(int64) error
|
||||
|
||||
// AgentTasksList returns a list of all tasks executed by an agent
|
||||
// AgentTasksList returns a list of all tasks executed by an agent.
|
||||
AgentTasksList(int64) ([]*Task, error)
|
||||
}
|
||||
|
|
64
woodpecker-go/woodpecker/org.go
Normal file
64
woodpecker-go/woodpecker/org.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
package woodpecker
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
pathOrg = "%s/api/orgs/%d"
|
||||
pathOrgLookup = "%s/api/orgs/lookup/%s"
|
||||
pathOrgSecrets = "%s/api/orgs/%d/secrets"
|
||||
pathOrgSecret = "%s/api/orgs/%d/secrets/%s"
|
||||
)
|
||||
|
||||
// Org returns an organization by id.
|
||||
func (c *client) Org(orgID int64) (*Org, error) {
|
||||
out := new(Org)
|
||||
uri := fmt.Sprintf(pathOrg, c.addr, orgID)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OrgLookup returns a organization by its name.
|
||||
func (c *client) OrgLookup(name string) (*Org, error) {
|
||||
out := new(Org)
|
||||
uri := fmt.Sprintf(pathOrgLookup, c.addr, name)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OrgSecret returns an organization secret by name.
|
||||
func (c *client) OrgSecret(orgID int64, secret string) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathOrgSecret, c.addr, orgID, secret)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OrgSecretList returns a list of all organization secrets.
|
||||
func (c *client) OrgSecretList(orgID int64) ([]*Secret, error) {
|
||||
var out []*Secret
|
||||
uri := fmt.Sprintf(pathOrgSecrets, c.addr, orgID)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OrgSecretCreate creates an organization secret.
|
||||
func (c *client) OrgSecretCreate(orgID int64, in *Secret) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathOrgSecrets, c.addr, orgID)
|
||||
err := c.post(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OrgSecretUpdate updates an organization secret.
|
||||
func (c *client) OrgSecretUpdate(orgID int64, in *Secret) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathOrgSecret, c.addr, orgID, in.Name)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OrgSecretDelete deletes an organization secret.
|
||||
func (c *client) OrgSecretDelete(orgID int64, secret string) error {
|
||||
uri := fmt.Sprintf(pathOrgSecret, c.addr, orgID, secret)
|
||||
return c.delete(uri)
|
||||
}
|
13
woodpecker-go/woodpecker/pipeline.go
Normal file
13
woodpecker-go/woodpecker/pipeline.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package woodpecker
|
||||
|
||||
import "fmt"
|
||||
|
||||
const pathPipelineQueue = "%s/api/pipelines"
|
||||
|
||||
// PipelineQueue returns a list of enqueued pipelines.
|
||||
func (c *client) PipelineQueue() ([]*Feed, error) {
|
||||
var out []*Feed
|
||||
uri := fmt.Sprintf(pathPipelineQueue, c.addr)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
13
woodpecker-go/woodpecker/queue.go
Normal file
13
woodpecker-go/woodpecker/queue.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package woodpecker
|
||||
|
||||
import "fmt"
|
||||
|
||||
const pathQueue = "%s/api/queue"
|
||||
|
||||
// QueueInfo returns queue info.
|
||||
func (c *client) QueueInfo() (*Info, error) {
|
||||
out := new(Info)
|
||||
uri := fmt.Sprintf(pathQueue+"/info", c.addr)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
116
woodpecker-go/woodpecker/queue_test.go
Normal file
116
woodpecker-go/woodpecker/queue_test.go
Normal file
|
@ -0,0 +1,116 @@
|
|||
package woodpecker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestClient_QueueInfo(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
expected *Info
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprint(w, `{
|
||||
"pending": null,
|
||||
"running": [
|
||||
{
|
||||
"id": "4696",
|
||||
"data": "",
|
||||
"labels": {
|
||||
"platform": "linux/amd64",
|
||||
"repo": "woodpecker-ci/woodpecker"
|
||||
},
|
||||
"Dependencies": [],
|
||||
"DepStatus": {},
|
||||
"RunOn": null
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"worker_count": 2,
|
||||
"pending_count": 0,
|
||||
"waiting_on_deps_count": 0,
|
||||
"running_count": 0,
|
||||
"completed_count": 0
|
||||
},
|
||||
"Paused": false
|
||||
}`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
expected: &Info{
|
||||
Running: []Task{
|
||||
{
|
||||
ID: "4696",
|
||||
Data: []byte{},
|
||||
Labels: map[string]string{
|
||||
"platform": "linux/amd64",
|
||||
"repo": "woodpecker-ci/woodpecker",
|
||||
},
|
||||
Dependencies: []string{},
|
||||
DepStatus: nil,
|
||||
RunOn: nil,
|
||||
},
|
||||
},
|
||||
Stats: struct {
|
||||
Workers int `json:"worker_count"`
|
||||
Pending int `json:"pending_count"`
|
||||
WaitingOnDeps int `json:"waiting_on_deps_count"`
|
||||
Running int `json:"running_count"`
|
||||
Complete int `json:"completed_count"`
|
||||
}{
|
||||
Workers: 2,
|
||||
Pending: 0,
|
||||
WaitingOnDeps: 0,
|
||||
Running: 0,
|
||||
Complete: 0,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "server error",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid response",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprint(w, `invalid json`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ts := httptest.NewServer(tt.handler)
|
||||
defer ts.Close()
|
||||
|
||||
client := NewClient(ts.URL, http.DefaultClient)
|
||||
info, err := client.QueueInfo()
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, info)
|
||||
})
|
||||
}
|
||||
}
|
304
woodpecker-go/woodpecker/repo.go
Normal file
304
woodpecker-go/woodpecker/repo.go
Normal file
|
@ -0,0 +1,304 @@
|
|||
package woodpecker
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
pathRepoPost = "%s/api/repos?forge_remote_id=%d"
|
||||
pathRepo = "%s/api/repos/%d"
|
||||
pathRepoLookup = "%s/api/repos/lookup/%s"
|
||||
pathRepoMove = "%s/api/repos/%d/move?to=%s"
|
||||
pathChown = "%s/api/repos/%d/chown"
|
||||
pathRepair = "%s/api/repos/%d/repair"
|
||||
pathPipelines = "%s/api/repos/%d/pipelines"
|
||||
pathPipeline = "%s/api/repos/%d/pipelines/%v"
|
||||
pathPipelineLogs = "%s/api/repos/%d/logs/%d"
|
||||
pathStepLogs = "%s/api/repos/%d/logs/%d/%d"
|
||||
pathApprove = "%s/api/repos/%d/pipelines/%d/approve"
|
||||
pathDecline = "%s/api/repos/%d/pipelines/%d/decline"
|
||||
pathStop = "%s/api/repos/%d/pipelines/%d/cancel"
|
||||
pathRepoSecrets = "%s/api/repos/%d/secrets"
|
||||
pathRepoSecret = "%s/api/repos/%d/secrets/%s"
|
||||
pathRepoRegistries = "%s/api/repos/%d/registry"
|
||||
pathRepoRegistry = "%s/api/repos/%d/registry/%s"
|
||||
pathRepoCrons = "%s/api/repos/%d/cron"
|
||||
pathRepoCron = "%s/api/repos/%d/cron/%d"
|
||||
)
|
||||
|
||||
// Repo returns a repository by id.
|
||||
func (c *client) Repo(repoID int64) (*Repo, error) {
|
||||
out := new(Repo)
|
||||
uri := fmt.Sprintf(pathRepo, c.addr, repoID)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoLookup returns a repository by name.
|
||||
func (c *client) RepoLookup(fullName string) (*Repo, error) {
|
||||
out := new(Repo)
|
||||
uri := fmt.Sprintf(pathRepoLookup, c.addr, fullName)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoPost activates a repository.
|
||||
func (c *client) RepoPost(forgeRemoteID int64) (*Repo, error) {
|
||||
out := new(Repo)
|
||||
uri := fmt.Sprintf(pathRepoPost, c.addr, forgeRemoteID)
|
||||
err := c.post(uri, nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoChown updates a repository owner.
|
||||
func (c *client) RepoChown(repoID int64) (*Repo, error) {
|
||||
out := new(Repo)
|
||||
uri := fmt.Sprintf(pathChown, c.addr, repoID)
|
||||
err := c.post(uri, nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoRepair repairs the repository hooks.
|
||||
func (c *client) RepoRepair(repoID int64) error {
|
||||
uri := fmt.Sprintf(pathRepair, c.addr, repoID)
|
||||
return c.post(uri, nil, nil)
|
||||
}
|
||||
|
||||
// RepoPatch updates a repository.
|
||||
func (c *client) RepoPatch(repoID int64, in *RepoPatch) (*Repo, error) {
|
||||
out := new(Repo)
|
||||
uri := fmt.Sprintf(pathRepo, c.addr, repoID)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoDel deletes a repository.
|
||||
func (c *client) RepoDel(repoID int64) error {
|
||||
uri := fmt.Sprintf(pathRepo, c.addr, repoID)
|
||||
err := c.delete(uri)
|
||||
return err
|
||||
}
|
||||
|
||||
// RepoMove moves a repository.
|
||||
func (c *client) RepoMove(repoID int64, newFullName string) error {
|
||||
uri := fmt.Sprintf(pathRepoMove, c.addr, repoID, newFullName)
|
||||
return c.post(uri, nil, nil)
|
||||
}
|
||||
|
||||
// Registry returns a registry by hostname.
|
||||
func (c *client) Registry(repoID int64, hostname string) (*Registry, error) {
|
||||
out := new(Registry)
|
||||
uri := fmt.Sprintf(pathRepoRegistry, c.addr, repoID, hostname)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RegistryList returns a list of all repository registries.
|
||||
func (c *client) RegistryList(repoID int64) ([]*Registry, error) {
|
||||
var out []*Registry
|
||||
uri := fmt.Sprintf(pathRepoRegistries, c.addr, repoID)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RegistryCreate creates a registry.
|
||||
func (c *client) RegistryCreate(repoID int64, in *Registry) (*Registry, error) {
|
||||
out := new(Registry)
|
||||
uri := fmt.Sprintf(pathRepoRegistries, c.addr, repoID)
|
||||
err := c.post(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RegistryUpdate updates a registry.
|
||||
func (c *client) RegistryUpdate(repoID int64, in *Registry) (*Registry, error) {
|
||||
out := new(Registry)
|
||||
uri := fmt.Sprintf(pathRepoRegistry, c.addr, repoID, in.Address)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RegistryDelete deletes a registry.
|
||||
func (c *client) RegistryDelete(repoID int64, hostname string) error {
|
||||
uri := fmt.Sprintf(pathRepoRegistry, c.addr, repoID, hostname)
|
||||
return c.delete(uri)
|
||||
}
|
||||
|
||||
// Secret returns a secret by name.
|
||||
func (c *client) Secret(repoID int64, secret string) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathRepoSecret, c.addr, repoID, secret)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// SecretList returns a list of all repository secrets.
|
||||
func (c *client) SecretList(repoID int64) ([]*Secret, error) {
|
||||
var out []*Secret
|
||||
uri := fmt.Sprintf(pathRepoSecrets, c.addr, repoID)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// SecretCreate creates a secret.
|
||||
func (c *client) SecretCreate(repoID int64, in *Secret) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathRepoSecrets, c.addr, repoID)
|
||||
err := c.post(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// SecretUpdate updates a secret.
|
||||
func (c *client) SecretUpdate(repoID int64, in *Secret) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathRepoSecret, c.addr, repoID, in.Name)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// SecretDelete deletes a secret.
|
||||
func (c *client) SecretDelete(repoID int64, secret string) error {
|
||||
uri := fmt.Sprintf(pathRepoSecret, c.addr, repoID, secret)
|
||||
return c.delete(uri)
|
||||
}
|
||||
|
||||
// CronList returns a list of cronjobs for the specified repository.
|
||||
func (c *client) CronList(repoID int64) ([]*Cron, error) {
|
||||
out := make([]*Cron, 0, 5)
|
||||
uri := fmt.Sprintf(pathRepoCrons, c.addr, repoID)
|
||||
return out, c.get(uri, &out)
|
||||
}
|
||||
|
||||
// CronCreate creates a new cron job for the specified repository.
|
||||
func (c *client) CronCreate(repoID int64, in *Cron) (*Cron, error) {
|
||||
out := new(Cron)
|
||||
uri := fmt.Sprintf(pathRepoCrons, c.addr, repoID)
|
||||
return out, c.post(uri, in, out)
|
||||
}
|
||||
|
||||
// CronUpdate updates an existing cron job for the specified repository.
|
||||
func (c *client) CronUpdate(repoID int64, in *Cron) (*Cron, error) {
|
||||
out := new(Cron)
|
||||
uri := fmt.Sprintf(pathRepoCron, c.addr, repoID, in.ID)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// CronDelete deletes a cron job by cron-id for the specified repository.
|
||||
func (c *client) CronDelete(repoID, cronID int64) error {
|
||||
uri := fmt.Sprintf(pathRepoCron, c.addr, repoID, cronID)
|
||||
return c.delete(uri)
|
||||
}
|
||||
|
||||
// CronGet returns a cron job by cron-id for the specified repository.
|
||||
func (c *client) CronGet(repoID, cronID int64) (*Cron, error) {
|
||||
out := new(Cron)
|
||||
uri := fmt.Sprintf(pathRepoCron, c.addr, repoID, cronID)
|
||||
return out, c.get(uri, out)
|
||||
}
|
||||
|
||||
// Pipeline returns a repository pipeline by pipeline-id.
|
||||
func (c *client) Pipeline(repoID, pipeline int64) (*Pipeline, error) {
|
||||
out := new(Pipeline)
|
||||
uri := fmt.Sprintf(pathPipeline, c.addr, repoID, pipeline)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// Pipeline returns the latest repository pipeline by branch.
|
||||
func (c *client) PipelineLast(repoID int64, branch string) (*Pipeline, error) {
|
||||
out := new(Pipeline)
|
||||
uri := fmt.Sprintf(pathPipeline, c.addr, repoID, "latest")
|
||||
if len(branch) != 0 {
|
||||
uri += "?branch=" + branch
|
||||
}
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// PipelineList returns a list of recent pipelines for the
|
||||
// the specified repository.
|
||||
func (c *client) PipelineList(repoID int64) ([]*Pipeline, error) {
|
||||
var out []*Pipeline
|
||||
uri := fmt.Sprintf(pathPipelines, c.addr, repoID)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// PipelineCreate creates a new pipeline for the specified repository.
|
||||
func (c *client) PipelineCreate(repoID int64, options *PipelineOptions) (*Pipeline, error) {
|
||||
var out *Pipeline
|
||||
uri := fmt.Sprintf(pathPipelines, c.addr, repoID)
|
||||
err := c.post(uri, options, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// PipelineStart re-starts a stopped pipeline.
|
||||
func (c *client) PipelineStart(repoID, pipeline int64, params map[string]string) (*Pipeline, error) {
|
||||
out := new(Pipeline)
|
||||
val := mapValues(params)
|
||||
uri := fmt.Sprintf(pathPipeline, c.addr, repoID, pipeline)
|
||||
err := c.post(uri+"?"+val.Encode(), nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// PipelineStop cancels the running step.
|
||||
func (c *client) PipelineStop(repoID, pipeline int64) error {
|
||||
uri := fmt.Sprintf(pathStop, c.addr, repoID, pipeline)
|
||||
err := c.post(uri, nil, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// PipelineApprove approves a blocked pipeline.
|
||||
func (c *client) PipelineApprove(repoID, pipeline int64) (*Pipeline, error) {
|
||||
out := new(Pipeline)
|
||||
uri := fmt.Sprintf(pathApprove, c.addr, repoID, pipeline)
|
||||
err := c.post(uri, nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// PipelineDecline declines a blocked pipeline.
|
||||
func (c *client) PipelineDecline(repoID, pipeline int64) (*Pipeline, error) {
|
||||
out := new(Pipeline)
|
||||
uri := fmt.Sprintf(pathDecline, c.addr, repoID, pipeline)
|
||||
err := c.post(uri, nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// PipelineKill force kills the running pipeline.
|
||||
func (c *client) PipelineKill(repoID, pipeline int64) error {
|
||||
uri := fmt.Sprintf(pathPipeline, c.addr, repoID, pipeline)
|
||||
err := c.delete(uri)
|
||||
return err
|
||||
}
|
||||
|
||||
// LogsPurge purges the pipeline all steps logs for the specified pipeline.
|
||||
func (c *client) LogsPurge(repoID, pipeline int64) error {
|
||||
uri := fmt.Sprintf(pathPipelineLogs, c.addr, repoID, pipeline)
|
||||
err := c.delete(uri)
|
||||
return err
|
||||
}
|
||||
|
||||
// Deploy triggers a deployment for an existing pipeline using the
|
||||
// specified target environment.
|
||||
func (c *client) Deploy(repoID, pipeline int64, env string, params map[string]string) (*Pipeline, error) {
|
||||
out := new(Pipeline)
|
||||
val := mapValues(params)
|
||||
val.Set("event", EventDeploy)
|
||||
val.Set("deploy_to", env)
|
||||
uri := fmt.Sprintf(pathPipeline, c.addr, repoID, pipeline)
|
||||
err := c.post(uri+"?"+val.Encode(), nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// StepLogEntries returns the pipeline logs for the specified step.
|
||||
func (c *client) StepLogEntries(repoID, num, step int64) ([]*LogEntry, error) {
|
||||
uri := fmt.Sprintf(pathStepLogs, c.addr, repoID, num, step)
|
||||
var out []*LogEntry
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// StepLogsPurge purges the pipeline logs for the specified step.
|
||||
func (c *client) StepLogsPurge(repoID, pipelineNumber, stepID int64) error {
|
||||
uri := fmt.Sprintf(pathStepLogs, c.addr, repoID, pipelineNumber, stepID)
|
||||
err := c.delete(uri)
|
||||
return err
|
||||
}
|
|
@ -181,12 +181,22 @@ type (
|
|||
Commit string `json:"commit,omitempty"`
|
||||
}
|
||||
|
||||
// QueueStats struct {
|
||||
// Workers int `json:"worker_count"`
|
||||
// Pending int `json:"pending_count"`
|
||||
// WaitingOnDeps int `json:"waiting_on_deps_count"`
|
||||
// Running int `json:"running_count"`
|
||||
// Complete int `json:"completed_count"`
|
||||
// }
|
||||
|
||||
// Info provides queue stats.
|
||||
Info struct {
|
||||
Pending []Task `json:"pending"`
|
||||
WaitingOnDeps []Task `json:"waiting_on_deps"`
|
||||
Running []Task `json:"running"`
|
||||
Stats struct {
|
||||
// TODO use dedicated struct in 3.x
|
||||
// Stats QueueStats `json:"stats"`
|
||||
Stats struct {
|
||||
Workers int `json:"worker_count"`
|
||||
Pending int `json:"pending_count"`
|
||||
WaitingOnDeps int `json:"waiting_on_deps_count"`
|
||||
|
@ -196,7 +206,7 @@ type (
|
|||
Paused bool `json:"paused,omitempty"`
|
||||
}
|
||||
|
||||
// LogLevel is for checking/setting logging level
|
||||
// LogLevel is for checking/setting logging level.
|
||||
LogLevel struct {
|
||||
Level string `json:"log-level"`
|
||||
}
|
||||
|
@ -211,7 +221,7 @@ type (
|
|||
Type LogEntryType `json:"type"`
|
||||
}
|
||||
|
||||
// Cron is the JSON data of a cron job
|
||||
// Cron is the JSON data of a cron job.
|
||||
Cron struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
@ -223,13 +233,13 @@ type (
|
|||
Branch string `json:"branch"`
|
||||
}
|
||||
|
||||
// PipelineOptions is the JSON data for creating a new pipeline
|
||||
// PipelineOptions is the JSON data for creating a new pipeline.
|
||||
PipelineOptions struct {
|
||||
Branch string `json:"branch"`
|
||||
Variables map[string]string `json:"variables"`
|
||||
}
|
||||
|
||||
// Agent is the JSON data for an agent
|
||||
// Agent is the JSON data for an agent.
|
||||
Agent struct {
|
||||
ID int64 `json:"id"`
|
||||
Created int64 `json:"created"`
|
||||
|
@ -245,7 +255,7 @@ type (
|
|||
NoSchedule bool `json:"no_schedule"`
|
||||
}
|
||||
|
||||
// Task is the JSON data for a task
|
||||
// Task is the JSON data for a task.
|
||||
Task struct {
|
||||
ID string `json:"id"`
|
||||
Data []byte `json:"data"`
|
||||
|
@ -256,7 +266,7 @@ type (
|
|||
AgentID int64 `json:"agent_id"`
|
||||
}
|
||||
|
||||
// Org is the JSON data for an organization
|
||||
// Org is the JSON data for an organization.
|
||||
Org struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
|
75
woodpecker-go/woodpecker/user.go
Normal file
75
woodpecker-go/woodpecker/user.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package woodpecker
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
pathSelf = "%s/api/user"
|
||||
pathRepos = "%s/api/user/repos"
|
||||
pathUsers = "%s/api/users"
|
||||
pathUser = "%s/api/users/%s"
|
||||
)
|
||||
|
||||
// Self returns the currently authenticated user.
|
||||
func (c *client) Self() (*User, error) {
|
||||
out := new(User)
|
||||
uri := fmt.Sprintf(pathSelf, c.addr)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// User returns a user by login.
|
||||
func (c *client) User(login string) (*User, error) {
|
||||
out := new(User)
|
||||
uri := fmt.Sprintf(pathUser, c.addr, login)
|
||||
err := c.get(uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// UserList returns a list of all registered users.
|
||||
func (c *client) UserList() ([]*User, error) {
|
||||
var out []*User
|
||||
uri := fmt.Sprintf(pathUsers, c.addr)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// UserPost creates a new user account.
|
||||
func (c *client) UserPost(in *User) (*User, error) {
|
||||
out := new(User)
|
||||
uri := fmt.Sprintf(pathUsers, c.addr)
|
||||
err := c.post(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// UserPatch updates a user account.
|
||||
func (c *client) UserPatch(in *User) (*User, error) {
|
||||
out := new(User)
|
||||
uri := fmt.Sprintf(pathUser, c.addr, in.Login)
|
||||
err := c.patch(uri, in, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// UserDel deletes a user account.
|
||||
func (c *client) UserDel(login string) error {
|
||||
uri := fmt.Sprintf(pathUser, c.addr, login)
|
||||
err := c.delete(uri)
|
||||
return err
|
||||
}
|
||||
|
||||
// RepoList returns a list of all repositories to which
|
||||
// the user has explicit access in the host system.
|
||||
func (c *client) RepoList() ([]*Repo, error) {
|
||||
var out []*Repo
|
||||
uri := fmt.Sprintf(pathRepos, c.addr)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// RepoListOpts returns a list of all repositories to which
|
||||
// the user has explicit access in the host system.
|
||||
func (c *client) RepoListOpts(all bool) ([]*Repo, error) {
|
||||
var out []*Repo
|
||||
uri := fmt.Sprintf(pathRepos+"?all=%v", c.addr, all)
|
||||
err := c.get(uri, &out)
|
||||
return out, err
|
||||
}
|
267
woodpecker-go/woodpecker/user_test.go
Normal file
267
woodpecker-go/woodpecker/user_test.go
Normal file
|
@ -0,0 +1,267 @@
|
|||
package woodpecker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestClient_UserList(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
expected []*User
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprint(w, `[{"id":1,"login":"user1"},{"id":2,"login":"user2"}]`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
expected: []*User{{ID: 1, Login: "user1"}, {ID: 2, Login: "user2"}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty response",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprint(w, `[]`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
expected: []*User{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "server error",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ts := httptest.NewServer(tt.handler)
|
||||
defer ts.Close()
|
||||
|
||||
client := NewClient(ts.URL, http.DefaultClient)
|
||||
users, err := client.UserList()
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, users, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_UserPost(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
input *User
|
||||
expected *User
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_, err := fmt.Fprint(w, `{"id":1,"login":"new_user"}`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
input: &User{Login: "new_user"},
|
||||
expected: &User{ID: 1, Login: "new_user"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid input",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
},
|
||||
input: &User{},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "server error",
|
||||
handler: func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
},
|
||||
input: &User{Login: "new_user"},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ts := httptest.NewServer(tt.handler)
|
||||
defer ts.Close()
|
||||
|
||||
client := NewClient(ts.URL, http.DefaultClient)
|
||||
user, err := client.UserPost(tt.input)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, user, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_UserPatch(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
input *User
|
||||
expected *User
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPatch {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := fmt.Fprint(w, `{"id":1,"login":"updated_user"}`)
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
input: &User{ID: 1, Login: "existing_user"},
|
||||
expected: &User{ID: 1, Login: "updated_user"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPatch {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
},
|
||||
input: &User{ID: 999, Login: "nonexistent_user"},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid input",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPatch {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
},
|
||||
input: &User{},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "server error",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPatch {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
},
|
||||
input: &User{ID: 1, Login: "existing_user"},
|
||||
expected: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ts := httptest.NewServer(tt.handler)
|
||||
defer ts.Close()
|
||||
|
||||
client := NewClient(ts.URL, http.DefaultClient)
|
||||
user, err := client.UserPatch(tt.input)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, user, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_UserDel(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handler http.HandlerFunc
|
||||
login string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
},
|
||||
login: "existing_user",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
},
|
||||
login: "nonexistent_user",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "server error",
|
||||
handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
},
|
||||
login: "existing_user",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ts := httptest.NewServer(tt.handler)
|
||||
defer ts.Close()
|
||||
|
||||
client := NewClient(ts.URL, http.DefaultClient)
|
||||
err := client.UserDel(tt.login)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue