### QueryEditor Stream Selection UI Example (TypeScript) Source: https://context7.com/aveva/sample-adh-grafana_backend_plugin-datasource/llms.txt This TypeScript code demonstrates a React component for a Grafana query editor, specifically for selecting SDS streams. It illustrates the initial query state, user input handling (debounced search), search result processing, and the final updated query object. It also outlines the backend logic for fetching time-series data or stream lists based on the query. ```typescript import React from 'react'; import { QueryEditorProps, SelectableValue } from '@grafana/data'; import { SdsQuery } from './types'; // Example of query editor state and behavior function exampleQueryEditor() { // Initial query state const initialQuery: SdsQuery = { refId: 'A', collection: 'streams', queryText: '', id: '', name: '', }; // User types "temp" in the search box // Debounced function calls datasource.getStreams('*temp*') // After 1 second delay, search results are returned: const searchResults: Array> = [ { value: 'temp-sensor-01', label: 'Building A Temperature' }, { value: 'temp-sensor-02', label: 'Building B Temperature' }, { value: 'ambient-temp', label: 'Ambient Temperature' }, ]; // User selects "Building A Temperature" // onChange is called with updated query: const selectedQuery: SdsQuery = { refId: 'A', collection: 'streams', queryText: '*temp*', // Last search query id: 'temp-sensor-01', // Selected stream ID name: 'Building A Temperature', // Selected stream name }; console.log('Initial query:', initialQuery); console.log('Search results:', searchResults); console.log('Final query:', selectedQuery); // When the query is executed: // - If id is set: Backend calls StreamsDataQuery to get time-series data // - If id is empty: Backend calls StreamsQuery to get stream list // The selected stream's data is fetched using the dashboard's time range // and displayed in the panel visualization } exampleQueryEditor(); ``` -------------------------------- ### Initialize Grafana Data Source Plugin with Go Source: https://context7.com/aveva/sample-adh-grafana_backend_plugin-datasource/llms.txt This Go code snippet demonstrates how to initialize a Grafana data source plugin using the Grafana Plugin SDK for Go. It registers a factory function (`cds.NewCdsDataSource`) that Grafana will use to create new data source instances when requests are received. The `datasource.Manage` function handles the core logic of instance management. ```go package main import ( "context" "github.com/aveva/connect-data-services/pkg/cds" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" "log" "os" ) func main() { // The Manage function creates data source instances automatically // when Grafana sends requests, using NewCdsDataSource as the factory err := datasource.Manage( "aveva-connectdataservices-datasource", cds.NewCdsDataSource, datasource.ManageOpts{}, ) if err != nil { log.DefaultLogger.Error(err.Error()) os.Exit(1) } } // Example of what happens internally when a data source is configured func createDataSourceExample(ctx context.Context) { settings := backend.DataSourceInstanceSettings{ JSONData: []byte(`{ "resource": "https://uswe.datahub.connect.aveva.com", "apiVersion": "v1", "tenantId": "12345678-1234-1234-1234-123456789abc", "namespaceId": "default", "clientId": "client-id-here", "useCommunity": false, "oauthPassThru": false }`), DecryptedSecureJSONData: map[string]string{ "clientSecret": "client-secret-here", }, } instance, err := cds.NewCdsDataSource(ctx, settings) if err != nil { // Handle error - invalid configuration panic(err) } // instance is now ready to handle queries } ``` -------------------------------- ### Grafana Backend Query Handler - Go Source: https://context7.com/aveva/sample-adh-grafana_backend_plugin-datasource/llms.txt Simulates the main query handler for a Grafana backend plugin, processing data requests from Grafana dashboards. It demonstrates how to parse incoming queries, manage time ranges, and prepare a response structure, routing to appropriate data fetching functions. ```go package main import ( "context" "encoding/json" "fmt" "time" "github.com/grafana/grafana-plugin-sdk-go/backend" ) func queryDataExample() { // This function is called by Grafana when a dashboard panel requests data // The following simulates what happens when a user views a dashboard // Example query request from Grafana queryJSON := `{ "collection": "streams", "id": "temperature-sensor-01", "queryText": "", "refId": "A" }` req := &backend.QueryDataRequest{ Queries: []backend.DataQuery{ { RefID: "A", TimeRange: backend.TimeRange{ From: time.Now().Add(-1 * time.Hour), To: time.Now(), }, JSON: []byte(queryJSON), }, }, Headers: map[string]string{ // If oauthPassThru is enabled, contains "Bearer token..." "Authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", }, } // The datasource QueryData method processes this request: // 1. Retrieves or generates authentication token // 2. Unmarshals query JSON to determine query type // 3. Routes to StreamsDataQuery or StreamsQuery based on presence of "id" // 4. Returns data frames for each query // Response structure: // response := backend.QueryDataResponse{ // Responses: map[string]backend.DataResponse{ // "A": { // Frames: []*data.Frame{frame}, // Error: nil, // }, // }, // } fmt.Println("Query processed successfully - data frames returned to Grafana") } ``` -------------------------------- ### Data Source Configuration UI in TypeScript Source: https://context7.com/aveva/sample-adh-grafana_backend_plugin-datasource/llms.txt React component for configuring data source connections to AVEVA ADH or EDS, including options for authentication and endpoints. Depends on @grafana/data for plugin options and custom types for SdsDataSourceOptions. Inputs are configuration objects for different connection types, outputting structured settings for secure storage. Limitations include requirements for valid tenant/client IDs and Grafana's secure data handling for secrets. ```typescript import React from 'react'; import { DataSourcePluginOptionsEditorProps } from '@grafana/data'; import { SdsDataSourceOptions, SdsDataSourceType } from './types'; // Example of data source configuration object structure function exampleConfiguration() { // Configuration for CONNECT data services with client credentials const adhConfig: SdsDataSourceOptions = { type: SdsDataSourceType.ADH, resource: 'https://uswe.datahub.connect.aveva.com', apiVersion: 'v1', tenantId: '12345678-1234-1234-1234-123456789abc', namespaceId: 'default', clientId: 'client-credentials-id', useCommunity: false, communityId: '', oauthPassThru: false, edsPort: '', }; // Configuration for CONNECT data services community data const communityConfig: SdsDataSourceOptions = { type: SdsDataSourceType.ADH, resource: 'https://uswe.datahub.connect.aveva.com', apiVersion: 'v1', tenantId: '12345678-1234-1234-1234-123456789abc', namespaceId: '', clientId: 'client-credentials-id', useCommunity: true, communityId: 'community-abc-123', oauthPassThru: false, edsPort: '', }; // Configuration for OAuth passthrough (uses Grafana user's token) const oauthConfig: SdsDataSourceOptions = { type: SdsDataSourceType.ADH, resource: 'https://uswe.datahub.connect.aveva.com', apiVersion: 'v1', tenantId: '12345678-1234-1234-1234-123456789abc', namespaceId: 'default', clientId: '', useCommunity: false, communityId: '', oauthPassThru: true, // Requires generic OAuth configuration in Grafana edsPort: '', }; // Configuration for Edge Data Store (local) const edsConfig: SdsDataSourceOptions = { type: SdsDataSourceType.EDS, edsPort: '5590', namespaceId: 'default', // or 'diagnostics' resource: '', apiVersion: 'v1', tenantId: '', clientId: '', useCommunity: false, communityId: '', oauthPassThru: false, }; console.log('ADH Configuration:', adhConfig); console.log('Community Configuration:', communityConfig); console.log('OAuth Configuration:', oauthConfig); console.log('EDS Configuration:', edsConfig); // Secure fields (not in jsonData) const secureData = { clientSecret: 'your-client-secret-here', }; // When saved, clientSecret is encrypted by Grafana and only available // to backend plugin via DecryptedSecureJSONData } exampleConfiguration(); ``` -------------------------------- ### Grafana ADH OAuth Configuration (INI) Source: https://github.com/aveva/sample-adh-grafana_backend_plugin-datasource/blob/main/README.md This INI configuration enables generic OAuth authentication with AVEVA Data Hub. Ensure you replace placeholders with your actual client ID and consider the implications of allow_sign_up and offline access. ```ini [auth.generic_oauth] enabled = true name = AVEVA Data Hub allow_sign_up = true client_id = scopes = openid profile email ocsapi offline_access auth_url = https://uswe.datahub.connect.aveva.com/identity/connect/authorize token_url = https://uswe.datahub.connect.aveva.com/identity/connect/token api_url = https://uswe.datahub.connect.aveva.com/identity/connect/userinfo role_attribute_path = contains(role_type[*], '2dc742ab-39ea-4fc0-a39e-2bcb71c26a5f') && 'Admin' || contains(role_type[*], 'f1439595-e5a2-487f-8a4f-0627fefe75df') && 'Editor' || 'Viewer' use_pkce = true ``` -------------------------------- ### Search Community Streams by Pattern (Go) Source: https://context7.com/aveva/sample-adh-grafana_backend_plugin-datasource/llms.txt Searches for streams within a CONNECT data services community using the community search API. This enables access to shared data from multiple organizations. The function requires a CDS client, community ID, authentication token, and a query pattern. The output data frame contains stream IDs (as self-referencing URLs) and stream names. ```go package main import ( "fmt" "github.com/aveva/connect-data-services/pkg/cds" ) func searchCommunityStreamsExample() { client := cds.NewCdsClient( "https://uswe.datahub.connect.aveva.com", "v1", "12345678-1234-1234-1234-123456789abc", "client-id", "client-secret", ) token, err := cds.GetClientToken(&client) if err != nil { panic(err) } // Search for streams in a specific community communityId := "community-abc-123" query := "*pressure*" frame, err := cds.CommunityStreamsQuery(&client, communityId, token, query) if err != nil { fmt.Printf("Community stream query failed: %v\n", err) return } // Frame contains stream IDs (as self-referencing URLs) and names // IDs are returned as full API paths for later data retrieval fmt.Printf("Found %d community streams matching '%s'\n", frame.Fields[0].Len(), query) for i := 0; i < frame.Fields[0].Len(); i++ { streamUrl := frame.Fields[0].At(i) streamName := frame.Fields[1].At(i) fmt.Printf("Stream: %s\nURL: %s\n\n", streamName, streamUrl) } } ``` -------------------------------- ### Manage OAuth Tokens for CONNECT Data Services with Go Source: https://context7.com/aveva/sample-adh-grafana_backend_plugin-datasource/llms.txt This Go code demonstrates how to manage OAuth access tokens for authenticating API requests to AVEVA's CONNECT data services. The `cds.GetClientToken` function handles retrieving and caching tokens, including automatic refresh before expiration. It requires a pre-configured CDS client with necessary credentials. ```go package main import ( "fmt" "github.com/aveva/connect-data-services/pkg/cds" ) func authenticateExample() { // Create a CDS client client := cds.NewCdsClient( "https://uswe.datahub.connect.aveva.com", "v1", "12345678-1234-1234-1234-123456789abc", "your-client-id", "your-client-secret", ) // Get an access token - it will be cached and reused until near expiration token, err := cds.GetClientToken(&client) if err != nil { fmt.Printf("Failed to get token: %v\n", err) return } // Token is returned with "Bearer " prefix ready for use fmt.Printf("Token obtained: %s...\n", token[:20]) // Subsequent calls within token lifetime return cached token token2, _ := cds.GetClientToken(&client) // token2 will be the same as token if called within expiration window // Token automatically refreshes when within 5 minutes of expiration } ``` -------------------------------- ### Go: Implement Data Source Health Check Source: https://context7.com/aveva/sample-adh-grafana_backend_plugin-datasource/llms.txt Validates the data source configuration by attempting to authenticate and make a test API request. This function is called when a user saves and tests the data source configuration or when Grafana periodically checks availability. It simulates token retrieval and API request verification. ```go package main import ( "context" "fmt" "github.com/grafana/grafana-plugin-sdk-go/backend" ) func checkHealthExample() { // This function is called when: // 1. A user clicks "Save & Test" in data source configuration // 2. Grafana periodically checks data source availability // Simulated health check process: // For client credentials mode: // Step 1: Attempt to get OAuth token // token, err := GetClientToken(client) // if err != nil { // return HealthStatusError, "Unable to retrieve token" // } // Step 2: Make test request to namespace or community endpoint // For namespace: GET /api/v1/tenants/{tenantId}/namespaces/{namespaceId} // For community: GET /api/v1/tenants/{tenantId}/communities/{communityId} // Step 3: Parse response to verify access // Expected response: {"Id": "namespace-id", ...} successResult := &backend.CheckHealthResult{ Status: backend.HealthStatusOk, Message: "Data source is working", } errorResult := &backend.CheckHealthResult{ Status: backend.HealthStatusError, Message: "Invalid Configuration", } fmt.Println("Health check examples:") fmt.Printf("Success: %+v\n", successResult) fmt.Printf("Error: %+v\n", errorResult) // For OAuth passthrough mode, health check returns OK without API test // since user token is not available during configuration } ``` -------------------------------- ### Search SDS Streams by Name/Pattern (Go) Source: https://context7.com/aveva/sample-adh-grafana_backend_plugin-datasource/llms.txt Queries the SDS API to search for streams by name or pattern. It returns a data frame containing stream IDs and names, which can be used for selection in the Grafana UI. This function requires a CDS client, authentication token, namespace ID, and a query pattern. The output is a data frame with 'Id' and 'Name' fields. ```go package main import ( "fmt" "github.com/aveva/connect-data-services/pkg/cds" ) func searchStreamsExample() { client := cds.NewCdsClient( "https://uswe.datahub.connect.aveva.com", "v1", "12345678-1234-1234-1234-123456789abc", "client-id", "client-secret", ) token, err := cds.GetClientToken(&client) if err != nil { panic(err) } // Search for streams matching a query pattern // Query syntax supports wildcards: "*temp*" finds all streams with "temp" in name frame, err := cds.StreamsQuery(&client, "default", token, "*temperature*") if err != nil { fmt.Printf("Stream query failed: %v\n", err) return } // Frame contains two fields: Id and Name // Example output: // Id: ["stream-temp-01", "stream-temp-02", "room-temperature"] // Name: ["Temperature Sensor 1", "Temperature Sensor 2", "Room Temperature"] for i := 0; i < frame.Fields[0].Len(); i++ { id := frame.Fields[0].At(i) name := frame.Fields[1].At(i) fmt.Printf("Found stream: %s (%s)\n", name, id) } } ``` -------------------------------- ### TypeScript: Dispatch Queries to ADH or EDS Source: https://context7.com/aveva/sample-adh-grafana_backend_plugin-datasource/llms.txt Routes query requests to either the backend plugin for ADH or directly to the Edge Data Store REST API for EDS. This method handles query building and execution, returning an Observable of query results. ```typescript import { DataQueryRequest, DataQueryResponse } from '@grafana/data'; import { DataSource } from './datasource'; import { SdsQuery, SdsDataSourceType } from './types'; import { Observable } from 'rxjs'; // Example usage in a Grafana dashboard panel function exampleQuery() { // Create data source instance const dataSource = new DataSource({ id: 1, uid: 'cds-datasource', type: 'aveva-connectdataservices-datasource', name: 'CONNECT Data Services', jsonData: { type: SdsDataSourceType.ADH, resource: 'https://uswe.datahub.connect.aveva.com', apiVersion: 'v1', tenantId: '12345678-1234-1234-1234-123456789abc', namespaceId: 'default', clientId: 'client-id', useCommunity: false, oauthPassThru: false, edsPort: '5590', }, }); // Build query request const request: DataQueryRequest = { requestId: 'Q123', interval: '1m', intervalMs: 60000, range: { from: new Date(Date.now() - 3600000), // 1 hour ago to: new Date(), raw: { from: 'now-1h', to: 'now' }, }, scopedVars: {}, targets: [ { refId: 'A', collection: 'streams', id: 'temperature-sensor-01', queryText: '', name: 'Temperature Sensor 1', }, ], timezone: 'browser', app: 'dashboard', startTime: Date.now() - 3600000, }; // Execute query - returns Observable const response: Observable = dataSource.query(request); // Subscribe to get results response.subscribe({ next: (result) => { console.log('Query results:', result.data); // result.data contains array of data frames with time-series data // Each frame has fields array with time and value columns }, error: (err) => console.error('Query failed:', err), }); } // Example for Edge Data Store (direct REST query) function exampleEDSQuery() { const dataSource = new DataSource({ id: 2, uid: 'eds-datasource', type: 'aveva-connectdataservices-datasource', name: 'Edge Data Store', jsonData: { type: SdsDataSourceType.EDS, edsPort: '5590', namespaceId: 'default', resource: '', apiVersion: 'v1', tenantId: '', clientId: '', useCommunity: false, oauthPassThru: false, communityId: '', }, }); // EDS queries go directly to localhost:5590 // GET http://localhost:5590/api/v1/tenants/default/namespaces/default/streams/{id}/data?startIndex={from}&endIndex={to} } exampleQuery(); ``` -------------------------------- ### Retrieve Community Stream Data - Go Source: https://context7.com/aveva/sample-adh-grafana_backend_plugin-datasource/llms.txt Fetches time-series data from a community-shared stream using the Aveva Connect Data Services SDK. It requires client credentials, community ID, and stream details. The function returns a data frame that includes the retrieved data points and their associated fields. ```go package main import ( "fmt" "time" "github.com/aveva/connect-data-services/pkg/cds" ) func getCommunityStreamDataExample() { client := cds.NewCdsClient( "https://uswe.datahub.connect.aveva.com", "v1", "12345678-1234-1234-1234-123456789abc", "client-id", "client-secret", ) token, err := cds.GetClientToken(&client) if err != nil { panic(err) } // Community ID and stream self-reference URL from search results communityId := "community-abc-123" streamSelf := "https://uswe.datahub.connect.aveva.com/api/v1/tenants/tenant-id/namespaces/namespace-id/streams/stream-id" startTime := time.Now().Add(-7 * 24 * time.Hour).Format(time.RFC3339) endTime := time.Now().Format(time.RFC3339) // Query community stream data with Community-Id header frame, err := cds.CommunityStreamsDataQuery( &client, communityId, token, streamSelf, startTime, endTime, ) if err != nil { fmt.Printf("Community data query failed: %v\n", err) return } // Frame structure matches the stream's resolved type definition fmt.Printf("Retrieved %d data points from community stream\n", frame.Fields[0].Len()) fmt.Printf("Data frame name: %s\n", frame.Name) fmt.Printf("Fields: ") for _, field := range frame.Fields { fmt.Printf("%s (%s) ", field.Name, field.Type()) } fmt.Println() } ``` -------------------------------- ### Stream Autocomplete in TypeScript Source: https://context7.com/aveva/sample-adh-grafana_backend_plugin-datasource/llms.txt This asynchronous method searches for streams and returns selectable options for the query editor dropdown. It depends on React for state management, @grafana/data for SelectableValue types, and the DataSource class for backend queries. Inputs include search text (e.g., '*temp*'), and outputs are arrays of stream objects with id and label. Limitations include dependency on valid data source configuration and potential errors if the backend is unreachable. ```typescript import { SelectableValue } from '@grafana/data'; import { DataSource } from './datasource'; import React from 'react'; // Example usage in QueryEditor component function exampleStreamAutocomplete() { const [streamOptions, setStreamOptions] = React.useState>>([]); const dataSource = new DataSource({ // ... configuration }); // Function to load streams based on user input async function loadStreams(searchText: string) { try { // getStreams internally calls DataSource.query with collection: 'streams' // and no id, which triggers StreamsQuery in backend const results = await dataSource.getStreams(searchText, setStreamOptions); // Results format: // [ // { value: 'stream-id-1', label: 'Temperature Sensor 1' }, // { value: 'stream-id-2', label: 'Pressure Sensor A' }, // { value: 'stream-id-3', label: 'Flow Rate Meter' } // ] console.log(`Found ${results.length} streams matching "${searchText}"`); return results; } catch (error) { console.error('Failed to load streams:', error); return []; } } // Example searches loadStreams('*temp*').then(streams => { console.log('Temperature streams:', streams); }); loadStreams('*').then(streams => { console.log('All streams:', streams); }); // The results are used by AsyncSelect component: // loadStreams(input)} // defaultOptions={streamOptions} // onChange={(selected) => { // Update query with selected stream // onChange({ ...query, id: selected.value, name: selected.label }); // }} // /> } exampleStreamAutocomplete(); ``` -------------------------------- ### Retrieve Time-Series Data from SDS Stream (Go) Source: https://context7.com/aveva/sample-adh-grafana_backend_plugin-datasource/llms.txt Fetches time-series data from a specific SDS stream within a defined time range. It automatically resolves the stream's type schema and converts values into Grafana-compatible data frames. This function requires a CDS client, authentication token, stream ID, and start/end times formatted in RFC3339. The output data frame includes all properties defined in the stream's type, such as Timestamp and Value. ```go package main import ( "fmt" "time" "github.com/aveva/connect-data-services/pkg/cds" ) func getStreamDataExample() { client := cds.NewCdsClient( "https://uswe.datahub.connect.aveva.com", "v1", "12345678-1234-1234-1234-123456789abc", "client-id", "client-secret", ) token, err := cds.GetClientToken(&client) if err != nil { panic(err) } // Define time range for query startTime := time.Now().Add(-24 * time.Hour).Format(time.RFC3339) endTime := time.Now().Format(time.RFC3339) // Query data from a specific stream streamId := "temperature-sensor-01" frame, err := cds.StreamsDataQuery( &client, "default", // namespace ID token, streamId, startTime, endTime, ) if err != nil { fmt.Printf("Data query failed: %v\n", err) return } // Frame contains all properties from the stream's type definition // Example for a stream with Timestamp and Value properties: // Field[0]: Timestamp (time.Time) - [2024-01-01T00:00:00Z, 2024-01-01T01:00:00Z, ...] // Field[1]: Value (float64) - [72.5, 73.2, 71.8, ...] fmt.Printf("Retrieved %d data points from stream %s\n", frame.Fields[0].Len(), streamId) for i := 0; i < frame.Fields[0].Len(); i++ { fmt.Printf("Time: %v, ", frame.Fields[0].At(i)) for j := 1; j < len(frame.Fields); j++ { fmt.Printf("%s: %v " , frame.Fields[j].Name, frame.Fields[j].At(i)) } fmt.Println() } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.