mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-11 22:14:02 +01:00
console: move /sankey and /graph to /graph/sankey and /graph/line
Also rename stuff to GraphSankey and GraphLine for consistency.
This commit is contained in:
@@ -58,12 +58,12 @@ import {
|
||||
} from "./VisualizePage/OptionsPanel.vue";
|
||||
import type { GraphType } from "./VisualizePage/graphtypes";
|
||||
import type {
|
||||
SankeyHandlerInput,
|
||||
GraphHandlerInput,
|
||||
SankeyHandlerOutput,
|
||||
GraphHandlerOutput,
|
||||
SankeyHandlerResult,
|
||||
GraphHandlerResult,
|
||||
GraphSankeyHandlerInput,
|
||||
GraphLineHandlerInput,
|
||||
GraphSankeyHandlerOutput,
|
||||
GraphLineHandlerOutput,
|
||||
GraphSankeyHandlerResult,
|
||||
GraphLineHandlerResult,
|
||||
} from "./VisualizePage";
|
||||
import { isEqual, omit, pick } from "lodash-es";
|
||||
|
||||
@@ -125,7 +125,9 @@ watch(
|
||||
const encodedState = computed(() => encodeState(state.value));
|
||||
|
||||
// Fetch data
|
||||
const fetchedData = ref<GraphHandlerResult | SankeyHandlerResult | null>(null);
|
||||
const fetchedData = ref<
|
||||
GraphLineHandlerResult | GraphSankeyHandlerResult | null
|
||||
>(null);
|
||||
const orderedJSONPayload = <T extends Record<string, any>>(input: T): T => {
|
||||
return Object.keys(input)
|
||||
.sort()
|
||||
@@ -135,10 +137,10 @@ const orderedJSONPayload = <T extends Record<string, any>>(input: T): T => {
|
||||
) as T;
|
||||
};
|
||||
const jsonPayload = computed(
|
||||
(): SankeyHandlerInput | GraphHandlerInput | null => {
|
||||
(): GraphSankeyHandlerInput | GraphLineHandlerInput | null => {
|
||||
if (state.value === null) return null;
|
||||
if (state.value.graphType === "sankey") {
|
||||
const input: SankeyHandlerInput = {
|
||||
const input: GraphSankeyHandlerInput = {
|
||||
...omit(state.value, [
|
||||
"graphType",
|
||||
"bidirectional",
|
||||
@@ -149,7 +151,7 @@ const jsonPayload = computed(
|
||||
};
|
||||
return orderedJSONPayload(input);
|
||||
} else {
|
||||
const input: GraphHandlerInput = {
|
||||
const input: GraphLineHandlerInput = {
|
||||
...omit(state.value, [
|
||||
"graphType",
|
||||
"previousPeriod",
|
||||
@@ -176,20 +178,20 @@ const { data, execute, isFetching, aborted, abort, canAbort, error } = useFetch(
|
||||
return ctx;
|
||||
}
|
||||
const endpoint: Record<GraphType, string> = {
|
||||
stacked: "graph",
|
||||
stacked100: "graph",
|
||||
lines: "graph",
|
||||
grid: "graph",
|
||||
stacked: "line",
|
||||
stacked100: "line",
|
||||
lines: "line",
|
||||
grid: "line",
|
||||
sankey: "sankey",
|
||||
};
|
||||
const url = endpoint[state.value.graphType];
|
||||
return {
|
||||
...ctx,
|
||||
url: `/api/v0/console/${url}`,
|
||||
url: `/api/v0/console/graph/${url}`,
|
||||
};
|
||||
},
|
||||
async afterFetch(
|
||||
ctx: AfterFetchContext<GraphHandlerOutput | SankeyHandlerOutput>
|
||||
ctx: AfterFetchContext<GraphLineHandlerOutput | GraphSankeyHandlerOutput>
|
||||
) {
|
||||
// Update data. Not done in a computed value as we want to keep the
|
||||
// previous data in case of errors.
|
||||
@@ -203,13 +205,13 @@ const { data, execute, isFetching, aborted, abort, canAbort, error } = useFetch(
|
||||
if (state.value.graphType === "sankey") {
|
||||
fetchedData.value = {
|
||||
graphType: "sankey",
|
||||
...(data as SankeyHandlerOutput),
|
||||
...(data as GraphSankeyHandlerOutput),
|
||||
...pick(state.value, ["start", "end", "dimensions", "units"]),
|
||||
};
|
||||
} else {
|
||||
fetchedData.value = {
|
||||
graphType: state.value.graphType,
|
||||
...(data as GraphHandlerOutput),
|
||||
...(data as GraphLineHandlerOutput),
|
||||
...pick(state.value, [
|
||||
"start",
|
||||
"end",
|
||||
@@ -240,7 +242,9 @@ const { data, execute, isFetching, aborted, abort, canAbort, error } = useFetch(
|
||||
}
|
||||
)
|
||||
.post(jsonPayload, "json")
|
||||
.json<GraphHandlerOutput | SankeyHandlerOutput | { message: string }>();
|
||||
.json<
|
||||
GraphLineHandlerOutput | GraphSankeyHandlerOutput | { message: string }
|
||||
>();
|
||||
watch(jsonPayload, () => execute(), { immediate: true });
|
||||
|
||||
const errorMessage = computed(() => {
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject } from "vue";
|
||||
import DataGraphTimeSeries from "./DataGraphTimeSeries.vue";
|
||||
import DataGraphLine from "./DataGraphLine.vue";
|
||||
import DataGraphSankey from "./DataGraphSankey.vue";
|
||||
import type { GraphHandlerResult, SankeyHandlerResult } from ".";
|
||||
import type { GraphLineHandlerResult, GraphSankeyHandlerResult } from ".";
|
||||
import { ThemeKey } from "@/components/ThemeProvider.vue";
|
||||
const { isDark } = inject(ThemeKey)!;
|
||||
|
||||
const props = defineProps<{
|
||||
data: GraphHandlerResult | SankeyHandlerResult | null;
|
||||
data: GraphLineHandlerResult | GraphSankeyHandlerResult | null;
|
||||
}>();
|
||||
|
||||
const component = computed(() => {
|
||||
@@ -28,7 +28,7 @@ const component = computed(() => {
|
||||
case "stacked100":
|
||||
case "lines":
|
||||
case "grid":
|
||||
return DataGraphTimeSeries;
|
||||
return DataGraphLine;
|
||||
case "sankey":
|
||||
return DataGraphSankey;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { ref, watch, inject, computed, onMounted, nextTick } from "vue";
|
||||
import { useMediaQuery } from "@vueuse/core";
|
||||
import { formatXps, dataColor, dataColorGrey } from "@/utils";
|
||||
import { ThemeKey } from "@/components/ThemeProvider.vue";
|
||||
import type { GraphHandlerResult } from ".";
|
||||
import type { GraphLineHandlerResult } from ".";
|
||||
import { uniqWith, isEqual, findIndex } from "lodash-es";
|
||||
import { use, graphic, type ComposeOption } from "echarts/core";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
@@ -58,7 +58,7 @@ type ECOption = ComposeOption<
|
||||
>;
|
||||
|
||||
const props = defineProps<{
|
||||
data: GraphHandlerResult;
|
||||
data: GraphLineHandlerResult;
|
||||
highlight: number | null;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
@@ -9,7 +9,7 @@
|
||||
import { inject, computed } from "vue";
|
||||
import { formatXps, dataColor, dataColorGrey } from "@/utils";
|
||||
import { ThemeKey } from "@/components/ThemeProvider.vue";
|
||||
import type { SankeyHandlerResult } from ".";
|
||||
import type { GraphSankeyHandlerResult } from ".";
|
||||
import { use, type ComposeOption } from "echarts/core";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
import { SankeyChart, type SankeySeriesOption } from "echarts/charts";
|
||||
@@ -26,7 +26,7 @@ type ECOption = ComposeOption<
|
||||
>;
|
||||
|
||||
const props = defineProps<{
|
||||
data: SankeyHandlerResult;
|
||||
data: GraphSankeyHandlerResult;
|
||||
}>();
|
||||
|
||||
const { isDark } = inject(ThemeKey)!;
|
||||
|
||||
@@ -90,11 +90,11 @@ import { computed, inject, ref } from "vue";
|
||||
import { uniqWith, isEqual, findIndex, takeWhile, toPairs } from "lodash-es";
|
||||
import { formatXps, dataColor, dataColorGrey } from "@/utils";
|
||||
import { ThemeKey } from "@/components/ThemeProvider.vue";
|
||||
import type { GraphHandlerResult, SankeyHandlerResult } from ".";
|
||||
import type { GraphLineHandlerResult, GraphSankeyHandlerResult } from ".";
|
||||
const { isDark } = inject(ThemeKey)!;
|
||||
|
||||
const props = defineProps<{
|
||||
data: GraphHandlerResult | SankeyHandlerResult | null;
|
||||
data: GraphLineHandlerResult | GraphSankeyHandlerResult | null;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: "highlighted", index: number | null): void;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import type { GraphType } from "./graphtypes";
|
||||
|
||||
export type Units = "l3bps" | "l2bps" | "pps" | "inl2%" | "outl2%";
|
||||
export type SankeyHandlerInput = {
|
||||
export type GraphSankeyHandlerInput = {
|
||||
start: string;
|
||||
end: string;
|
||||
dimensions: string[];
|
||||
@@ -12,12 +12,12 @@ export type SankeyHandlerInput = {
|
||||
filter: string;
|
||||
units: Units;
|
||||
};
|
||||
export type GraphHandlerInput = SankeyHandlerInput & {
|
||||
export type GraphLineHandlerInput = GraphSankeyHandlerInput & {
|
||||
points: number;
|
||||
bidirectional: boolean;
|
||||
"previous-period": boolean;
|
||||
};
|
||||
export type SankeyHandlerOutput = {
|
||||
export type GraphSankeyHandlerOutput = {
|
||||
rows: string[][];
|
||||
xps: number[];
|
||||
nodes: string[];
|
||||
@@ -27,7 +27,7 @@ export type SankeyHandlerOutput = {
|
||||
xps: number;
|
||||
}[];
|
||||
};
|
||||
export type GraphHandlerOutput = {
|
||||
export type GraphLineHandlerOutput = {
|
||||
t: string[];
|
||||
rows: string[][];
|
||||
points: number[][];
|
||||
@@ -38,12 +38,12 @@ export type GraphHandlerOutput = {
|
||||
max: number[];
|
||||
"95th": number[];
|
||||
};
|
||||
export type SankeyHandlerResult = SankeyHandlerOutput & {
|
||||
export type GraphSankeyHandlerResult = GraphSankeyHandlerOutput & {
|
||||
graphType: Extract<GraphType, "sankey">;
|
||||
} & Pick<SankeyHandlerInput, "start" | "end" | "dimensions" | "units">;
|
||||
export type GraphHandlerResult = GraphHandlerOutput & {
|
||||
} & Pick<GraphSankeyHandlerInput, "start" | "end" | "dimensions" | "units">;
|
||||
export type GraphLineHandlerResult = GraphLineHandlerOutput & {
|
||||
graphType: Exclude<GraphType, "sankey">;
|
||||
} & Pick<
|
||||
GraphHandlerInput,
|
||||
GraphLineHandlerInput,
|
||||
"start" | "end" | "dimensions" | "units" | "bidirectional"
|
||||
>;
|
||||
|
||||
@@ -18,8 +18,8 @@ import (
|
||||
"akvorado/console/query"
|
||||
)
|
||||
|
||||
// graphHandlerInput describes the input for the /graph endpoint.
|
||||
type graphHandlerInput struct {
|
||||
// graphLineHandlerInput describes the input for the /graph/line endpoint.
|
||||
type graphLineHandlerInput struct {
|
||||
schema *schema.Component
|
||||
Start time.Time `json:"start" binding:"required"`
|
||||
End time.Time `json:"end" binding:"required,gtfield=Start"`
|
||||
@@ -32,11 +32,11 @@ type graphHandlerInput struct {
|
||||
PreviousPeriod bool `json:"previous-period"`
|
||||
}
|
||||
|
||||
// graphHandlerOutput describes the output for the /graph endpoint. A
|
||||
// graphLineHandlerOutput describes the output for the /graph/line endpoint. A
|
||||
// row is a set of values for dimensions. Currently, axis 1 is for the
|
||||
// direct direction and axis 2 is for the reverse direction. Rows are
|
||||
// sorted by axis, then by the sum of traffic.
|
||||
type graphHandlerOutput struct {
|
||||
type graphLineHandlerOutput struct {
|
||||
Time []time.Time `json:"t"`
|
||||
Rows [][]string `json:"rows"` // List of rows
|
||||
Points [][]int `json:"points"` // t → row → xps
|
||||
@@ -50,7 +50,7 @@ type graphHandlerOutput struct {
|
||||
|
||||
// reverseDirection reverts the direction of a provided input. It does not
|
||||
// modify the original.
|
||||
func (input graphHandlerInput) reverseDirection() graphHandlerInput {
|
||||
func (input graphLineHandlerInput) reverseDirection() graphLineHandlerInput {
|
||||
input.Filter.Swap()
|
||||
input.Dimensions = slices.Clone(input.Dimensions)
|
||||
query.Columns(input.Dimensions).Reverse(input.schema)
|
||||
@@ -82,7 +82,7 @@ func nearestPeriod(period time.Duration) (time.Duration, string) {
|
||||
// period, this is the day. For less than 2-weeks, this is the week,
|
||||
// for less than 2-months, this is the month, otherwise, this is the
|
||||
// year. Also, dimensions are stripped.
|
||||
func (input graphHandlerInput) previousPeriod() graphHandlerInput {
|
||||
func (input graphLineHandlerInput) previousPeriod() graphLineHandlerInput {
|
||||
input.Dimensions = []query.Column{}
|
||||
diff := input.End.Sub(input.Start)
|
||||
period, _ := nearestPeriod(diff)
|
||||
@@ -105,7 +105,7 @@ type toSQL1Options struct {
|
||||
offsetedStart time.Time
|
||||
}
|
||||
|
||||
func (input graphHandlerInput) toSQL1(axis int, options toSQL1Options) string {
|
||||
func (input graphLineHandlerInput) toSQL1(axis int, options toSQL1Options) string {
|
||||
var startForInterval *time.Time
|
||||
var offsetShift string
|
||||
if !options.offsetedStart.IsZero() {
|
||||
@@ -195,8 +195,8 @@ ORDER BY time WITH FILL
|
||||
return strings.TrimSpace(sqlQuery)
|
||||
}
|
||||
|
||||
// graphHandlerInputToSQL converts a graph input to an SQL request
|
||||
func (input graphHandlerInput) toSQL() string {
|
||||
// toSQL converts a graph input to an SQL request
|
||||
func (input graphLineHandlerInput) toSQL() string {
|
||||
parts := []string{input.toSQL1(1, toSQL1Options{})}
|
||||
// Handle specific options. We have to align time periods in
|
||||
// case the previous period does not use the same offsets.
|
||||
@@ -222,9 +222,9 @@ func (input graphHandlerInput) toSQL() string {
|
||||
return strings.Join(parts, "\nUNION ALL\n")
|
||||
}
|
||||
|
||||
func (c *Component) graphHandlerFunc(gc *gin.Context) {
|
||||
func (c *Component) graphLineHandlerFunc(gc *gin.Context) {
|
||||
ctx := c.t.Context(gc.Request.Context())
|
||||
input := graphHandlerInput{schema: c.d.Schema}
|
||||
input := graphLineHandlerInput{schema: c.d.Schema}
|
||||
if err := gc.ShouldBindJSON(&input); err != nil {
|
||||
gc.JSON(http.StatusBadRequest, gin.H{"message": helpers.Capitalize(err.Error())})
|
||||
return
|
||||
@@ -276,7 +276,7 @@ func (c *Component) graphHandlerFunc(gc *gin.Context) {
|
||||
}
|
||||
|
||||
// Set time axis. We assume the first returned axis has the complete view.
|
||||
output := graphHandlerOutput{
|
||||
output := graphLineHandlerOutput{
|
||||
Time: []time.Time{},
|
||||
}
|
||||
lastTime := time.Time{}
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
"akvorado/console/query"
|
||||
)
|
||||
|
||||
func TestGraphInputReverseDirection(t *testing.T) {
|
||||
input := graphHandlerInput{
|
||||
func TestGraphLineInputReverseDirection(t *testing.T) {
|
||||
input := graphLineHandlerInput{
|
||||
schema: schema.NewMock(t),
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
@@ -31,7 +31,7 @@ func TestGraphInputReverseDirection(t *testing.T) {
|
||||
Units: "l3bps",
|
||||
}
|
||||
original1 := fmt.Sprintf("%+v", input)
|
||||
expected := graphHandlerInput{
|
||||
expected := graphLineHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Points: 100,
|
||||
@@ -115,7 +115,7 @@ func TestGraphPreviousPeriod(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("time.Parse(%q) error:\n%+v", tc.ExpectedEnd, err)
|
||||
}
|
||||
input := graphHandlerInput{
|
||||
input := graphLineHandlerInput{
|
||||
schema: schema.NewMock(t),
|
||||
Start: start,
|
||||
End: end,
|
||||
@@ -126,7 +126,7 @@ func TestGraphPreviousPeriod(t *testing.T) {
|
||||
}
|
||||
query.Columns(input.Dimensions).Validate(input.schema)
|
||||
got := input.previousPeriod()
|
||||
expected := graphHandlerInput{
|
||||
expected := graphLineHandlerInput{
|
||||
Start: expectedStart,
|
||||
End: expectedEnd,
|
||||
Dimensions: []query.Column{},
|
||||
@@ -141,12 +141,12 @@ func TestGraphPreviousPeriod(t *testing.T) {
|
||||
func TestGraphQuerySQL(t *testing.T) {
|
||||
cases := []struct {
|
||||
Description string
|
||||
Input graphHandlerInput
|
||||
Input graphLineHandlerInput
|
||||
Expected string
|
||||
}{
|
||||
{
|
||||
Description: "no dimensions, no filters, bps",
|
||||
Input: graphHandlerInput{
|
||||
Input: graphLineHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Points: 100,
|
||||
@@ -172,7 +172,7 @@ ORDER BY time WITH FILL
|
||||
{{ end }}`,
|
||||
}, {
|
||||
Description: "no dimensions, no filters, l2 bps",
|
||||
Input: graphHandlerInput{
|
||||
Input: graphLineHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Points: 100,
|
||||
@@ -199,7 +199,7 @@ ORDER BY time WITH FILL
|
||||
`,
|
||||
}, {
|
||||
Description: "no dimensions, no filters, pps",
|
||||
Input: graphHandlerInput{
|
||||
Input: graphLineHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Points: 100,
|
||||
@@ -225,7 +225,7 @@ ORDER BY time WITH FILL
|
||||
{{ end }}`,
|
||||
}, {
|
||||
Description: "no dimensions",
|
||||
Input: graphHandlerInput{
|
||||
Input: graphLineHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Points: 100,
|
||||
@@ -251,7 +251,7 @@ ORDER BY time WITH FILL
|
||||
{{ end }}`,
|
||||
}, {
|
||||
Description: "no dimensions, escaped filter",
|
||||
Input: graphHandlerInput{
|
||||
Input: graphLineHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Points: 100,
|
||||
@@ -277,7 +277,7 @@ ORDER BY time WITH FILL
|
||||
{{ end }}`,
|
||||
}, {
|
||||
Description: "no dimensions, reverse direction",
|
||||
Input: graphHandlerInput{
|
||||
Input: graphLineHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Points: 100,
|
||||
@@ -320,7 +320,7 @@ ORDER BY time WITH FILL
|
||||
{{ end }}`,
|
||||
}, {
|
||||
Description: "no dimensions, reverse direction, inl2%",
|
||||
Input: graphHandlerInput{
|
||||
Input: graphLineHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Points: 100,
|
||||
@@ -363,7 +363,7 @@ ORDER BY time WITH FILL
|
||||
{{ end }}`,
|
||||
}, {
|
||||
Description: "no filters",
|
||||
Input: graphHandlerInput{
|
||||
Input: graphLineHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Points: 100,
|
||||
@@ -395,7 +395,7 @@ ORDER BY time WITH FILL
|
||||
{{ end }}`,
|
||||
}, {
|
||||
Description: "no filters, reverse",
|
||||
Input: graphHandlerInput{
|
||||
Input: graphLineHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Points: 100,
|
||||
@@ -444,7 +444,7 @@ ORDER BY time WITH FILL
|
||||
{{ end }}`,
|
||||
}, {
|
||||
Description: "no filters, previous period",
|
||||
Input: graphHandlerInput{
|
||||
Input: graphLineHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Points: 100,
|
||||
@@ -512,7 +512,7 @@ ORDER BY time WITH FILL
|
||||
}
|
||||
}
|
||||
|
||||
func TestGraphHandler(t *testing.T) {
|
||||
func TestGraphLineHandler(t *testing.T) {
|
||||
_, h, mockConn, _ := NewMock(t, DefaultConfiguration())
|
||||
base := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
|
||||
|
||||
@@ -622,7 +622,7 @@ func TestGraphHandler(t *testing.T) {
|
||||
helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
|
||||
{
|
||||
Description: "single direction",
|
||||
URL: "/api/v0/console/graph",
|
||||
URL: "/api/v0/console/graph/line",
|
||||
JSONInput: gin.H{
|
||||
"start": time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
"end": time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
@@ -697,7 +697,7 @@ func TestGraphHandler(t *testing.T) {
|
||||
},
|
||||
}, {
|
||||
Description: "bidirectional",
|
||||
URL: "/api/v0/console/graph",
|
||||
URL: "/api/v0/console/graph/line",
|
||||
JSONInput: gin.H{
|
||||
"start": time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
"end": time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
@@ -816,7 +816,7 @@ func TestGraphHandler(t *testing.T) {
|
||||
},
|
||||
}, {
|
||||
Description: "previous period",
|
||||
URL: "/api/v0/console/graph",
|
||||
URL: "/api/v0/console/graph/line",
|
||||
JSONInput: gin.H{
|
||||
"start": time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
"end": time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
@@ -92,8 +92,8 @@ func (c *Component) Start() error {
|
||||
endpoint.GET("/widget/exporters", c.d.HTTP.CacheByRequestPath(30*time.Second), c.widgetExportersHandlerFunc)
|
||||
endpoint.GET("/widget/top/:name", c.d.HTTP.CacheByRequestPath(30*time.Second), c.widgetTopHandlerFunc)
|
||||
endpoint.GET("/widget/graph", c.d.HTTP.CacheByRequestPath(5*time.Minute), c.widgetGraphHandlerFunc)
|
||||
endpoint.POST("/graph", c.d.HTTP.CacheByRequestBody(c.config.CacheTTL), c.graphHandlerFunc)
|
||||
endpoint.POST("/sankey", c.d.HTTP.CacheByRequestBody(c.config.CacheTTL), c.sankeyHandlerFunc)
|
||||
endpoint.POST("/graph/line", c.d.HTTP.CacheByRequestBody(c.config.CacheTTL), c.graphLineHandlerFunc)
|
||||
endpoint.POST("/graph/sankey", c.d.HTTP.CacheByRequestBody(c.config.CacheTTL), c.graphSankeyHandlerFunc)
|
||||
endpoint.POST("/filter/validate", c.filterValidateHandlerFunc)
|
||||
endpoint.POST("/filter/complete", c.d.HTTP.CacheByRequestBody(time.Minute), c.filterCompleteHandlerFunc)
|
||||
endpoint.GET("/filter/saved", c.filterSavedListHandlerFunc)
|
||||
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
"akvorado/console/query"
|
||||
)
|
||||
|
||||
// sankeyHandlerInput describes the input for the /sankey endpoint.
|
||||
type sankeyHandlerInput struct {
|
||||
// graphSankeyHandlerInput describes the input for the /graph/sankey endpoint.
|
||||
type graphSankeyHandlerInput struct {
|
||||
schema *schema.Component
|
||||
Start time.Time `json:"start" binding:"required"`
|
||||
End time.Time `json:"end" binding:"required,gtfield=Start"`
|
||||
@@ -28,8 +28,8 @@ type sankeyHandlerInput struct {
|
||||
Units string `json:"units" binding:"required,oneof=pps l3bps l2bps inl2% outl2%"`
|
||||
}
|
||||
|
||||
// sankeyHandlerOutput describes the output for the /sankey endpoint.
|
||||
type sankeyHandlerOutput struct {
|
||||
// graphSankeyHandlerOutput describes the output for the /graph/sankey endpoint.
|
||||
type graphSankeyHandlerOutput struct {
|
||||
// Unprocessed data for table view
|
||||
Rows [][]string `json:"rows"`
|
||||
Xps []int `json:"xps"` // row → xps
|
||||
@@ -44,7 +44,7 @@ type sankeyLink struct {
|
||||
}
|
||||
|
||||
// sankeyHandlerInputToSQL converts a sankey query to an SQL request
|
||||
func (input sankeyHandlerInput) toSQL() (string, error) {
|
||||
func (input graphSankeyHandlerInput) toSQL() (string, error) {
|
||||
where := templateWhere(input.Filter)
|
||||
|
||||
// Select
|
||||
@@ -95,9 +95,9 @@ ORDER BY xps DESC
|
||||
return strings.TrimSpace(sqlQuery), nil
|
||||
}
|
||||
|
||||
func (c *Component) sankeyHandlerFunc(gc *gin.Context) {
|
||||
func (c *Component) graphSankeyHandlerFunc(gc *gin.Context) {
|
||||
ctx := c.t.Context(gc.Request.Context())
|
||||
input := sankeyHandlerInput{schema: c.d.Schema}
|
||||
input := graphSankeyHandlerInput{schema: c.d.Schema}
|
||||
if err := gc.ShouldBindJSON(&input); err != nil {
|
||||
gc.JSON(http.StatusBadRequest, gin.H{"message": helpers.Capitalize(err.Error())})
|
||||
return
|
||||
@@ -131,7 +131,7 @@ func (c *Component) sankeyHandlerFunc(gc *gin.Context) {
|
||||
}
|
||||
|
||||
// Prepare output
|
||||
output := sankeyHandlerOutput{
|
||||
output := graphSankeyHandlerOutput{
|
||||
Rows: make([][]string, 0, len(results)),
|
||||
Xps: make([]int, 0, len(results)),
|
||||
Nodes: make([]string, 0),
|
||||
|
||||
@@ -19,12 +19,12 @@ import (
|
||||
func TestSankeyQuerySQL(t *testing.T) {
|
||||
cases := []struct {
|
||||
Description string
|
||||
Input sankeyHandlerInput
|
||||
Input graphSankeyHandlerInput
|
||||
Expected string
|
||||
}{
|
||||
{
|
||||
Description: "two dimensions, no filters, l3 bps",
|
||||
Input: sankeyHandlerInput{
|
||||
Input: graphSankeyHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Dimensions: []query.Column{
|
||||
@@ -51,7 +51,7 @@ ORDER BY xps DESC
|
||||
{{ end }}`,
|
||||
}, {
|
||||
Description: "two dimensions, no filters, l2 bps",
|
||||
Input: sankeyHandlerInput{
|
||||
Input: graphSankeyHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Dimensions: []query.Column{
|
||||
@@ -79,7 +79,7 @@ ORDER BY xps DESC
|
||||
`,
|
||||
}, {
|
||||
Description: "two dimensions, no filters, pps",
|
||||
Input: sankeyHandlerInput{
|
||||
Input: graphSankeyHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Dimensions: []query.Column{
|
||||
@@ -106,7 +106,7 @@ ORDER BY xps DESC
|
||||
{{ end }}`,
|
||||
}, {
|
||||
Description: "two dimensions, with filter",
|
||||
Input: sankeyHandlerInput{
|
||||
Input: graphSankeyHandlerInput{
|
||||
Start: time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
End: time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
Dimensions: []query.Column{
|
||||
@@ -192,7 +192,7 @@ func TestSankeyHandler(t *testing.T) {
|
||||
|
||||
helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
|
||||
{
|
||||
URL: "/api/v0/console/sankey",
|
||||
URL: "/api/v0/console/graph/sankey",
|
||||
JSONInput: gin.H{
|
||||
"start": time.Date(2022, 4, 10, 15, 45, 10, 0, time.UTC),
|
||||
"end": time.Date(2022, 4, 11, 15, 45, 10, 0, time.UTC),
|
||||
|
||||
Reference in New Issue
Block a user