mirror of
https://github.com/grafana/grafana.git
synced 2026-07-29 15:59:50 -05:00
Playlist: Use a different go struct for sql service vs k8s (#76393)
This commit is contained in:
@@ -1,117 +0,0 @@
|
||||
---
|
||||
keywords:
|
||||
- grafana
|
||||
- schema
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
title: Playlist kind
|
||||
---
|
||||
> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
|
||||
|
||||
## Playlist
|
||||
|
||||
#### Maturity: [merged](../../../maturity/#merged)
|
||||
#### Version: 0.0
|
||||
|
||||
A playlist is a series of dashboards that is automatically rotated in the browser, on a configurable interval.
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|------------|---------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `metadata` | [object](#metadata) | **Yes** | | metadata contains embedded CommonMetadata and can be extended with custom string fields<br/>TODO: use CommonMetadata instead of redefining here; currently needs to be defined here<br/>without external reference as using the CommonMetadata reference breaks thema codegen. |
|
||||
| `spec` | [object](#spec) | **Yes** | | |
|
||||
| `status` | [object](#status) | **Yes** | | |
|
||||
|
||||
### Metadata
|
||||
|
||||
metadata contains embedded CommonMetadata and can be extended with custom string fields
|
||||
TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
|
||||
without external reference as using the CommonMetadata reference breaks thema codegen.
|
||||
|
||||
It extends [_kubeObjectMetadata](#_kubeobjectmetadata).
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|---------------------|------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `createdBy` | string | **Yes** | | |
|
||||
| `creationTimestamp` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `extraFields` | [object](#extrafields) | **Yes** | | extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata |
|
||||
| `finalizers` | string[] | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `labels` | map[string]string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `resourceVersion` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `uid` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
| `updateTimestamp` | string | **Yes** | | |
|
||||
| `updatedBy` | string | **Yes** | | |
|
||||
| `deletionTimestamp` | string | No | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* |
|
||||
|
||||
### _kubeObjectMetadata
|
||||
|
||||
_kubeObjectMetadata is metadata found in a kubernetes object's metadata field.
|
||||
It is not exhaustive and only includes fields which may be relevant to a kind's implementation,
|
||||
As it is also intended to be generic enough to function with any API Server.
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|---------------------|-------------------|----------|---------|-------------|
|
||||
| `creationTimestamp` | string | **Yes** | | |
|
||||
| `finalizers` | string[] | **Yes** | | |
|
||||
| `labels` | map[string]string | **Yes** | | |
|
||||
| `resourceVersion` | string | **Yes** | | |
|
||||
| `uid` | string | **Yes** | | |
|
||||
| `deletionTimestamp` | string | No | | |
|
||||
|
||||
### ExtraFields
|
||||
|
||||
extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
|
||||
### Spec
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|------------|---------------------------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `interval` | string | **Yes** | `5m` | Interval sets the time between switching views in a playlist.<br/>FIXME: Is this based on a standardized format or what options are available? Can datemath be used? |
|
||||
| `name` | string | **Yes** | | Name of the playlist. |
|
||||
| `uid` | string | **Yes** | | Unique playlist identifier. Generated on creation, either by the<br/>creator of the playlist of by the application. |
|
||||
| `items` | [PlaylistItem](#playlistitem)[] | No | | The ordered list of items that the playlist will iterate over.<br/>FIXME! This should not be optional, but changing it makes the godegen awkward |
|
||||
|
||||
### PlaylistItem
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|--------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `type` | string | **Yes** | | Type of the item.<br/>Possible values are: `dashboard_by_uid`, `dashboard_by_id`, `dashboard_by_tag`. |
|
||||
| `value` | string | **Yes** | | Value depends on type and describes the playlist item.<br/><br/> - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This<br/> is not portable as the numerical identifier is non-deterministic between different instances.<br/> Will be replaced by dashboard_by_uid in the future. (deprecated)<br/> - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All<br/> dashboards behind the tag will be added to the playlist.<br/> - dashboard_by_uid: The value is the dashboard UID |
|
||||
| `title` | string | No | | Title is an unused property -- it will be removed in the future |
|
||||
|
||||
### Status
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|--------------------|------------------------------------------------------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `additionalFields` | [object](#additionalfields) | No | | additionalFields is reserved for future use |
|
||||
| `operatorStates` | map[string][status.#OperatorState](#status.#operatorstate) | No | | operatorStates is a map of operator ID to operator state evaluations.<br/>Any operator which consumes this kind SHOULD add its state evaluation information to this field. |
|
||||
|
||||
### AdditionalFields
|
||||
|
||||
additionalFields is reserved for future use
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
|
||||
### Status.#OperatorState
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|--------------------|--------------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `lastEvaluation` | string | **Yes** | | lastEvaluation is the ResourceVersion last evaluated |
|
||||
| `state` | string | **Yes** | | state describes the state of the lastEvaluation.<br/>It is limited to three possible states for machine evaluation.<br/>Possible values are: `success`, `in_progress`, `failed`. |
|
||||
| `descriptiveState` | string | No | | descriptiveState is an optional more descriptive state field which has no requirements on format |
|
||||
| `details` | [object](#details) | No | | details contains any extra information that is operator-specific |
|
||||
|
||||
### Details
|
||||
|
||||
details contains any extra information that is operator-specific
|
||||
|
||||
| Property | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package kind
|
||||
|
||||
name: "Playlist"
|
||||
maturity: "merged"
|
||||
description: "A playlist is a series of dashboards that is automatically rotated in the browser, on a configurable interval."
|
||||
|
||||
lineage: schemas: [{
|
||||
version: [0, 0]
|
||||
schema: {
|
||||
spec: {
|
||||
// Unique playlist identifier. Generated on creation, either by the
|
||||
// creator of the playlist of by the application.
|
||||
uid: string
|
||||
|
||||
// Name of the playlist.
|
||||
name: string
|
||||
|
||||
// Interval sets the time between switching views in a playlist.
|
||||
// FIXME: Is this based on a standardized format or what options are available? Can datemath be used?
|
||||
interval: string | *"5m"
|
||||
|
||||
// The ordered list of items that the playlist will iterate over.
|
||||
// FIXME! This should not be optional, but changing it makes the godegen awkward
|
||||
items?: [...#PlaylistItem]
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
///////////////////////////////////////
|
||||
// Definitions (referenced above) are declared below
|
||||
|
||||
#PlaylistItem: {
|
||||
// Type of the item.
|
||||
type: "dashboard_by_uid" | "dashboard_by_id" | "dashboard_by_tag"
|
||||
// Value depends on type and describes the playlist item.
|
||||
//
|
||||
// - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This
|
||||
// is not portable as the numerical identifier is non-deterministic between different instances.
|
||||
// Will be replaced by dashboard_by_uid in the future. (deprecated)
|
||||
// - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All
|
||||
// dashboards behind the tag will be added to the playlist.
|
||||
// - dashboard_by_uid: The value is the dashboard UID
|
||||
value: string
|
||||
|
||||
// Title is an unused property -- it will be removed in the future
|
||||
title?: string
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
}]
|
||||
@@ -124,15 +124,6 @@ export type {
|
||||
// TODO generate code such that tsc enforces type compatibility between raw and veneer decls
|
||||
export type { LibraryPanel } from './veneer/librarypanel.types';
|
||||
|
||||
// Raw generated types from Playlist kind.
|
||||
export type {
|
||||
Playlist,
|
||||
PlaylistItem
|
||||
} from './raw/playlist/x/playlist_types.gen';
|
||||
|
||||
// Raw generated enums and default consts from playlist kind.
|
||||
export { defaultPlaylist } from './raw/playlist/x/playlist_types.gen';
|
||||
|
||||
// Raw generated types from Preferences kind.
|
||||
export type {
|
||||
Preferences,
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// TSResourceJenny
|
||||
// LatestMajorsOrXJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export interface PlaylistItem {
|
||||
/**
|
||||
* Title is an unused property -- it will be removed in the future
|
||||
*/
|
||||
title?: string;
|
||||
/**
|
||||
* Type of the item.
|
||||
*/
|
||||
type: ('dashboard_by_uid' | 'dashboard_by_id' | 'dashboard_by_tag');
|
||||
/**
|
||||
* Value depends on type and describes the playlist item.
|
||||
*
|
||||
* - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This
|
||||
* is not portable as the numerical identifier is non-deterministic between different instances.
|
||||
* Will be replaced by dashboard_by_uid in the future. (deprecated)
|
||||
* - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All
|
||||
* dashboards behind the tag will be added to the playlist.
|
||||
* - dashboard_by_uid: The value is the dashboard UID
|
||||
*/
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Playlist {
|
||||
/**
|
||||
* Interval sets the time between switching views in a playlist.
|
||||
* FIXME: Is this based on a standardized format or what options are available? Can datemath be used?
|
||||
*/
|
||||
interval: string;
|
||||
/**
|
||||
* The ordered list of items that the playlist will iterate over.
|
||||
* FIXME! This should not be optional, but changing it makes the godegen awkward
|
||||
*/
|
||||
items?: Array<PlaylistItem>;
|
||||
/**
|
||||
* Name of the playlist.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Unique playlist identifier. Generated on creation, either by the
|
||||
* creator of the playlist of by the application.
|
||||
*/
|
||||
uid: string;
|
||||
}
|
||||
|
||||
export const defaultPlaylist: Partial<Playlist> = {
|
||||
interval: '5m',
|
||||
items: [],
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
)
|
||||
|
||||
type namespaceMapper = func(orgId int64) string
|
||||
|
||||
func orgNamespaceMapper(orgId int64) string {
|
||||
if orgId == 1 {
|
||||
return "default"
|
||||
}
|
||||
return fmt.Sprintf("org-%d", orgId)
|
||||
}
|
||||
|
||||
func convertToK8sResource(v *playlist.PlaylistDTO, namespacer namespaceMapper) *Playlist {
|
||||
spec := Spec{
|
||||
Title: v.Name,
|
||||
Interval: v.Interval,
|
||||
}
|
||||
for _, item := range v.Items {
|
||||
spec.Items = append(spec.Items, Item{
|
||||
Type: ItemType(item.Type),
|
||||
Value: item.Value,
|
||||
})
|
||||
}
|
||||
return &Playlist{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Playlist",
|
||||
APIVersion: APIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: v.Uid,
|
||||
UID: types.UID(v.Uid),
|
||||
ResourceVersion: fmt.Sprintf("%d", v.UpdatedAt),
|
||||
CreationTimestamp: metav1.NewTime(time.UnixMilli(v.CreatedAt)),
|
||||
Namespace: namespacer(v.OrgID),
|
||||
},
|
||||
Spec: spec,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
)
|
||||
|
||||
func TestPlaylistConversion(t *testing.T) {
|
||||
src := &playlist.PlaylistDTO{
|
||||
OrgID: 3,
|
||||
Uid: "abc", // becomes k8s name
|
||||
Name: "MyPlaylists", // becomes title
|
||||
Interval: "10s",
|
||||
CreatedAt: 12345,
|
||||
UpdatedAt: 54321,
|
||||
Items: []playlist.PlaylistItemDTO{
|
||||
{Type: "dashboard_by_uid", Value: "UID0"},
|
||||
{Type: "dashboard_by_tag", Value: "tagA"},
|
||||
{Type: "dashboard_by_id", Value: "123"}, // deprecated
|
||||
},
|
||||
}
|
||||
dst := convertToK8sResource(src, orgNamespaceMapper)
|
||||
|
||||
require.Equal(t, "abc", src.Uid)
|
||||
require.Equal(t, "abc", dst.Name)
|
||||
require.Equal(t, src.Name, dst.Spec.Title)
|
||||
|
||||
out, err := json.MarshalIndent(dst, "", " ")
|
||||
require.NoError(t, err)
|
||||
//fmt.Printf("%s", string(out))
|
||||
require.JSONEq(t, `{
|
||||
"kind": "Playlist",
|
||||
"apiVersion": "playlist.x.grafana.com/v0alpha1",
|
||||
"metadata": {
|
||||
"name": "abc",
|
||||
"namespace": "org-3",
|
||||
"uid": "abc",
|
||||
"resourceVersion": "54321",
|
||||
"creationTimestamp": "1970-01-01T00:00:12Z"
|
||||
},
|
||||
"spec": {
|
||||
"title": "MyPlaylists",
|
||||
"interval": "10s",
|
||||
"items": [
|
||||
{
|
||||
"type": "dashboard_by_uid",
|
||||
"value": "UID0"
|
||||
},
|
||||
{
|
||||
"type": "dashboard_by_tag",
|
||||
"value": "tagA"
|
||||
},
|
||||
{
|
||||
"type": "dashboard_by_id",
|
||||
"value": "123"
|
||||
}
|
||||
]
|
||||
}
|
||||
}`, string(out))
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
playlistkind "github.com/grafana/grafana/pkg/kinds/playlist"
|
||||
grafanarequest "github.com/grafana/grafana/pkg/services/grafana-apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
)
|
||||
@@ -81,21 +80,14 @@ func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListO
|
||||
},
|
||||
}
|
||||
for _, v := range res {
|
||||
p := Playlist{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Playlist",
|
||||
APIVersion: APIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: v.UID,
|
||||
},
|
||||
Spec: playlistkind.Spec{
|
||||
Name: v.Name,
|
||||
Uid: v.UID,
|
||||
Interval: v.Interval,
|
||||
},
|
||||
p, err := s.service.Get(ctx, &playlist.GetPlaylistByUidQuery{
|
||||
UID: v.UID,
|
||||
OrgId: orgId, // required
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list.Items = append(list.Items, p)
|
||||
list.Items = append(list.Items, *convertToK8sResource(p, orgNamespaceMapper))
|
||||
}
|
||||
if len(list.Items) == limit {
|
||||
list.Continue = "<more>" // TODO?
|
||||
@@ -109,30 +101,16 @@ func (s *legacyStorage) Get(ctx context.Context, name string, options *metav1.Ge
|
||||
orgId = 1 // TODO: default org ID 1 for now
|
||||
}
|
||||
|
||||
p, err := s.service.Get(ctx, &playlist.GetPlaylistByUidQuery{
|
||||
dto, err := s.service.Get(ctx, &playlist.GetPlaylistByUidQuery{
|
||||
UID: name,
|
||||
OrgId: orgId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p == nil {
|
||||
if dto == nil {
|
||||
return nil, fmt.Errorf("not found?")
|
||||
}
|
||||
|
||||
return &Playlist{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Playlist",
|
||||
APIVersion: APIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: p.Uid,
|
||||
},
|
||||
Spec: playlistkind.Spec{
|
||||
Name: p.Name,
|
||||
Uid: p.Uid,
|
||||
Interval: p.Interval,
|
||||
Items: p.Items,
|
||||
},
|
||||
}, nil
|
||||
return convertToK8sResource(dto, orgNamespaceMapper), nil
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
common "k8s.io/kube-openapi/pkg/common"
|
||||
spec "k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
// NOTE: this must match the golang fully qualifid name!
|
||||
const kindKey = "github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Playlist"
|
||||
|
||||
func getOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
|
||||
return map[string]common.OpenAPIDefinition{
|
||||
kindKey: schema_pkg_Playlist(ref),
|
||||
kindKey + "List": schema_pkg_PlaylistList(ref),
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_Playlist(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Playlist",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
|
||||
},
|
||||
},
|
||||
// TODO! add a real spec here
|
||||
// "spec": {
|
||||
// SchemaProps: spec.SchemaProps{
|
||||
// Default: map[string]interface{}{},
|
||||
// Ref: ref("github.com/grafana/google-sheets-datasource/pkg/apis/googlesheets/v1.DatasourceSpec"),
|
||||
// },
|
||||
// },
|
||||
// "status": {
|
||||
// SchemaProps: spec.SchemaProps{
|
||||
// Default: map[string]interface{}{},
|
||||
// Ref: ref("github.com/grafana/google-sheets-datasource/pkg/apis/googlesheets/v1.DatasourceStatus"),
|
||||
// },
|
||||
// },
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_PlaylistList(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "PlaylistList",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref(kindKey),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"items"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
kindKey,
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
)
|
||||
|
||||
// GroupName is the group name for this API.
|
||||
const GroupName = "playlist.x.grafana.com"
|
||||
const VersionID = "v0alpha1" //
|
||||
const APIVersion = GroupName + "/" + VersionID
|
||||
|
||||
var _ grafanaapiserver.APIGroupBuilder = (*PlaylistAPIBuilder)(nil)
|
||||
|
||||
// This is used just so wire has something unique to return
|
||||
|
||||
@@ -2,10 +2,13 @@ package v0alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/kinds/playlist"
|
||||
)
|
||||
|
||||
// GroupName is the group name for this API.
|
||||
const GroupName = "playlist.x.grafana.com"
|
||||
const VersionID = "v0alpha1" //
|
||||
const APIVersion = GroupName + "/" + VersionID
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type Playlist struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
@@ -14,7 +17,7 @@ type Playlist struct {
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec playlist.Spec `json:"spec,omitempty"`
|
||||
Spec Spec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
@@ -23,5 +26,45 @@ type PlaylistList struct {
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []Playlist `json:"playlists,omitempty"`
|
||||
Items []Playlist `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
// Spec defines model for Spec.
|
||||
type Spec struct {
|
||||
// Name of the playlist.
|
||||
Title string `json:"title"`
|
||||
|
||||
// Interval sets the time between switching views in a playlist.
|
||||
Interval string `json:"interval"`
|
||||
|
||||
// The ordered list of items that the playlist will iterate over.
|
||||
Items []Item `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
// Defines values for ItemType.
|
||||
const (
|
||||
ItemTypeDashboardByTag ItemType = "dashboard_by_tag"
|
||||
ItemTypeDashboardByUid ItemType = "dashboard_by_uid"
|
||||
|
||||
// deprecated -- should use UID
|
||||
ItemTypeDashboardById ItemType = "dashboard_by_id"
|
||||
)
|
||||
|
||||
// Item defines model for Item.
|
||||
type Item struct {
|
||||
// Type of the item.
|
||||
Type ItemType `json:"type"`
|
||||
|
||||
// Value depends on type and describes the playlist item.
|
||||
//
|
||||
// - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This
|
||||
// is not portable as the numerical identifier is non-deterministic between different instances.
|
||||
// Will be replaced by dashboard_by_uid in the future. (deprecated)
|
||||
// - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All
|
||||
// dashboards behind the tag will be added to the playlist.
|
||||
// - dashboard_by_uid: The value is the dashboard UID
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// Type of the item.
|
||||
type ItemType string
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestPlaylistClone(t *testing.T) {
|
||||
src := Playlist{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Playlist",
|
||||
APIVersion: APIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "TheUID",
|
||||
ResourceVersion: "12345",
|
||||
CreationTimestamp: metav1.NewTime(time.Now()),
|
||||
Annotations: map[string]string{
|
||||
"grafana.com/updatedTime": time.Now().Format(time.RFC3339),
|
||||
},
|
||||
},
|
||||
Spec: Spec{
|
||||
Title: "A title",
|
||||
Interval: "20s",
|
||||
Items: []Item{
|
||||
{Type: ItemTypeDashboardByTag, Value: "graph-ng"},
|
||||
},
|
||||
},
|
||||
}
|
||||
copy := src.DeepCopyObject()
|
||||
|
||||
json0, err := json.Marshal(src)
|
||||
require.NoError(t, err)
|
||||
json1, err := json.Marshal(copy)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.JSONEq(t, string(json0), string(json1))
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
@@ -9,11 +11,28 @@ import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Item) DeepCopyInto(out *Item) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Item.
|
||||
func (in *Item) DeepCopy() *Item {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Item)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Playlist) DeepCopyInto(out *Playlist) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,17 +88,22 @@ func (in *PlaylistList) DeepCopyObject() runtime.Object {
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *legacyStorage) DeepCopyInto(out *legacyStorage) {
|
||||
func (in *Spec) DeepCopyInto(out *Spec) {
|
||||
*out = *in
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Item, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Storage.
|
||||
func (in *legacyStorage) DeepCopy() *legacyStorage {
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Spec.
|
||||
func (in *Spec) DeepCopy() *Spec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(legacyStorage)
|
||||
out := new(Spec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by openapi-gen. DO NOT EDIT.
|
||||
|
||||
// This file was autogenerated by openapi-gen. Do not edit it manually!
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
common "k8s.io/kube-openapi/pkg/common"
|
||||
spec "k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
func getOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
|
||||
return map[string]common.OpenAPIDefinition{
|
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Item": schema_pkg_apis_playlist_v0alpha1_Item(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Playlist": schema_pkg_apis_playlist_v0alpha1_Playlist(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.PlaylistList": schema_pkg_apis_playlist_v0alpha1_PlaylistList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Spec": schema_pkg_apis_playlist_v0alpha1_Spec(ref),
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_playlist_v0alpha1_Item(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Item defines model for Item.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"type": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Type of the item.",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"value": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Value depends on type and describes the playlist item.\n\n - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This\n is not portable as the numerical identifier is non-deterministic between different instances.\n Will be replaced by dashboard_by_uid in the future. (deprecated)\n - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All\n dashboards behind the tag will be added to the playlist.\n - dashboard_by_uid: The value is the dashboard UID",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"type", "value"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_playlist_v0alpha1_Playlist(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata",
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Spec"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Spec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_playlist_v0alpha1_PlaylistList(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Playlist"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Playlist", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_playlist_v0alpha1_Spec(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Spec defines model for Spec.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"title": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Name of the playlist.",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"interval": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Interval sets the time between switching views in a playlist.",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The ordered list of items that the playlist will iterate over.",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Item"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"title", "interval"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Item"},
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoTypesJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package playlist
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/kinds"
|
||||
)
|
||||
|
||||
// Resource is the kubernetes style representation of Playlist. (TODO be better)
|
||||
type K8sResource = kinds.GrafanaResource[Spec, Status]
|
||||
|
||||
// NewResource creates a new instance of the resource with a given name (UID)
|
||||
func NewK8sResource(name string, s *Spec) K8sResource {
|
||||
return K8sResource{
|
||||
Kind: "Playlist",
|
||||
APIVersion: "v0-0-alpha",
|
||||
Metadata: kinds.GrafanaResourceMetadata{
|
||||
Name: name,
|
||||
Annotations: make(map[string]string),
|
||||
Labels: make(map[string]string),
|
||||
},
|
||||
Spec: s,
|
||||
}
|
||||
}
|
||||
|
||||
// Resource is the wire representation of Playlist.
|
||||
// It currently will soon be merged into the k8s flavor (TODO be better)
|
||||
type Resource struct {
|
||||
Metadata Metadata `json:"metadata"`
|
||||
Spec Spec `json:"spec"`
|
||||
Status Status `json:"status"`
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// CoreKindJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package playlist
|
||||
|
||||
import (
|
||||
"github.com/grafana/kindsys"
|
||||
"github.com/grafana/thema"
|
||||
"github.com/grafana/thema/vmux"
|
||||
|
||||
"github.com/grafana/grafana/pkg/cuectx"
|
||||
)
|
||||
|
||||
// rootrel is the relative path from the grafana repository root to the
|
||||
// directory containing the .cue files in which this kind is defined. Necessary
|
||||
// for runtime errors related to the definition and/or lineage to provide
|
||||
// a real path to the correct .cue file.
|
||||
const rootrel string = "kinds/playlist"
|
||||
|
||||
// TODO standard generated docs
|
||||
type Kind struct {
|
||||
kindsys.Core
|
||||
lin thema.ConvergentLineage[*Resource]
|
||||
jcodec vmux.Codec
|
||||
valmux vmux.ValueMux[*Resource]
|
||||
}
|
||||
|
||||
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
|
||||
var _ kindsys.Core = &Kind{}
|
||||
|
||||
// TODO standard generated docs
|
||||
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
|
||||
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k := &Kind{}
|
||||
k.Core, err = kindsys.BindCore(rt, def, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Get the thema.Schema that the meta says is in the current version (which
|
||||
// codegen ensures is always the latest)
|
||||
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
|
||||
tsch, err := thema.BindType(cursch, &Resource{})
|
||||
if err != nil {
|
||||
// Should be unreachable, modulo bugs in the Thema->Go code generator
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k.jcodec = vmux.NewJSONCodec("playlist.json")
|
||||
k.lin = tsch.ConvergentLineage()
|
||||
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
|
||||
// to the the Playlist [Resource] type generated from the current schema, v0.0.
|
||||
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
|
||||
return k.lin
|
||||
}
|
||||
|
||||
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
|
||||
// at any schematized dashboard version to an instance of Playlist [Resource].
|
||||
//
|
||||
// Validation and translation errors emitted from this func will identify the
|
||||
// input bytes as "dashboard.json".
|
||||
//
|
||||
// This is a thin wrapper around Thema's [vmux.ValueMux].
|
||||
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
|
||||
return k.valmux(b)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package playlist
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Metadata defines model for Metadata.
|
||||
type Metadata struct {
|
||||
CreatedBy string `json:"createdBy"`
|
||||
CreationTimestamp time.Time `json:"creationTimestamp"`
|
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
|
||||
|
||||
// extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata
|
||||
ExtraFields map[string]any `json:"extraFields"`
|
||||
Finalizers []string `json:"finalizers"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
ResourceVersion string `json:"resourceVersion"`
|
||||
Uid string `json:"uid"`
|
||||
UpdateTimestamp time.Time `json:"updateTimestamp"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
}
|
||||
|
||||
// _kubeObjectMetadata is metadata found in a kubernetes object's metadata field.
|
||||
// It is not exhaustive and only includes fields which may be relevant to a kind's implementation,
|
||||
// As it is also intended to be generic enough to function with any API Server.
|
||||
type KubeObjectMetadata struct {
|
||||
CreationTimestamp time.Time `json:"creationTimestamp"`
|
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
|
||||
Finalizers []string `json:"finalizers"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
ResourceVersion string `json:"resourceVersion"`
|
||||
Uid string `json:"uid"`
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package playlist
|
||||
|
||||
// Defines values for ItemType.
|
||||
const (
|
||||
ItemTypeDashboardById ItemType = "dashboard_by_id"
|
||||
ItemTypeDashboardByTag ItemType = "dashboard_by_tag"
|
||||
ItemTypeDashboardByUid ItemType = "dashboard_by_uid"
|
||||
)
|
||||
|
||||
// Item defines model for Item.
|
||||
type Item struct {
|
||||
// Title is an unused property -- it will be removed in the future
|
||||
Title *string `json:"title,omitempty"`
|
||||
|
||||
// Type of the item.
|
||||
Type ItemType `json:"type"`
|
||||
|
||||
// Value depends on type and describes the playlist item.
|
||||
//
|
||||
// - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This
|
||||
// is not portable as the numerical identifier is non-deterministic between different instances.
|
||||
// Will be replaced by dashboard_by_uid in the future. (deprecated)
|
||||
// - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All
|
||||
// dashboards behind the tag will be added to the playlist.
|
||||
// - dashboard_by_uid: The value is the dashboard UID
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// Type of the item.
|
||||
type ItemType string
|
||||
|
||||
// Spec defines model for Spec.
|
||||
type Spec struct {
|
||||
// Interval sets the time between switching views in a playlist.
|
||||
// FIXME: Is this based on a standardized format or what options are available? Can datemath be used?
|
||||
Interval string `json:"interval"`
|
||||
|
||||
// The ordered list of items that the playlist will iterate over.
|
||||
// FIXME! This should not be optional, but changing it makes the godegen awkward
|
||||
Items []Item `json:"items,omitempty"`
|
||||
|
||||
// Name of the playlist.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Unique playlist identifier. Generated on creation, either by the
|
||||
// creator of the playlist of by the application.
|
||||
Uid string `json:"uid"`
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package playlist
|
||||
|
||||
// Defines values for OperatorStateState.
|
||||
const (
|
||||
OperatorStateStateFailed OperatorStateState = "failed"
|
||||
OperatorStateStateInProgress OperatorStateState = "in_progress"
|
||||
OperatorStateStateSuccess OperatorStateState = "success"
|
||||
)
|
||||
|
||||
// Defines values for StatusOperatorStateState.
|
||||
const (
|
||||
StatusOperatorStateStateFailed StatusOperatorStateState = "failed"
|
||||
StatusOperatorStateStateInProgress StatusOperatorStateState = "in_progress"
|
||||
StatusOperatorStateStateSuccess StatusOperatorStateState = "success"
|
||||
)
|
||||
|
||||
// OperatorState defines model for OperatorState.
|
||||
type OperatorState struct {
|
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
DescriptiveState *string `json:"descriptiveState,omitempty"`
|
||||
|
||||
// details contains any extra information that is operator-specific
|
||||
Details map[string]any `json:"details,omitempty"`
|
||||
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
LastEvaluation string `json:"lastEvaluation"`
|
||||
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
State OperatorStateState `json:"state"`
|
||||
}
|
||||
|
||||
// OperatorStateState state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
type OperatorStateState string
|
||||
|
||||
// Status defines model for Status.
|
||||
type Status struct {
|
||||
// additionalFields is reserved for future use
|
||||
AdditionalFields map[string]any `json:"additionalFields,omitempty"`
|
||||
|
||||
// operatorStates is a map of operator ID to operator state evaluations.
|
||||
// Any operator which consumes this kind SHOULD add its state evaluation information to this field.
|
||||
OperatorStates map[string]StatusOperatorState `json:"operatorStates,omitempty"`
|
||||
}
|
||||
|
||||
// StatusOperatorState defines model for status.#OperatorState.
|
||||
type StatusOperatorState struct {
|
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
DescriptiveState *string `json:"descriptiveState,omitempty"`
|
||||
|
||||
// details contains any extra information that is operator-specific
|
||||
Details map[string]any `json:"details,omitempty"`
|
||||
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
LastEvaluation string `json:"lastEvaluation"`
|
||||
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
State StatusOperatorStateState `json:"state"`
|
||||
}
|
||||
|
||||
// StatusOperatorStateState state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
type StatusOperatorStateState string
|
||||
@@ -1322,31 +1322,26 @@
|
||||
},
|
||||
"playlist": {
|
||||
"category": "core",
|
||||
"codeowners": [
|
||||
"grafana/grafana-as-code",
|
||||
"grafana/grafana-frontend-platform",
|
||||
"grafana/plugins-platform-frontend"
|
||||
],
|
||||
"codeowners": [],
|
||||
"crd": {
|
||||
"dummySchema": false,
|
||||
"group": "playlist.core.grafana.com",
|
||||
"scope": "Namespaced"
|
||||
"group": "",
|
||||
"scope": ""
|
||||
},
|
||||
"currentVersion": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"description": "A playlist is a series of dashboards that is automatically rotated in the browser, on a configurable interval.",
|
||||
"grafanaMaturityCount": 0,
|
||||
"lineageIsGroup": false,
|
||||
"links": {
|
||||
"docs": "https://grafana.com/docs/grafana/next/developers/kinds/core/playlist/schema-reference",
|
||||
"go": "https://github.com/grafana/grafana/tree/main/pkg/kinds/playlist",
|
||||
"schema": "https://github.com/grafana/grafana/tree/main/kinds/playlist/playlist_kind.cue",
|
||||
"ts": "https://github.com/grafana/grafana/tree/main/packages/grafana-schema/src/raw/playlist/x/playlist_types.gen.ts"
|
||||
"docs": "n/a",
|
||||
"go": "n/a",
|
||||
"schema": "n/a",
|
||||
"ts": "n/a"
|
||||
},
|
||||
"machineName": "playlist",
|
||||
"maturity": "merged",
|
||||
"maturity": "planned",
|
||||
"name": "Playlist",
|
||||
"pluralMachineName": "playlists",
|
||||
"pluralName": "Playlists"
|
||||
@@ -2277,7 +2272,6 @@
|
||||
"folder",
|
||||
"googlecloudmonitoringdataquery",
|
||||
"heatmappanelcfg",
|
||||
"playlist",
|
||||
"preferences",
|
||||
"publicdashboard",
|
||||
"role",
|
||||
@@ -2286,7 +2280,7 @@
|
||||
"timeseriespanelcfg",
|
||||
"trendpanelcfg"
|
||||
],
|
||||
"count": 14
|
||||
"count": 13
|
||||
},
|
||||
"planned": {
|
||||
"name": "planned",
|
||||
@@ -2319,6 +2313,7 @@
|
||||
"mysqldataquery",
|
||||
"mysqldatasourcecfg",
|
||||
"parcadatasourcecfg",
|
||||
"playlist",
|
||||
"postgresqldataquery",
|
||||
"postgresqldatasourcecfg",
|
||||
"prometheusdatasourcecfg",
|
||||
@@ -2335,7 +2330,7 @@
|
||||
"zipkindataquery",
|
||||
"zipkindatasourcecfg"
|
||||
],
|
||||
"count": 43
|
||||
"count": 44
|
||||
},
|
||||
"stable": {
|
||||
"name": "stable",
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/kinds/dashboard"
|
||||
"github.com/grafana/grafana/pkg/kinds/folder"
|
||||
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
||||
"github.com/grafana/grafana/pkg/kinds/playlist"
|
||||
"github.com/grafana/grafana/pkg/kinds/preferences"
|
||||
"github.com/grafana/grafana/pkg/kinds/publicdashboard"
|
||||
"github.com/grafana/grafana/pkg/kinds/role"
|
||||
@@ -47,7 +46,6 @@ type Base struct {
|
||||
dashboard *dashboard.Kind
|
||||
folder *folder.Kind
|
||||
librarypanel *librarypanel.Kind
|
||||
playlist *playlist.Kind
|
||||
preferences *preferences.Kind
|
||||
publicdashboard *publicdashboard.Kind
|
||||
role *role.Kind
|
||||
@@ -61,7 +59,6 @@ var (
|
||||
_ kindsys.Core = &dashboard.Kind{}
|
||||
_ kindsys.Core = &folder.Kind{}
|
||||
_ kindsys.Core = &librarypanel.Kind{}
|
||||
_ kindsys.Core = &playlist.Kind{}
|
||||
_ kindsys.Core = &preferences.Kind{}
|
||||
_ kindsys.Core = &publicdashboard.Kind{}
|
||||
_ kindsys.Core = &role.Kind{}
|
||||
@@ -89,11 +86,6 @@ func (b *Base) LibraryPanel() *librarypanel.Kind {
|
||||
return b.librarypanel
|
||||
}
|
||||
|
||||
// Playlist returns the [kindsys.Interface] implementation for the playlist kind.
|
||||
func (b *Base) Playlist() *playlist.Kind {
|
||||
return b.playlist
|
||||
}
|
||||
|
||||
// Preferences returns the [kindsys.Interface] implementation for the preferences kind.
|
||||
func (b *Base) Preferences() *preferences.Kind {
|
||||
return b.preferences
|
||||
@@ -147,12 +139,6 @@ func doNewBase(rt *thema.Runtime) *Base {
|
||||
}
|
||||
reg.all = append(reg.all, reg.librarypanel)
|
||||
|
||||
reg.playlist, err = playlist.NewKind(rt)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error while initializing the playlist Kind: %s", err))
|
||||
}
|
||||
reg.all = append(reg.all, reg.playlist)
|
||||
|
||||
reg.preferences, err = preferences.NewKind(rt)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error while initializing the preferences Kind: %s", err))
|
||||
|
||||
@@ -2,8 +2,6 @@ package playlist
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/kinds/playlist"
|
||||
)
|
||||
|
||||
// Typed errors
|
||||
@@ -27,9 +25,47 @@ type Playlist struct {
|
||||
UpdatedAt int64 `json:"-" db:"updated_at"`
|
||||
}
|
||||
|
||||
type PlaylistDTO = playlist.Spec
|
||||
type PlaylistItemDTO = playlist.Item
|
||||
type PlaylistItemType = playlist.ItemType
|
||||
type PlaylistDTO struct {
|
||||
// Unique playlist identifier. Generated on creation, either by the
|
||||
// creator of the playlist of by the application.
|
||||
Uid string `json:"uid"`
|
||||
|
||||
// Name of the playlist.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Interval sets the time between switching views in a playlist.
|
||||
Interval string `json:"interval"`
|
||||
|
||||
// The ordered list of items that the playlist will iterate over.
|
||||
Items []PlaylistItemDTO `json:"items,omitempty"`
|
||||
|
||||
// Returned for k8s
|
||||
CreatedAt int64 `json:"-"`
|
||||
|
||||
// Returned for k8s
|
||||
UpdatedAt int64 `json:"-"`
|
||||
|
||||
// Returned for k8s
|
||||
OrgID int64 `json:"-"`
|
||||
}
|
||||
|
||||
type PlaylistItemDTO struct {
|
||||
// Title is an unused property -- it will be removed in the future
|
||||
Title *string `json:"title,omitempty"`
|
||||
|
||||
// Type of the item.
|
||||
Type string `json:"type"`
|
||||
|
||||
// Value depends on type and describes the playlist item.
|
||||
//
|
||||
// - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This
|
||||
// is not portable as the numerical identifier is non-deterministic between different instances.
|
||||
// Will be replaced by dashboard_by_uid in the future. (deprecated)
|
||||
// - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All
|
||||
// dashboards behind the tag will be added to the playlist.
|
||||
// - dashboard_by_uid: The value is the dashboard UID
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type PlaylistItem struct {
|
||||
Id int64 `db:"id"`
|
||||
@@ -88,10 +124,3 @@ type GetPlaylistItemsByUidQuery struct {
|
||||
PlaylistUID string
|
||||
OrgId int64
|
||||
}
|
||||
|
||||
func PlaylistToResource(p PlaylistDTO) playlist.K8sResource {
|
||||
copy := p
|
||||
r := playlist.NewK8sResource(p.Uid, ©)
|
||||
copy.Uid = "" // remove it from the payload
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
@@ -15,11 +13,10 @@ type Service struct {
|
||||
|
||||
var _ playlist.Service = &Service{}
|
||||
|
||||
func ProvideService(db db.DB, toggles featuremgmt.FeatureToggles, objserver entity.EntityStoreServer) playlist.Service {
|
||||
sqlstore := &sqlStore{
|
||||
func ProvideService(db db.DB) playlist.Service {
|
||||
return &Service{store: &sqlStore{
|
||||
db: db,
|
||||
}
|
||||
return &Service{store: sqlstore}
|
||||
}}
|
||||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context, cmd *playlist.CreatePlaylistCommand) (*playlist.Playlist, error) {
|
||||
@@ -48,7 +45,7 @@ func (s *Service) Get(ctx context.Context, q *playlist.GetPlaylistByUidQuery) (*
|
||||
}
|
||||
items := make([]playlist.PlaylistItemDTO, len(rawItems))
|
||||
for i := 0; i < len(rawItems); i++ {
|
||||
items[i].Type = playlist.PlaylistItemType(rawItems[i].Type)
|
||||
items[i].Type = rawItems[i].Type
|
||||
items[i].Value = rawItems[i].Value
|
||||
|
||||
// Add the unused title to the result
|
||||
@@ -58,10 +55,13 @@ func (s *Service) Get(ctx context.Context, q *playlist.GetPlaylistByUidQuery) (*
|
||||
}
|
||||
}
|
||||
return &playlist.PlaylistDTO{
|
||||
Uid: v.UID,
|
||||
Name: v.Name,
|
||||
Interval: v.Interval,
|
||||
Items: items,
|
||||
Uid: v.UID,
|
||||
Name: v.Name,
|
||||
Interval: v.Interval,
|
||||
Items: items,
|
||||
CreatedAt: v.CreatedAt,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
OrgID: v.OrgId,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package playlist
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/kinds/playlist"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||
)
|
||||
|
||||
func GetEntityKindInfo() entity.EntityKindInfo {
|
||||
return entity.EntityKindInfo{
|
||||
ID: entity.StandardKindPlaylist,
|
||||
Name: "Playlist",
|
||||
Description: "Cycle though a collection of dashboards automatically",
|
||||
}
|
||||
}
|
||||
|
||||
func GetEntitySummaryBuilder() entity.EntitySummaryBuilder {
|
||||
return summaryBuilder
|
||||
}
|
||||
|
||||
func summaryBuilder(ctx context.Context, uid string, body []byte) (*entity.EntitySummary, []byte, error) {
|
||||
obj := &playlist.Spec{}
|
||||
err := json.Unmarshal(body, obj)
|
||||
if err != nil {
|
||||
return nil, nil, err // unable to read object
|
||||
}
|
||||
|
||||
// TODO: fix model so this is not possible
|
||||
if obj.Items == nil {
|
||||
temp := make([]playlist.Item, 0)
|
||||
obj.Items = temp
|
||||
}
|
||||
|
||||
obj.Uid = uid // make sure they are consistent
|
||||
summary := &entity.EntitySummary{
|
||||
UID: uid,
|
||||
Name: obj.Name,
|
||||
Description: fmt.Sprintf("%d items, refreshed every %s", len(obj.Items), obj.Interval),
|
||||
}
|
||||
|
||||
for _, item := range obj.Items {
|
||||
switch item.Type {
|
||||
case playlist.ItemTypeDashboardByUid:
|
||||
summary.References = append(summary.References, &entity.EntityExternalReference{
|
||||
Family: entity.StandardKindDashboard,
|
||||
Identifier: item.Value,
|
||||
})
|
||||
|
||||
case playlist.ItemTypeDashboardByTag:
|
||||
if summary.Labels == nil {
|
||||
summary.Labels = make(map[string]string, 0)
|
||||
}
|
||||
summary.Labels[item.Value] = ""
|
||||
|
||||
case playlist.ItemTypeDashboardById:
|
||||
// obviously insufficient long term... but good to have an example :)
|
||||
summary.Error = &entity.EntityErrorInfo{
|
||||
Message: "Playlist uses deprecated internal id system",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out, err := json.MarshalIndent(obj, "", " ")
|
||||
return summary, out, err
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package playlist
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/kinds/playlist"
|
||||
)
|
||||
|
||||
func TestPlaylistSummary(t *testing.T) {
|
||||
builder := GetEntitySummaryBuilder()
|
||||
|
||||
// Do not parse invalid input
|
||||
_, _, err := builder(context.Background(), "abc", []byte("{invalid json"))
|
||||
require.Error(t, err)
|
||||
|
||||
playlist := playlist.Spec{
|
||||
Interval: "30s",
|
||||
Name: "test",
|
||||
Items: []playlist.Item{
|
||||
{Type: playlist.ItemTypeDashboardByUid, Value: "D1"},
|
||||
{Type: playlist.ItemTypeDashboardByTag, Value: "tagA"},
|
||||
{Type: playlist.ItemTypeDashboardByUid, Value: "D3"},
|
||||
},
|
||||
}
|
||||
out, err := json.Marshal(playlist)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, out)
|
||||
|
||||
// Do not parse invalid input
|
||||
summary, body, err := builder(context.Background(), "abc", out)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "test", summary.Name)
|
||||
require.Equal(t, 2, len(summary.References))
|
||||
require.Equal(t, map[string]string{"tagA": ""}, summary.Labels)
|
||||
require.True(t, json.Valid(body))
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/store/kind/folder"
|
||||
"github.com/grafana/grafana/pkg/services/store/kind/geojson"
|
||||
"github.com/grafana/grafana/pkg/services/store/kind/jsonobj"
|
||||
"github.com/grafana/grafana/pkg/services/store/kind/playlist"
|
||||
"github.com/grafana/grafana/pkg/services/store/kind/png"
|
||||
"github.com/grafana/grafana/pkg/services/store/kind/preferences"
|
||||
"github.com/grafana/grafana/pkg/services/store/kind/snapshot"
|
||||
@@ -30,10 +29,6 @@ type KindRegistry interface {
|
||||
|
||||
func NewKindRegistry() KindRegistry {
|
||||
kinds := make(map[string]*kindValues)
|
||||
kinds[entity.StandardKindPlaylist] = &kindValues{
|
||||
info: playlist.GetEntityKindInfo(),
|
||||
builder: playlist.GetEntitySummaryBuilder(),
|
||||
}
|
||||
kinds[entity.StandardKindDashboard] = &kindValues{
|
||||
info: dashboard.GetEntityKindInfo(),
|
||||
builder: dashboard.GetEntitySummaryBuilder(),
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||
"github.com/grafana/grafana/pkg/services/store/kind/dummy"
|
||||
)
|
||||
|
||||
@@ -24,21 +23,14 @@ func TestKindRegistry(t *testing.T) {
|
||||
"frame",
|
||||
"geojson",
|
||||
"jsonobj",
|
||||
"playlist",
|
||||
"png",
|
||||
"preferences",
|
||||
"snapshot",
|
||||
"test",
|
||||
}, ids)
|
||||
|
||||
// Check playlist exists
|
||||
info, err := registry.GetInfo(entity.StandardKindPlaylist)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Playlist", info.Name)
|
||||
require.False(t, info.IsRaw)
|
||||
|
||||
// Check that we registered a test item
|
||||
info, err = registry.GetInfo("test")
|
||||
info, err := registry.GetInfo("test")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "test", info.Name)
|
||||
require.True(t, info.IsRaw)
|
||||
|
||||
@@ -46,7 +46,7 @@ interface K8sPlaylist {
|
||||
name: string;
|
||||
};
|
||||
spec: {
|
||||
name: string;
|
||||
title: string;
|
||||
interval: string;
|
||||
items: PlaylistItem[];
|
||||
};
|
||||
@@ -58,10 +58,7 @@ class K8sAPI implements PlaylistAPI {
|
||||
|
||||
async getAllPlaylist(): Promise<Playlist[]> {
|
||||
const result = await getBackendSrv().get<K8sPlaylistList>(this.url);
|
||||
console.log('getAllPlaylist', result);
|
||||
const v = result.playlists.map(k8sResourceAsPlaylist);
|
||||
console.log('after', v);
|
||||
return v;
|
||||
return result.playlists.map(k8sResourceAsPlaylist);
|
||||
}
|
||||
|
||||
async getPlaylist(uid: string): Promise<Playlist> {
|
||||
@@ -118,9 +115,12 @@ class K8sAPI implements PlaylistAPI {
|
||||
// the main difference is that k8s uses metdata.name as the uid
|
||||
// to avoid future confusion, the display name is now called "title"
|
||||
function k8sResourceAsPlaylist(r: K8sPlaylist): Playlist {
|
||||
const { spec, metadata } = r;
|
||||
return {
|
||||
...r.spec,
|
||||
uid: r.metadata.name, // replace the uid from the k8s name
|
||||
uid: metadata.name, // use the k8s name as uid
|
||||
name: spec.title,
|
||||
interval: spec.interval,
|
||||
items: spec.items,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user