Merge pull request 'feat: initial commit' (#1) from feature/initial-commit into main

Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2026-06-23 11:20:43 -05:00
790 changed files with 149255 additions and 218 deletions
+10
View File
@@ -0,0 +1,10 @@
.dockerignore
Dockerfile
Dockerfile.local
.env
.gitignore
.vs
.vscode
**/bin
**/obj
**/.toolstarget
+86 -217
View File
@@ -1,12 +1,67 @@
# ---> VisualStudio
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
###################
# compiled source #
###################
*.com
*.class
*.dll
*.exe
*.pdb
*.dll.config
*.cache
*.suo
# Include dlls if theyre in the NuGet packages directory
!/packages/*/lib/*.dll
# Include dlls if they're in the CommonReferences directory
!*CommonReferences/*.dll
####################
# VS Upgrade stuff #
####################
_UpgradeReport_Files/
###############
# Directories #
###############
bin/
obj/
TestResults/
###################
# Web publish log #
###################
*.Publish.xml
#############
# Resharper #
#############
/_ReSharper.*
*.ReSharper.*
############
# Packages #
############
# its better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
######################
# Logs and databases #
######################
*.log
*.sqlite
# OS generated files #
######################
.DS_Store
ehthumbs.db
Icon?
Thumbs.db
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
@@ -14,9 +69,6 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
@@ -24,76 +76,47 @@ mono_crash.*
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
build/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
# Visual Studo 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
# NUNIT
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*_i.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
@@ -108,21 +131,14 @@ _Chutzpah*
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
@@ -135,29 +151,18 @@ _ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
@@ -181,76 +186,50 @@ DocProject/Help/html
# Click-Once directory
publish/
ClickOnce/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
**/packages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
#!**/packages/repositories.config
# Microsoft Azure Build Output
# Windows Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
# Windows Store app package directory
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
bower_components/
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
@@ -261,32 +240,21 @@ _UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
**/node_modules/*
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
@@ -294,109 +262,10 @@ node_modules/
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
# SonarQube files
sonarqube/
.vs/
*.orig
buildlogs/
src/Strata.Analytics.Web/analytics/.env
+114
View File
@@ -0,0 +1,114 @@
ARG PROJECT=Strata.Analytics
ARG VERSION=0.0.0
ARG SONARURL=''
ARG SONARLOGIN=''
ARG SONARBRANCH=''
##############
# Base image #
##############
FROM ecr.ops.stratanetwork.net/strata.microsoft.dotnet.aspnet:6.0 AS base
###############
# Build image #
###############
FROM ecr.ops.stratanetwork.net/strata.microsoft.dotnet.sdk:6.0 AS build
ARG PROJECT
ARG VERSION
ARG SONARURL
ARG SONARLOGIN
ARG SONARBRANCH
WORKDIR /src
# Copy project files and restore
COPY ["${PROJECT}.sln", "${PROJECT}.sln"]
COPY src/**/*.csproj ./
RUN for file in $(ls *.csproj); do mkdir -p src/${file%.*}/ && mv $file src/${file%.*}/; done
COPY tests/**/*.csproj ./
RUN for file in $(ls *.csproj); do mkdir -p tests/${file%.*}/ && mv $file tests/${file%.*}/; done
RUN dotnet restore "${PROJECT}.sln" \
--source https://api.nuget.org/v3/index.json \
--source https://proget.sdt.local/nuget/nuget/v3/index.json
# Copy files and build
COPY . .
RUN if [ "${SONARLOGIN}" != "" ] ; then dotnet sonarscanner begin \
/k:"${PROJECT}" \
/d:sonar.scm.provider=git \
/d:sonar.host.url="${SONARURL}" \
${SONARBRANCH} \
/d:sonar.login="${SONARLOGIN}" \
/d:sonar.cs.opencover.reportsPaths="/src/cover.xml" \
/d:sonar.dependencyCheck.jsonReportPath=/src/buildlogs/dependency-check-report.json \
/d:sonar.dependencyCheck.htmlReportPath=/src/buildlogs/dependency-check-report.html \
/v:sonar.projectVersion="${VERSION}"; fi
RUN dotnet build "${PROJECT}.sln" \
--configuration Release \
--no-restore
# Run unit tests
RUN dotnet test "${PROJECT}.sln" \
--configuration Release \
--no-restore \
--no-build \
--verbosity=normal \
-p:CollectCoverage=true \
-p:CoverletOutputFormat="opencover" \
-p:CoverletOutput=/src/cover.xml
# Publish project
RUN dotnet publish "src/${PROJECT}.Api/${PROJECT}.Api.csproj" \
--configuration Release \
--no-restore \
--no-build \
--output /app
# Pack client projects
RUN dotnet pack "src/${PROJECT}.Client/${PROJECT}.Client.csproj" \
--configuration Release \
--no-restore \
--no-build \
--include-symbols \
--include-source \
--output /pack \
-property:PackageVersion=${VERSION}
RUN dotnet pack "src/${PROJECT}.Models/${PROJECT}.Models.csproj" \
--configuration Release \
--no-restore \
--no-build \
--include-symbols \
--include-source \
--output /pack \
-property:PackageVersion=${VERSION}
RUN if [ "${SONARLOGIN}" != "" ] ; then \
/dc/dependency-check/bin/dependency-check.sh -f JSON -f HTML -s . -o ./buildlogs \
--suppression ./sonarsuppressions.xml \
--noupdate --nodeAuditSkipDevDependencies --disableNodeJS --ossIndexRemoteErrorWarnOnly true \
--dbDriverName "com.microsoft.sqlserver.jdbc.SQLServerDriver" \
--connectionString "jdbc:sqlserver://ddensqldevops01.sdt.local;Database=DependencyCheck;encrypt=true;trustServerCertificate=true;" \
--dbUser "dcuser" --dbPassword "${SONARLOGIN}" && \
dotnet sonarscanner end /d:sonar.login="${SONARLOGIN}"; \
fi
###############
# Final image #
###############
FROM base AS final
ARG PROJECT
WORKDIR /app
ARG PROJECT
ENV PROJECTDLL=${PROJECT}.Api.dll
ENV DOTNET_ENVIRONMENT=Development
ENV ASPNETCORE_ENVIRONMENT=Development
COPY --from=build /app /app
COPY --from=build /pack /pack
ENTRYPOINT dotnet ${PROJECTDLL}
+55
View File
@@ -0,0 +1,55 @@
ARG PROJECT=Strata.Analytics
ARG VERSION=0.0.0
##############
# Base image #
##############
FROM ecr.ops.stratanetwork.net/strata.microsoft.dotnet.aspnet:6.0 AS base
###############
# Build image #
###############
FROM ecr.ops.stratanetwork.net/strata.microsoft.dotnet.sdk:6.0 AS build
ARG PROJECT
ARG VERSION
WORKDIR /src
# Copy project files and restore
COPY ["${PROJECT}.sln", "${PROJECT}.sln"]
COPY src/**/*.csproj ./
RUN for file in $(ls *.csproj); do mkdir -p src/${file%.*}/ && mv $file src/${file%.*}/; done
COPY tests/**/*.csproj ./
RUN for file in $(ls *.csproj); do mkdir -p tests/${file%.*}/ && mv $file tests/${file%.*}/; done
RUN dotnet restore "${PROJECT}.sln" \
--source https://api.nuget.org/v3/index.json \
--source https://proget.sdt.local/nuget/nuget/v3/index.json
# Copy files and build
COPY . .
RUN dotnet build "${PROJECT}.sln" \
--configuration Release \
--no-restore
# Publish project
RUN dotnet publish "src/${PROJECT}.Hangfire.Dashboard/${PROJECT}.Hangfire.Dashboard.csproj" \
--configuration Release \
--no-restore \
--no-build \
--output /app
###############
# Final image #
###############
FROM base AS final
ARG PROJECT
WORKDIR /app
ARG PROJECT
ENV PROJECTDLL=${PROJECT}.Hangfire.Dashboard.dll
ENV DOTNET_ENVIRONMENT=Development
ENV ASPNETCORE_ENVIRONMENT=Development
COPY --from=build /app /app
ENTRYPOINT dotnet ${PROJECTDLL}
+56
View File
@@ -0,0 +1,56 @@
ARG PROJECT=Strata.Analytics
ARG VERSION=0.0.0
##############
# Base image #
##############
FROM ecr.ops.stratanetwork.net/strata.microsoft.dotnet.aspnet:6.0 AS base
###############
# Build image #
###############
FROM ecr.ops.stratanetwork.net/strata.microsoft.dotnet.sdk:6.0 AS build
ARG PROJECT
ARG VERSION
WORKDIR /src
# Copy project files and restore
COPY ["${PROJECT}.sln", "${PROJECT}.sln"]
COPY src/**/*.csproj ./
RUN for file in $(ls *.csproj); do mkdir -p src/${file%.*}/ && mv $file src/${file%.*}/; done
COPY tests/**/*.csproj ./
RUN for file in $(ls *.csproj); do mkdir -p tests/${file%.*}/ && mv $file tests/${file%.*}/; done
RUN dotnet restore "${PROJECT}.sln" \
--source https://api.nuget.org/v3/index.json \
--source https://proget.sdt.local/nuget/nuget/v3/index.json
# Copy files and build
COPY . .
RUN dotnet build "${PROJECT}.sln" \
--configuration Release \
--no-restore
# Publish project
RUN dotnet publish "src/${PROJECT}.Hangfire/${PROJECT}.Hangfire.csproj" \
--configuration Release \
--no-restore \
--no-build \
--output /app
###############
# Final image #
###############
FROM base AS final
ARG PROJECT
WORKDIR /app
ARG PROJECT
ENV PROJECTDLL=${PROJECT}.Hangfire.dll
ENV DOTNET_ENVIRONMENT=Development
ENV ASPNETCORE_ENVIRONMENT=Development
COPY --from=build /app /app
ENTRYPOINT dotnet ${PROJECTDLL}
+25
View File
@@ -0,0 +1,25 @@
FROM node:18.16.0
ENV CI=true
# Set working directory
WORKDIR /app
# Install dependencies
RUN npm config set @strata:registry http://proget.sdt.local/npm/npm
RUN npm config set strict-ssl false
COPY src/Strata.Analytics.Web/analytics/package*.json ./
RUN npm ci --legacy-peer-deps
# Activate Kendo UI License
RUN --mount=type=secret,id=KENDO_UI_LICENSE \
KENDO_UI_LICENSE=$(cat /run/secrets/KENDO_UI_LICENSE) npx kendo-ui-license activate
# Copy in rest of app and build
COPY src/Strata.Analytics.Web/analytics .
RUN npm run build
# Build release container
FROM nginx:alpine
COPY --from=0 /app/build /usr/share/nginx/html
COPY src/Strata.Analytics.Web/nginx.conf /etc/nginx/conf.d/default.conf
+59 -1
View File
@@ -1,2 +1,60 @@
# analytics
# Strata.Analytics
[![build](https://github.com/stratadecision/analytics/actions/workflows/build.yaml/badge.svg)](https://github.com/stratadecision/analytics/actions/workflows/build.yaml)
[![Strata.Analytics.Octopus](https://img.shields.io/badge/octopus-Strata.Analytics-004880?logo=OctopusDeploy)](https://octopus.sdt.local/app#/Spaces-1/projects/analytics)
[![SonarQube](https://img.shields.io/badge/SonarQube-Strata.Analytics-004880?logo=sonarqube)](https://sonarqube.ops.stratanetwork.net/dashboard?id=Strata.Analytics)
[![Strata.Analytics.Client](https://img.shields.io/badge/nuget-Strata.Analytics.Client-004880?logo=nuget&logoColor=004880)](https://proget.ops.stratanetwork.net/feeds/nuget/Strata.Analytics.Client/versions?HidePrerelease=True)
[![Strata.Analytics.Api](https://img.shields.io/badge/api-Strata.Analytics-004880?logo=swagger&logoColor=00FF00)](https://analytics-api.dev.stratanetwork.net)
[![Strata.Analytics.Web](https://img.shields.io/badge/web-Strata.Analytics-004880?logo=react&logoColor=61DBFB)](https://analytics.dev.stratanetwork.net)
[![Strata.Analytics.Hangfire](https://img.shields.io/badge/hangfire-Strata.Analytics-004880?logo=MediaFire&logoColor=FF0000)](https://analytics-dashboard.dev.stratanetwork.net)
## Branching and Versioning
### Epic / Feature branches
For epics / features that have multiple stories or are a big chunk of work, use feature release branches. There should be a feature release `main` branch and all PRs for that feature will use that as the base. When the entire feature is ready to be deployed, merge the feature release `main` back into `main main`.
For example, if the feature is for `Content Migration`, create the feature release main as `feature/[team]/release/content-migration/main`. For stories tied to that feature (for example, JAZZ-4231), branch off the feature release main and name it `feature/[team]/release/content-migration/JAZZ-4231-content-migration-mapping`.
### Bugs / Small Changes
For bugs and small changes, use regular `feature/[team]/JAZZ-1234-description` and `fix/[team]/JAZZ-1234-description` branches that will be merged into main
## Usage
### AWS Profile
```
[profile sdt-analytics-service-role]
source_profile = 809820578507_SDT-Dev-Developers
role_arn = arn:aws:iam::809820578507:role/sdt-analytics-service-role
region=us-east-1
```
### Build and Run
```
docker-compose up -d --build
```
- https://localhost:8443/index (swagger api)
- https://localhost:8443/hangfire (hangfire dashboard)
- http://localhost:8081 (redis commander)
### Extracting nuget package
```
docker create --name throwaway strataanalytics_analytics-api
docker cp throwaway:/pack .
docker rm throwaway
```
### Cleanup
```
docker-compose down
```
+69
View File
@@ -0,0 +1,69 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32825.248
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Strata.Analytics.Api", "src\Strata.Analytics.Api\Strata.Analytics.Api.csproj", "{D624FD73-92D9-4653-9C73-AE975F03FF57}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Strata.Analytics.Test.Unit", "tests\Strata.Analytics.Test.Unit\Strata.Analytics.Test.Unit.csproj", "{AD44DB53-E521-4CAD-97F9-49C5AEF3E5E2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Strata.Analytics.Client", "src\Strata.Analytics.Client\Strata.Analytics.Client.csproj", "{F66B27E9-4414-45DC-87BC-E913E44B2F28}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Strata.Analytics.Biz", "src\Strata.Analytics.Biz\Strata.Analytics.Biz.csproj", "{1CE90323-6EBE-46E1-A7F2-3F53164B5D3D}"
ProjectSection(ProjectDependencies) = postProject
{7DC6DCA8-E235-422D-A312-82F52A7A88D1} = {7DC6DCA8-E235-422D-A312-82F52A7A88D1}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Strata.Analytics.Hangfire", "src\Strata.Analytics.Hangfire\Strata.Analytics.Hangfire.csproj", "{88525DCB-DAFB-4EDE-8724-646E28AD2BFC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Strata.Analytics.Hangfire.Dashboard", "src\Strata.Analytics.Hangfire.Dashboard\Strata.Analytics.Hangfire.Dashboard.csproj", "{1D6CFA8D-A2F7-474B-B6B0-91CC3699942C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Strata.Analytics.Web", "src\Strata.Analytics.Web\Strata.Analytics.Web.csproj", "{F9C44292-B9B3-41A5-9BB3-50AE9CB2A663}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Strata.Analytics.Models", "src\Strata.Analytics.Models\Strata.Analytics.Models.csproj", "{7DC6DCA8-E235-422D-A312-82F52A7A88D1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D624FD73-92D9-4653-9C73-AE975F03FF57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D624FD73-92D9-4653-9C73-AE975F03FF57}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D624FD73-92D9-4653-9C73-AE975F03FF57}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D624FD73-92D9-4653-9C73-AE975F03FF57}.Release|Any CPU.Build.0 = Release|Any CPU
{AD44DB53-E521-4CAD-97F9-49C5AEF3E5E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD44DB53-E521-4CAD-97F9-49C5AEF3E5E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD44DB53-E521-4CAD-97F9-49C5AEF3E5E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD44DB53-E521-4CAD-97F9-49C5AEF3E5E2}.Release|Any CPU.Build.0 = Release|Any CPU
{F66B27E9-4414-45DC-87BC-E913E44B2F28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F66B27E9-4414-45DC-87BC-E913E44B2F28}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F66B27E9-4414-45DC-87BC-E913E44B2F28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F66B27E9-4414-45DC-87BC-E913E44B2F28}.Release|Any CPU.Build.0 = Release|Any CPU
{1CE90323-6EBE-46E1-A7F2-3F53164B5D3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1CE90323-6EBE-46E1-A7F2-3F53164B5D3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1CE90323-6EBE-46E1-A7F2-3F53164B5D3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1CE90323-6EBE-46E1-A7F2-3F53164B5D3D}.Release|Any CPU.Build.0 = Release|Any CPU
{88525DCB-DAFB-4EDE-8724-646E28AD2BFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{88525DCB-DAFB-4EDE-8724-646E28AD2BFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88525DCB-DAFB-4EDE-8724-646E28AD2BFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{88525DCB-DAFB-4EDE-8724-646E28AD2BFC}.Release|Any CPU.Build.0 = Release|Any CPU
{1D6CFA8D-A2F7-474B-B6B0-91CC3699942C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1D6CFA8D-A2F7-474B-B6B0-91CC3699942C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D6CFA8D-A2F7-474B-B6B0-91CC3699942C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D6CFA8D-A2F7-474B-B6B0-91CC3699942C}.Release|Any CPU.Build.0 = Release|Any CPU
{F9C44292-B9B3-41A5-9BB3-50AE9CB2A663}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F9C44292-B9B3-41A5-9BB3-50AE9CB2A663}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F9C44292-B9B3-41A5-9BB3-50AE9CB2A663}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F9C44292-B9B3-41A5-9BB3-50AE9CB2A663}.Release|Any CPU.Build.0 = Release|Any CPU
{7DC6DCA8-E235-422D-A312-82F52A7A88D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DC6DCA8-E235-422D-A312-82F52A7A88D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DC6DCA8-E235-422D-A312-82F52A7A88D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DC6DCA8-E235-422D-A312-82F52A7A88D1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7329473D-7E3A-4729-A4EC-397E12FC3E74}
EndGlobalSection
EndGlobal
+3
View File
@@ -0,0 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Analytics/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Hangfire/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
+10
View File
@@ -0,0 +1,10 @@
{
"baseUrl": "https://analytics.dev.stratanetwork.net/workbooks/",
"userName": "dssadmin",
"basicUserName": "capbasic",
"userPassword": "Password1",
"dbguid": "102e1bc4-f789-4ee8-8810-828af508a8ab",
"orgPin": "0777",
"dbName": "QA Master",
"clientId": "StrataUI"
}
+45
View File
@@ -0,0 +1,45 @@
{
"baseUrl": "https://analytics.dev.stratanetwork.net/workbooks/",
"requestTimeout": 8000,
"defaultCommandTimeout": 10000,
"trashAssetsBeforeRuns": true,
"downloadsFolder": "cypress/fixtures",
"viewportWidth": 1440,
"viewportHeight": 900,
"scrollBehavior": false,
"chromeWebSecurity": false,
"ignoreTestFiles": [
"**/__snapshots__/*",
"**/__image_snapshots__/*"
],
"cypress-plugin-snapshots": {
"autoCleanUp": false,
"autopassNewSnapshots": true,
"diffLines": 3,
"excludeFields": [],
"ignoreExtraArrayItems": false,
"ignoreExtraFields": false,
"normalizeJson": true,
"prettier": true,
"imageConfig": {
"createDiffImage": true,
"resizeDevicePixelRatio": true,
"threshold": 0.05,
"thresholdType": "percent"
},
"screenshotConfig": {
"blackout": [],
"capture": "fullPage",
"clip": null,
"disableTimersAndAnimations": true,
"log": false,
"scale": false,
"timeout": 30000
},
"serverEnabled": true,
"serverHost": "localhost",
"serverPort": 2121,
"updateSnapshots": false,
"backgroundBlend": "difference"
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"baseUrl": "https://analytics.dev.stratanetwork.net/analytics/",
"env": {
"basePageUrl": "https://analytics.dev.stratanetwork.net/analytics/",
"apiUrl": "https://analytics-api.dev.stratanetwork.net/api/",
"username": "username",
"password": "password",
"dbguid": "102e1bc4-f789-4ee8-8810-828af508a8ab",
"orgPin": "0777",
"dbName": "QA Master",
"clientId": "StrataUI"
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"baseUrl": "http://localhost:3000/analytics/",
"env": {
"basePageUrl": "http://localhost:3000/analytics/",
"apiUrl": "https://localhost:8443/api/",
"username": "username",
"password": "password",
"dbguid": "102e1bc4-f789-4ee8-8810-828af508a8ab",
"orgPin": "0777",
"dbName": "QA Master",
"clientId": "StrataUI"
}
}
@@ -0,0 +1,42 @@
import * as dayjs from 'dayjs';
import advancedAnalyticsDatamartPage from '../pageObjects/advancedAnalyticsDataSourcePageObject';
const dataSourcePage = new advancedAnalyticsDatamartPage();
const dataSourceName1 = `Cypress_Test_Datamart1_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const dataSourceName2 = `Cypress_Test_Datamart2_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const datamartDescription = 'Test datamart made in Cypress - datamart security test';
const dssPatientEncounter = '55370';
before(() => {
cy.vaultLoginByUserTitle('IDTestUser');
cy.createDataSource(dataSourceName1, datamartDescription);
cy.createDataSource(dataSourceName2, datamartDescription);
cy.addTableToDataSource(dssPatientEncounter, dataSourceName1);
cy.addTableToDataSource(dssPatientEncounter, dataSourceName2);
});
beforeEach(() => {
cy.wait(1);
});
after(() => {
cy.deleteDataSource(dataSourceName1);
cy.deleteDataSource(dataSourceName2);
cy.visit('/');
});
describe('Advanced Analytics data mart tests', () => {
it('Data mart can be secured from users', () => {
cy.goToDataSource(dataSourceName2);
dataSourcePage.addUsersToDataSource(dataSourceName2, [{ userName: 'Capital Administrator' }]);
cy.logout();
cy.vaultLoginByUserTitle('Capital Administrator');
cy.findByText('Add Workbook').click();
cy.get('#defaultDataSourceId').click();
cy.get('div').contains(dataSourceName1).should('not.exist');
cy.get('div').contains(dataSourceName2).should('exist');
cy.logout();
cy.vaultLoginByUserTitle('IDTestUser');
});
});
@@ -0,0 +1,87 @@
import * as dayjs from 'dayjs';
import advancedAnalyticsDatamartPage from '../pageObjects/advancedAnalyticsDataSourcePageObject';
const dataSourcePage = new advancedAnalyticsDatamartPage();
const dataSourceName = `Cypress_Test_Data_Source_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const dataSourceNameEdit = `Cypress_Test_Data_Source_Edit_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const dataSourceDescription = 'Test data source made in Cypress - smoke tests';
const dataSourceDescriptionEdit = 'Edited test data source made in Cypress - smoke tests';
const standardDataSourceName = `Cypress_Test_Standard_Data_Source_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const standardDataSourceNameEdit = `Cypress_Test_Standard_Data_Source_Edit_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const standardDataSourceDescription = 'Test standard data source made in Cypress - smoke tests';
const standardDataSourceDescriptionEdit = 'Edited test standard data source made in Cypress - smoke tests';
before(() => {
cy.vaultLoginByUserTitle('IDTestUser');
});
afterEach(() => {
cy.visit('/');
});
after(() => {
cy.deleteDataSource(dataSourceName);
cy.deleteDataSource(dataSourceNameEdit);
});
describe('Advanced Analytics smoke tests', () => {
it('Create Data Source', () => {
dataSourcePage.addDataSource(dataSourceName, dataSourceDescription);
dataSourcePage.goToDataSources();
cy.findByText(dataSourceName).should('exist');
});
it('Edit Data Source', () => {
dataSourcePage.editDataSource(dataSourceName, dataSourceNameEdit, dataSourceDescriptionEdit);
cy.findByText('Save').click();
cy.findByText('Saved', { timeout: 5000 }).should('exist');
dataSourcePage.goToDataSources();
cy.findByText(dataSourceNameEdit).should('exist');
cy.findByText(dataSourceName).should('not.exist');
});
it('Delete Data Source', () => {
dataSourcePage.goToDataSources();
dataSourcePage.deleteDataSourceFromPage(dataSourceNameEdit);
dataSourcePage.goToDataSources();
cy.get('main').find('span').contains(dataSourceNameEdit).should('not.exist');
});
// Skipping until issue with IDTestUser not being a strata employee
it.skip('Create Standard Data Source', () => {
dataSourcePage.addStandardDataSource(standardDataSourceName, standardDataSourceDescription);
cy.findByText('Back to Standard Data Sources', { timeout: 5000 }).click();
cy.findByText(standardDataSourceName).should('exist');
});
// Skipping until issue with IDTestUser not being a strata employee
it.skip('Edit Standard Data Source', () => {
dataSourcePage.goToStandardDataSources();
//Verify unsaved changes are not kept
cy.goToStandardDataSource(standardDataSourceName);
dataSourcePage.openDataSourceEditWindow();
cy.get('#name').clear().type(standardDataSourceNameEdit);
cy.findByText('Apply').click();
cy.findByText('Back to Standard Data Sources').click();
cy.findByText('Discard your unsaved changes?').should('exist');
cy.findByText('Discard Changes').click();
cy.findByText('Standard Data Sources').should('exist');
cy.findByText(standardDataSourceName).should('exist');
cy.findByText(standardDataSourceNameEdit).should('not.exist');
//Verify changes are saved
dataSourcePage.editStandardDataSource(standardDataSourceName, standardDataSourceNameEdit, standardDataSourceDescriptionEdit);
cy.findByText('Save').click();
cy.findByText('Saved', { timeout: 5000 }).should('exist');
dataSourcePage.goToStandardDataSources();
cy.findByText(standardDataSourceNameEdit).should('exist');
cy.findByText(standardDataSourceName).should('not.exist');
});
// Skipping until issue with IDTestUser not being a strata employee
it.skip('Delete Standard Data Source', () => {
dataSourcePage.goToStandardDataSources();
dataSourcePage.deleteDataSourceFromPage(standardDataSourceNameEdit);
dataSourcePage.goToStandardDataSources();
cy.get('main').find('span').contains(standardDataSourceNameEdit).should('not.exist');
});
});
@@ -0,0 +1,111 @@
import * as dayjs from 'dayjs';
import advancedAnalyticsExtractPage from '../pageObjects/advancedAnalyticsExtractPageObject';
const extractPage = new advancedAnalyticsExtractPage();
const extractAdminName = `Cypress_Test_Extract_Admin_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const extractNonAdminName = `Cypress_Test_Extract_Non_Aadmin_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const extractAdminEditName = `Cypress_Test_Extract_Admin_Edit_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const extractNonAdminEditName = `Cypress_Test_Extract_Non_Admin_Edit_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const extractShareName = `Cypress_Test_Extract_Share_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const extractShareNoAccessName = `Cypress_Test_Extract_Share_No_Access_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const extractScheduleName = `Cypress_Test_Extract_Shedulee_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const extractDescription = 'Test extract made in Cypress - CRUD';
const extractEditDescription = 'Test extract made in Cypress - CRUD - edit';
const extractShareDescription = 'Test extract made in Cypress - Share';
const extractScheduleDescription = 'Test extract made in Cypress - Schedule';
before(() => {
cy.vaultLoginByUserTitle('obAdmin');
});
beforeEach(() => {
cy.visit('/extracts');
});
after(() => {
cy.switchUser('IDTestUser');
cy.visit('/extracts');
cy.deleteExtract(extractAdminName);
cy.deleteExtract(extractNonAdminName);
cy.deleteExtract(extractAdminEditName);
cy.deleteExtract(extractNonAdminEditName);
cy.deleteExtract(extractShareName);
cy.deleteExtract(extractShareNoAccessName);
cy.deleteExtract(extractScheduleName);
});
describe('Advanced Analytics extract CRUD tests', () => {
it('Non-admin users can create extracts', () => {
extractPage.addNewExtract(extractNonAdminName, 'Encounter Summary', extractDescription);
cy.visit('/extracts');
cy.findByText(extractNonAdminName).should('exist');
});
it('Non-admin users can edit extracts', () => {
extractPage.editExtract(extractNonAdminName, extractNonAdminEditName, extractEditDescription);
cy.findByText(extractNonAdminEditName).should('exist');
cy.findByText(extractNonAdminName).should('not.exist');
});
it('Non-admin users can delete extracts', () => {
extractPage.deleteExtract(extractNonAdminEditName);
cy.findByText('Extracts').should('be.visible');
cy.findByText(extractNonAdminEditName).should('not.exist');
});
it('Admin users can create extracts', () => {
cy.switchUser('IDTestUser');
cy.visit('/extracts');
extractPage.addNewCustomExtract(extractAdminName, extractDescription);
cy.findByText(extractAdminName).should('exist');
});
it('Admin users can edit extracts', () => {
extractPage.editExtract(extractAdminName, extractAdminEditName, extractEditDescription);
cy.findByText(extractAdminEditName).should('exist');
cy.findByText(extractAdminName).should('not.exist');
});
it('Admin users can delete extracts', () => {
extractPage.deleteExtract(extractAdminEditName);
cy.findByText('Extracts').should('be.visible');
cy.findByText(extractAdminEditName).should('not.exist');
});
});
describe('Advanced Analytics extract tests', () => {
it('Users can share extracts', () => {
extractPage.addNewCustomExtract(extractShareName, extractShareDescription);
cy.visit('/extracts');
cy.findByText(extractShareName).should('exist');
extractPage.shareExtract(extractShareName, 'Capital - System Administrator');
cy.visit('/extracts');
extractPage.addNewCustomExtract(extractShareNoAccessName, extractShareDescription);
cy.logout();
cy.vaultLoginByUserTitle('Capital Administrator');
cy.visit('/extracts');
cy.findByText(extractShareName).should('exist');
cy.findByText(extractShareNoAccessName).should('not.exist');
});
// Waiting on Jon's fix
it.skip('Scheduling an extract will create an entry in the UI', () => {
cy.switchUser('IDTestUser');
cy.visit('/extracts');
cy.addExtract(extractScheduleName, extractScheduleDescription, 'Encounter Summary');
extractPage.scheduleExtract(extractScheduleName, '2:35 A', 'Daily');
cy.findByText('Daily at 2:35 AM').should('exist');
});
// Waiting on Jon's fix
it.skip('User can enable and disable a schedule', () => {
cy.goToExtract(extractScheduleName);
extractPage.selectMenuOption('Schedule');
cy.get('.ant-switch').click();
cy.get('.ant-switch').invoke('attr', 'aria-checked').should('eq', 'true');
cy.findByText('Daily at 2:35 AM').should('exist');
cy.get('.ant-switch').click();
cy.get('.ant-switch').invoke('attr', 'aria-checked').should('eq', 'false');
cy.findByText('Daily at 2:35 AM').should('exist');
});
});
@@ -0,0 +1,433 @@
import * as dayjs from 'dayjs';
import advancedAnalyticsPage from '../pageObjects/advancedAnalyticsPageObject';
const advPage = new advancedAnalyticsPage();
const workbookName = `Cypress_Test_Workbook_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const workbookDescription = 'Test workbook made in Cypress';
const dataSourceName = `Cypress_Test_Data_Source_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const dataSourceName2 = `Cypress_Test_Data_Source2_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const dataSourceNameNoAccess = `Cypress_Test_Data_Source_NoAccess_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const datamartDescription = 'Test datamart made in Cypress';
const dssPatientEncounter = '55370';
const generalLedgerAndStaffing = '55426';
before(() => {
cy.vaultLoginByUserTitle('IDTestUser');
cy.createDataSource(dataSourceName, datamartDescription);
cy.addTableToDataSource(dssPatientEncounter, dataSourceName);
cy.addUserToDataSource(dataSourceName, 'obadmin', 'viewer');
cy.createDataSource(dataSourceName2, datamartDescription);
cy.addTableToDataSource(generalLedgerAndStaffing, dataSourceName2);
cy.addUserToDataSource(dataSourceName2, 'obadmin', 'viewer');
cy.createDataSource(dataSourceNameNoAccess, datamartDescription);
cy.addTableToDataSource(dssPatientEncounter, dataSourceNameNoAccess);
cy.logout();
cy.vaultLogin(true);
cy.addWorkbook(workbookName, workbookDescription, 'Encounter Summary');
cy.goToWorkbook(workbookName);
cy.wait(2000);
advPage.addDashboard();
cy.findByText('Save').click();
cy.wait(500);
});
beforeEach(() => {
cy.goToWorkbook(workbookName);
});
after(() => {
cy.logout();
cy.vaultLoginByUserTitle('IDTestUser');
cy.deleteDataSource(dataSourceName);
cy.deleteDataSource(dataSourceName2);
cy.deleteDataSource(dataSourceNameNoAccess);
cy.logout();
cy.vaultLogin(true);
cy.deleteWorkbook(workbookName);
cy.visit('/');
});
describe('Advanced Analytics widget filter tests', () => {
it('Calendar Date Time options are correctly disabled', () => {
advPage.addVisualizationWidget();
advPage.addwidgetFilter('Admit Date', 'Calendar Date Time');
advPage.openEditFilter('Admit Date - Calendar Date Time');
cy.wait(2000);
cy.get('.ant-popover-content').find('.ant-select-selector').click();
cy.get('.rc-virtual-list-holder-inner').findByText('Fiscal Year').click();
cy.findByText('Time Frame')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).not.to.contains('disabled');
});
cy.findByText('Time Frame')
.parent()
.next()
.findByText('Calendar')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).to.contains('disabled');
});
cy.findByText('List')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).not.to.contains('disabled');
});
cy.get('.ant-popover-content').find('.ant-select-selector').click();
cy.get('.rc-virtual-list-holder-inner').findByText('Fiscal Quarter').click();
cy.findByText('Time Frame')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).not.to.contains('disabled');
});
cy.findByText('Time Frame')
.parent()
.next()
.findByText('Calendar')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).to.contains('disabled');
});
cy.findByText('List')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).not.to.contains('disabled');
});
cy.get('.ant-popover-content').find('.ant-select-selector').click();
cy.get('.rc-virtual-list-holder-inner').findByText('Calendar Year').click();
cy.findByText('Time Frame')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).not.to.contains('disabled');
});
cy.findByText('Time Frame')
.parent()
.next()
.findByText('Calendar')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).not.to.contains('disabled');
});
cy.findByText('List')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).not.to.contains('disabled');
});
cy.get('.ant-popover-content').find('.ant-select-selector').click();
cy.get('.rc-virtual-list-holder-inner').findByText('Month').click();
cy.findByText('Time Frame')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).not.to.contains('disabled');
});
cy.findByText('Time Frame')
.parent()
.next()
.findByText('Calendar')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).not.to.contains('disabled');
});
cy.findByText('List')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).not.to.contains('disabled');
});
cy.get('.ant-popover-content').find('.ant-select-selector').click();
cy.get('.rc-virtual-list-holder-inner').findByText('Day').click();
cy.findByText('Time Frame')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).not.to.contains('disabled');
});
cy.findByText('Time Frame')
.parent()
.next()
.findByText('Calendar')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).not.to.contains('disabled');
});
cy.findByText('List')
.prev()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).to.contains('disabled');
});
cy.get('.ant-popover-content').findByText('Cancel').click();
cy.wait(500);
});
it('And/Or selection works correctly', () => {
advPage.addVisualizationWidget();
advPage.addwidgetFilter('Age', 'Age');
advPage.openEditFilter('Age - Age');
cy.findByText('Conditions').click();
cy.findByText('Add Condition').click();
cy.findByText('And').click();
cy.findByText('Add Condition').parent().parent().parent().siblings().last().contains('And').should('exist');
cy.findByText('Add Condition').parent().parent().parent().siblings().last().contains('Or').should('not.exist');
cy.findByText('Or').click();
cy.findByText('Add Condition').parent().parent().parent().siblings().last().contains('Or').should('exist');
cy.findByText('Add Condition').parent().parent().parent().siblings().last().contains('And').should('not.exist');
});
it('Not In shows up correctly', () => {
advPage.addVisualizationWidget();
advPage.addwidgetFilter('Age', 'Rollup');
advPage.openEditFilter('Age - Rollup');
cy.get('.tempo-action-bar__filters').find('input[type="checkbox"]').click();
cy.findByText('0-17').click();
cy.findByText('Apply').click();
cy.get('.filters-bar__item').should('contain', 'Not In: 0-17');
});
it('Filter can be renamed', () => {
advPage.addVisualizationWidget();
advPage.addwidgetFilter('Age', 'Rollup');
advPage.editFilterName('Age - Rollup', 'Updated Filter Name');
cy.findByText('Updated Filter Name:').should('exist');
});
it('Custom date filters', () => {
const filterAttribute = 'Calendar Date Time';
advPage.addVisualizationWidget();
advPage.addwidgetFilter('Admit Date', filterAttribute);
advPage.openEditFilter(`Admit Date - ${filterAttribute}`);
cy.get('.ant-popover-content').find('.ant-select-selector').click();
cy.get('.rc-virtual-list-holder-inner').findByText('Fiscal Year').click();
cy.findByText('Custom').click();
cy.findByText('Custom').parentsUntil('.tempo-spacing.tempo-spacing--col').last().next().find('input').click();
cy.findByText('This + Previous Fiscal Years').click();
cy.findByText('This + Previous').parent().next().clear().type('3');
cy.findByText('Apply').click();
cy.get('.filters-bar__item').contains(`${filterAttribute}: Custom`).should('exist');
advPage.openEditFilter(`Admit Date - ${filterAttribute}`);
cy.findByText('This + Previous').parent().next().should('have.value', '3');
});
it('Fiscal Year/Quarter filters are filtered to -10 and +8 years in a visualization widget', () => {
const tooEarlyYear = parseInt(dayjs().format('YYYY')) - 11;
const earliestYear = parseInt(dayjs().format('YYYY')) - 10;
const latestYear = parseInt(dayjs().format('YYYY')) + 8;
const toolateYear = parseInt(dayjs().format('YYYY')) + 9;
advPage.addVisualizationWidget();
advPage.addwidgetFilter('Admit Date', 'Calendar Date Time');
advPage.openEditFilter('Admit Date - Calendar Date Time');
cy.get('.ant-popover-content').find('.ant-select.ant-select-single.ant-select-show-arrow').click();
cy.findByText('Fiscal Year').click();
cy.findByText('List').click();
cy.findByText('This Fiscal Year').should('not.exist');
cy.get('.ant-spin.ant-spin-spinning', { timeout: 10000 }).should('exist');
cy.get('.ant-spin.ant-spin-spinning', { timeout: 10000 }).should('not.exist');
cy.get('.ant-popover-content').find('.p-datatable-scrollable-body-table').should('exist');
cy.get('.tempo-datacell').contains(`FY${earliestYear}`).should('exist');
cy.get('.tempo-datacell').contains(`FY${tooEarlyYear}`).should('not.exist');
//This is commented out until data past 2026 is available
//cy.get('.tempo-datacell').contains(`FY${latestYear}`).should('exist');
cy.get('.tempo-datacell').contains(`FY${toolateYear}`).should('not.exist');
cy.get('.ant-popover-content').find('.ant-select.ant-select-single.ant-select-show-arrow').click();
cy.findByText('Fiscal Quarter').click();
cy.get('.tempo-datacell').contains(`FY${earliestYear} Q1`).should('exist');
cy.get('.tempo-datacell').contains(`FY${tooEarlyYear} Q4`).should('not.exist');
//This is commented out until data past 2026 is available
//cy.get('.tempo-datacell').contains(`FY${latestYear} Q4`).should('exist');
cy.get('.tempo-datacell').contains(`FY${toolateYear} Q1`).should('not.exist');
});
it('Fiscal Year/Quarter filters are filtered to -10 and +8 years in a data sheet', () => {
const tooEarlyYear = parseInt(dayjs().format('YYYY')) - 11;
const earliestYear = parseInt(dayjs().format('YYYY')) - 10;
const latestYear = parseInt(dayjs().format('YYYY')) + 8;
const toolateYear = parseInt(dayjs().format('YYYY')) + 9;
advPage.addDataAnlysisSheet();
advPage.addwidgetFilter('Admit Date', 'Calendar Date Time');
advPage.openEditFilter('Admit Date - Calendar Date Time');
cy.get('.ant-popover-content').find('.ant-select.ant-select-single.ant-select-show-arrow').click();
cy.findByText('Fiscal Year').click();
cy.findByText('List').click();
cy.findByText('This Fiscal Year').should('not.exist');
cy.get('.ant-spin.ant-spin-spinning', { timeout: 10000 }).should('exist');
cy.get('.ant-spin.ant-spin-spinning', { timeout: 10000 }).should('not.exist');
cy.get('.ant-popover-content').find('.p-datatable-scrollable-body-table').should('exist');
cy.get('.tempo-datacell').contains(`FY${earliestYear}`).should('exist');
cy.get('.tempo-datacell').contains(`FY${tooEarlyYear}`).should('not.exist');
//This is commented out until data past 2026 is available
//cy.get('.tempo-datacell').contains(`FY${latestYear}`).should('exist');
cy.get('.tempo-datacell').contains(`FY${toolateYear}`).should('not.exist');
cy.get('.ant-popover-content').find('.ant-select.ant-select-single.ant-select-show-arrow').click();
cy.findByText('Fiscal Quarter').click();
cy.get('.tempo-datacell').contains(`FY${earliestYear} Q1`).should('exist');
cy.get('.tempo-datacell').contains(`FY${tooEarlyYear} Q4`).should('not.exist');
//This is commented out until data past 2026 is available
//cy.get('.tempo-datacell').contains(`FY${latestYear} Q4`).should('exist');
cy.get('.tempo-datacell').contains(`FY${toolateYear} Q1`).should('not.exist');
});
// JAZZ-58142
it.skip('Create filter by custom list option', () => {
advPage.addDataAnlysisSheet();
advPage.addwidgetFilter('Admit Date', 'Calendar Month');
advPage.openEditFilter('Admit Date - Calendar Month');
cy.findByText('Custom List').click();
cy.findByText('Upload List', { timeout: 5000 }).should('exist');
cy.get('#list').type('5, 6, 7, 9, 10');
cy.findByText('Apply').click();
cy.findByText('Configure').should('not.exist');
cy.wait(5000);
advPage.dragAndDropArea('Admit Date', 'Calendar Month', 'Rows');
advPage.dragAndDropArea('Measures', 'Account Balance', 'Columns');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view', { timeout: 5000 }).find('.p-treetable-row.p-treetable-row--child').contains('5').should('exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view', { timeout: 5000 }).find('.p-treetable-row.p-treetable-row--child').contains('6').should('exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view', { timeout: 5000 }).find('.p-treetable-row.p-treetable-row--child').contains('7').should('exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view', { timeout: 5000 }).find('.p-treetable-row.p-treetable-row--child').contains('9').should('exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view', { timeout: 5000 }).find('.p-treetable-row.p-treetable-row--child').contains('10').should('exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view', { timeout: 5000 }).find('.p-treetable-row.p-treetable-row--child').contains('4').should('not.exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view', { timeout: 5000 }).find('.p-treetable-row.p-treetable-row--child').contains('8').should('not.exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view', { timeout: 5000 }).find('.p-treetable-row.p-treetable-row--child').contains('11').should('not.exist');
});
// JAZZ-58142
it.skip('New list filter option overwrites the old selection', () => {
advPage.addDataAnlysisSheet();
advPage.addwidgetFilter('Admit Date', 'Calendar Month');
advPage.openEditFilter('Admit Date - Calendar Month');
cy.findByText('Custom List').click();
cy.findByText('Upload List', { timeout: 5000 }).should('exist');
cy.get('#list').type('5, 6, 7, 9, 10');
cy.findByText('Apply').click();
cy.findByText('Configure').should('not.exist');
cy.wait(5000);
advPage.setFilterValue('Admit Date - Calendar Month', '8');
cy.wait(5000);
advPage.dragAndDropArea('Admit Date', 'Calendar Month', 'Rows');
advPage.dragAndDropArea('Measures', 'Account Balance', 'Columns');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view').find('.p-treetable-row.p-treetable-row--child').contains('8').should('exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view').find('.p-treetable-row.p-treetable-row--child').contains('5').should('not.exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view').find('.p-treetable-row.p-treetable-row--child').contains('6').should('not.exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view').find('.p-treetable-row.p-treetable-row--child').contains('7').should('not.exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view').find('.p-treetable-row.p-treetable-row--child').contains('9').should('not.exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view').find('.p-treetable-row.p-treetable-row--child').contains('10').should('not.exist');
advPage.openEditFilter('Admit Date - Calendar Month');
cy.findByText('Custom List').click();
cy.findByText('Upload List', { timeout: 5000 }).should('exist');
cy.get('#list').type('11\n12');
cy.findByText('Apply').click();
cy.findByText('Configure').should('not.exist');
cy.wait(5000);
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view').find('.p-treetable-row.p-treetable-row--child').contains('11').should('exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view').find('.p-treetable-row.p-treetable-row--child').contains('12').should('exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view').find('.p-treetable-row.p-treetable-row--child').contains('8').should('not.exist');
});
});
describe('Advanced Analytics dashboard filter tests', () => {
it('Dashboard filter can have datamart changed', () => {
cy.findByText('Add Filter').click();
cy.findByText('Admit Date').should('exist');
cy.findByText('Charge Code').should('not.exist');
advPage.setCurrentDashboardFilterDataSource(dataSourceName2);
cy.findByText('Charge Code').should('exist');
cy.findByText('Admit Date').should('not.exist');
});
it('User cannot select a datamart they do not have access to', () => {
cy.findByText('Add Filter').click();
cy.findByTitle('Dimensions').parent().find('.ant-tree-switcher.ant-tree-switcher_open', { timeout: 10000 }).should('exist');
cy.findByText('Data Source').parent().next().click();
cy.findByText(dataSourceName).should('exist');
cy.findByText(dataSourceNameNoAccess).should('not.exist');
});
it('User cannot add multiple filters of the same attribute', () => {
advPage.addDashboardFilter('Admit Date', 'Calendar Month');
advPage.addDashboardFilter('Admit Date', 'Calendar Month');
cy.wait(1000);
cy.get('.ant-popover-content').findByText('Add').should('exist');
cy.get('.filters-bar__item').should('have.length', 1);
});
it('User can edit filters in present mode', () => {
advPage.addDashboardFilter('Admit Date', 'Calendar Month');
advPage.setDashboardFilter('Admit Date - Calendar Month', '1');
cy.findByText('Save').click();
cy.findByText('Saved').should('exist');
cy.findByText('Present').click();
cy.findByText('Exit').should('exist');
advPage.setDashboardFilter('Admit Date - Calendar Month', '2');
cy.findByText('Exit').click();
cy.findByText('Save').should('exist');
});
it('Fiscal Year/Quarter filters are filtered to -10 and +8 years in dashboard filter', () => {
const tooEarlyYear = parseInt(dayjs().format('YYYY')) - 11;
const earliestYear = parseInt(dayjs().format('YYYY')) - 10;
const latestYear = parseInt(dayjs().format('YYYY')) + 8;
const toolateYear = parseInt(dayjs().format('YYYY')) + 9;
advPage.addDashboardFilter('Admit Date', 'Calendar Date Time');
cy.findByText('Admit Date - Calendar Date Time:').click();
cy.get('.ant-popover-content').find('.ant-select.ant-select-single.ant-select-show-arrow').click();
cy.findByText('Fiscal Year').click();
cy.findByText('List').click();
cy.findByText('This Fiscal Year').should('not.exist');
cy.get('.ant-spin.ant-spin-spinning', { timeout: 10000 }).should('exist');
cy.get('.ant-spin.ant-spin-spinning', { timeout: 10000 }).should('not.exist');
cy.get('.ant-popover-content').find('.p-datatable-scrollable-body-table').should('exist');
cy.get('.tempo-datacell').contains(`FY${earliestYear}`).should('exist');
cy.get('.tempo-datacell').contains(`FY${tooEarlyYear}`).should('not.exist');
//This is commented out until data past 2026 is available
//cy.get('.tempo-datacell').contains(`FY${latestYear}`).should('exist');
cy.get('.tempo-datacell').contains(`FY${toolateYear}`).should('not.exist');
cy.get('.ant-popover-content').find('.ant-select.ant-select-single.ant-select-show-arrow').click();
cy.findByText('Fiscal Quarter').click();
cy.get('.tempo-datacell').contains(`FY${earliestYear} Q1`).should('exist');
cy.get('.tempo-datacell').contains(`FY${tooEarlyYear} Q4`).should('not.exist');
//This is commented out until data past 2026 is available
//cy.get('.tempo-datacell').contains(`FY${latestYear} Q4`).should('exist');
cy.get('.tempo-datacell').contains(`FY${toolateYear} Q1`).should('not.exist');
});
});
@@ -0,0 +1,42 @@
import * as dayjs from 'dayjs';
import advancedAnalyticsPage from '../pageObjects/advancedAnalyticsPageObject';
const advPage = new advancedAnalyticsPage();
const workbookName = `Cypress_Test_Workbook_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const workbookDescription = 'Test workbook made in Cypress - search tests';
before(() => {
cy.vaultLogin(true);
cy.wait(1000);
cy.addWorkbook(workbookName, workbookDescription, 'Encounter Summary');
cy.visit('/');
cy.goToWorkbook(workbookName);
advPage.addDashboard();
cy.findByText('Add Widget').click();
cy.findByText('Add Visual').click();
cy.wait(2000);
});
after(() => {
cy.deleteWorkbook(workbookName);
cy.visit('/');
});
describe('Advanced Analytics search tests', () => {
it('Clearing search will return to default state', () => {
advPage.expandNode('Account Class', 'open');
cy.get('div').contains('Account Class Code').should('exist');
cy.get('div').contains('Account Class Description').should('exist');
advPage.searchForAttribute('Admit Date');
advPage.expandNode('Admit Date', 'open');
cy.wait(500);
cy.get('span[title="Admit Date"]').should('exist');
cy.get('div').contains('Calendar Date Time').should('exist');
cy.get('div').contains('Calendar Month').should('exist');
advPage.clearSearch();
cy.get('span[title="Admit Date"]').should('exist');
cy.get('div').contains('Calendar Date Time').should('not.exist');
cy.get('div').contains('Calendar Month').should('not.exist');
cy.get('div').contains('Account Class Code').should('not.exist');
cy.get('div').contains('Account Class Description').should('not.exist');
});
});
@@ -0,0 +1,68 @@
import * as dayjs from 'dayjs';
import advancedAnalyticsPage from '../pageObjects/advancedAnalyticsPageObject';
const advPage = new advancedAnalyticsPage();
const workbookName = `Cypress_Test_Workbook_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const deleteWorkbookName = `dummy_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const workbookDescription = 'Test workbook made in Cypress - smoke tests';
before(() => {
cy.vaultLogin(true);
});
afterEach(() => {
cy.visit('/');
});
after(() => {
cy.logout();
cy.vaultLogin(true);
cy.deleteWorkbook(workbookName);
cy.deleteWorkbook(deleteWorkbookName);
});
describe('Advanced Analytics smoke tests', () => {
it('Create workbook', () => {
advPage.createWorkbook(workbookName, 'Encounter Summary', workbookDescription);
advPage.addDashboard();
advPage.addDashboardWidget(
'Bar',
'Clustered',
[{ dimensionGroup: 'Account Class', dimensionName: 'Account Class Code' }],
[{ measureGroup: 'Measures', mesureName: 'Account Balance' }],
[{ dimensionsOrMeasures: 'Dimensions', filterAttributeGroup: 'Age', filterAttributeName: 'Rollup', filterAttributeValue: '0-17' }]
);
advPage.addDataAnalysis([{ dimensionGroup: 'Account Class', dimensionName: 'Account Class Code' }], [{ measureGroup: 'Measures', mesureName: 'Account Balance' }]);
cy.findByText('Save').click();
cy.findByText('Saved').should('exist');
});
it('Update workbook', () => {
advPage.selectWorkbookFromTable(workbookName);
advPage.openEditWidget('Untitled Visualization');
advPage.deleteAttributeFromWidget('Account Class - Account Class Code', 'Categories');
cy.wait(2000);
advPage.addAttributeToWidget('Account Class', 'Account Class Description', 'Dimensions', 'Categories');
//Commented out because of JAZZ-58105
//advPage.setFilterValue('Rollup', '18-64');
advPage.saveWidget();
cy.findByText('Save').click();
cy.findByText('Saved').should('exist');
});
it('Delete all dashboards', () => {
cy.goToWorkbook(workbookName);
advPage.deleteDashboard('Dashboard');
advPage.deleteDashboard('Data Sheet');
cy.findByText('New workbook content available from Add button.').should('exist');
});
it('Delete workbook', () => {
cy.goToWorkbook(workbookName);
advPage.deleteWorkbookFromWorkbook(workbookName);
cy.addWorkbook(deleteWorkbookName, workbookDescription, 'Encounter Summary');
cy.visit('/');
advPage.deleteWorkbookFromTable(deleteWorkbookName);
});
});
@@ -0,0 +1,463 @@
import * as dayjs from 'dayjs';
import advancedAnalyticsPage from '../pageObjects/advancedAnalyticsPageObject';
const advPage = new advancedAnalyticsPage();
const workbookName = `Cypress_Test_Workbook_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const workbookName2 = `Cypress_Test_Workbook2_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const workbookName3 = `Cypress_Test_Workbook3_Crossout_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const dataSourceName = `Cypress_Test_Data_Source_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const dataSourceName2 = `Cypress_Test_Data_Source_For_Crossout_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const workbookDescription = 'Test workbook made in Cypress - widget tests';
const workbookDescription2 = 'Test workbook made in Cypress - widget tests - crossout';
const dataSourceDescription = 'Test data source made in Cypress - widget tests';
const dataSourceDescription2 = 'Test data source made in Cypress - widget tests - crossout';
const dssPatientEncounter = '55370';
const generalLedgerAndStaffing = '55426';
const formula = 'sum([Actual Dollars])*0+0.1234567';
before(() => {
cy.vaultLoginByUserTitle('IDTestUser');
cy.createDataSource(dataSourceName, dataSourceDescription);
cy.createDataSource(dataSourceName2, dataSourceDescription2);
cy.addTableToDataSource(generalLedgerAndStaffing, dataSourceName);
cy.addTableToDataSource(generalLedgerAndStaffing, dataSourceName2);
cy.addTableToDataSource(dssPatientEncounter, dataSourceName2);
cy.addUserToDataSource(dataSourceName, 'obadmin', 'viewer');
cy.addUserToDataSource(dataSourceName2, 'obadmin', 'viewer');
cy.logout();
cy.vaultLogin(true);
cy.addWorkbook(workbookName, workbookDescription, 'Encounter Summary');
cy.addWorkbook(workbookName2, workbookDescription, dataSourceName);
cy.addWorkbook(workbookName3, workbookDescription2, dataSourceName2);
cy.visit('/');
cy.goToWorkbook(workbookName);
advPage.addDashboard();
cy.findByText('Add Widget').click();
cy.findByText('Add Text').click();
cy.findByText('Ok').click();
cy.findByText('Save').click();
cy.findByText('Saved').should('exist');
});
beforeEach(() => {
cy.goToWorkbook(workbookName);
cy.wait(1000);
});
after(() => {
cy.logout();
cy.vaultLoginByUserTitle('IDTestUser');
cy.deleteDataSource(dataSourceName);
cy.deleteDataSource(dataSourceName2);
cy.logout();
cy.vaultLogin(true);
cy.deleteWorkbook(workbookName);
cy.deleteWorkbook(workbookName2);
cy.deleteWorkbook(workbookName3);
cy.logout();
cy.vaultLoginByUserTitle('IDTestUser');
cy.deleteDataSource(dataSourceName);
});
describe('Advanced Analytics text widget tests', () => {
it('Text widget - formatting options', () => {
advPage.editTextWidget();
//Bold
cy.get('.ql-bold').click();
cy.get('.ant-modal-content').find('.ql-editor').type('Bold Text\n');
cy.get('.ql-bold').click();
//Italics
cy.get('.ql-italic').click();
cy.get('.ant-modal-content').find('.ql-editor').type('Italic Text\n');
cy.get('.ql-italic').click();
//Underline
cy.get('.ql-underline').click();
cy.get('.ant-modal-content').find('.ql-editor').type('Underline Text\n');
cy.get('.ql-underline').click();
//Strikeout
cy.get('.ql-strike').click();
cy.get('.ant-modal-content').find('.ql-editor').type('Strikeout Text\n');
cy.get('.ql-strike').click();
cy.get('strong').should(($p) => {
expect($p).to.contain('Bold Text');
});
cy.get('em').should(($p) => {
expect($p).to.contain('Italic Text');
});
cy.get('u').should(($p) => {
expect($p).to.contain('Underline Text');
});
cy.get('s').should(($p) => {
expect($p).to.contain('Strikeout Text');
});
});
it('Text widget - Hyperlink testing', () => {
advPage.editTextWidget();
const text = 'Hyperlink Text';
cy.get('.ant-modal-content').find('.ql-editor').type(`${text}\n`);
cy.get('.ant-modal-content').find('.ql-editor').type('{selectall}');
cy.get('.ql-link').click();
cy.get('div[class$="ql-editing"]').find('input').type('https://www.google.com');
cy.get('.ql-action').click();
cy.wait(1000);
cy.findByText('Ok').click();
cy.wait(1000);
cy.get('a')
.contains(text)
.invoke('attr', 'href')
.then((classVal) => {
expect(classVal).to.be.equal('https://www.google.com');
});
cy.get('a').contains(text).click();
cy.wait(5000);
cy.location('href').should((loc) => {
expect(loc).contains('https://www.google.com');
});
});
});
describe('Advanced Analytics visualization widget tests', () => {
it('Visualization Widget - Each type has the correct options', () => {
cy.findByText('Add Widget').click();
cy.findByText('Add Visual').click();
cy.findByText('Visualization Type').parent().next().click();
cy.get('.ant-select-item-option-content').contains('Bar').click();
cy.findByText('Visualization Type').parent().next().next().click();
cy.get('div[aria-label="Clustered"]').should('exist');
cy.get('div[aria-label="Stacked"]').should('exist');
cy.findByText('Visualization Type').parent().next().click();
cy.get('.ant-select-item-option-content').contains('Column').click();
cy.findByText('Visualization Type').parent().next().next().click();
cy.get('div[aria-label="Clustered"]').should('exist');
cy.get('div[aria-label="Stacked"]').should('exist');
cy.findByText('Visualization Type').parent().next().click();
cy.get('.ant-select-item-option-content').contains('Line').click();
cy.findByText('Visualization Type').parent().next().next().should('not.exist');
cy.findByText('Visualization Type').parent().next().click();
cy.get('.ant-select-item-option-content').contains('Pie').click();
cy.findByText('Visualization Type').parent().next().next().click();
cy.get('div[aria-label="Donut"]').should('exist');
cy.get('div[aria-label="Pie"]').should('exist');
cy.findByText('Visualization Type').parent().next().click();
cy.get('.ant-select-item-option-content').contains('Calendar').click();
cy.findByText('Visualization Type').parent().next().next().should('not.exist');
cy.findByText('Visualization Type').parent().next().click();
cy.get('.ant-select-item-option-content').contains('Metric').click();
cy.findByText('Visualization Type').parent().next().next().should('not.exist');
});
it('Visualization Widget - create pie chart', () => {
advPage.addDashboardWidget('Pie', 'Pie', [{ dimensionGroup: 'Age', dimensionName: 'Rollup' }], [{ measureGroup: 'Measures', mesureName: 'Net Revenue' }]);
});
it('Switching around the legend', () => {
advPage.addDashboardWidgetWithPivots(
'Bar',
[{ dimensionGroup: 'Age', dimensionName: 'Rollup' }],
[{ measureGroup: 'Measures', mesureName: 'Net Revenue' }],
[{ pivoteGroup: 'Admit Date', pivotName: 'Fiscal Year' }]
);
advPage.openEditWidget('Untitled Visualization');
cy.findByText('Design').click();
cy.findByText('Positioning').scrollIntoView();
cy.wait(2000);
cy.findByText('Top').click({ force: true });
cy.wait(2000);
cy.findByText('Right').click({ force: true });
cy.wait(2000);
cy.findByText('Bottom').click({ force: true });
cy.wait(2000);
cy.findByText('Left').click({ force: true });
cy.wait(2000);
});
it('Switching data marts in a widget', () => {
cy.goToWorkbook(workbookName);
advPage.addVisualizationWidget();
advPage.dragAndDropArea('Account Class', 'Account Class Code', 'Categories');
advPage.dragAndDropArea('Measures', 'Account Balance', 'Values');
advPage.clearSearch();
//Account Class is in the original data mart and Charge Code is in the one that will be changed to
cy.findByText('Account Class').should('exist');
cy.findByText('Charge Code').should('not.exist');
advPage.switchDataSource(dataSourceName);
cy.contains('Encounter Summary').should('not.exist');
cy.contains(dataSourceName).should('exist');
cy.get('.data-designer__fields').find('div').contains('Account Class Code').should('not.exist');
cy.get('.data-designer__fields').find('div').contains('Account Balance').should('not.exist');
//Charge Code is in the new data mart and Account Class was in the original data mart
cy.findByText('Account Class').should('not.exist');
cy.findByText('Charge Code').should('exist');
});
// Cross out was taken out
it.skip('Cross out for dimensions and measures in data vizualization widget', () => {
cy.logout();
cy.vaultLoginByUserTitle('IDTestUser');
cy.addTableToDataSource(dssPatientEncounter, dataSourceName);
cy.logout();
cy.vaultLogin(true);
cy.goToWorkbook(workbookName2);
advPage.addDashboard();
advPage.addVisualizationWidget();
//Categories
advPage.dragAndDropArea('Account Class', 'Account Class Code', 'Categories');
advPage.searchForAttribute('Actual Dollars');
cy.get('div')
.contains('Actual Dollars')
.parentsUntil('.ant-tree-treenode.ant-tree-treenode-disabled')
.last()
.parent()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).to.contain('ant-tree-treenode-disabled');
});
advPage.clearSearch();
//Values
advPage.deleteAttributeFromWidget('Account Class - Account Class Code', 'Categories');
advPage.dragAndDropArea('Measures', 'Actual Dollars', 'Values');
advPage.searchForAttribute('Account Class Code');
cy.get('div')
.contains('Account Class Code')
.parentsUntil('.ant-tree-treenode.ant-tree-treenode-disabled')
.last()
.parent()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).to.contain('ant-tree-treenode-disabled');
});
advPage.clearSearch();
//Break By
advPage.deleteAttributeFromWidget('Actual Dollars', 'Values');
advPage.dragAndDropArea('Account Class', 'Account Class Code', 'Break By');
advPage.searchForAttribute('Actual Dollars');
cy.get('div')
.contains('Actual Dollars')
.parentsUntil('.ant-tree-treenode.ant-tree-treenode-disabled')
.last()
.parent()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).to.contain('ant-tree-treenode-disabled');
});
advPage.clearSearch();
//Filters
advPage.deleteAttributeFromWidget('Account Class - Account Class Code', 'Break By');
advPage.addwidgetFilter('Measures', 'Actual Dollars');
advPage.searchForAttribute('Account Class Code');
cy.get('div')
.contains('Account Class Code')
.parentsUntil('.ant-tree-treenode.ant-tree-treenode-disabled')
.last()
.parent()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).to.contain('ant-tree-treenode-disabled');
});
advPage.clearSearch();
});
});
describe('Advanced Analytics data analysis tests', () => {
// Cross out was taken out
it.skip('Cross out for dimensions and measures in data analysis', () => {
cy.goToWorkbook(workbookName3);
advPage.addDataAnlysisSheet();
//Rows
advPage.dragAndDropArea('Account Class', 'Account Class Code', 'Rows');
advPage.searchForAttribute('Actual Dollars');
cy.get('div')
.contains('Actual Dollars')
.parentsUntil('.ant-tree-treenode.ant-tree-treenode-disabled')
.last()
.parent()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).to.contain('ant-tree-treenode-disabled');
});
advPage.clearSearch();
//Values
advPage.deleteAttributeFromWidget('Account Class - Account Class Code', 'Rows');
advPage.dragAndDropArea('Measures', 'Actual Dollars', 'Columns');
advPage.searchForAttribute('Account Class Code');
cy.get('div')
.contains('Account Class Code')
.parentsUntil('.ant-tree-treenode.ant-tree-treenode-disabled')
.last()
.parent()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).to.contain('ant-tree-treenode-disabled');
});
advPage.clearSearch();
//Filters
advPage.deleteAttributeFromWidget('Actual Dollars', 'Columns');
advPage.addwidgetFilter('Account Class', 'Account Class Code');
advPage.searchForAttribute('Actual Dollars');
cy.get('div')
.contains('Actual Dollars')
.parentsUntil('.ant-tree-treenode.ant-tree-treenode-disabled')
.last()
.parent()
.invoke('attr', 'class')
.then((classVal) => {
expect(classVal).to.contain('ant-tree-treenode-disabled');
});
advPage.clearSearch();
});
it('Duplicate values', () => {
cy.goToWorkbook(workbookName3);
advPage.addDataAnlysisSheet();
advPage.dragAndDropArea('Measures', 'Account Balance', 'Columns');
advPage.duplicateValue('Account Balance', 'Account Balance_1');
cy.findAllByText('Account Balance_1').should('exist');
});
it('Test decimal places in data sheets', () => {
cy.goToWorkbook(workbookName3);
advPage.addDataAnlysisSheet();
advPage.dragAndDropArea('Account Class', 'Account Class Code', 'Rows');
advPage.addDataSheetCustomCalculatedValue('test', null, formula);
advPage.addDataSheetCustomCalculatedValue('test 2', 'Number (2 Decimals)', formula);
advPage.addDataSheetCustomCalculatedValue('test 3', 'Number (4 Decimals)', formula);
cy.findByText('0.12', { timeout: 10000 }).should('exist');
cy.findByText('0.1235', { timeout: 10000 }).should('exist');
});
it('Measures can be added to multiple groups when they exist', () => {
advPage.addDataAnlysisSheet();
advPage.addNewDataSheetGroup();
advPage.addNewDataSheetGroup();
advPage.dragAndDropArea('Measures', 'Account Balance', 'Columns');
advPage.dragAndDropArea('Measures', 'Birth Weight', 'Columns', 1);
cy.get('.fields').eq(1).findByText('Account Balance').should('exist');
cy.get('.fields').eq(2).findByText('Birth Weight').should('exist');
advPage.deleteNumberedDataSheetGroup(2);
cy.get('.fields').eq(1).findByText('Account Balance').should('exist');
cy.get('.fields').eq(2).findByText('Birth Weight').should('not.exist');
});
it('Test disabling auto refresh', () => {
cy.goToWorkbook(workbookName);
advPage.addDataAnlysisSheet();
//cy.findByText('Turn on Auto Refresh to see current data').should('not.exist');
cy.findByText('Auto Refresh').click();
//cy.findByText('Turn on Auto Refresh to see current data').should('exist');
advPage.dragAndDropArea('Account Class', 'Account Class Code', 'Rows');
advPage.dragAndDropArea('Measures', 'Account Balance', 'Columns');
cy.get('.p-treetable-scrollable-wrapper').children().eq(0).findByText('Auto Refresh Disabled').should('exist');
cy.get('.p-treetable-scrollable-wrapper').children().eq(1).findByText('Auto Refresh Disabled').should('exist');
cy.get('.p-treetable-scrollable-wrapper').children().eq(0).find('.p-treetable-scrollable-header').findByText('Account Class Code').should('not.exist');
cy.get('.p-treetable-scrollable-wrapper').children().eq(1).find('.p-treetable-scrollable-header').findByText('Account Balance').should('exist');
cy.findByText('Auto Refresh').click();
cy.get('ant-spin.ant-spin-spinning', { timeout: 10000 }).should('not.exist');
cy.get('.p-treetable-scrollable-view.p-treetable-frozen-view').find('.p-treetable-tbody').findByText('0');
cy.get('.p-treetable-scrollable-view.p-treetable-unfrozen-view').find('.p-treetable-tbody').findByText('28,375,615');
});
it('Calculated values cannot be saved without all fields filled in', () => {
cy.goToWorkbook(workbookName3);
advPage.addDataAnlysisSheet();
cy.findByText('Columns').parentsUntil('.fields-column-header').last().next().click();
cy.findByText('Add Calculation').click();
cy.get('#name').should('be.visible');
cy.get('.calculated-value-editor').find('textarea').type(formula, { force: true }).wait(1000);
cy.findByText('Apply').click({ force: true });
cy.findByTitle('Name').parent().next().findByText('Required').should('exist');
cy.findByTitle('Calculation').parent().next().findByText('Required').should('not.exist');
cy.findByText('Cancel').click();
cy.findByText('Cancel').should('not.exist');
});
it('Creating a filter in a calculation with multiple tables', () => {
cy.goToWorkbook(workbookName2);
advPage.addDataAnlysisSheet();
advPage.dragAndDropArea('Account', 'Account Code', 'Rows');
advPage.addDataSheetCustomCalculatedValue('test', null, formula);
advPage.editValue('test', null, null, 'sum([Net Revenue]) - sum([Fixed Direct Cost]) + sum([Account Balance])');
advPage.openCalculatedValue('test');
advPage.addCalculationFilter('Account Class', 'Account Class Code');
cy.findByText('Apply').click({ force: true });
cy.get('.ant-modal-title').contains('test').should('not.exist');
});
it('Target moving an attribute into a column and row', () => {
cy.goToWorkbook(workbookName);
advPage.addDataAnlysisSheet();
cy.findByText('Auto Refresh').click();
advPage.dragAndDropArea('Account Class', 'Account Class Code', 'Rows');
advPage.dragAndDropArea('Measures', 'Account Balance', 'Columns');
advPage.dragAndDropAreaToPosition('Account Class', 'Account Class', 'Rows', 0);
advPage.dragAndDropAreaToPosition('Account Class', 'Account Class Description', 'Rows', 1);
advPage.dragAndDropAreaToPosition('Measures', 'Date Of Birth', 'Columns', 0);
cy.wait(3000);
//Tests the Expand To dropdown
cy.findByText('Expand To').parent().parent().click();
cy.get('.rc-virtual-list-holder-inner').children().first().invoke('attr', 'title').should('eq', 'Account Class - Account Class');
cy.get('.rc-virtual-list-holder-inner').children().eq(1).invoke('attr', 'title').should('eq', 'Account Class - Account Class Description');
cy.get('.rc-virtual-list-holder-inner').children().last().invoke('attr', 'title').should('eq', 'Account Class - Account Class Code');
//Checks the rows
cy.findAllByText('Rows').eq(0).parentsUntil('.fields').last().next().find('.sortable-list').children().first().findByText('Account Class - Account Class');
cy.findAllByText('Rows').eq(0).parentsUntil('.fields').last().next().find('.sortable-list').children().eq(1).findByText('Account Class - Account Class Description');
cy.findAllByText('Rows').eq(0).parentsUntil('.fields').last().next().find('.sortable-list').children().last().findByText('Account Class - Account Class Code');
//Checks the columns
cy.findAllByText('Columns').eq(0).parentsUntil('.fields').last().next().find('.sortable-list').children().first().findByText('Date Of Birth');
cy.findAllByText('Columns').eq(0).parentsUntil('.fields').last().next().find('.sortable-list').children().last().findByText('Account Balance');
});
it('Column groups can be duplicated', () => {
cy.goToWorkbook(workbookName);
advPage.addDataAnlysisSheet();
advPage.dragAndDropArea('Measures', 'Account Balance', 'Columns');
advPage.addDataSheetCustomCalculatedValueToNumberedGroup(1, 'test', null, 'sum([Net Revenue]) - sum([Fixed Direct Cost]) + sum([Account Balance])');
advPage.DuplicateGroup(1);
cy.findAllByText('Columns').should('have.length', 2);
});
});
@@ -0,0 +1,82 @@
import * as dayjs from 'dayjs';
import advancedAnalyticsPage from '../pageObjects/advancedAnalyticsPageObject';
const advPage = new advancedAnalyticsPage();
const workbookNameOb = `Cypress_Test_OB_Workbook_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const workbookNameObShareViewer = `Cypress_Test_OB_Share_Viewer_Workbook_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const workbookNameObShareEditor = `Cypress_Test_OB_Share_Editor_Workbook_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const workbookNameCap = `Cypress_Test_Cap_Workbook_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const workbookDescription = 'Test workbook made in Cypress - workbook security tests';
before(() => {
cy.vaultLogin(true);
cy.addWorkbook(workbookNameOb, workbookDescription, 'Encounter Summary');
cy.addWorkbook(workbookNameObShareViewer, workbookDescription, 'Encounter Summary');
cy.addUserToWorkbook(workbookNameObShareViewer, 'capadmin', 'viewer');
cy.addWorkbook(workbookNameObShareEditor, workbookDescription, 'Encounter Summary');
cy.addUserToWorkbook(workbookNameObShareEditor, 'capadmin', 'editor');
});
after(() => {
cy.logout();
cy.vaultLogin(true);
cy.deleteWorkbook(workbookNameOb);
cy.deleteWorkbook(workbookNameObShareViewer);
cy.deleteWorkbook(workbookNameObShareEditor);
cy.logout();
cy.vaultLoginByUserTitle('Capital Administrator');
cy.deleteWorkbook(workbookNameCap);
});
describe('Advanced Analytics workbook security tests', () => {
it('Users see the correct workbooks', () => {
cy.logout();
cy.vaultLoginByUserTitle('Capital Administrator');
cy.addWorkbook(workbookNameCap, workbookDescription, 'Encounter Summary');
cy.visit('/');
cy.findByText('Created by Me').click();
cy.get('main').find('span').contains(workbookNameCap).should('exist');
cy.get('main').find('span').contains(workbookNameOb).should('not.exist');
cy.get('main').find('span').contains(workbookNameObShareViewer).should('not.exist');
cy.get('main').find('span').contains(workbookNameObShareEditor).should('not.exist');
cy.findByText('Shared with Me').click();
cy.get('main').find('span').contains(workbookNameCap).should('not.exist');
cy.get('main').find('span').contains(workbookNameOb).should('not.exist');
cy.get('main').find('span').contains(workbookNameObShareViewer).should('exist');
cy.get('main').find('span').contains(workbookNameObShareEditor).should('exist');
cy.goToWorkbook(workbookNameObShareEditor);
cy.findByText('Add').should('exist');
advPage.openWorkbookKabob(workbookNameObShareEditor);
cy.findByText('Edit').should('exist');
cy.findByText('Open in a New Tab').should('exist');
cy.findByText('Share').should('exist');
cy.findByText('Delete').should('exist');
cy.goToWorkbook(workbookNameObShareViewer);
cy.findByText('Add').should('not.exist');
advPage.openWorkbookKabobOneButton(workbookNameObShareViewer);
cy.findByText('Edit').should('not.be.visible');
cy.findByText('Open in a New Tab').should('exist');
cy.findByText('Share').should('not.be.visible');
cy.findByText('Delete').should('not.be.visible');
cy.logout();
cy.vaultLogin(true);
cy.findByText('Created by Me').click();
cy.get('main').find('span').contains(workbookNameCap).should('not.exist');
cy.get('main').find('span').contains(workbookNameOb).should('exist');
cy.get('main').find('span').contains(workbookNameObShareViewer).should('exist');
cy.get('main').find('span').contains(workbookNameObShareEditor).should('exist');
cy.findByText('Shared with Me').click();
cy.get('main').find('span').contains(workbookNameCap).should('not.exist');
cy.get('main').find('span').contains(workbookNameOb).should('not.exist');
cy.get('main').find('span').contains(workbookNameObShareViewer).should('not.exist');
cy.get('main').find('span').contains(workbookNameObShareEditor).should('not.exist');
});
});
@@ -0,0 +1,31 @@
import * as dayjs from 'dayjs';
import advancedAnalyticsPage from '../pageObjects/advancedAnalyticsPageObject';
const advPage = new advancedAnalyticsPage();
const workbookName = `Cypress_Test_Workbook_migrate_adhoc_${dayjs().format('YYYY-MM-DD_HHmmss')}`;
const workbookDescription = 'Test workbook made in Cypress - migrate from ad-hoc';
before(() => {
cy.vaultLoginByUserTitle('IDTestUser');
cy.addWorkbook(workbookName, workbookDescription, 'Encounter Summary');
});
beforeEach(() => {
cy.goToWorkbook(workbookName);
});
afterEach(() => {
cy.visit('/');
});
after(() => {
cy.deleteWorkbook(workbookName);
});
describe('Advanced Analytics smoke tests', () => {
it('Add from ad-hoc with multiple columns', () => {
advPage.addFromAdHoc('BK Test Migrate');
cy.get('.fields').eq(0).findByText('Admit Date - Fiscal Year').should('exist');
cy.get('.fields').eq(1).findByText('Cases').should('exist');
});
});
@@ -0,0 +1,89 @@
export default class advancedAnalyticsPage {
addDataSource = (dataSourceName: string, dataSourceDescription?: string | undefined): void => {
this.goToDataSources();
cy.findByText('Add Data Source').click();
this.fillOutDataSourceDetailsForm(dataSourceName, dataSourceDescription);
};
addStandardDataSource = (dataSourceName: string, dataSourceDescription?: string | undefined): void => {
this.goToStandardDataSources();
cy.findByText('Add Standard Data Source').click();
this.fillOutDataSourceDetailsForm(dataSourceName, dataSourceDescription);
};
editDataSource = (oldDataSourceName: string, dataSourceName: string, dataSourceDescription?: string | undefined): void => {
cy.goToDataSource(oldDataSourceName);
cy.get('h1').contains(oldDataSourceName, { timeout: 5000 }).should('exist');
this.editCurrentDataSource(dataSourceName, dataSourceDescription);
};
editStandardDataSource = (oldDataSourceName: string, dataSourceName: string, dataSourceDescription?: string | undefined): void => {
cy.goToStandardDataSource(oldDataSourceName);
cy.get('h1').contains(oldDataSourceName, { timeout: 5000 }).should('exist');
this.editCurrentDataSource(dataSourceName, dataSourceDescription);
};
deleteDataSourceFromPage = (dataSourceName: string): void => {
cy.findByText(dataSourceName).parentsUntil('tr').last().siblings().last().find('span[aria-label="delete"]').click({ force: true });
cy.get('span').contains(`Permanently delete ${dataSourceName}?`).parentsUntil('.ant-modal-confirm-body-wrapper').last().next().findByText('Delete Data Source').click();
};
deleteStandardDataSourceFromPage = (dataSourceName: string): void => {
cy.findByText(dataSourceName).parentsUntil('tr').last().siblings().last().find('span[aria-label="delete"]').click({ force: true });
cy.get('span').contains(`Permanently delete ${dataSourceName}?`).parentsUntil('.ant-modal-confirm-body-wrapper').last().next().findByText('Delete Data Source').click();
};
goToDataSources = (): void => {
cy.visit('/datasources');
};
goToStandardDataSources = (): void => {
cy.visit('/standard-datasources');
};
fillOutDataSourceDetailsForm = (dataSourceName: string, dataSourceDescription?: string | undefined): void => {
cy.get('#name').clear();
cy.get('#name').type(dataSourceName);
if (dataSourceDescription !== undefined) {
cy.get('#description').clear().type(dataSourceDescription);
}
cy.findByText('Apply').click();
cy.get('#name', { timeout: 5000 }).should('not.exist');
};
openDataSourceEditWindow = (): void => {
cy.get('.tempo-header--extra-wrapper').find('button').first().click();
cy.findByText('Edit').click();
};
editCurrentDataSource = (dataSourceName: string, dataSourceDescription?: string | undefined): void => {
this.openDataSourceEditWindow();
this.fillOutDataSourceDetailsForm(dataSourceName, dataSourceDescription);
};
addDataTable = (dataTableName: string): void => {
cy.findByText('Add Data Table').click();
cy.wait(500);
cy.get('.ant-modal-body').find('input').type(dataTableName);
cy.wait(500);
cy.findByText(dataTableName).click();
cy.findByText('Apply').click();
};
addUsersToDataSource = (dataSourceName: string, userNames: { userName: string }[]): void => {
cy.goToDataSource(dataSourceName);
cy.findByText(dataSourceName).should('exist');
cy.findByText(dataSourceName).parentsUntil('.tempo-header').next().find('button').first().click();
cy.findByText('Security').click();
cy.findByText('Add Users').click();
cy.wait(2000);
userNames.forEach((username) => {
cy.get('.ant-drawer-body').find('input[type="text"]').type(username.userName);
cy.get('span').contains(username.userName).click();
cy.get('.ant-drawer-body').find('input[type="text"]').clear();
});
cy.findByText('Add').click();
cy.findByText('Save').click();
cy.wait(2000);
};
}
@@ -0,0 +1,100 @@
export default class advancedAnalyticsPage {
addNewExtract = (extractName: string, extractDataSource: string, extractDescription?: string | undefined): void => {
cy.findByText('Add Extract').click();
cy.get('.ant-dropdown-menu-title-content').contains('Extract').click();
cy.get('#name').type(extractName);
if (extractDescription !== undefined) {
cy.get('#description').type(extractDescription);
}
cy.get('#dataSourceId').click();
cy.findByText(extractDataSource).click();
cy.findByText('Save').click();
cy.findByText(extractName).should('exist');
};
addNewCustomExtract = (extractName: string, extractDescription?: string | undefined): void => {
cy.findByText('Add Extract').click();
cy.findByText('Custom Sql Extract').click();
cy.get('#name').type(extractName);
if (extractDescription !== undefined) {
cy.get('#description').type(extractDescription);
}
cy.findByText('Save').click();
cy.findByText(extractName).should('exist');
};
editExtract = (extractName: string, newExtractName: string, newExtractDescription?: string | undefined): void => {
this.goToExtract(extractName);
cy.findByText('Back to Extracts').should('exist');
cy.findByText(extractName).should('exist');
this.selectMenuOption('Edit');
cy.findByText('Edit Extract').should('exist');
cy.get('#name').clear().type(newExtractName);
if (newExtractDescription !== undefined) {
cy.get('#description').clear().type(newExtractDescription);
}
cy.findByText('Apply').click();
};
deleteExtract = (extractName: string): void => {
cy.get('.tempo-link__text').then((items) => {
const list = Array.from(items, (item) => item.innerText);
list.forEach((element) => {
if (element === extractName) {
cy.findByText(extractName).parentsUntil('.p-datatable-tbody').last().find('.anticon.anticon-delete').scrollIntoView();
cy.findByText(extractName).parentsUntil('.p-datatable-tbody').last().find('.anticon.anticon-delete').click();
cy.findByText('Delete Extract').click();
cy.findByText(`Permanently delete ${extractName}?`).should('not.exist');
}
});
});
};
goToExtract = (extractName: string): void => {
cy.findByText(extractName).click();
cy.get('h1').contains(extractName).should('exist');
};
shareExtract = (extractName: string, userGroupName: string): void => {
this.goToExtract(extractName);
this.selectMenuOption('Share');
cy.findByText(`Share ${extractName}`).should('exist');
cy.findByText('Add Groups').click();
cy.findByText('User Groups').should('exist');
cy.get('input[type="text"]').type(userGroupName);
cy.wait(500);
cy.get('span').contains(userGroupName).click();
cy.get('.ant-drawer-title').contains('Add Groups').parentsUntil('.ant-drawer-wrapper-body').last().siblings().last().findByText('Add').click();
cy.findByText(`Share ${extractName}`).should('exist');
cy.findByText('Save').click();
cy.findByText(`Share ${extractName}`).should('not.exist');
};
scheduleExtract = (extractName: string, startTime: string, frequency: string): void => {
cy.goToExtract(extractName);
this.selectMenuOption('Schedule');
cy.findByText('Schedule Extract', { timeout: 5000 }).should('exist');
cy.wait(3000);
cy.findByText('Add Schedule').click();
cy.get('#startTime', { timeout: 2000 }).should('exist');
cy.get('#startTime').click();
cy.get('#startTime').clear().type(startTime).type('{enter}');
cy.findByText('Repeats').parent().next().click();
cy.get('span').contains(frequency).click();
cy.findByText('Save').click();
cy.get('#startTime', { timeout: 2000 }).should('not.exist');
};
selectMenuOption = (menuOption: string): void => {
cy.findByText('Export').parentsUntil('.tempo-spacing.tempo-spacing--row').last().next().click();
cy.findByText(menuOption).click();
};
}
@@ -0,0 +1,511 @@
export default class advancedAnalyticsPage {
createWorkbook = (workbookName: string, dataSourceName: string, workbookDescription?: string | undefined): void => {
cy.visit('/');
cy.findByText('Add Workbook').click({ force: true });
cy.get('#name').type(workbookName);
cy.get('#description').type(workbookDescription);
cy.get('#defaultDataSourceId').click();
cy.findByText(dataSourceName).click();
cy.findByText('Save').click();
cy.wait(2000);
};
addDashboard = (): void => {
cy.findByText('Add').click();
cy.findByText('Dashboard').click();
};
addDashboardWidget = (
widgetType: string,
widgetSubtype: string,
rowValues: { dimensionGroup: string; dimensionName: string }[],
valuesValues: { measureGroup: string; mesureName: string }[],
filters?: { dimensionsOrMeasures: string; filterAttributeGroup: string; filterAttributeName: string; filterAttributeValue: string }[] | undefined
): void => {
this.addVisualizationWidget();
this.setWidgetType(widgetType, widgetSubtype);
rowValues.forEach((rowValue) => {
this.dragAndDropArea(rowValue.dimensionGroup, rowValue.dimensionName, 'Categories');
});
valuesValues.forEach((valuesValue) => {
this.dragAndDropArea(valuesValue.measureGroup, valuesValue.mesureName, 'Values');
});
//Commented out because of JAZZ-58105
//if (filters !== undefined) {
// filters.forEach((filter) => {
// this.addwidgetFilter(filter.filterAttributeGroup, filter.filterAttributeName, filter.filterAttributeValue);
// });
//}
cy.get('.ant-modal-content').findByText('Save').click();
};
saveWidget = () => {
cy.get('.ant-modal-content').findByText('Save').click();
};
setWidgetType = (widgetType: string, widgetSubType: string) => {
cy.findByText('Visualization Type').parent().next().click();
cy.get('.ant-select-item-option-content').contains(widgetType).click();
cy.findByText('Visualization Type').parent().next().next().click();
cy.findAllByRole('listbox').last().next().findByText(widgetSubType).click();
};
addVisualizationWidget = (): void => {
cy.findByText('Add Widget').click();
cy.findByText('Add Visual').click();
cy.findByTitle('Dimensions').parent().find('.ant-tree-switcher.ant-tree-switcher_open', { timeout: 10000 }).should('exist');
};
addDataAnlysisSheet = (): void => {
cy.findByText('Add').click();
cy.findByText('Data Sheet').click();
cy.findByTitle('Dimensions').parent().find('.ant-tree-switcher.ant-tree-switcher_open', { timeout: 10000 }).should('exist');
};
addAttributesToWidget = (attributes: { attributeGroup: string; attributeName: string; sectionName: string; attributeDestination: string }[]): void => {
attributes.forEach((attribute) => {
this.addAttributeToWidget(attribute.attributeGroup, attribute.attributeName, attribute.sectionName, attribute.attributeDestination);
});
};
addAttributeToWidget = (attributeGroup: string, attributeName: string, attributeSection: string, attributeDestination: string): void => {
this.dragAndDropArea(attributeGroup, attributeName, attributeDestination);
};
addDashboardWidgetWithPivots = (
widgetType: string,
rowValues: { dimensionGroup: string; dimensionName: string }[],
valuesValues: { measureGroup: string; mesureName: string }[],
pivotValues: { pivoteGroup: string; pivotName: string }[],
filters?: { filterAttributeGroup: string; filterAttributeName: string; filterAttributeValue: string }[] | undefined
): void => {
this.addVisualizationWidget();
rowValues.forEach((rowValue) => {
this.dragAndDropArea(rowValue.dimensionGroup, rowValue.dimensionName, 'Categories');
});
valuesValues.forEach((valuesValue) => {
this.dragAndDropArea(valuesValue.measureGroup, valuesValue.mesureName, 'Values');
});
pivotValues.forEach((pivotValue) => {
this.dragAndDropArea(pivotValue.pivoteGroup, pivotValue.pivotName, 'Break By');
});
if (filters !== undefined) {
filters.forEach((filter) => {
this.addwidgetFilter(filter.filterAttributeGroup, filter.filterAttributeName, filter.filterAttributeValue);
});
}
cy.findByText('Save').click();
};
expandNode = (nodeName: string, openOrClose: string): void => {
cy.findByTitle(nodeName)
.invoke('attr', 'class')
.then((classVal) => {
if (classVal.includes('ant-tree-node-content-wrapper-close') && openOrClose === 'open') {
cy.findByTitle(nodeName).siblings().last().click();
}
if (classVal.includes('ant-tree-node-content-wrapper-open') && openOrClose === 'close') {
cy.findByTitle(nodeName).siblings().last().click();
}
});
cy.wait(500);
};
addDataAnalysis = (
rowValues: { dimensionGroup: string; dimensionName: string }[],
valuesValues: { measureGroup: string; mesureName: string }[],
filters?: { dimensionsOrMeasures: string; filterAttributeGroup: string; filterAttributeName: string; filterAttributeValue: string }[] | undefined
): void => {
this.addDataAnlysisSheet();
rowValues.forEach((rowValue) => {
this.dragAndDropArea(rowValue.dimensionGroup, rowValue.dimensionName, 'Rows');
});
valuesValues.forEach((valuesValue) => {
this.dragAndDropArea(valuesValue.measureGroup, valuesValue.mesureName, 'Columns');
});
if (filters === undefined) return;
filters.forEach((filter) => {
this.addwidgetFilter(filter.filterAttributeGroup, filter.filterAttributeName);
});
};
dragAndDropArea = (dimensionGroup: string, dimensionName: string, areaToDrop: string, whichSection = 0): void => {
const dataTransfer = new DataTransfer();
this.searchForAttribute(dimensionName);
this.expandNode(dimensionGroup, 'open');
cy.get(`span[title="${dimensionGroup}"]`).parent().next().findByText(dimensionName).trigger('dragstart', {
dataTransfer
});
cy.findAllByText(areaToDrop).then((elm) => {
const elementLength = elm.length;
if (elementLength === 1) {
whichSection = 0;
cy.log(`There were not multiple ${areaToDrop} sections, defaulting to 0`);
cy.findAllByText(areaToDrop).eq(0).parentsUntil('.fields').last().next().trigger('drop', {
dataTransfer
});
} else {
cy.findAllByText(areaToDrop).eq(whichSection).parentsUntil('.fields').last().next().trigger('drop', {
dataTransfer
});
}
});
this.clearSearch();
};
dragAndDropAreaToPosition = (dimensionGroup: string, dimensionName: string, areaToDrop: string, positionNumber: number, whichSection = 0): void => {
const dataTransfer = new DataTransfer();
this.searchForAttribute(dimensionName);
this.expandNode(dimensionGroup, 'open');
cy.get(`span[title="${dimensionGroup}"]`).parent().next().findByText(dimensionName).trigger('dragstart', {
dataTransfer
});
cy.findAllByText(areaToDrop).then((elm) => {
const numberOfSections = elm.length;
cy.log(`Number of elements is ${numberOfSections}`);
if (numberOfSections === 1) {
whichSection = 0;
cy.log(`There were not multiple ${areaToDrop} sections, defaulting to 0`);
cy.findAllByText(areaToDrop).eq(0).parentsUntil('.fields').last().next().find(`#div-dropzone-${positionNumber}`).trigger('drop', {
dataTransfer
});
} else {
cy.findAllByText(areaToDrop).eq(whichSection).parentsUntil('.fields').last().next().find(`#div-dropzone-${positionNumber}`).trigger('drop', {
dataTransfer
});
}
});
};
addwidgetFilter = (dimensionGroup: string, dimensionName: string, filterValue?: string | null): void => {
const dataTransfer = new DataTransfer();
this.searchForAttribute(dimensionName);
this.expandNode(dimensionGroup, 'open');
cy.get(`span[title="${dimensionGroup}"]`).parent().next().findByText(dimensionName).trigger('dragstart', {
dataTransfer
});
cy.get('.filters-bar').findByText('Filters').parent().parent().parent().next().trigger('drop', {
dataTransfer
});
if (filterValue != null) {
cy.findByText(filterValue).click();
}
cy.findByText('Apply').click();
};
addCalculationFilter = (filterAttributeGroup: string, filterAttribute: string): void => {
cy.findByText('Add Filter').click();
cy.get('.ant-modal-content').find('input').type(filterAttribute);
cy.findByTitle(filterAttributeGroup).parent().next().findByTitle(filterAttribute).click();
cy.get('.ant-modal-content').findByText('Apply').click();
cy.findByText('Filter').should('not.exist');
};
searchForAttribute = (attributeName: string): void => {
cy.get('input[name="Search"]').clear().type(attributeName).wait(500);
};
setFilterValue = (filterAttribute: string, filterValue: string): void => {
cy.wait(1);
this.openEditFilter(filterAttribute);
cy.findByText('List').click();
cy.get('.tempo-action-bar__filters').find('input[inputmode="text"]').clear().type(filterValue).wait(1000);
cy.findByText(filterValue).click();
cy.findByText('Apply').click();
};
openEditFilter = (filter: string): void => {
cy.get('.filters-bar').findByText(`${filter}:`).click();
};
openEditWidget = (widgetName: string): void => {
cy.findByText(widgetName)
.parent()
.parent()
.parent()
.parent()
.then((widget) => {
cy.wrap(widget).trigger('mouseover');
cy.wrap(widget).find('span[aria-label="edit"]').click();
});
};
selectWorkbookFromTable = (workbookName: string): void => {
cy.get('main').findByText(workbookName).click();
};
selectWorkbookFromPane = (workbookName: string): void => {
cy.get('.ant-menu-item-group').findByText(workbookName).parent().click();
};
deleteAttributesFromWidget = (attributes: { attributeName: string; sectionName: string }[]): void => {
attributes.forEach((attribute) => {
this.deleteAttributeFromWidget(attribute.attributeName, attribute.sectionName);
});
};
deleteAttributeFromWidget = (attributeName: string, sectionName: string): void => {
if (sectionName === 'Values' || sectionName === 'Columns') {
this.deleteAttributeFromValues(attributeName, sectionName);
} else {
cy.get('.designer').findByText(sectionName).parentsUntil('.fields').last().next().findByText(attributeName).next().find('span[aria-label="delete"]').click();
}
};
deleteAttributeFromValues = (attributeName: string, sectionName: string): void => {
cy.get('.designer').findByText(sectionName).parentsUntil('.fields').last().next().findByText(attributeName).next().click();
cy.findByText('Delete').click();
};
deleteWorkbookFromWorkbook = (workbookName: string): void => {
cy.goToWorkbook(workbookName);
this.openWorkbookKabob(workbookName);
cy.findByText('Delete').click().wait(1000);
cy.get('.ant-modal-confirm-btns').findByText('Delete Workbook').click();
cy.findByText('Delete Workbook').should('not.exist');
};
deleteWorkbookFromTable = (workbookName: string): void => {
cy.visit('/');
cy.get('.p-datatable-tbody').findByText(workbookName).parent().parent().parent().find('span[class$="anticon-delete"]').click();
cy.get('.ant-modal-confirm-btns').findByText('Delete Workbook').click();
cy.findByText('Delete Workbook').should('not.exist');
};
deleteDashboard = (dashboardName: string): void => {
cy.get('.worksheet-tab__title').contains(dashboardName).next().click();
cy.findByText('Delete').click();
cy.findByText(`Delete ${dashboardName}`).should('exist');
cy.findByText('Cancel').parent().next().click();
cy.findByText(`Delete ${dashboardName}`).should('not.exist');
};
clearSearch = (): void => {
cy.get('input[name="Search"]').next().click();
};
editTextWidget = (): void => {
cy.get('.text-widget--richtext').trigger('mouseover');
cy.get('.ql-snow').parent().prev().find('span[class$="anticon-edit"]').click();
};
addDataSource = (dataSourceName: string, dataSourceDescription?: string | undefined): void => {
this.goToDataSources();
cy.findByText('Add Data Source').click();
cy.get('#name').type(dataSourceName);
cy.get('#description').type(dataSourceDescription);
cy.findByText('Apply').click();
};
goToDataSources = (): void => {
cy.visit('/datasources');
};
shareWorkbook = (workbookName: string, userName: string): void => {
cy.goToWorkbook(workbookName);
this.openWorkbookKabob(workbookName);
cy.findByText('Share').click();
cy.findByText('Add User').click();
cy.findByText('Users').parentsUntil('.ant-drawer-body').last().find('.ant-tabs-content-holder').find('input[type="text"]').type(userName);
cy.get('span').contains(userName).click();
cy.get('.ant-drawer-title').contains('Add User').parent().siblings().last().findByText('Add').click();
cy.wait(2000);
cy.findByText('Save').click();
cy.wait(2000);
};
openWorkbookKabobOneButton = (workbookName: string): void => {
cy.get('.ant-layout-content').findByText(workbookName).parentsUntil('.tempo-header').last().next().find('button').click();
};
openWorkbookKabob = (workbookName: string): void => {
cy.get('.ant-layout-content').findByText(workbookName).parentsUntil('.tempo-header').last().next().findByText('Present').parentsUntil('.tempo-spacing.tempo-spacing--row').last().next().click();
};
setRoleOfWorkbookSharedUser = (workbookName: string, userName: string, viewerOrEditor: string): void => {
cy.goToWorkbook(workbookName);
this.openWorkbookKabob(workbookName);
cy.findByText('Share').click();
cy.get('div').contains(userName).parent().next().click();
cy.get('.ant-select-item-option-content').contains(viewerOrEditor).click();
cy.findByText('Save').click();
cy.wait(2000);
};
switchDataSource = (newDataSourceName: string): void => {
cy.get('.designer__datasource-tree-selector').prev().find('button').click();
cy.findByTitle('Data Source').parent().next().find('.ant-form-item-control-input').click();
cy.findByText(newDataSourceName).click();
cy.wait(2000);
cy.findByText('Change Data Source').parentsUntil('.ant-modal-content').last().parent().findByText('Save').click();
//cy.get('.ant-modal-content').findByText('Save').click({ force: true });
};
addDashboardFilter = (filterGroup: string, filterMember: string, dataSourceName?: string | undefined): void => {
cy.findByText('Add Filter').click();
cy.findByTitle('Dimensions').parent().find('.ant-tree-switcher.ant-tree-switcher_open', { timeout: 10000 }).should('exist');
this.searchForAttribute(filterMember);
cy.findByTitle(filterGroup).parent().next().findByTitle(filterMember).click();
if (dataSourceName !== undefined) {
this.setCurrentDashboardFilterDataSource(dataSourceName);
}
cy.get('.ant-popover-content').findByText('Add').click();
};
setDashboardFilterDataSource = (filterName: string, newDataSourceName: string): void => {
cy.findByText(`${filterName}:`).click();
this.setCurrentDashboardFilterDataSource(newDataSourceName);
};
setCurrentDashboardFilterDataSource = (newDataSourceName: string): void => {
cy.findByText('Data Source').parent().next().click();
cy.findByText(newDataSourceName).scrollIntoView().click();
};
editFilterName = (filterName: string, newFilterName: string): void => {
cy.findByText(`${filterName}:`).click();
cy.findByText('Configure').click();
cy.get('#name').clear().type(newFilterName);
cy.findByText('Apply').click();
};
setDashboardFilter = (filterName: string, filterValue: string): void => {
this.openDashboardFilter(filterName);
cy.get('.tempo-action-bar__filters').find('input[inputmode="text"]').clear().type(filterValue).wait(1000);
cy.findByText(filterValue).click();
cy.findByText('Apply').click();
};
openDashboardFilter = (filterName: string): void => {
cy.get('.filters-bar__item').findByText(`${filterName}:`).click();
cy.findByText('Conditions').should('exist');
};
duplicateValue = (valueToDuplicate: string, duplicatedValueName: string): void => {
cy.get('.sortable-list-item__content').contains(valueToDuplicate).next().click();
cy.findByText('Duplicate').should('exist').click();
cy.get('#name', { timeout: 5000 }).should('exist');
cy.get('#name').clear().type(duplicatedValueName).wait(1000);
cy.findByText('Apply').click();
cy.get('#name', { timeout: 5000 }).should('not.exist');
};
editValue = (valueToUpdate: string, updateName?: string, updateFormula?: string, updateCalculation?: string): void => {
this.openCalculatedValue(valueToUpdate);
this.editCurrentCalculatedValue(updateName, updateFormula, updateCalculation);
cy.findByText('Apply').click({ force: true });
cy.findByText('Apply').should('not.exist');
};
openCalculatedValue = (valueToUpdate: string): void => {
cy.get('.sortable-list-item__content').contains(valueToUpdate).next().click();
cy.findByText('Edit').click();
cy.get('h3', { timeout: 5000 }).contains(valueToUpdate).should('exist');
};
editCurrentCalculatedValue = (calcValName?: string, calcValFormula?: string, calcValCalculation?: string): void => {
if (calcValName !== null) {
cy.get('#name').clear().type(calcValName).wait(1000);
}
if (calcValFormula !== null) {
cy.findByText('Format').parent().next().click();
cy.findByText(calcValFormula).click();
}
if (calcValCalculation !== null) {
cy.get('.calculated-value-editor').find('textarea').clear({ force: true }).type(calcValCalculation, { force: true }).wait(1000);
}
};
addCustomCalculatedValue = (calcValName: string, updateFormula?: string, updateCalculation?: string): void => {
cy.findByText('Values').parentsUntil('.fields-header').last().next().click();
cy.get('#name').should('be.visible');
this.editCurrentCalculatedValue(calcValName, updateFormula, updateCalculation);
cy.findByText('Apply').click();
cy.findByText('Apply').should('not.exist');
};
addDataSheetCustomCalculatedValue = (calcValName: string, updateFormula?: string, updateCalculation?: string): void => {
cy.findByText('Columns').parentsUntil('.fields-column-header').last().next().click();
cy.findByText('Add Calculation').click();
cy.get('#name').should('be.visible');
this.editCurrentCalculatedValue(calcValName, updateFormula, updateCalculation);
cy.findByText('Apply').click({ force: true });
cy.findByText('Apply').should('not.exist');
};
addDataSheetCustomCalculatedValueToNumberedGroup = (whichGroup: number, calcValName: string, calculationFormula?: string, calculationCalculation?: string): void => {
cy.findAllByText('Columns')
.eq(whichGroup - 1)
.parentsUntil('.fields-column-header')
.last()
.next()
.click();
cy.findByText('Add Calculation').click();
cy.get('#name').should('be.visible');
this.editCurrentCalculatedValue(calcValName, calculationFormula, calculationCalculation);
cy.findByText('Apply').click();
cy.findByText('Apply').should('not.exist');
};
addNewDataSheetGroup = (): void => {
cy.findAllByText('Columns').first().parentsUntil('.fields-column-header').last().next().click();
cy.findByText('Add Group').click();
};
DuplicateGroup = (whichGroup: number): void => {
cy.findAllByText('Columns')
.eq(whichGroup - 1)
.parentsUntil('.fields-column-header')
.last()
.next()
.click();
cy.findByText('Duplicate Group').click();
};
deleteNumberedDataSheetGroup = (whichGroup: number): void => {
cy.findAllByText('Columns')
.eq(whichGroup - 1)
.parentsUntil('.fields-column-header')
.last()
.next()
.click();
cy.findAllByText('Delete Group').last().click();
};
addFromAdHoc = (adHocReport: string): void => {
cy.findByText('Add').click();
cy.findByText('From Adhoc', { timeout: 5000 }).should('exist');
cy.findByText('From Adhoc').click();
cy.findByText('Create from Ad-hoc', { timeout: 10000 }).should('exist');
cy.get('.ant-input').type(adHocReport);
cy.findByText(adHocReport).click();
cy.findByText('Create').click();
cy.get('.worksheet-tab__title').contains(adHocReport, { timeout: 10000 }).should('exist');
};
}
+43
View File
@@ -0,0 +1,43 @@
import fs = require('fs');
import path = require('path');
import { spawnSync } from 'child_process';
import fsExtra = require('fs-extra');
/// <reference types="cypress" />
/// <reference types="cypress-plugin-snapshots" />
/// <reference types="cypress-iframe" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-var-requires
function getConfigurationByFile(file: any) {
const pathToConfigFile = path.resolve('..', 'cypress/cypress', `cypress.${file}.json`);
return fsExtra.readJson(pathToConfigFile);
}
const sleep = (ms: number) => {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
module.exports = (on: any, config: any) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
//initPlugin(on, config);
const configResolved = config.env.fileconfig != null ? getConfigurationByFile(config.env.fileconfig) : null;
return configResolved;
};
+6
View File
@@ -0,0 +1,6 @@
export interface IUserData {
Title: string;
UserName: string;
Password: string;
Description: string;
}
+401
View File
@@ -0,0 +1,401 @@
import '@testing-library/cypress/add-commands';
import { IUserData } from './IUserData';
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
import '@testing-library/cypress/add-commands';
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
interface Chainable {
logout(): Chainable<Element>;
vaultLogin(admin?: boolean): Chainable<void>;
vaultLoginByUserTitle(userTitle: string): Chainable<void>;
switchUser(userTitle: string): Chainable<void>;
deleteWorkbook(workbookName: string): Chainable<void>;
addWorkbook(workbookName: string, workbookDescription: string, datamartName: string): Chainable<void>;
goToWorkbook(workbookName: string): Chainable<void>;
goToDataSource(dataMartName: string): Chainable<void>;
goToStandardDataSource(dataMartName: string): Chainable<void>;
verifyDataSourceExists(dataMartName: string): Chainable<void>;
verifyDataSourceDoesNotExist(dataMartName: string): Chainable<void>;
createDataSource(dataMartName: string, datamartDescription: string): Chainable<void>;
deleteDataSource(dataMartName: string): Chainable<void>;
addUserToDataSource(datamartName: string, userName: string, viewerOrEditor: string): Chainable<void>;
addTableToDataSource(dataTableId: string, dataMartName: string): Chainable<void>;
addUserToWorkbook(workbookName: string, userName: string, viewerOrEditor: string): Chainable<void>;
addExtract(extractName: string, extractDescription: string, datamartName: string): Chainable<void>;
goToExtract(extractName: string): Chainable<void>;
deleteExtract(extractName: string): Chainable<void>;
}
}
}
Cypress.Commands.add('logout', () => {
cy.window().then((win) => {
win.sessionStorage.clear();
});
cy.visit('/');
});
// USE THIS FOR ALL TEST LOGINS TO PULL FROM VAULT
Cypress.Commands.add('vaultLogin', (admin = true) => {
document.cookie = 'StrataIdentityServerLogin=true';
const userTitle = admin ? 'obAdmin' : 'capBasic';
cy.request({
method: 'GET',
url: `https://vault.sdt.local/api/searchpasswords/122?title=${userTitle}`,
headers: {
apiKey: 'a69d2e478c3f5b395b687ecdf34a3239'
}
})
.then((response) => response.body[0])
.then((user: IUserData) => {
// Test against your own test user
//cy.login(Cypress.env('username'), Cypress.env('password'), Cypress.env('dbguid'), Cypress.env('clientId'), '/');
cy.login(user.UserName, user.Password, Cypress.env('dbguid'), Cypress.env('clientId'), '/');
});
});
Cypress.Commands.add('vaultLoginByUserTitle', (userTitle: string) => {
document.cookie = 'StrataIdentityServerLogin=true';
cy.request({
method: 'GET',
url: `https://vault.sdt.local/api/searchpasswords/122?title=${userTitle}`,
headers: {
apiKey: 'a69d2e478c3f5b395b687ecdf34a3239'
}
})
.then((response) => response.body[0])
.then((user: IUserData) => {
// Test against your own test user
//cy.login(Cypress.env('username'), Cypress.env('password'), Cypress.env('dbguid'), Cypress.env('clientId'), '/');
cy.login(user.UserName, user.Password, Cypress.env('dbguid'), Cypress.env('clientId'), '/');
});
});
Cypress.Commands.add('switchUser', (userTitle: string) => {
cy.logout();
cy.vaultLoginByUserTitle(userTitle);
});
Cypress.Commands.add('deleteWorkbook', (workbookName: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/workbooks`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const workbooks = response.body as { workbookId: string; name: string }[];
const matchingWorkbooks = workbooks.find((r) => r.name === workbookName);
if (matchingWorkbooks === undefined) return;
const workbookId = matchingWorkbooks.workbookId;
cy.request({
method: 'DELETE',
url: `${Cypress.env('apiUrl')}v1/workbooks/${workbookId}`,
headers: { Authorization: `Bearer ${token}` }
});
});
});
Cypress.Commands.add('addWorkbook', (workbookName: string, workbookDescription: string, dataSourceName: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/data-sources`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const dataSources = response.body as { dataSourceId: string; name: string }[];
const dataSourceId = dataSources.find((r) => r.name === dataSourceName).dataSourceId;
cy.request({
method: 'POST',
url: `${Cypress.env('apiUrl')}v1/workbooks/`,
headers: { Authorization: `Bearer ${token}` },
body: {
name: workbookName,
description: workbookDescription,
defaultDataSourceId: dataSourceId
}
});
});
});
Cypress.Commands.add('goToWorkbook', (workbookName: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/workbooks`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const workbooks = response.body as { workbookId: string; name: string }[];
const matchingWorkbooks = workbooks.find((r) => r.name === workbookName);
if (matchingWorkbooks === undefined) return;
const workbookId = matchingWorkbooks.workbookId;
cy.visit(`/workbooks/${workbookId}`);
});
cy.get('h1').findByText(workbookName, { timeout: 25000 }).should('exist');
});
Cypress.Commands.add('goToDataSource', (dataSourceName: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/data-sources`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const dataSources = response.body as { dataSourceId: string; name: string }[];
const dataSourceId = dataSources.find((r) => r.name === dataSourceName).dataSourceId;
cy.visit(`/datasources/${dataSourceId}`);
});
});
Cypress.Commands.add('goToStandardDataSource', (dataSourceName: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/standard-data-sources`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const dataSources = response.body as { standardDataSourceId: string; name: string }[];
const dataSourceId = dataSources.find((r) => r.name === dataSourceName).standardDataSourceId;
cy.visit(`/standard-datasources/${dataSourceId}`);
});
});
Cypress.Commands.add('verifyDataSourceExists', (dataSourceName: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/data-sources`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const dataSources = response.body as { dataSourceId: string; name: string }[];
const dataSource = dataSources.find((r) => r.name === dataSourceName);
expect(dataSource.name).to.eq(dataSourceName);
});
});
Cypress.Commands.add('verifyDataSourceDoesNotExist', (dataSourceName: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/data-sources`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const dataSources = response.body as { dataSourceId: string; name: string }[];
const dataSource = dataSources.find((r) => r.name === dataSourceName);
expect(dataSource).to.be(null);
});
});
Cypress.Commands.add('createDataSource', (dataSourceName: string, dataSourceDescription: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'POST',
url: `${Cypress.env('apiUrl')}v1/data-sources`,
headers: { Authorization: `Bearer ${token}` },
body: {
name: dataSourceName,
description: dataSourceDescription
}
});
});
Cypress.Commands.add('deleteDataSource', (dataSourceName: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/data-sources`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const dataSources = response.body as { dataSourceId: string; name: string }[];
const matchingDataSources = dataSources.find((r) => r.name === dataSourceName);
if (matchingDataSources === undefined) return;
const dataSourceId = matchingDataSources.dataSourceId;
cy.request({
method: 'DELETE',
url: `${Cypress.env('apiUrl')}v1/data-sources/${dataSourceId}`,
headers: { Authorization: `Bearer ${token}` }
});
});
});
Cypress.Commands.add('addUserToDataSource', (dataSourceName: string, userName: string, viewerOrEditor: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/users`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const users = response.body as { userGuid: string; username: string }[];
const matchingUsers = users.find((r) => r.username === userName);
if (matchingUsers === undefined) return;
const userGuid = matchingUsers.userGuid;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/data-sources`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const dataSources = response.body as { dataSourceId: string; name: string }[];
const matchingDataSources = dataSources.find((r) => r.name === dataSourceName);
if (matchingDataSources === undefined) return;
const dataSourceId = matchingDataSources.dataSourceId;
cy.request({
method: 'POST',
url: `${Cypress.env('apiUrl')}v1/data-sources/${dataSourceId}/role-assignments/batch`,
headers: { Authorization: `Bearer ${token}` },
body: {
updates: [
{
identityGuid: userGuid,
identityName: userName,
identityType: 'user',
role: viewerOrEditor
}
]
}
});
});
});
});
Cypress.Commands.add('addTableToDataSource', (dataTableId: string, dataSourceName: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/data-sources`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const dataSources = response.body as { dataSourceId: string; name: string }[];
const dataSourceId = dataSources.find((r) => r.name === dataSourceName).dataSourceId;
cy.request({
method: 'POST',
url: `${Cypress.env('apiUrl')}v1/data-sources/${dataSourceId}/tables/${dataTableId}`,
headers: { Authorization: `Bearer ${token}` }
});
});
});
Cypress.Commands.add('addUserToWorkbook', (workbookName: string, userName: string, viewerOrEditor: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/users`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const users = response.body as { userGuid: string; username: string }[];
const matchingUsers = users.find((r) => r.username === userName);
if (matchingUsers === undefined) return;
const userGuid = matchingUsers.userGuid;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/workbooks`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const workbooks = response.body as { workbookId: string; name: string }[];
const matchingWorkbooks = workbooks.find((r) => r.name === workbookName);
if (matchingWorkbooks === undefined) return;
const workbookId = matchingWorkbooks.workbookId;
cy.request({
method: 'POST',
url: `${Cypress.env('apiUrl')}v1/workbooks/${workbookId}/role-assignments/batch`,
headers: { Authorization: `Bearer ${token}` },
body: {
updates: [
{
identityGuid: userGuid,
identityName: userName,
identityType: 'user',
role: viewerOrEditor
}
]
}
});
});
});
});
Cypress.Commands.add('addExtract', (extractName: string, extractDescription: string, dataMartName: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/data-sources`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const datamarts = response.body as { dataSourceId: string; name: string }[];
const dataSourceId = datamarts.find((r) => r.name === dataMartName).dataSourceId;
cy.request({
method: 'POST',
url: `${Cypress.env('apiUrl')}v1/extracts/`,
headers: { Authorization: `Bearer ${token}` },
body: {
name: extractName,
description: extractDescription,
defaultdataSourceId: dataSourceId
}
});
});
});
Cypress.Commands.add('goToExtract', (extractName: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/extracts`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const extracts = response.body as { extractId: string; name: string }[];
const matchingExtracts = extracts.find((r) => r.name === extractName);
if (matchingExtracts === undefined) return;
const extractId = matchingExtracts.extractId;
cy.visit(`/extracts/${extractId}`);
});
cy.get('h1').findByText(extractName, { timeout: 25000 }).should('exist');
});
Cypress.Commands.add('deleteExtract', (extractName: string) => {
const token = JSON.parse(window.sessionStorage.getItem('oidc.user:https://identity.dev.stratanetwork.net/:StrataUI')).access_token;
cy.request({
method: 'GET',
url: `${Cypress.env('apiUrl')}v1/extracts`,
headers: { Authorization: `Bearer ${token}` }
}).then((response) => {
const extracts = response.body as { extractId: string; name: string }[];
const matchingExtracts = extracts.find((r) => r.name === extractName);
if (matchingExtracts === undefined) return;
const extractId = matchingExtracts.extractId;
cy.request({
method: 'DELETE',
url: `${Cypress.env('apiUrl')}v1/extracts/${extractId}`,
headers: { Authorization: `Bearer ${token}` }
});
});
});
+29
View File
@@ -0,0 +1,29 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
import 'cypress-file-upload';
import { expandTreeCommand, selectTreeCommand, checkTreeCommand } from '@strata/cypress/lib/tree';
import { loginCommand } from '@strata/cypress/lib/login';
loginCommand();
expandTreeCommand();
selectTreeCommand();
checkTreeCommand();
// Alternatively you can use CommonJS syntax:
// require('./commands')
Cypress.on('uncaught:exception', (err) => !err.message.includes('ResizeObserver loop limit exceeded'));
+22
View File
@@ -0,0 +1,22 @@
{
"compilerOptions": {
"noEmit": true,
"target": "es5",
"lib": [
"es5",
"dom"
],
"types": [
"cypress",
"@testing-library/cypress",
"node"
]
},
"include": [
"../node_modules",
"**/*.ts",
"support/commands.js",
"cypress/plugins/index.ts",
"cypress/support/index.js"
]
}
+9229
View File
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
{
"name": "advanced-analytics-e2e",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "cypress open",
"test-local": "cypress open --env fileconfig=local",
"test-dev": "cypress open --env fileconfig=development"
},
"eslintConfig": {
"extends": "@strata"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@percy/cli": "^1.17.0",
"@percy/cypress": "^3.1.2",
"@testing-library/cypress": "^8.0.2",
"@types/node": "^16.11.6",
"cypress": "^9.5.3",
"cypress-file-upload": "^5.0.8",
"cypress-iframe": "^1.0.1",
"js-base64": "^2.5.2",
"node-xlsx": "^0.17.2",
"typescript": "^4.6.3"
},
"dependencies": {
"@strata/cypress": "^1.7.0",
"@strata/eslint-config": "^3.6.0",
"@typescript-eslint/eslint-plugin": "^4.28.0",
"dayjs": "^1.11.7",
"eslint": "^7.29.0",
"eslint-plugin-flowtype": "^5.7.2",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"prettier": "^2.3.1"
},
"volta": {
"node": "14.19.1"
}
}
+47
View File
@@ -0,0 +1,47 @@
version: "3"
services:
analytics-api:
build:
context: .
dockerfile: Dockerfile.api
ports:
- "8080:80"
- "8443:443"
environment: - hangfire__redis__prefix=analytics
- hangfire__redis__configOptions=redis:6379
analytics-hangfire:
build:
context: .
dockerfile: Dockerfile.hangfire
environment:
- hangfire__redis__prefix=analytics
- hangfire__redis__configOptions=redis:6379
analytics-hangfire-dashboard:
build:
context: .
dockerfile: Dockerfile.dashboard
ports:
- 8082:80
environment:
- hangfire__redis__prefix=analytics
- hangfire__redis__configOptions=redis:6379
redis:
image: redis:alpine
ports:
- 6379:6379
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redis-data:/data
redis-commander:
image: rediscommander/redis-commander:latest
environment:
- REDIS_HOSTS=local:redis:6379
- HTTP_USER=root
- HTTP_PASSWORD=pass
ports:
- 8081:8081
depends_on:
- redis
volumes:
redis-data:
+10
View File
@@ -0,0 +1,10 @@
FROM golang:1-alpine as builder
WORKDIR $GOPATH/src/github.com/loadimpact/k6
ADD . .
RUN apk --no-cache add --virtual .build-deps git make build-base && \
go get . && CGO_ENABLED=0 go install -a -ldflags '-s -w'
FROM alpine:3.7
RUN apk add --no-cache ca-certificates
COPY --from=builder /go/bin/k6 /usr/bin/k6
ENTRYPOINT ["k6"]
+19
View File
@@ -0,0 +1,19 @@
FROM grafana/grafana:5.2.4
USER root
RUN apt-get update && apt-get -y install curl
# Change the default data directory (otherwise grafana.db won't persist)
RUN mkdir /var/lib/grafanadb
ENV GF_PATHS_DATA /var/lib/grafanadb
# Init Grafana sqlite db and preconfigure our data source to be our influxdb k6 db
RUN bash -c '/run.sh & sleep 15 && curl -s -H "Content-Type: application/json" -X POST \
--data '"'"'{"name": "myinfluxdb", "type": "influxdb", "access": "proxy", "url": "http://influxdb:8086", "database": "k6", "isDefault": true}'"'"' \
http://admin:admin@localhost:3000/api/datasources \
&& kill -SIGINT %%'
CMD ["/run.sh"]
+6
View File
@@ -0,0 +1,6 @@
FROM influxdb:1.8
# Create a k6 db
RUN /bin/bash -c "influxd run & sleep 5 && influx -execute 'CREATE DATABASE k6' && kill %1 && sleep 5"
CMD ["influxd"]
@@ -0,0 +1,8 @@
apiVersion: 1
providers:
- name: 'default'
org_id: 1
folder: ''
type: 'file'
options:
path: /var/lib/grafana/dashboards
@@ -0,0 +1,9 @@
apiVersion: 1
datasources:
- name: k6influxdb
type: influxdb
access: proxy
database: k6
url: http://influxdb:8086
isDefault: true
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
version: "3.4"
networks:
k6:
grafana:
influxdb:
services:
influxdb:
container_name: influxdb
image: influxdb:1.8
networks:
- k6
- grafana
ports:
- "8086:8086"
environment:
- INFLUXDB_DB=k6
grafana:
container_name: grafana
image: grafana/grafana:7.5.5
networks:
- grafana
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_BASIC_ENABLED=false
- GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=grafana-datadog-datasource
- GF_INSTALL_PLUGINS=https://github.com/sirkjohannsen/grafana-datasource-datadog/archive/refs/heads/master.zip;datadog
volumes:
- ./grafana:/var/lib/grafana
k6:
container_name: k6
networks:
- k6
image: grafana/k6:latest
environment:
- K6_OUT=influxdb=http://influxdb:8086/k6
ports:
- "6565:6565"
volumes:
- ./testRunner:/testRunner
@@ -0,0 +1,53 @@
{{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ if gt (.Alerts.Resolved | len) 0 }}, RESOLVED:{{ .Alerts.Resolved | len }}{{ end }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }}
{{ define "__text_values_list" }}{{ $len := len .Values }}{{ if $len }}{{ $first := gt $len 1 }}{{ range $refID, $value := .Values -}}
{{ $refID }}={{ $value }}{{ if $first }}, {{ end }}{{ $first = false }}{{ end -}}
{{ else }}[no value]{{ end }}{{ end }}
{{ define "__text_alert_list" }}{{ range . }}
Value: {{ template "__text_values_list" . }}
Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}{{ if gt (len .GeneratorURL) 0 }}Source: {{ .GeneratorURL }}
{{ end }}{{ if gt (len .SilenceURL) 0 }}Silence: {{ .SilenceURL }}
{{ end }}{{ if gt (len .DashboardURL) 0 }}Dashboard: {{ .DashboardURL }}
{{ end }}{{ if gt (len .PanelURL) 0 }}Panel: {{ .PanelURL }}
{{ end }}{{ end }}{{ end }}
{{ define "default.title" }}{{ template "__subject" . }}{{ end }}
{{ define "default.message" }}{{ if gt (len .Alerts.Firing) 0 }}**Firing**
{{ template "__text_alert_list" .Alerts.Firing }}{{ if gt (len .Alerts.Resolved) 0 }}
{{ end }}{{ end }}{{ if gt (len .Alerts.Resolved) 0 }}**Resolved**
{{ template "__text_alert_list" .Alerts.Resolved }}{{ end }}{{ end }}
{{ define "__teams_text_alert_list" }}{{ range . }}
Value: {{ template "__text_values_list" . }}
Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}
Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}
{{ if gt (len .GeneratorURL) 0 }}Source: [{{ .GeneratorURL }}]({{ .GeneratorURL }})
{{ end }}{{ if gt (len .SilenceURL) 0 }}Silence: [{{ .SilenceURL }}]({{ .SilenceURL }})
{{ end }}{{ if gt (len .DashboardURL) 0 }}Dashboard: [{{ .DashboardURL }}]({{ .DashboardURL }})
{{ end }}{{ if gt (len .PanelURL) 0 }}Panel: [{{ .PanelURL }}]({{ .PanelURL }})
{{ end }}
{{ end }}{{ end }}
{{ define "teams.default.message" }}{{ if gt (len .Alerts.Firing) 0 }}**Firing**
{{ template "__teams_text_alert_list" .Alerts.Firing }}{{ if gt (len .Alerts.Resolved) 0 }}
{{ end }}{{ end }}{{ if gt (len .Alerts.Resolved) 0 }}**Resolved**
{{ template "__teams_text_alert_list" .Alerts.Resolved }}{{ end }}{{ end }}
Binary file not shown.
@@ -0,0 +1,3 @@
# Don't diff files in dist/
*.map binary
dist/** binary
@@ -0,0 +1 @@
node_modules
@@ -0,0 +1,14 @@
{
"esnext": true,
"disallowImplicitTypeConversion": ["string"],
"disallowKeywords": ["with"],
"disallowMultipleLineBreaks": true,
"disallowMixedSpacesAndTabs": true,
"disallowTrailingWhitespace": true,
"requireSpacesInFunctionExpression": {
"beforeOpeningCurlyBrace": true
},
"disallowSpacesInsideArrayBrackets": true,
"disallowSpacesInsideParentheses": true,
"validateIndentation": 2
}
@@ -0,0 +1,42 @@
{
"browser": true,
"esnext": true,
"bitwise":false,
"curly": true,
"eqnull": true,
"strict": false,
"devel": true,
"eqeqeq": true,
"forin": false,
"immed": true,
"supernew": true,
"expr": true,
"indent": 2,
"latedef": false,
"newcap": true,
"noarg": true,
"noempty": true,
"undef": true,
"boss": true,
"trailing": true,
"laxbreak": true,
"laxcomma": true,
"sub": true,
"unused": true,
"maxdepth": 6,
"maxlen": 140,
"globals": {
"System": true,
"Promise": true,
"define": true,
"require": true,
"Chromath": false,
"setImmediate": true,
"describe": true,
"beforeEach": true,
"it": true,
"expect": true,
"EventSource": true
}
}
@@ -0,0 +1,4 @@
language: node_js
before_script:
- npm install -g grunt
script: grunt
@@ -0,0 +1,144 @@
module.exports = function(grunt) {
require('load-grunt-tasks')(grunt);
grunt.loadNpmTasks('grunt-execute');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.initConfig({
clean: ["dist"],
copy: {
src_to_dist: {
cwd: 'src',
expand: true,
src: ['**/*', '!**/*.js', '!**/*.scss'],
dest: 'dist'
},
vendor_to_dist: {
cwd: 'vendor',
expand: true,
src: ['**/*'],
dest: 'dist'
},
vendor_to_test: {
cwd: 'vendor',
expand: true,
src: ['**/*'],
dest: 'dist/test'
},
pluginDef: {
expand: true,
src: ['README.md'],
dest: 'dist'
}
},
watch: {
rebuild_all: {
files: ['src/**/*'],
tasks: ['watchTask'],
options: {spawn: false}
}
},
babel: {
options: {
sourceMap: true,
presets: ['es2015']
},
dist: {
options: {
plugins: ['transform-es2015-modules-systemjs', 'transform-es2015-for-of']
},
files: [{
cwd: 'src',
expand: true,
src: ['**/*.js'],
dest: 'dist',
ext:'.js'
}]
},
distTestNoSystemJs: {
files: [{
cwd: 'src',
expand: true,
src: ['**/*.js'],
dest: 'dist/test',
ext:'.js'
}]
},
distTestsSpecsNoSystemJs: {
files: [{
expand: true,
cwd: 'spec',
src: ['**/*.js'],
dest: 'dist/test/spec',
ext:'.js'
}]
}
},
jshint: {
source: {
files: {
src: ['src/**/*.js'],
}
},
options: {
jshintrc: true,
reporter: require('jshint-stylish'),
ignores: [
'node_modules/*',
'dist/*',
]
}
},
jscs: {
src: ['src/**/*.js'],
options: {
config: ".jscs.json",
},
},
mochaTest: {
test: {
options: {
reporter: 'spec'
},
src: [
'dist/test/spec/test-main.js',
'dist/test/spec/*_spec.js'
]
}
}
});
grunt.registerTask('default', [
'clean',
'copy:src_to_dist',
'copy:pluginDef',
'jshint',
'jscs',
'babel',
'copy:vendor_to_dist',
'copy:vendor_to_test',
'mochaTest'
]);
grunt.registerTask('dev', ['default', 'watch']);
grunt.registerTask('watchTask', [
// 'clean',
'copy:src_to_dist',
'copy:pluginDef',
'babel',
'copy:vendor_to_dist',
'jshint',
'jscs'
]);
};
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Grafana
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,30 @@
# Datadog Grafana Datasource Plugin
![Build Passing](https://travis-ci.org/voidfiles/grafana-datasource-datadog.svg?branch=master)
Datadog has a nice dashboard system, but I was having trouble creating a dashboard that combined multiple datasources and grafana was the easiest way to bring it all together.
## To Contribute
Fork, make your change, and submit a Pull Request. Make sure you add tests where approprate, and don't break exisisting tests.
## Usage
### Query Editor
![query editor](https://cloud.githubusercontent.com/assets/4932851/22584216/337c26aa-ea02-11e6-84a8-20207bedd15b.png)
It's easy - select aggregation and metric. If you want to filter result, select one or more tags.
DataDog datasource supports advanced functions. Select it from dropdown and arrange by clicking function name.
### Annotations
![annotations editor](https://cloud.githubusercontent.com/assets/4932851/22584362/1705598c-ea03-11e6-94a0-b51435d74420.png)
Annotations allows to get events from DataDog and display it on graph. You can filter events by sources, tags and priority.
### Templating
There are two options for getting values of tempate variable - metrics and tags. For fetching list of available metrics leave _Query_ field blank or specify `*`. For tags type `tag`. Filter results by using _Regex_ field. Multi-value variables are supported for using in tags (selected values will be converted into comma separated list of tags).
#### Ad-hoc filters
There's new special type of template variable in Grafana called _Ad-hoc filters_. This variable will apply to _all_ DataDog queries on dashboard (all with specified datasource). This alollows to use it like a quick filter. Ad-hoc variable for DataDog fetches all key-value pairs from tags, for example, `region:east, region:west`, and use it as queries tags. To create this variable, select _Ad-hoc filters_ type and choose your DataDog datasource. You can set any name to this variable.
+30
View File
@@ -0,0 +1,30 @@
# Datadog Grafana Datasource Plugin
![Build Passing](https://travis-ci.org/voidfiles/grafana-datasource-datadog.svg?branch=master)
Datadog has a nice dashboard system, but I was having trouble creating a dashboard that combined multiple datasources and grafana was the easiest way to bring it all together.
## To Contribute
Fork, make your change, and submit a Pull Request. Make sure you add tests where approprate, and don't break exisisting tests.
## Usage
### Query Editor
![query editor](https://cloud.githubusercontent.com/assets/4932851/22584216/337c26aa-ea02-11e6-84a8-20207bedd15b.png)
It's easy - select aggregation and metric. If you want to filter result, select one or more tags.
DataDog datasource supports advanced functions. Select it from dropdown and arrange by clicking function name.
### Annotations
![annotations editor](https://cloud.githubusercontent.com/assets/4932851/22584362/1705598c-ea03-11e6-94a0-b51435d74420.png)
Annotations allows to get events from DataDog and display it on graph. You can filter events by sources, tags and priority.
### Templating
There are two options for getting values of tempate variable - metrics and tags. For fetching list of available metrics leave _Query_ field blank or specify `*`. For tags type `tag`. Filter results by using _Regex_ field. Multi-value variables are supported for using in tags (selected values will be converted into comma separated list of tags).
#### Ad-hoc filters
There's new special type of template variable in Grafana called _Ad-hoc filters_. This variable will apply to _all_ DataDog queries on dashboard (all with specified datasource). This alollows to use it like a quick filter. Ad-hoc variable for DataDog fetches all key-value pairs from tags, for example, `region:east, region:west`, and use it as queries tags. To create this variable, select _Ad-hoc filters_ type and choose your DataDog datasource. You can set any name to this variable.
@@ -0,0 +1,116 @@
'use strict';
System.register(['angular', 'lodash', 'jquery', './dfunc'], function (_export, _context) {
"use strict";
var angular, _, $, dfunc;
function getAllFunctionNames(categories) {
return _.reduce(categories, function (list, category) {
_.each(category, function (func) {
list.push(func.name);
});
return list;
}, []);
}
function createFunctionDropDownMenu(categories) {
return _.map(categories, function (list, category) {
return {
text: category,
submenu: _.map(list, function (value) {
return {
text: value.name,
click: "ctrl.addFunction('" + value.name + "')"
};
})
};
});
}
return {
setters: [function (_angular) {
angular = _angular.default;
}, function (_lodash) {
_ = _lodash.default;
}, function (_jquery) {
$ = _jquery.default;
}, function (_dfunc) {
dfunc = _dfunc.default;
}],
execute: function () {
angular.module('grafana.directives').directive('datadogAddFunc', function ($compile) {
var inputTemplate = '<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>';
var buttonTemplate = '<a class="gf-form-label query-part dropdown-toggle"' + ' tabindex="1" gf-dropdown="functionMenu" data-toggle="dropdown">' + '<i class="fa fa-plus"></i></a>';
return {
link: function link($scope, elem) {
var categories = dfunc.getCategories();
var allFunctions = getAllFunctionNames(categories);
var ctrl = $scope.ctrl;
$scope.functionMenu = createFunctionDropDownMenu(categories);
var $input = $(inputTemplate);
var $button = $(buttonTemplate);
$input.appendTo(elem);
$button.appendTo(elem);
$input.attr('data-provide', 'typeahead');
$input.typeahead({
source: allFunctions,
minLength: 1,
items: 10,
updater: function updater(value) {
var funcDef = dfunc.getFuncDef(value);
if (!funcDef) {
// try find close match
value = value.toLowerCase();
funcDef = _.find(allFunctions, function (funcName) {
return funcName.toLowerCase().indexOf(value) === 0;
});
if (!funcDef) {
return;
}
}
$scope.$apply(function () {
ctrl.addFunction(funcDef);
});
$input.trigger('blur');
return '';
}
});
$button.click(function () {
$button.hide();
$input.show();
$input.focus();
});
$input.keyup(function () {
elem.toggleClass('open', $input.val() === '');
});
$input.blur(function () {
// clicking the function dropdown menu wont
// work if you remove class at once
setTimeout(function () {
$input.val('');
$input.hide();
$button.show();
elem.removeClass('open');
}, 200);
});
$compile(elem.contents())($scope);
}
};
});
}
};
});
//# sourceMappingURL=add_datadog_func.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,433 @@
'use strict';
System.register(['lodash', './showdown.min.js', './query_builder'], function (_export, _context) {
"use strict";
var _, showdown, queryBuilder, _createClass, DataDogDatasource;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
/*
* Convert tags to key-value pairs
* [region:east, region:nw] => {region: [east, nw]}
*/
function mapTagsToKVPairs(tags) {
var kv_tags = _.filter(tags, function (tag) {
return tag.indexOf(':') !== -1;
});
var kv_pairs = kv_tags.map(function (tag) {
return tag.split(':', 2); // Limit to 2
});
var kv_object = {};
kv_pairs.forEach(function (pair) {
var key = pair[0];
var val = pair[1];
if (kv_object[key]) {
kv_object[key].push(val);
} else {
kv_object[key] = [val];
}
});
return kv_object;
}
/*
* Convert DataDog event text from markdown to pure HTML
* http://docs.datadoghq.com/guides/markdown/
*/
function convertDataDogMdToHtml(str) {
var MD_START = '%%%\n';
var MD_END = '\n%%%';
var md_start_index = str.indexOf(MD_START) + MD_START.length;
var md_end_index = str.indexOf(MD_END);
var md = str.substring(md_start_index, md_end_index);
md = removeImagesFromDataDogMarkdown(md);
var converter = new showdown.Converter();
return converter.makeHtml(md);
}
function isDataDogMarkdown(str) {
var MD_START = '%%%\n';
return str.indexOf(MD_START) >= 0;
}
function removeImagesFromDataDogMarkdown(str) {
var dataDogImagePattern = /\[{0,1}\!\[.*\]\(https{0,1}\:\/\/p\.datadoghq\.com\/snapshot.+\)/g;
return str.replace(dataDogImagePattern, '');
}
function dataDogTagFormat(value) {
if (_.isArray(value)) {
return value.join(',');
}
return value;
}
return {
setters: [function (_lodash) {
_ = _lodash.default;
}, function (_showdownMinJs) {
showdown = _showdownMinJs.default;
}, function (_query_builder) {
queryBuilder = _query_builder;
}],
execute: function () {
_createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
_export('DataDogDatasource', DataDogDatasource = function () {
function DataDogDatasource(instanceSettings, backendSrv, templateSrv) {
_classCallCheck(this, DataDogDatasource);
this.type = instanceSettings.type;
this.url = instanceSettings.url;
this.name = instanceSettings.name;
this.api_key = instanceSettings.jsonData.api_key;
this.application_key = instanceSettings.jsonData.app_key;
this.supportMetrics = true;
this.backendSrv = backendSrv;
this.templateSrv = templateSrv;
this._cached_metrics = false;
}
// Function to check Datasource health
_createClass(DataDogDatasource, [{
key: 'testDatasource',
value: function testDatasource() {
return this.invokeDataDogApiRequest('/downtime').then(function () {
return {
status: "success",
title: "Success",
message: "Data source is working"
};
}).catch(function (error) {
var message = "Connection error";
if (error && error.message) {
message = error.message;
}
return {
status: "error",
title: "Error",
message: message
};
});
}
}, {
key: 'tagFindQuery',
value: function tagFindQuery() {
return this.getTagsFromCache().then(function (tags) {
return _.map(tags, function (hosts, tag) {
return {
text: tag,
value: tag
};
});
});
}
}, {
key: 'metricFindQuery',
value: function metricFindQuery(query) {
var _this = this;
if (query === 'tag') {
return this.tagFindQuery();
}
if (this._cached_metrics) {
return Promise.resolve(this._cached_metrics);
}
if (this.fetching) {
return this.fetching;
}
var d = new Date();
d.setDate(d.getDate() - 1);
var from = Math.floor(d.getTime() / 1000);
this.fetching = this.getMetrics(from).then(function (metrics) {
_this._cached_metrics = _.map(metrics, function (metric) {
return {
text: metric,
value: metric
};
});
return _this._cached_metrics;
});
return this.fetching;
}
}, {
key: 'getTagKeys',
value: function getTagKeys() {
return this.getTagsFromCache().then(function (tagsHosts) {
var tags = Object.keys(tagsHosts);
var kv = mapTagsToKVPairs(tags);
var grafanaTags = Object.keys(kv);
return grafanaTags.map(function (tag) {
return {
text: tag,
value: tag
};
});
});
}
}, {
key: 'getTagValues',
value: function getTagValues(options) {
return this.getTagsFromCache().then(function (tagsHosts) {
var tags = Object.keys(tagsHosts);
var kv = mapTagsToKVPairs(tags);
var grafanaValues = kv[options.key];
return grafanaValues.map(function (val) {
return {
text: val,
value: val
};
});
});
}
}, {
key: 'query',
value: function query(options) {
var from = Math.floor(options.range.from.valueOf() / 1000);
var to = Math.floor(options.range.to.valueOf() / 1000);
var targets = options.targets.filter(function (t) {
return !t.hide;
});
if (targets.length <= 0) {
return Promise.resolve({ data: [] });
}
// add global adhoc filters
var adhocFilters = this.templateSrv.getAdhocFilters(this.name);
var queries = _.map(targets, function (target) {
var query = void 0;
if (target.rawQuery) {
query = target.query;
} else {
query = queryBuilder.buildQuery(target, adhocFilters);
}
return query;
});
var queryString = queries.join(',');
queryString = this.templateSrv.replace(queryString, options.scopedVars, dataDogTagFormat);
var params = {
from: from,
to: to,
query: queryString
};
return this.invokeDataDogApiRequest('/query', params).then(function (result) {
var dataResponse = _.map(result.series, function (series, i) {
var target = targets[i];
return {
'target': target.alias || series.expression,
'datapoints': _.map(series.pointlist, function (point) {
return [point[1], point[0]];
})
};
});
return { data: dataResponse };
});
}
}, {
key: 'annotationQuery',
value: function annotationQuery(options) {
var timeFrom = Math.floor(options.range.from.valueOf() / 1000);
var timeTo = Math.floor(options.range.to.valueOf() / 1000);
var _options$annotation = options.annotation,
priority = _options$annotation.priority,
sources = _options$annotation.sources,
tags = _options$annotation.tags;
return this.getEventStream(timeFrom, timeTo, priority, sources, tags).then(function (eventStreams) {
var eventAnnotations = eventStreams.map(function (eventStream) {
var allEvents = eventStream.children;
var filteredEvents = _.filter(allEvents, function (event) {
return event.alert_type !== 'success';
});
return _.map(filteredEvents, function (event) {
var renderedText = eventStream.text;
if (isDataDogMarkdown(eventStream.text)) {
renderedText = convertDataDogMdToHtml(eventStream.text);
}
return {
annotation: options.annotation,
time: event.date_happened * 1000,
title: eventStream.title,
text: renderedText,
tags: eventStream.tags
};
});
});
return _.flatten(eventAnnotations);
});
}
}, {
key: 'getHosts',
value: function getHosts() {
return this.searchEntities('hosts');
}
}, {
key: 'searchEntities',
value: function searchEntities(entity) {
var params = { q: '' };
if (entity) {
params.q = entity + ':';
}
return this.invokeDataDogApiRequest('/search', params).then(function (result) {
if (result && result.results) {
return result.results[entity];
}
});
}
}, {
key: 'getMetrics',
value: function getMetrics(timeFrom) {
var params = {};
if (timeFrom) {
params.from = timeFrom;
}
return this.invokeDataDogApiRequest('/metrics', params).then(function (result) {
if (result.metrics) {
return result.metrics;
} else {
return [];
}
});
}
}, {
key: 'getTagsHosts',
value: function getTagsHosts() {
return this.invokeDataDogApiRequest('/tags/hosts').then(function (result) {
if (result && result.tags) {
return result.tags;
}
});
}
}, {
key: 'getTagsFromCache',
value: function getTagsFromCache() {
var _this2 = this;
var getTags = void 0;
if (this._cached_tags && this._cached_tags.length) {
getTags = Promise.resolve(this._cached_tags);
} else {
getTags = this.getTagsHosts().then(function (tags) {
_this2._cached_tags = tags;
return tags;
});
}
return getTags;
}
}, {
key: 'getEventStream',
value: function getEventStream(timeFrom, timeTo, priority, sources, tags) {
var params = {
start: timeFrom,
end: timeTo
};
if (priority) {
params.priority = priority;
}
if (sources) {
params.sources = sources;
}
if (tags) {
params.tags = tags;
}
return this.invokeDataDogApiRequest('/events', params).then(function (result) {
if (result.events) {
return result.events;
} else {
return [];
}
});
}
}, {
key: 'invokeDataDogApiRequest',
value: function invokeDataDogApiRequest(url) {
var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// Set auth params
params.api_key = this.api_key;
params.application_key = this.application_key;
return this.backendSrv.datasourceRequest({
method: 'GET',
url: this.url + url,
params: params
}).then(function (response) {
if (response.data) {
return response.data;
} else {
throw { message: 'DataDog API request error' };
}
}).catch(function (error) {
var message = 'DataDog API request error';
if (error.statusText) {
message = error.status + ' ' + error.statusText;
throw { message: message };
} else if (error.err.statusText) {
throw { message: error.err.statusText };
} else {
throw { message: message };
}
});
}
}]);
return DataDogDatasource;
}());
_export('DataDogDatasource', DataDogDatasource);
}
};
});
//# sourceMappingURL=datasource.js.map
File diff suppressed because one or more lines are too long
+293
View File
@@ -0,0 +1,293 @@
'use strict';
System.register(['lodash'], function (_export, _context) {
"use strict";
var _, index, categories, createFuncInstance, getFuncDef, getCategories, foo;
function addFuncDef(funcDef) {
funcDef.params = funcDef.params || [];
funcDef.defaultParams = funcDef.defaultParams || [];
funcDef.append = funcDef.append || false;
if (funcDef.category) {
funcDef.category.push(funcDef);
}
index[funcDef.name] = funcDef;
index[funcDef.name] = funcDef;
}
function FuncInstance(funcDef, options) {
this.def = funcDef;
this.params = [];
if (options && options.withDefaultParams) {
this.params = funcDef.defaultParams.slice(0);
}
this.updateText();
}
return {
setters: [function (_lodash) {
_ = _lodash.default;
}],
execute: function () {
index = [];
categories = {
Arithmatic: [],
Interpolation: [],
Timeshift: [],
Rate: [],
Smoothing: [],
Rollup: [],
Rank: [],
Count: [],
Regression: [],
Algorithms: []
};
addFuncDef({
name: 'abs',
category: categories.Arithmatic
});
addFuncDef({
name: 'log2',
category: categories.Arithmatic
});
addFuncDef({
name: 'cumsum',
category: categories.Arithmatic
});
addFuncDef({
name: 'integral',
category: categories.Arithmatic
});
addFuncDef({
name: 'fill',
category: categories.Interpolation,
params: [{ name: "fill", type: 'string', options: ['null', 'zero', 'linear', 'last'] }, { name: "limit", type: 'int', optional: true }],
defaultParams: ['zero'],
append: true
});
addFuncDef({
name: 'hour_before',
category: categories.Timeshift
});
addFuncDef({
name: 'day_before',
category: categories.Timeshift
});
addFuncDef({
name: 'week_before',
category: categories.Timeshift
});
addFuncDef({
name: 'month_before',
category: categories.Timeshift
});
addFuncDef({
name: 'per_second',
category: categories.Rate
});
addFuncDef({
name: 'per_minute',
category: categories.Rate
});
addFuncDef({
name: 'per_hour',
category: categories.Rate
});
addFuncDef({
name: 'dt',
category: categories.Rate
});
addFuncDef({
name: 'diff',
category: categories.Rate
});
addFuncDef({
name: 'derivative',
category: categories.Rate
});
addFuncDef({
name: 'ewma_3',
category: categories.Smoothing
});
addFuncDef({
name: 'ewma_5',
category: categories.Smoothing
});
addFuncDef({
name: 'ewma_10',
category: categories.Smoothing
});
addFuncDef({
name: 'ewma_20',
category: categories.Smoothing
});
addFuncDef({
name: 'median_3',
category: categories.Smoothing
});
addFuncDef({
name: 'median_5',
category: categories.Smoothing
});
addFuncDef({
name: 'median_7',
category: categories.Smoothing
});
addFuncDef({
name: 'median_9',
category: categories.Smoothing
});
addFuncDef({
name: 'rollup',
category: categories.Rollup,
params: [{ name: "rollup", type: 'string', options: ['sum', 'avg', 'min', 'max'] }, { name: "period", type: 'int' }],
defaultParams: ['sum', 600],
append: true
});
addFuncDef({
name: 'count_nonzero',
category: categories.Count
});
addFuncDef({
name: 'count_not_null',
category: categories.Count
});
addFuncDef({
name: 'top',
category: categories.Rank,
params: [{ name: "top", type: 'int', options: [5, 10, 15] }, { name: "aggr", type: 'string', options: ['mean', 'min', 'max', 'area', 'l2norm', 'last'] }, { name: "dir", type: 'string', options: ['asc', 'desc'] }],
defaultParams: [5, 'mean', 'dir']
});
addFuncDef({
name: 'robust_trend',
category: categories.Regression
});
addFuncDef({
name: 'piecewise_constant',
category: categories.Regression
});
addFuncDef({
name: 'anomalies',
category: categories.Algorithms,
params: [{ name: "anomalies", type: 'string', options: ['basic', 'agile', 'robust', 'adaptive'] }, { name: "bonds", type: 'int', options: [1, 2, 3, 4, 5, 6] }],
defaultParams: ['basic', 2]
});
addFuncDef({
name: 'anomalies',
category: categories.Algorithms,
params: [{ name: "anomalies", type: 'string', options: ['basic', 'agile', 'robust', 'adaptive'] }, { name: "bonds", type: 'int', options: [1, 2, 3, 4, 5, 6] }],
defaultParams: ['basic', 2]
});
addFuncDef({
name: 'outliers',
category: categories.Algorithms,
params: [{ name: "outliers", type: 'string', options: ['DBSCAN', 'MAD', 'scaledDBSCAN', 'scaledMAD'] }, { name: "tolerance", type: 'int', options: [0.33, 0.5, 1, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0] }],
defaultParams: ['basic', 2]
});
_.each(categories, function (funcList, catName) {
categories[catName] = _.sortBy(funcList, 'name');
});FuncInstance.prototype.render = function (metricExp) {
var str = this.def.name + '(';
var parameters = _.map(this.params, function (value) {
return value;
});
if (metricExp && !this.def.append) {
parameters.unshift(metricExp);
}
var funcRender = str + parameters.join(', ') + ')';
console.log(funcRender, this.def.append);
if (this.def.append) {
return metricExp + '.' + funcRender;
} else {
return funcRender;
}
};
FuncInstance.prototype.updateParam = function (strValue, index) {
// handle optional parameters
// if string contains ',' and next param is optional, split and update both
if (strValue === '' && this.def.params[index].optional) {
this.params.splice(index, 1);
} else {
this.params[index] = strValue;
}
this.updateText();
};
FuncInstance.prototype.updateText = function () {
if (this.params.length === 0) {
this.text = this.def.name + '()';
return;
}
var text = this.def.name + '(';
text += this.params.join(', ');
text += ')';
this.text = text;
};
createFuncInstance = function createFuncInstance(funcDef, options) {
if (_.isString(funcDef)) {
if (!index[funcDef]) {
throw { message: 'Method not found ' + name };
}
funcDef = index[funcDef];
}
return new FuncInstance(funcDef, options);
};
getFuncDef = function getFuncDef(name) {
return index[name];
};
getCategories = function getCategories() {
return categories;
};
_export('default', foo = {
createFuncInstance: createFuncInstance,
getFuncDef: getFuncDef,
getCategories: getCategories
});
}
};
});
//# sourceMappingURL=dfunc.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,251 @@
'use strict';
System.register(['angular', 'lodash', 'jquery'], function (_export, _context) {
"use strict";
var angular, _, $;
return {
setters: [function (_angular) {
angular = _angular.default;
}, function (_lodash) {
_ = _lodash.default;
}, function (_jquery) {
$ = _jquery.default;
}],
execute: function () {
'use strict';
angular.module('grafana.directives').directive('datadogFuncEditor', function ($compile, templateSrv) {
var funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
var paramTemplate = '<input type="text" style="display:none"' + ' class="input-mini tight-form-func-param"></input>';
var funcControlsTemplate = '<div class="tight-form-func-controls">' + '<span class="pointer fa fa-arrow-left"></span>' + '<span class="pointer fa fa-question-circle"></span>' + '<span class="pointer fa fa-remove" ></span>' + '<span class="pointer fa fa-arrow-right"></span>' + '</div>';
return {
restrict: 'A',
link: function postLink($scope, elem) {
var $funcLink = $(funcSpanTemplate);
var $funcControls = $(funcControlsTemplate);
var ctrl = $scope.ctrl;
var func = $scope.func;
var funcDef = func.def;
var scheduledRelink = false;
var paramCountAtLink = 0;
function clickFuncParam(paramIndex) {
/*jshint validthis:true */
var $link = $(this);
var $input = $link.next();
$input.val(func.params[paramIndex]);
$input.css('width', $link.width() + 16 + 'px');
$link.hide();
$input.show();
$input.focus();
$input.select();
var typeahead = $input.data('typeahead');
if (typeahead) {
$input.val('');
typeahead.lookup();
}
}
function scheduledRelinkIfNeeded() {
if (paramCountAtLink === func.params.length) {
return;
}
if (!scheduledRelink) {
scheduledRelink = true;
setTimeout(function () {
relink();
scheduledRelink = false;
}, 200);
}
}
function inputBlur(paramIndex) {
/*jshint validthis:true */
var $input = $(this);
var $link = $input.prev();
var newValue = $input.val();
if (newValue !== '' || func.def.params[paramIndex].optional) {
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
func.updateParam($input.val(), paramIndex);
scheduledRelinkIfNeeded();
$scope.$apply(function () {
ctrl.targetChanged();
});
$input.hide();
$link.show();
}
}
function inputKeyPress(paramIndex, e) {
/*jshint validthis:true */
if (e.which === 13) {
inputBlur.call(this, paramIndex);
}
}
function inputKeyDown() {
/*jshint validthis:true */
this.style.width = (3 + this.value.length) * 8 + 'px';
}
function addTypeahead($input, paramIndex) {
$input.attr('data-provide', 'typeahead');
var options = funcDef.params[paramIndex].options;
if (funcDef.params[paramIndex].type === 'int') {
options = _.map(options, function (val) {
return val.toString();
});
}
$input.typeahead({
source: options,
minLength: 0,
items: 20,
updater: function updater(value) {
setTimeout(function () {
inputBlur.call($input[0], paramIndex);
}, 0);
return value;
}
});
var typeahead = $input.data('typeahead');
typeahead.lookup = function () {
this.query = this.$element.val() || '';
return this.process(this.source);
};
}
function toggleFuncControls() {
var targetDiv = elem.closest('.tight-form');
if (elem.hasClass('show-function-controls')) {
elem.removeClass('show-function-controls');
targetDiv.removeClass('has-open-function');
$funcControls.hide();
return;
}
elem.addClass('show-function-controls');
targetDiv.addClass('has-open-function');
$funcControls.show();
}
function addElementsAndCompile() {
$funcControls.appendTo(elem);
$funcLink.appendTo(elem);
_.each(funcDef.params, function (param, index) {
if (param.optional && func.params.length <= index) {
return;
}
if (index > 0) {
$('<span>, </span>').appendTo(elem);
}
var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
var $paramLink = $('<a ng-click="" class="datadog-func-param-link">' + paramValue + '</a>');
var $input = $(paramTemplate);
paramCountAtLink++;
$paramLink.appendTo(elem);
$input.appendTo(elem);
$input.blur(_.partial(inputBlur, index));
$input.keyup(inputKeyDown);
$input.keypress(_.partial(inputKeyPress, index));
$paramLink.click(_.partial(clickFuncParam, index));
if (funcDef.params[index].options) {
addTypeahead($input, index);
}
});
$('<span>)</span>').appendTo(elem);
$compile(elem.contents())($scope);
}
function ifJustAddedFocusFistParam() {
if ($scope.func.added) {
$scope.func.added = false;
setTimeout(function () {
elem.find('.datadog-func-param-link').first().click();
}, 10);
}
}
function registerFuncControlsToggle() {
$funcLink.click(toggleFuncControls);
}
function registerFuncControlsActions() {
$funcControls.click(function (e) {
var $target = $(e.target);
if ($target.hasClass('fa-remove')) {
toggleFuncControls();
$scope.$apply(function () {
ctrl.removeFunction($scope.func);
});
return;
}
if ($target.hasClass('fa-arrow-left')) {
$scope.$apply(function () {
_.move(ctrl.functions, $scope.$index, $scope.$index - 1);
ctrl.targetChanged();
});
return;
}
if ($target.hasClass('fa-arrow-right')) {
$scope.$apply(function () {
_.move(ctrl.functions, $scope.$index, $scope.$index + 1);
ctrl.targetChanged();
});
return;
}
if ($target.hasClass('fa-question-circle')) {
window.open("http://docs.datadoghq.com/graphing/#apply-more-advanced-functions", '_blank');
return;
}
});
}
function relink() {
elem.children().remove();
addElementsAndCompile();
ifJustAddedFocusFistParam();
registerFuncControlsToggle();
registerFuncControlsActions();
}
relink();
}
};
});
}
};
});
//# sourceMappingURL=func_editor.js.map
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

+51
View File
@@ -0,0 +1,51 @@
'use strict';
System.register(['./datasource', './query_ctrl'], function (_export, _context) {
"use strict";
var DataDogDatasource, DataDogQueryCtrl, DataDogConfigCtrl, DataDogQueryOptionsCtrl, DataDogAnnotationsQueryCtrl;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
return {
setters: [function (_datasource) {
DataDogDatasource = _datasource.DataDogDatasource;
}, function (_query_ctrl) {
DataDogQueryCtrl = _query_ctrl.DataDogQueryCtrl;
}],
execute: function () {
_export('ConfigCtrl', DataDogConfigCtrl = function DataDogConfigCtrl() {
_classCallCheck(this, DataDogConfigCtrl);
});
DataDogConfigCtrl.templateUrl = "partials/config.html";
_export('QueryOptionsCtrl', DataDogQueryOptionsCtrl = function DataDogQueryOptionsCtrl() {
_classCallCheck(this, DataDogQueryOptionsCtrl);
});
DataDogQueryOptionsCtrl.templateUrl = "partials/query.options.html";
_export('AnnotationsQueryCtrl', DataDogAnnotationsQueryCtrl = function DataDogAnnotationsQueryCtrl() {
_classCallCheck(this, DataDogAnnotationsQueryCtrl);
});
DataDogAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';
_export('Datasource', DataDogDatasource);
_export('QueryCtrl', DataDogQueryCtrl);
_export('ConfigCtrl', DataDogConfigCtrl);
_export('QueryOptionsCtrl', DataDogQueryOptionsCtrl);
_export('AnnotationsQueryCtrl', DataDogAnnotationsQueryCtrl);
}
};
});
//# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../src/module.js"],"names":["DataDogDatasource","DataDogQueryCtrl","DataDogConfigCtrl","templateUrl","DataDogQueryOptionsCtrl","DataDogAnnotationsQueryCtrl"],"mappings":";;;;;;;;;;;;;;;AAAQA,uB,eAAAA,iB;;AACAC,sB,eAAAA,gB;;;4BAEFC,iB;;;;AACNA,wBAAkBC,WAAlB,GAAgC,sBAAhC;;kCAEMC,uB;;;;AACNA,8BAAwBD,WAAxB,GAAsC,6BAAtC;;sCAEME,2B;;;;AACNA,kCAA4BF,WAA5B,GAA0C,kCAA1C;;4BAGEH,iB;;2BACAC,gB;;4BACAC,iB;;kCACAE,uB;;sCACAC,2B","file":"module.js","sourcesContent":["import {DataDogDatasource} from './datasource';\nimport {DataDogQueryCtrl} from './query_ctrl';\n\nclass DataDogConfigCtrl {}\nDataDogConfigCtrl.templateUrl = \"partials/config.html\";\n\nclass DataDogQueryOptionsCtrl {}\nDataDogQueryOptionsCtrl.templateUrl = \"partials/query.options.html\";\n\nclass DataDogAnnotationsQueryCtrl {}\nDataDogAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';\n\nexport {\n DataDogDatasource as Datasource,\n DataDogQueryCtrl as QueryCtrl,\n DataDogConfigCtrl as ConfigCtrl,\n DataDogQueryOptionsCtrl as QueryOptionsCtrl,\n DataDogAnnotationsQueryCtrl as AnnotationsQueryCtrl\n};\n"]}
@@ -0,0 +1,29 @@
<div class="gf-form-group">
<div class="gf-form max-width-30">
<label class="gf-form-label width-7">Sources</label>
<input type="text" class="gf-form-input"
ng-model='ctrl.annotation.sources'
placeholder="">
</input>
<info-popover mode="right-absolute">
A comma separated string of sources
</info-popover>
</div>
<div class="gf-form max-width-30">
<label class="gf-form-label width-7">Tags</label>
<input type="text" class="gf-form-input"
ng-model='ctrl.annotation.tags'
placeholder="">
</input>
<info-popover mode="right-absolute">
A comma separated string of tags
</info-popover>
</div>
<div class="gf-form">
<label class="gf-form-label width-7">Priority</label>
<select class="gf-form-input width-7"
ng-model="ctrl.annotation.priority"
ng-options="p.v as p.n for p in [{n: 'None', v: undefined}, {n: 'Low', v: 'low'}, {n: 'Normal', v: 'normal'}]">
</select>
</div>
</div>
@@ -0,0 +1,35 @@
<datasource-http-settings
current="ctrl.current"
suggest-url="https://app.datadoghq.com/api/v1">
</datasource-http-settings>
<div class="gf-form-group">
<h3 class="page-heading">DataDog settings</h3>
<p>You can manage your account's API and application keys <a href="https://app.datadoghq.com/account/settings#api">here</a>.</p>
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-30">
<span class="gf-form-label width-7">API Key</span>
<input class="gf-form-input" type="text"
ng-model='ctrl.current.jsonData.api_key' min-length="0"
required></input>
<info-popover mode="right-absolute">
<p>Specify your datadog API Key</p>
</info-popover>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form max-width-30">
<span class="gf-form-label width-7">APP Key</span>
<input class="gf-form-input" type="text"
ng-model='ctrl.current.jsonData.app_key' min-length="0"
required></input>
<info-popover mode="right-absolute">
<p>Specify your datadog APP Key</p>
</info-popover>
</div>
</div>
</div>
</div>
@@ -0,0 +1,72 @@
<query-editor-row query-ctrl="ctrl" has-text-edit-mode="true" can-collapse="true">
<div class="gf-form" ng-if="ctrl.target.rawQuery">
<input type="text" class="gf-form-input" ng-model="ctrl.target.query" spellcheck="false" ng-blur="ctrl.refresh()"></input>
</div>
<div ng-if="!ctrl.target.rawQuery">
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">AGG</label>
<metric-segment segment="ctrl.aggregationSegment" get-options="ctrl.getAggregations()" on-change="ctrl.aggregationChanged()"></metric-segment>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">METRIC</label>
<metric-segment segment="ctrl.metricSegment" get-options="ctrl.getMetrics()" on-change="ctrl.metricChanged()"></metric-segment>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">POST</label>
<metric-segment segment="ctrl.asSegment" get-options="ctrl.getAs()" on-change="ctrl.asChanged()"></metric-segment>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">SCOPE</label>
</div>
<div class="gf-form" ng-repeat="segment in ctrl.tagSegments">
<metric-segment segment="segment" get-options="ctrl.getTags(segment, $index)" on-change="ctrl.tagSegmentUpdated(segment, $index)"></metric-segment>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">FUNCS</label>
</div>
<div ng-repeat="func in ctrl.functions" class="gf-form">
<span datadog-func-editor class="gf-form-label query-part"></span>
</div>
<div class="gf-form dropdown">
<span datadog-add-func></span>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form max-width-30">
<label class="gf-form-label query-keyword width-7">ALIAS BY</label>
<input type="text" class="gf-form-input" ng-model="ctrl.target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="ctrl.refresh()">
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
</div>
</query-editor-row>
@@ -0,0 +1,2 @@
<section class="grafana-metric-options">
</section>
@@ -0,0 +1,34 @@
{
"id": "grafana-datadog-datasource",
"name": "DataDog",
"type": "datasource",
"metrics": true,
"annotations": true,
"staticRoot": ".",
"info": {
"description": "datasource plugin for DataDog",
"author": {
"name": "Alex Kessinger",
"url": "http://rumproarious.com"
},
"keywords": ["datadog"],
"logos": {
"small": "img/datadog.png",
"large": "img/datadog.png"
},
"links": [
{"name": "GitHub", "url": "https://github.com/voidfiles/grafana-datasource-datagog"},
{"name": "License", "url": "https://github.com/voidfiles/grafana-datasource-datagog/blob/master/LICENSE"}
],
"version": "0.0.1",
"updated": "2016-12-18"
},
"dependencies": {
"grafanaVersion": "3.x.x",
"plugins": [ ]
}
}
@@ -0,0 +1,85 @@
'use strict';
System.register(['lodash', './dfunc'], function (_export, _context) {
"use strict";
var _, dfunc;
function buildQuery(target, adhocFilters) {
var aggregation = target.aggregation,
metric = target.metric,
tags = target.tags,
functions = target.functions;
var query = aggregation + ':' + metric;
var functionInstances = getFunctionInstances(functions);
if (tags && tags.length || adhocFilters && adhocFilters.length) {
query += '{';
if (tags && tags.length) {
query += tags.join(',');
}
if (adhocFilters && adhocFilters.length) {
var adhocTags = buildAdHocFilterString(adhocFilters);
if (tags && tags.length) {
query += ',';
}
query += adhocTags;
}
query += '}';
} else {
query += '{*}';
}
if (target.as) {
query += '.' + target.as + '()';
}
var groupedFuncs = _.groupBy(functionInstances, function (func) {
if (func.def.append) {
return 'appends';
} else {
return 'wraps';
}
});
_.each(groupedFuncs.appends, function (func) {
query += '.' + func.render();
});
_.each(groupedFuncs.wraps, function (func) {
query = func.render(query);
});
return query;
}
_export('buildQuery', buildQuery);
function getFunctionInstances(functions) {
return _.map(functions, function (func) {
var f = dfunc.createFuncInstance(func.funcDef, { withDefaultParams: false });
f.params = func.params.slice();
return f;
});
}
function buildAdHocFilterString(adhocFilters) {
return adhocFilters.map(function (filter) {
return filter.key + ':' + filter.value;
}).join(',');
}
return {
setters: [function (_lodash) {
_ = _lodash.default;
}, function (_dfunc) {
dfunc = _dfunc.default;
}],
execute: function () {}
};
});
//# sourceMappingURL=query_builder.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../src/query_builder.js"],"names":["buildQuery","target","adhocFilters","aggregation","metric","tags","functions","query","functionInstances","getFunctionInstances","length","join","adhocTags","buildAdHocFilterString","as","groupedFuncs","_","groupBy","func","def","append","each","appends","render","wraps","map","f","dfunc","createFuncInstance","funcDef","withDefaultParams","params","slice","filter","key","value"],"mappings":";;;;;;;AAGO,WAASA,UAAT,CAAoBC,MAApB,EAA4BC,YAA5B,EAA0C;AAAA,QAC1CC,WAD0C,GACFF,MADE,CAC1CE,WAD0C;AAAA,QAC7BC,MAD6B,GACFH,MADE,CAC7BG,MAD6B;AAAA,QACrBC,IADqB,GACFJ,MADE,CACrBI,IADqB;AAAA,QACfC,SADe,GACFL,MADE,CACfK,SADe;;AAE/C,QAAIC,QAAWJ,WAAX,SAA0BC,MAA9B;;AAEA,QAAII,oBAAqBC,qBAAqBH,SAArB,CAAzB;;AAEA,QAAKD,QAAQA,KAAKK,MAAd,IAA0BR,gBAAgBA,aAAaQ,MAA3D,EAAoE;AAClEH,eAAS,GAAT;;AAEA,UAAIF,QAAQA,KAAKK,MAAjB,EAAyB;AACvBH,iBAASF,KAAKM,IAAL,CAAU,GAAV,CAAT;AACD;;AAED,UAAIT,gBAAgBA,aAAaQ,MAAjC,EAAyC;AACvC,YAAIE,YAAYC,uBAAuBX,YAAvB,CAAhB;AACA,YAAIG,QAAQA,KAAKK,MAAjB,EAAyB;AACvBH,mBAAS,GAAT;AACD;AACDA,iBAASK,SAAT;AACD;;AAEDL,eAAS,GAAT;AACD,KAhBD,MAgBO;AACLA,eAAS,KAAT;AACD;;AAED,QAAIN,OAAOa,EAAX,EAAe;AACbP,eAAS,MAAMN,OAAOa,EAAb,GAAkB,IAA3B;AACD;;AAED,QAAIC,eAAeC,EAAEC,OAAF,CAAUT,iBAAV,EAA6B,gBAAQ;AACtD,UAAIU,KAAKC,GAAL,CAASC,MAAb,EAAqB;AACnB,eAAO,SAAP;AACD,OAFD,MAEO;AACL,eAAO,OAAP;AACD;AACF,KANkB,CAAnB;;AAQAJ,MAAEK,IAAF,CAAON,aAAaO,OAApB,EAA6B,gBAAQ;AACnCf,eAAS,MAAMW,KAAKK,MAAL,EAAf;AACD,KAFD;;AAIAP,MAAEK,IAAF,CAAON,aAAaS,KAApB,EAA2B,gBAAQ;AACjCjB,cAAQW,KAAKK,MAAL,CAAYhB,KAAZ,CAAR;AACD,KAFD;;AAIA,WAAOA,KAAP;AACD;;wBA/CeP,U;;AAiDhB,WAASS,oBAAT,CAA8BH,SAA9B,EAAyC;AACvC,WAAOU,EAAES,GAAF,CAAMnB,SAAN,EAAiB,gBAAQ;AAC9B,UAAIoB,IAAIC,MAAMC,kBAAN,CAAyBV,KAAKW,OAA9B,EAAuC,EAACC,mBAAmB,KAApB,EAAvC,CAAR;AACAJ,QAAEK,MAAF,GAAWb,KAAKa,MAAL,CAAYC,KAAZ,EAAX;AACA,aAAON,CAAP;AACD,KAJM,CAAP;AAKD;;AAED,WAASb,sBAAT,CAAgCX,YAAhC,EAA8C;AAC5C,WAAOA,aAAauB,GAAb,CAAiB,kBAAU;AAChC,aAAOQ,OAAOC,GAAP,GAAa,GAAb,GAAmBD,OAAOE,KAAjC;AACD,KAFM,EAEJxB,IAFI,CAEC,GAFD,CAAP;AAGD;;;AAhEMK,O;;AACAW,W","file":"query_builder.js","sourcesContent":["import _ from 'lodash';\nimport dfunc from './dfunc';\n\nexport function buildQuery(target, adhocFilters) {\n let {aggregation, metric, tags, functions} = target;\n let query = `${aggregation}:${metric}`;\n\n let functionInstances = getFunctionInstances(functions);\n\n if ((tags && tags.length) || (adhocFilters && adhocFilters.length)) {\n query += '{';\n\n if (tags && tags.length) {\n query += tags.join(',');\n }\n\n if (adhocFilters && adhocFilters.length) {\n let adhocTags = buildAdHocFilterString(adhocFilters);\n if (tags && tags.length) {\n query += ',';\n }\n query += adhocTags;\n }\n\n query += '}';\n } else {\n query += '{*}';\n }\n\n if (target.as) {\n query += '.' + target.as + '()';\n }\n\n var groupedFuncs = _.groupBy(functionInstances, func => {\n if (func.def.append) {\n return 'appends';\n } else {\n return 'wraps';\n }\n });\n\n _.each(groupedFuncs.appends, func => {\n query += '.' + func.render();\n });\n\n _.each(groupedFuncs.wraps, func => {\n query = func.render(query);\n });\n\n return query;\n}\n\nfunction getFunctionInstances(functions) {\n return _.map(functions, func => {\n var f = dfunc.createFuncInstance(func.funcDef, {withDefaultParams: false});\n f.params = func.params.slice();\n return f;\n });\n}\n\nfunction buildAdHocFilterString(adhocFilters) {\n return adhocFilters.map(filter => {\n return filter.key + ':' + filter.value;\n }).join(',');\n}\n"]}
@@ -0,0 +1,264 @@
'use strict';
System.register(['lodash', './dfunc', 'app/plugins/sdk', './func_editor', './add_datadog_func', './query_builder'], function (_export, _context) {
"use strict";
var _, dfunc, QueryCtrl, queryBuilder, _createClass, DataDogQueryCtrl;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
return {
setters: [function (_lodash) {
_ = _lodash.default;
}, function (_dfunc) {
dfunc = _dfunc.default;
}, function (_appPluginsSdk) {
QueryCtrl = _appPluginsSdk.QueryCtrl;
}, function (_func_editor) {}, function (_add_datadog_func) {}, function (_query_builder) {
queryBuilder = _query_builder;
}],
execute: function () {
_createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
_export('DataDogQueryCtrl', DataDogQueryCtrl = function (_QueryCtrl) {
_inherits(DataDogQueryCtrl, _QueryCtrl);
function DataDogQueryCtrl($scope, $injector, uiSegmentSrv, templateSrv) {
_classCallCheck(this, DataDogQueryCtrl);
var _this = _possibleConstructorReturn(this, (DataDogQueryCtrl.__proto__ || Object.getPrototypeOf(DataDogQueryCtrl)).call(this, $scope, $injector));
_this.removeText = '-- remove tag --';
_this.uiSegmentSrv = uiSegmentSrv;
_this.templateSrv = templateSrv;
if (_this.target.aggregation) {
_this.aggregationSegment = new uiSegmentSrv.newSegment(_this.target.aggregation);
} else {
_this.aggregationSegment = new uiSegmentSrv.newSegment({
value: 'Select Aggregation',
fake: true,
custom: false
});
}
if (_this.target.metric) {
_this.metricSegment = new uiSegmentSrv.newSegment(_this.target.metric);
} else {
_this.metricSegment = new uiSegmentSrv.newSegment({
value: 'Select Metric',
fake: true,
custom: false
});
}
_this.target.tags = _this.target.tags || [];
_this.tagSegments = _this.target.tags.map(uiSegmentSrv.newSegment);
_this.fixTagSegments();
_this.functions = [];
_this.target.functions = _this.target.functions || [];
_this.functions = _.map(_this.target.functions, function (func) {
var f = dfunc.createFuncInstance(func.funcDef, { withDefaultParams: false });
f.params = func.params.slice();
return f;
});
if (_this.target.as) {
_this.asSegment = uiSegmentSrv.newSegment(_this.target.as);
} else {
_this.asSegment = uiSegmentSrv.newSegment({
value: 'Select As',
fake: true,
custom: false
});
}
return _this;
}
_createClass(DataDogQueryCtrl, [{
key: 'toggleEditorMode',
value: function toggleEditorMode() {
this.target.rawQuery = !this.target.rawQuery;
}
}, {
key: 'getMetrics',
value: function getMetrics() {
return this.datasource.metricFindQuery().then(this.uiSegmentSrv.transformToSegments(true));
}
}, {
key: 'getAggregations',
value: function getAggregations() {
return Promise.resolve([{ text: 'avg by', value: 'avg' }, { text: 'max by', value: 'max' }, { text: 'min by', value: 'min' }, { text: 'sub by', value: 'sum' }]);
}
}, {
key: 'getAs',
value: function getAs() {
return Promise.resolve([{ text: 'None', value: 'None' }, { text: 'as_count', value: 'as_count' }, { text: 'as_rate', value: 'as_rate' }]);
}
}, {
key: 'getTags',
value: function getTags(segment) {
var _this2 = this;
return this.datasource.tagFindQuery().then(this.uiSegmentSrv.transformToSegments(true)).then(function (results) {
if (segment.type !== 'plus-button') {
var removeSegment = _this2.uiSegmentSrv.newFake(_this2.removeText);
results.unshift(removeSegment);
}
return results;
});
}
}, {
key: 'aggregationChanged',
value: function aggregationChanged() {
this.target.aggregation = this.aggregationSegment.value;
this.panelCtrl.refresh();
}
}, {
key: 'metricChanged',
value: function metricChanged() {
this.target.metric = this.metricSegment.value;
this.panelCtrl.refresh();
}
}, {
key: 'asChanged',
value: function asChanged() {
if (this.asSegment.value === 'None') {
this.target.as = null;
} else {
this.target.as = this.asSegment.value;
}
this.panelCtrl.refresh();
}
}, {
key: 'fixTagSegments',
value: function fixTagSegments() {
var count = this.tagSegments.length;
var lastSegment = this.tagSegments[Math.max(count - 1, 0)];
if (!lastSegment || lastSegment.type !== 'plus-button') {
this.tagSegments.push(this.uiSegmentSrv.newPlusButton());
}
}
}, {
key: 'targetChanged',
value: function targetChanged() {
if (this.error) {
return;
}
this.panelCtrl.refresh();
}
}, {
key: 'persistFunctions',
value: function persistFunctions() {
this.target.functions = _.map(this.functions, function (func) {
return {
funcDef: func.def.name,
params: func.params.slice()
};
});
}
}, {
key: 'removeFunction',
value: function removeFunction(func) {
this.functions = _.without(this.functions, func);
this.persistFunctions();
this.targetChanged();
}
}, {
key: 'addFunction',
value: function addFunction(funcDef) {
var func = dfunc.createFuncInstance(funcDef, { withDefaultParams: true });
func.added = true;
this.functions.push(func);
this.persistFunctions();
this.targetChanged();
}
}, {
key: 'tagSegmentUpdated',
value: function tagSegmentUpdated(segment, index) {
if (segment.value === this.removeText) {
this.tagSegments.splice(index, 1);
}
var realSegments = _.filter(this.tagSegments, function (segment) {
return segment.value;
});
this.target.tags = realSegments.map(function (segment) {
return segment.value;
});
this.tagSegments = _.map(this.target.tags, this.uiSegmentSrv.newSegment);
this.fixTagSegments();
this.panelCtrl.refresh();
}
}, {
key: 'getCollapsedText',
value: function getCollapsedText() {
if (this.target.rawQuery) {
return this.target.query;
} else {
return queryBuilder.buildQuery(this.target);
}
}
}]);
return DataDogQueryCtrl;
}(QueryCtrl));
_export('DataDogQueryCtrl', DataDogQueryCtrl);
DataDogQueryCtrl.templateUrl = 'partials/query.editor.html';
}
};
});
//# sourceMappingURL=query_ctrl.js.map
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,115 @@
'use strict';
var _angular = require('angular');
var _angular2 = _interopRequireDefault(_angular);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _jquery = require('jquery');
var _jquery2 = _interopRequireDefault(_jquery);
var _dfunc = require('./dfunc');
var _dfunc2 = _interopRequireDefault(_dfunc);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
_angular2.default.module('grafana.directives').directive('datadogAddFunc', function ($compile) {
var inputTemplate = '<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>';
var buttonTemplate = '<a class="gf-form-label query-part dropdown-toggle"' + ' tabindex="1" gf-dropdown="functionMenu" data-toggle="dropdown">' + '<i class="fa fa-plus"></i></a>';
return {
link: function link($scope, elem) {
var categories = _dfunc2.default.getCategories();
var allFunctions = getAllFunctionNames(categories);
var ctrl = $scope.ctrl;
$scope.functionMenu = createFunctionDropDownMenu(categories);
var $input = (0, _jquery2.default)(inputTemplate);
var $button = (0, _jquery2.default)(buttonTemplate);
$input.appendTo(elem);
$button.appendTo(elem);
$input.attr('data-provide', 'typeahead');
$input.typeahead({
source: allFunctions,
minLength: 1,
items: 10,
updater: function updater(value) {
var funcDef = _dfunc2.default.getFuncDef(value);
if (!funcDef) {
// try find close match
value = value.toLowerCase();
funcDef = _lodash2.default.find(allFunctions, function (funcName) {
return funcName.toLowerCase().indexOf(value) === 0;
});
if (!funcDef) {
return;
}
}
$scope.$apply(function () {
ctrl.addFunction(funcDef);
});
$input.trigger('blur');
return '';
}
});
$button.click(function () {
$button.hide();
$input.show();
$input.focus();
});
$input.keyup(function () {
elem.toggleClass('open', $input.val() === '');
});
$input.blur(function () {
// clicking the function dropdown menu wont
// work if you remove class at once
setTimeout(function () {
$input.val('');
$input.hide();
$button.show();
elem.removeClass('open');
}, 200);
});
$compile(elem.contents())($scope);
}
};
});
function getAllFunctionNames(categories) {
return _lodash2.default.reduce(categories, function (list, category) {
_lodash2.default.each(category, function (func) {
list.push(func.name);
});
return list;
}, []);
}
function createFunctionDropDownMenu(categories) {
return _lodash2.default.map(categories, function (list, category) {
return {
text: category,
submenu: _lodash2.default.map(list, function (value) {
return {
text: value.name,
click: "ctrl.addFunction('" + value.name + "')"
};
})
};
});
}
//# sourceMappingURL=add_datadog_func.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,422 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DataDogDatasource = undefined;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _showdownMin = require('./showdown.min.js');
var _showdownMin2 = _interopRequireDefault(_showdownMin);
var _query_builder = require('./query_builder');
var queryBuilder = _interopRequireWildcard(_query_builder);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DataDogDatasource = exports.DataDogDatasource = function () {
function DataDogDatasource(instanceSettings, backendSrv, templateSrv) {
_classCallCheck(this, DataDogDatasource);
this.type = instanceSettings.type;
this.url = instanceSettings.url;
this.name = instanceSettings.name;
this.api_key = instanceSettings.jsonData.api_key;
this.application_key = instanceSettings.jsonData.app_key;
this.supportMetrics = true;
this.backendSrv = backendSrv;
this.templateSrv = templateSrv;
this._cached_metrics = false;
}
// Function to check Datasource health
_createClass(DataDogDatasource, [{
key: 'testDatasource',
value: function testDatasource() {
return this.invokeDataDogApiRequest('/downtime').then(function () {
return {
status: "success",
title: "Success",
message: "Data source is working"
};
}).catch(function (error) {
var message = "Connection error";
if (error && error.message) {
message = error.message;
}
return {
status: "error",
title: "Error",
message: message
};
});
}
}, {
key: 'tagFindQuery',
value: function tagFindQuery() {
return this.getTagsFromCache().then(function (tags) {
return _lodash2.default.map(tags, function (hosts, tag) {
return {
text: tag,
value: tag
};
});
});
}
}, {
key: 'metricFindQuery',
value: function metricFindQuery(query) {
var _this = this;
if (query === 'tag') {
return this.tagFindQuery();
}
if (this._cached_metrics) {
return Promise.resolve(this._cached_metrics);
}
if (this.fetching) {
return this.fetching;
}
var d = new Date();
d.setDate(d.getDate() - 1);
var from = Math.floor(d.getTime() / 1000);
this.fetching = this.getMetrics(from).then(function (metrics) {
_this._cached_metrics = _lodash2.default.map(metrics, function (metric) {
return {
text: metric,
value: metric
};
});
return _this._cached_metrics;
});
return this.fetching;
}
}, {
key: 'getTagKeys',
value: function getTagKeys() {
return this.getTagsFromCache().then(function (tagsHosts) {
var tags = Object.keys(tagsHosts);
var kv = mapTagsToKVPairs(tags);
var grafanaTags = Object.keys(kv);
return grafanaTags.map(function (tag) {
return {
text: tag,
value: tag
};
});
});
}
}, {
key: 'getTagValues',
value: function getTagValues(options) {
return this.getTagsFromCache().then(function (tagsHosts) {
var tags = Object.keys(tagsHosts);
var kv = mapTagsToKVPairs(tags);
var grafanaValues = kv[options.key];
return grafanaValues.map(function (val) {
return {
text: val,
value: val
};
});
});
}
}, {
key: 'query',
value: function query(options) {
var from = Math.floor(options.range.from.valueOf() / 1000);
var to = Math.floor(options.range.to.valueOf() / 1000);
var targets = options.targets.filter(function (t) {
return !t.hide;
});
if (targets.length <= 0) {
return Promise.resolve({ data: [] });
}
// add global adhoc filters
var adhocFilters = this.templateSrv.getAdhocFilters(this.name);
var queries = _lodash2.default.map(targets, function (target) {
var query = void 0;
if (target.rawQuery) {
query = target.query;
} else {
query = queryBuilder.buildQuery(target, adhocFilters);
}
return query;
});
var queryString = queries.join(',');
queryString = this.templateSrv.replace(queryString, options.scopedVars, dataDogTagFormat);
var params = {
from: from,
to: to,
query: queryString
};
return this.invokeDataDogApiRequest('/query', params).then(function (result) {
var dataResponse = _lodash2.default.map(result.series, function (series, i) {
var target = targets[i];
return {
'target': target.alias || series.expression,
'datapoints': _lodash2.default.map(series.pointlist, function (point) {
return [point[1], point[0]];
})
};
});
return { data: dataResponse };
});
}
}, {
key: 'annotationQuery',
value: function annotationQuery(options) {
var timeFrom = Math.floor(options.range.from.valueOf() / 1000);
var timeTo = Math.floor(options.range.to.valueOf() / 1000);
var _options$annotation = options.annotation,
priority = _options$annotation.priority,
sources = _options$annotation.sources,
tags = _options$annotation.tags;
return this.getEventStream(timeFrom, timeTo, priority, sources, tags).then(function (eventStreams) {
var eventAnnotations = eventStreams.map(function (eventStream) {
var allEvents = eventStream.children;
var filteredEvents = _lodash2.default.filter(allEvents, function (event) {
return event.alert_type !== 'success';
});
return _lodash2.default.map(filteredEvents, function (event) {
var renderedText = eventStream.text;
if (isDataDogMarkdown(eventStream.text)) {
renderedText = convertDataDogMdToHtml(eventStream.text);
}
return {
annotation: options.annotation,
time: event.date_happened * 1000,
title: eventStream.title,
text: renderedText,
tags: eventStream.tags
};
});
});
return _lodash2.default.flatten(eventAnnotations);
});
}
}, {
key: 'getHosts',
value: function getHosts() {
return this.searchEntities('hosts');
}
// entity should be 'hosts' or 'metrics'
// http://docs.datadoghq.com/api/?lang=console#search
}, {
key: 'searchEntities',
value: function searchEntities(entity) {
var params = { q: '' };
if (entity) {
params.q = entity + ':';
}
return this.invokeDataDogApiRequest('/search', params).then(function (result) {
if (result && result.results) {
return result.results[entity];
}
});
}
}, {
key: 'getMetrics',
value: function getMetrics(timeFrom) {
var params = {};
if (timeFrom) {
params.from = timeFrom;
}
return this.invokeDataDogApiRequest('/metrics', params).then(function (result) {
if (result.metrics) {
return result.metrics;
} else {
return [];
}
});
}
}, {
key: 'getTagsHosts',
value: function getTagsHosts() {
return this.invokeDataDogApiRequest('/tags/hosts').then(function (result) {
if (result && result.tags) {
return result.tags;
}
});
}
}, {
key: 'getTagsFromCache',
value: function getTagsFromCache() {
var _this2 = this;
var getTags = void 0;
if (this._cached_tags && this._cached_tags.length) {
getTags = Promise.resolve(this._cached_tags);
} else {
getTags = this.getTagsHosts().then(function (tags) {
_this2._cached_tags = tags;
return tags;
});
}
return getTags;
}
}, {
key: 'getEventStream',
value: function getEventStream(timeFrom, timeTo, priority, sources, tags) {
var params = {
start: timeFrom,
end: timeTo
};
if (priority) {
params.priority = priority;
}
if (sources) {
params.sources = sources;
}
if (tags) {
params.tags = tags;
}
return this.invokeDataDogApiRequest('/events', params).then(function (result) {
if (result.events) {
return result.events;
} else {
return [];
}
});
}
}, {
key: 'invokeDataDogApiRequest',
value: function invokeDataDogApiRequest(url) {
var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// Set auth params
params.api_key = this.api_key;
params.application_key = this.application_key;
return this.backendSrv.datasourceRequest({
method: 'GET',
url: this.url + url,
params: params
}).then(function (response) {
if (response.data) {
return response.data;
} else {
throw { message: 'DataDog API request error' };
}
}).catch(function (error) {
var message = 'DataDog API request error';
if (error.statusText) {
message = error.status + ' ' + error.statusText;
throw { message: message };
} else if (error.err.statusText) {
throw { message: error.err.statusText };
} else {
throw { message: message };
}
});
}
}]);
return DataDogDatasource;
}();
/*
* Convert tags to key-value pairs
* [region:east, region:nw] => {region: [east, nw]}
*/
function mapTagsToKVPairs(tags) {
var kv_tags = _lodash2.default.filter(tags, function (tag) {
return tag.indexOf(':') !== -1;
});
var kv_pairs = kv_tags.map(function (tag) {
return tag.split(':', 2); // Limit to 2
});
var kv_object = {};
kv_pairs.forEach(function (pair) {
var key = pair[0];
var val = pair[1];
if (kv_object[key]) {
kv_object[key].push(val);
} else {
kv_object[key] = [val];
}
});
return kv_object;
}
/*
* Convert DataDog event text from markdown to pure HTML
* http://docs.datadoghq.com/guides/markdown/
*/
function convertDataDogMdToHtml(str) {
var MD_START = '%%%\n';
var MD_END = '\n%%%';
var md_start_index = str.indexOf(MD_START) + MD_START.length;
var md_end_index = str.indexOf(MD_END);
var md = str.substring(md_start_index, md_end_index);
md = removeImagesFromDataDogMarkdown(md);
var converter = new _showdownMin2.default.Converter();
return converter.makeHtml(md);
}
function isDataDogMarkdown(str) {
var MD_START = '%%%\n';
return str.indexOf(MD_START) >= 0;
}
function removeImagesFromDataDogMarkdown(str) {
var dataDogImagePattern = /\[{0,1}\!\[.*\]\(https{0,1}\:\/\/p\.datadoghq\.com\/snapshot.+\)/g;
return str.replace(dataDogImagePattern, '');
}
function dataDogTagFormat(value) {
if (_lodash2.default.isArray(value)) {
return value.join(',');
}
return value;
}
//# sourceMappingURL=datasource.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,295 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var index = [];
var categories = {
Arithmatic: [],
Interpolation: [],
Timeshift: [],
Rate: [],
Smoothing: [],
Rollup: [],
Rank: [],
Count: [],
Regression: [],
Algorithms: []
};
function addFuncDef(funcDef) {
funcDef.params = funcDef.params || [];
funcDef.defaultParams = funcDef.defaultParams || [];
funcDef.append = funcDef.append || false;
if (funcDef.category) {
funcDef.category.push(funcDef);
}
index[funcDef.name] = funcDef;
index[funcDef.name] = funcDef;
}
addFuncDef({
name: 'abs',
category: categories.Arithmatic
});
addFuncDef({
name: 'log2',
category: categories.Arithmatic
});
addFuncDef({
name: 'cumsum',
category: categories.Arithmatic
});
addFuncDef({
name: 'integral',
category: categories.Arithmatic
});
addFuncDef({
name: 'fill',
category: categories.Interpolation,
params: [{ name: "fill", type: 'string', options: ['null', 'zero', 'linear', 'last'] }, { name: "limit", type: 'int', optional: true }],
defaultParams: ['zero'],
append: true
});
addFuncDef({
name: 'hour_before',
category: categories.Timeshift
});
addFuncDef({
name: 'day_before',
category: categories.Timeshift
});
addFuncDef({
name: 'week_before',
category: categories.Timeshift
});
addFuncDef({
name: 'month_before',
category: categories.Timeshift
});
addFuncDef({
name: 'per_second',
category: categories.Rate
});
addFuncDef({
name: 'per_minute',
category: categories.Rate
});
addFuncDef({
name: 'per_hour',
category: categories.Rate
});
addFuncDef({
name: 'dt',
category: categories.Rate
});
addFuncDef({
name: 'diff',
category: categories.Rate
});
addFuncDef({
name: 'derivative',
category: categories.Rate
});
addFuncDef({
name: 'ewma_3',
category: categories.Smoothing
});
addFuncDef({
name: 'ewma_5',
category: categories.Smoothing
});
addFuncDef({
name: 'ewma_10',
category: categories.Smoothing
});
addFuncDef({
name: 'ewma_20',
category: categories.Smoothing
});
addFuncDef({
name: 'median_3',
category: categories.Smoothing
});
addFuncDef({
name: 'median_5',
category: categories.Smoothing
});
addFuncDef({
name: 'median_7',
category: categories.Smoothing
});
addFuncDef({
name: 'median_9',
category: categories.Smoothing
});
addFuncDef({
name: 'rollup',
category: categories.Rollup,
params: [{ name: "rollup", type: 'string', options: ['sum', 'avg', 'min', 'max'] }, { name: "period", type: 'int' }],
defaultParams: ['sum', 600],
append: true
});
addFuncDef({
name: 'count_nonzero',
category: categories.Count
});
addFuncDef({
name: 'count_not_null',
category: categories.Count
});
addFuncDef({
name: 'top',
category: categories.Rank,
params: [{ name: "top", type: 'int', options: [5, 10, 15] }, { name: "aggr", type: 'string', options: ['mean', 'min', 'max', 'area', 'l2norm', 'last'] }, { name: "dir", type: 'string', options: ['asc', 'desc'] }],
defaultParams: [5, 'mean', 'dir']
});
addFuncDef({
name: 'robust_trend',
category: categories.Regression
});
addFuncDef({
name: 'piecewise_constant',
category: categories.Regression
});
addFuncDef({
name: 'anomalies',
category: categories.Algorithms,
params: [{ name: "anomalies", type: 'string', options: ['basic', 'agile', 'robust', 'adaptive'] }, { name: "bonds", type: 'int', options: [1, 2, 3, 4, 5, 6] }],
defaultParams: ['basic', 2]
});
addFuncDef({
name: 'anomalies',
category: categories.Algorithms,
params: [{ name: "anomalies", type: 'string', options: ['basic', 'agile', 'robust', 'adaptive'] }, { name: "bonds", type: 'int', options: [1, 2, 3, 4, 5, 6] }],
defaultParams: ['basic', 2]
});
addFuncDef({
name: 'outliers',
category: categories.Algorithms,
params: [{ name: "outliers", type: 'string', options: ['DBSCAN', 'MAD', 'scaledDBSCAN', 'scaledMAD'] }, { name: "tolerance", type: 'int', options: [0.33, 0.5, 1, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0] }],
defaultParams: ['basic', 2]
});
_lodash2.default.each(categories, function (funcList, catName) {
categories[catName] = _lodash2.default.sortBy(funcList, 'name');
});
function FuncInstance(funcDef, options) {
this.def = funcDef;
this.params = [];
if (options && options.withDefaultParams) {
this.params = funcDef.defaultParams.slice(0);
}
this.updateText();
}
FuncInstance.prototype.render = function (metricExp) {
var str = this.def.name + '(';
var parameters = _lodash2.default.map(this.params, function (value) {
return value;
});
if (metricExp && !this.def.append) {
parameters.unshift(metricExp);
}
var funcRender = str + parameters.join(', ') + ')';
console.log(funcRender, this.def.append);
if (this.def.append) {
return metricExp + '.' + funcRender;
} else {
return funcRender;
}
};
FuncInstance.prototype.updateParam = function (strValue, index) {
// handle optional parameters
// if string contains ',' and next param is optional, split and update both
if (strValue === '' && this.def.params[index].optional) {
this.params.splice(index, 1);
} else {
this.params[index] = strValue;
}
this.updateText();
};
FuncInstance.prototype.updateText = function () {
if (this.params.length === 0) {
this.text = this.def.name + '()';
return;
}
var text = this.def.name + '(';
text += this.params.join(', ');
text += ')';
this.text = text;
};
var createFuncInstance = function createFuncInstance(funcDef, options) {
if (_lodash2.default.isString(funcDef)) {
if (!index[funcDef]) {
throw { message: 'Method not found ' + name };
}
funcDef = index[funcDef];
}
return new FuncInstance(funcDef, options);
};
var getFuncDef = function getFuncDef(name) {
return index[name];
};
var getCategories = function getCategories() {
return categories;
};
var foo;
exports.default = foo = {
createFuncInstance: createFuncInstance,
getFuncDef: getFuncDef,
getCategories: getCategories
};
//# sourceMappingURL=dfunc.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,247 @@
'use strict';
var _angular = require('angular');
var _angular2 = _interopRequireDefault(_angular);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _jquery = require('jquery');
var _jquery2 = _interopRequireDefault(_jquery);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
'use strict';
_angular2.default.module('grafana.directives').directive('datadogFuncEditor', function ($compile, templateSrv) {
var funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
var paramTemplate = '<input type="text" style="display:none"' + ' class="input-mini tight-form-func-param"></input>';
var funcControlsTemplate = '<div class="tight-form-func-controls">' + '<span class="pointer fa fa-arrow-left"></span>' + '<span class="pointer fa fa-question-circle"></span>' + '<span class="pointer fa fa-remove" ></span>' + '<span class="pointer fa fa-arrow-right"></span>' + '</div>';
return {
restrict: 'A',
link: function postLink($scope, elem) {
var $funcLink = (0, _jquery2.default)(funcSpanTemplate);
var $funcControls = (0, _jquery2.default)(funcControlsTemplate);
var ctrl = $scope.ctrl;
var func = $scope.func;
var funcDef = func.def;
var scheduledRelink = false;
var paramCountAtLink = 0;
function clickFuncParam(paramIndex) {
/*jshint validthis:true */
var $link = (0, _jquery2.default)(this);
var $input = $link.next();
$input.val(func.params[paramIndex]);
$input.css('width', $link.width() + 16 + 'px');
$link.hide();
$input.show();
$input.focus();
$input.select();
var typeahead = $input.data('typeahead');
if (typeahead) {
$input.val('');
typeahead.lookup();
}
}
function scheduledRelinkIfNeeded() {
if (paramCountAtLink === func.params.length) {
return;
}
if (!scheduledRelink) {
scheduledRelink = true;
setTimeout(function () {
relink();
scheduledRelink = false;
}, 200);
}
}
function inputBlur(paramIndex) {
/*jshint validthis:true */
var $input = (0, _jquery2.default)(this);
var $link = $input.prev();
var newValue = $input.val();
if (newValue !== '' || func.def.params[paramIndex].optional) {
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
func.updateParam($input.val(), paramIndex);
scheduledRelinkIfNeeded();
$scope.$apply(function () {
ctrl.targetChanged();
});
$input.hide();
$link.show();
}
}
function inputKeyPress(paramIndex, e) {
/*jshint validthis:true */
if (e.which === 13) {
inputBlur.call(this, paramIndex);
}
}
function inputKeyDown() {
/*jshint validthis:true */
this.style.width = (3 + this.value.length) * 8 + 'px';
}
function addTypeahead($input, paramIndex) {
$input.attr('data-provide', 'typeahead');
var options = funcDef.params[paramIndex].options;
if (funcDef.params[paramIndex].type === 'int') {
options = _lodash2.default.map(options, function (val) {
return val.toString();
});
}
$input.typeahead({
source: options,
minLength: 0,
items: 20,
updater: function updater(value) {
setTimeout(function () {
inputBlur.call($input[0], paramIndex);
}, 0);
return value;
}
});
var typeahead = $input.data('typeahead');
typeahead.lookup = function () {
this.query = this.$element.val() || '';
return this.process(this.source);
};
}
function toggleFuncControls() {
var targetDiv = elem.closest('.tight-form');
if (elem.hasClass('show-function-controls')) {
elem.removeClass('show-function-controls');
targetDiv.removeClass('has-open-function');
$funcControls.hide();
return;
}
elem.addClass('show-function-controls');
targetDiv.addClass('has-open-function');
$funcControls.show();
}
function addElementsAndCompile() {
$funcControls.appendTo(elem);
$funcLink.appendTo(elem);
_lodash2.default.each(funcDef.params, function (param, index) {
if (param.optional && func.params.length <= index) {
return;
}
if (index > 0) {
(0, _jquery2.default)('<span>, </span>').appendTo(elem);
}
var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
var $paramLink = (0, _jquery2.default)('<a ng-click="" class="datadog-func-param-link">' + paramValue + '</a>');
var $input = (0, _jquery2.default)(paramTemplate);
paramCountAtLink++;
$paramLink.appendTo(elem);
$input.appendTo(elem);
$input.blur(_lodash2.default.partial(inputBlur, index));
$input.keyup(inputKeyDown);
$input.keypress(_lodash2.default.partial(inputKeyPress, index));
$paramLink.click(_lodash2.default.partial(clickFuncParam, index));
if (funcDef.params[index].options) {
addTypeahead($input, index);
}
});
(0, _jquery2.default)('<span>)</span>').appendTo(elem);
$compile(elem.contents())($scope);
}
function ifJustAddedFocusFistParam() {
if ($scope.func.added) {
$scope.func.added = false;
setTimeout(function () {
elem.find('.datadog-func-param-link').first().click();
}, 10);
}
}
function registerFuncControlsToggle() {
$funcLink.click(toggleFuncControls);
}
function registerFuncControlsActions() {
$funcControls.click(function (e) {
var $target = (0, _jquery2.default)(e.target);
if ($target.hasClass('fa-remove')) {
toggleFuncControls();
$scope.$apply(function () {
ctrl.removeFunction($scope.func);
});
return;
}
if ($target.hasClass('fa-arrow-left')) {
$scope.$apply(function () {
_lodash2.default.move(ctrl.functions, $scope.$index, $scope.$index - 1);
ctrl.targetChanged();
});
return;
}
if ($target.hasClass('fa-arrow-right')) {
$scope.$apply(function () {
_lodash2.default.move(ctrl.functions, $scope.$index, $scope.$index + 1);
ctrl.targetChanged();
});
return;
}
if ($target.hasClass('fa-question-circle')) {
window.open("http://docs.datadoghq.com/graphing/#apply-more-advanced-functions", '_blank');
return;
}
});
}
function relink() {
elem.children().remove();
addElementsAndCompile();
ifJustAddedFocusFistParam();
registerFuncControlsToggle();
registerFuncControlsActions();
}
relink();
}
};
});
//# sourceMappingURL=func_editor.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,37 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.AnnotationsQueryCtrl = exports.QueryOptionsCtrl = exports.ConfigCtrl = exports.QueryCtrl = exports.Datasource = undefined;
var _datasource = require('./datasource');
var _query_ctrl = require('./query_ctrl');
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DataDogConfigCtrl = function DataDogConfigCtrl() {
_classCallCheck(this, DataDogConfigCtrl);
};
DataDogConfigCtrl.templateUrl = "partials/config.html";
var DataDogQueryOptionsCtrl = function DataDogQueryOptionsCtrl() {
_classCallCheck(this, DataDogQueryOptionsCtrl);
};
DataDogQueryOptionsCtrl.templateUrl = "partials/query.options.html";
var DataDogAnnotationsQueryCtrl = function DataDogAnnotationsQueryCtrl() {
_classCallCheck(this, DataDogAnnotationsQueryCtrl);
};
DataDogAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';
exports.Datasource = _datasource.DataDogDatasource;
exports.QueryCtrl = _query_ctrl.DataDogQueryCtrl;
exports.ConfigCtrl = DataDogConfigCtrl;
exports.QueryOptionsCtrl = DataDogQueryOptionsCtrl;
exports.AnnotationsQueryCtrl = DataDogAnnotationsQueryCtrl;
//# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../../src/module.js"],"names":["DataDogConfigCtrl","templateUrl","DataDogQueryOptionsCtrl","DataDogAnnotationsQueryCtrl","Datasource","QueryCtrl","ConfigCtrl","QueryOptionsCtrl","AnnotationsQueryCtrl"],"mappings":";;;;;;;AAAA;;AACA;;;;IAEMA,iB;;;;AACNA,kBAAkBC,WAAlB,GAAgC,sBAAhC;;IAEMC,uB;;;;AACNA,wBAAwBD,WAAxB,GAAsC,6BAAtC;;IAEME,2B;;;;AACNA,4BAA4BF,WAA5B,GAA0C,kCAA1C;;QAGuBG,U;QACDC,S;QACCC,U,GAArBN,iB;QAC2BO,gB,GAA3BL,uB;QAC+BM,oB,GAA/BL,2B","file":"module.js","sourcesContent":["import {DataDogDatasource} from './datasource';\nimport {DataDogQueryCtrl} from './query_ctrl';\n\nclass DataDogConfigCtrl {}\nDataDogConfigCtrl.templateUrl = \"partials/config.html\";\n\nclass DataDogQueryOptionsCtrl {}\nDataDogQueryOptionsCtrl.templateUrl = \"partials/query.options.html\";\n\nclass DataDogAnnotationsQueryCtrl {}\nDataDogAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';\n\nexport {\n DataDogDatasource as Datasource,\n DataDogQueryCtrl as QueryCtrl,\n DataDogConfigCtrl as ConfigCtrl,\n DataDogQueryOptionsCtrl as QueryOptionsCtrl,\n DataDogAnnotationsQueryCtrl as AnnotationsQueryCtrl\n};\n"]}
@@ -0,0 +1,84 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.buildQuery = buildQuery;
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _dfunc = require('./dfunc');
var _dfunc2 = _interopRequireDefault(_dfunc);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function buildQuery(target, adhocFilters) {
var aggregation = target.aggregation,
metric = target.metric,
tags = target.tags,
functions = target.functions;
var query = aggregation + ':' + metric;
var functionInstances = getFunctionInstances(functions);
if (tags && tags.length || adhocFilters && adhocFilters.length) {
query += '{';
if (tags && tags.length) {
query += tags.join(',');
}
if (adhocFilters && adhocFilters.length) {
var adhocTags = buildAdHocFilterString(adhocFilters);
if (tags && tags.length) {
query += ',';
}
query += adhocTags;
}
query += '}';
} else {
query += '{*}';
}
if (target.as) {
query += '.' + target.as + '()';
}
var groupedFuncs = _lodash2.default.groupBy(functionInstances, function (func) {
if (func.def.append) {
return 'appends';
} else {
return 'wraps';
}
});
_lodash2.default.each(groupedFuncs.appends, function (func) {
query += '.' + func.render();
});
_lodash2.default.each(groupedFuncs.wraps, function (func) {
query = func.render(query);
});
return query;
}
function getFunctionInstances(functions) {
return _lodash2.default.map(functions, function (func) {
var f = _dfunc2.default.createFuncInstance(func.funcDef, { withDefaultParams: false });
f.params = func.params.slice();
return f;
});
}
function buildAdHocFilterString(adhocFilters) {
return adhocFilters.map(function (filter) {
return filter.key + ':' + filter.value;
}).join(',');
}
//# sourceMappingURL=query_builder.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../../src/query_builder.js"],"names":["buildQuery","target","adhocFilters","aggregation","metric","tags","functions","query","functionInstances","getFunctionInstances","length","join","adhocTags","buildAdHocFilterString","as","groupedFuncs","groupBy","func","def","append","each","appends","render","wraps","map","f","createFuncInstance","funcDef","withDefaultParams","params","slice","filter","key","value"],"mappings":";;;;;QAGgBA,U,GAAAA,U;;AAHhB;;;;AACA;;;;;;AAEO,SAASA,UAAT,CAAoBC,MAApB,EAA4BC,YAA5B,EAA0C;AAAA,MAC1CC,WAD0C,GACFF,MADE,CAC1CE,WAD0C;AAAA,MAC7BC,MAD6B,GACFH,MADE,CAC7BG,MAD6B;AAAA,MACrBC,IADqB,GACFJ,MADE,CACrBI,IADqB;AAAA,MACfC,SADe,GACFL,MADE,CACfK,SADe;;AAE/C,MAAIC,QAAWJ,WAAX,SAA0BC,MAA9B;;AAEA,MAAII,oBAAqBC,qBAAqBH,SAArB,CAAzB;;AAEA,MAAKD,QAAQA,KAAKK,MAAd,IAA0BR,gBAAgBA,aAAaQ,MAA3D,EAAoE;AAClEH,aAAS,GAAT;;AAEA,QAAIF,QAAQA,KAAKK,MAAjB,EAAyB;AACvBH,eAASF,KAAKM,IAAL,CAAU,GAAV,CAAT;AACD;;AAED,QAAIT,gBAAgBA,aAAaQ,MAAjC,EAAyC;AACvC,UAAIE,YAAYC,uBAAuBX,YAAvB,CAAhB;AACA,UAAIG,QAAQA,KAAKK,MAAjB,EAAyB;AACvBH,iBAAS,GAAT;AACD;AACDA,eAASK,SAAT;AACD;;AAEDL,aAAS,GAAT;AACD,GAhBD,MAgBO;AACLA,aAAS,KAAT;AACD;;AAED,MAAIN,OAAOa,EAAX,EAAe;AACbP,aAAS,MAAMN,OAAOa,EAAb,GAAkB,IAA3B;AACD;;AAED,MAAIC,eAAe,iBAAEC,OAAF,CAAUR,iBAAV,EAA6B,gBAAQ;AACtD,QAAIS,KAAKC,GAAL,CAASC,MAAb,EAAqB;AACnB,aAAO,SAAP;AACD,KAFD,MAEO;AACL,aAAO,OAAP;AACD;AACF,GANkB,CAAnB;;AAQA,mBAAEC,IAAF,CAAOL,aAAaM,OAApB,EAA6B,gBAAQ;AACnCd,aAAS,MAAMU,KAAKK,MAAL,EAAf;AACD,GAFD;;AAIA,mBAAEF,IAAF,CAAOL,aAAaQ,KAApB,EAA2B,gBAAQ;AACjChB,YAAQU,KAAKK,MAAL,CAAYf,KAAZ,CAAR;AACD,GAFD;;AAIA,SAAOA,KAAP;AACD;;AAED,SAASE,oBAAT,CAA8BH,SAA9B,EAAyC;AACvC,SAAO,iBAAEkB,GAAF,CAAMlB,SAAN,EAAiB,gBAAQ;AAC9B,QAAImB,IAAI,gBAAMC,kBAAN,CAAyBT,KAAKU,OAA9B,EAAuC,EAACC,mBAAmB,KAApB,EAAvC,CAAR;AACAH,MAAEI,MAAF,GAAWZ,KAAKY,MAAL,CAAYC,KAAZ,EAAX;AACA,WAAOL,CAAP;AACD,GAJM,CAAP;AAKD;;AAED,SAASZ,sBAAT,CAAgCX,YAAhC,EAA8C;AAC5C,SAAOA,aAAasB,GAAb,CAAiB,kBAAU;AAChC,WAAOO,OAAOC,GAAP,GAAa,GAAb,GAAmBD,OAAOE,KAAjC;AACD,GAFM,EAEJtB,IAFI,CAEC,GAFD,CAAP;AAGD","file":"query_builder.js","sourcesContent":["import _ from 'lodash';\nimport dfunc from './dfunc';\n\nexport function buildQuery(target, adhocFilters) {\n let {aggregation, metric, tags, functions} = target;\n let query = `${aggregation}:${metric}`;\n\n let functionInstances = getFunctionInstances(functions);\n\n if ((tags && tags.length) || (adhocFilters && adhocFilters.length)) {\n query += '{';\n\n if (tags && tags.length) {\n query += tags.join(',');\n }\n\n if (adhocFilters && adhocFilters.length) {\n let adhocTags = buildAdHocFilterString(adhocFilters);\n if (tags && tags.length) {\n query += ',';\n }\n query += adhocTags;\n }\n\n query += '}';\n } else {\n query += '{*}';\n }\n\n if (target.as) {\n query += '.' + target.as + '()';\n }\n\n var groupedFuncs = _.groupBy(functionInstances, func => {\n if (func.def.append) {\n return 'appends';\n } else {\n return 'wraps';\n }\n });\n\n _.each(groupedFuncs.appends, func => {\n query += '.' + func.render();\n });\n\n _.each(groupedFuncs.wraps, func => {\n query = func.render(query);\n });\n\n return query;\n}\n\nfunction getFunctionInstances(functions) {\n return _.map(functions, func => {\n var f = dfunc.createFuncInstance(func.funcDef, {withDefaultParams: false});\n f.params = func.params.slice();\n return f;\n });\n}\n\nfunction buildAdHocFilterString(adhocFilters) {\n return adhocFilters.map(filter => {\n return filter.key + ':' + filter.value;\n }).join(',');\n}\n"]}
@@ -0,0 +1,230 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DataDogQueryCtrl = undefined;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _dfunc = require('./dfunc');
var _dfunc2 = _interopRequireDefault(_dfunc);
var _sdk = require('app/plugins/sdk');
require('./func_editor');
require('./add_datadog_func');
var _query_builder = require('./query_builder');
var queryBuilder = _interopRequireWildcard(_query_builder);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var DataDogQueryCtrl = exports.DataDogQueryCtrl = function (_QueryCtrl) {
_inherits(DataDogQueryCtrl, _QueryCtrl);
function DataDogQueryCtrl($scope, $injector, uiSegmentSrv, templateSrv) {
_classCallCheck(this, DataDogQueryCtrl);
var _this = _possibleConstructorReturn(this, (DataDogQueryCtrl.__proto__ || Object.getPrototypeOf(DataDogQueryCtrl)).call(this, $scope, $injector));
_this.removeText = '-- remove tag --';
_this.uiSegmentSrv = uiSegmentSrv;
_this.templateSrv = templateSrv;
if (_this.target.aggregation) {
_this.aggregationSegment = new uiSegmentSrv.newSegment(_this.target.aggregation);
} else {
_this.aggregationSegment = new uiSegmentSrv.newSegment({
value: 'Select Aggregation',
fake: true,
custom: false
});
}
if (_this.target.metric) {
_this.metricSegment = new uiSegmentSrv.newSegment(_this.target.metric);
} else {
_this.metricSegment = new uiSegmentSrv.newSegment({
value: 'Select Metric',
fake: true,
custom: false
});
}
_this.target.tags = _this.target.tags || [];
_this.tagSegments = _this.target.tags.map(uiSegmentSrv.newSegment);
_this.fixTagSegments();
_this.functions = [];
_this.target.functions = _this.target.functions || [];
_this.functions = _lodash2.default.map(_this.target.functions, function (func) {
var f = _dfunc2.default.createFuncInstance(func.funcDef, { withDefaultParams: false });
f.params = func.params.slice();
return f;
});
if (_this.target.as) {
_this.asSegment = uiSegmentSrv.newSegment(_this.target.as);
} else {
_this.asSegment = uiSegmentSrv.newSegment({
value: 'Select As',
fake: true,
custom: false
});
}
return _this;
}
_createClass(DataDogQueryCtrl, [{
key: 'toggleEditorMode',
value: function toggleEditorMode() {
this.target.rawQuery = !this.target.rawQuery;
}
}, {
key: 'getMetrics',
value: function getMetrics() {
return this.datasource.metricFindQuery().then(this.uiSegmentSrv.transformToSegments(true));
}
}, {
key: 'getAggregations',
value: function getAggregations() {
return Promise.resolve([{ text: 'avg by', value: 'avg' }, { text: 'max by', value: 'max' }, { text: 'min by', value: 'min' }, { text: 'sub by', value: 'sum' }]);
}
}, {
key: 'getAs',
value: function getAs() {
return Promise.resolve([{ text: 'None', value: 'None' }, { text: 'as_count', value: 'as_count' }, { text: 'as_rate', value: 'as_rate' }]);
}
}, {
key: 'getTags',
value: function getTags(segment) {
var _this2 = this;
return this.datasource.tagFindQuery().then(this.uiSegmentSrv.transformToSegments(true)).then(function (results) {
if (segment.type !== 'plus-button') {
var removeSegment = _this2.uiSegmentSrv.newFake(_this2.removeText);
results.unshift(removeSegment);
}
return results;
});
}
}, {
key: 'aggregationChanged',
value: function aggregationChanged() {
this.target.aggregation = this.aggregationSegment.value;
this.panelCtrl.refresh();
}
}, {
key: 'metricChanged',
value: function metricChanged() {
this.target.metric = this.metricSegment.value;
this.panelCtrl.refresh();
}
}, {
key: 'asChanged',
value: function asChanged() {
if (this.asSegment.value === 'None') {
this.target.as = null;
} else {
this.target.as = this.asSegment.value;
}
this.panelCtrl.refresh();
}
}, {
key: 'fixTagSegments',
value: function fixTagSegments() {
var count = this.tagSegments.length;
var lastSegment = this.tagSegments[Math.max(count - 1, 0)];
if (!lastSegment || lastSegment.type !== 'plus-button') {
this.tagSegments.push(this.uiSegmentSrv.newPlusButton());
}
}
}, {
key: 'targetChanged',
value: function targetChanged() {
if (this.error) {
return;
}
this.panelCtrl.refresh();
}
}, {
key: 'persistFunctions',
value: function persistFunctions() {
this.target.functions = _lodash2.default.map(this.functions, function (func) {
return {
funcDef: func.def.name,
params: func.params.slice()
};
});
}
}, {
key: 'removeFunction',
value: function removeFunction(func) {
this.functions = _lodash2.default.without(this.functions, func);
this.persistFunctions();
this.targetChanged();
}
}, {
key: 'addFunction',
value: function addFunction(funcDef) {
var func = _dfunc2.default.createFuncInstance(funcDef, { withDefaultParams: true });
func.added = true;
this.functions.push(func);
this.persistFunctions();
this.targetChanged();
}
}, {
key: 'tagSegmentUpdated',
value: function tagSegmentUpdated(segment, index) {
if (segment.value === this.removeText) {
this.tagSegments.splice(index, 1);
}
var realSegments = _lodash2.default.filter(this.tagSegments, function (segment) {
return segment.value;
});
this.target.tags = realSegments.map(function (segment) {
return segment.value;
});
this.tagSegments = _lodash2.default.map(this.target.tags, this.uiSegmentSrv.newSegment);
this.fixTagSegments();
this.panelCtrl.refresh();
}
}, {
key: 'getCollapsedText',
value: function getCollapsedText() {
if (this.target.rawQuery) {
return this.target.query;
} else {
return queryBuilder.buildQuery(this.target);
}
}
}]);
return DataDogQueryCtrl;
}(_sdk.QueryCtrl);
DataDogQueryCtrl.templateUrl = 'partials/query.editor.html';
//# sourceMappingURL=query_ctrl.js.map
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,79 @@
"use strict";
var _module = require("../module");
var _q = require("q");
var _q2 = _interopRequireDefault(_q);
var _sinon = require("sinon");
var _sinon2 = _interopRequireDefault(_sinon);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
describe('DataDogDatasource', function () {
var ctx = {};
beforeEach(function () {
ctx.$q = _q2.default;
ctx.backendSrv = {
datasourceRequest: function datasourceRequest() {
return ctx.$q.when({
status: 200
});
}
};
ctx.templateSrv = {
replace: function replace(str) {
return str;
},
getAdhocFilters: function getAdhocFilters() {
return [];
}
};
var instanceSettings = {
url: 'https://app.datadoghq.com/api/v1',
jsonData: {
api_key: '0000deadbeaf0000',
app_key: '0000abcd0000abcd'
}
};
ctx.ds = new _module.Datasource(instanceSettings, ctx.backendSrv, ctx.templateSrv);
});
describe('When doing DataDog API request', function () {
beforeEach(function () {
var targets = [{ query: 'avg:system.load.5{*}', rawQuery: true }];
ctx.options = {
range: {
from: 12340000,
to: 12340000
},
targets: targets
};
});
it('should send request with proper params', function (done) {
var expected_params = {
method: 'GET',
url: 'https://app.datadoghq.com/api/v1/query',
params: {
api_key: '0000deadbeaf0000',
application_key: '0000abcd0000abcd',
from: 12340,
to: 12340,
query: 'avg:system.load.5{*}'
}
};
var datasourceRequest = _sinon2.default.spy(ctx.ds.backendSrv, 'datasourceRequest');
ctx.ds.query(ctx.options);
expect(datasourceRequest).to.have.been.calledWith(expected_params);
done();
});
});
});
//# sourceMappingURL=datasource_spec.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../../../spec/datasource_spec.js"],"names":["describe","ctx","beforeEach","$q","backendSrv","datasourceRequest","when","status","templateSrv","replace","str","getAdhocFilters","instanceSettings","url","jsonData","api_key","app_key","ds","targets","query","rawQuery","options","range","from","to","it","done","expected_params","method","params","application_key","spy","expect","have","been","calledWith"],"mappings":";;AAAA;;AACA;;;;AACA;;;;;;AAEAA,SAAS,mBAAT,EAA8B,YAAM;AAClC,MAAIC,MAAM,EAAV;;AAEAC,aAAW,YAAW;AACpBD,QAAIE,EAAJ;AACAF,QAAIG,UAAJ,GAAiB;AACfC,yBAAmB,6BAAM;AACvB,eAAOJ,IAAIE,EAAJ,CAAOG,IAAP,CAAY;AACjBC,kBAAQ;AADS,SAAZ,CAAP;AAGD;AALc,KAAjB;AAOAN,QAAIO,WAAJ,GAAkB;AAChBC,eAAS,iBAACC,GAAD;AAAA,eAASA,GAAT;AAAA,OADO;AAEhBC,uBAAiB;AAAA,eAAM,EAAN;AAAA;AAFD,KAAlB;;AAKA,QAAIC,mBAAmB;AACrBC,WAAK,kCADgB;AAErBC,gBAAU;AACRC,iBAAS,kBADD;AAERC,iBAAS;AAFD;AAFW,KAAvB;AAOAf,QAAIgB,EAAJ,GAAS,uBAAeL,gBAAf,EAAiCX,IAAIG,UAArC,EAAiDH,IAAIO,WAArD,CAAT;AACD,GAtBD;;AAwBAR,WAAS,gCAAT,EAA2C,YAAM;AAC/CE,eAAW,YAAW;AACpB,UAAIgB,UAAU,CACZ,EAAEC,OAAO,sBAAT,EAAiCC,UAAU,IAA3C,EADY,CAAd;;AAIAnB,UAAIoB,OAAJ,GAAc;AACZC,eAAO;AACLC,gBAAM,QADD;AAELC,cAAI;AAFC,SADK;AAKZN,iBAASA;AALG,OAAd;AAOD,KAZD;;AAcAO,OAAG,wCAAH,EAA6C,UAACC,IAAD,EAAU;AACrD,UAAIC,kBAAkB;AACpBC,gBAAQ,KADY;AAEpBf,aAAK,wCAFe;AAGpBgB,gBAAQ;AACNd,mBAAS,kBADH;AAENe,2BAAiB,kBAFX;AAGNP,gBAAM,KAHA;AAINC,cAAI,KAJE;AAKNL,iBAAO;AALD;AAHY,OAAtB;AAWA,UAAId,oBAAoB,gBAAM0B,GAAN,CAAU9B,IAAIgB,EAAJ,CAAOb,UAAjB,EAA6B,mBAA7B,CAAxB;;AAEAH,UAAIgB,EAAJ,CAAOE,KAAP,CAAalB,IAAIoB,OAAjB;AACAW,aAAO3B,iBAAP,EAA0BmB,EAA1B,CAA6BS,IAA7B,CAAkCC,IAAlC,CAAuCC,UAAvC,CAAkDR,eAAlD;AACAD;AACD,KAjBD;AAkBD,GAjCD;AAkCD,CA7DD","file":"datasource_spec.js","sourcesContent":["import {Datasource} from \"../module\";\nimport Q from \"q\";\nimport sinon from 'sinon';\n\ndescribe('DataDogDatasource', () => {\n let ctx = {};\n\n beforeEach(function() {\n ctx.$q = Q;\n ctx.backendSrv = {\n datasourceRequest: () => {\n return ctx.$q.when({\n status: 200\n });\n }\n };\n ctx.templateSrv = {\n replace: (str) => str,\n getAdhocFilters: () => []\n };\n\n let instanceSettings = {\n url: 'https://app.datadoghq.com/api/v1',\n jsonData: {\n api_key: '0000deadbeaf0000',\n app_key: '0000abcd0000abcd'\n }\n };\n ctx.ds = new Datasource(instanceSettings, ctx.backendSrv, ctx.templateSrv);\n });\n\n describe('When doing DataDog API request', () => {\n beforeEach(function() {\n let targets = [\n { query: 'avg:system.load.5{*}', rawQuery: true }\n ];\n\n ctx.options = {\n range: {\n from: 12340000,\n to: 12340000\n },\n targets: targets\n };\n });\n\n it('should send request with proper params', (done) => {\n let expected_params = {\n method: 'GET',\n url: 'https://app.datadoghq.com/api/v1/query',\n params: {\n api_key: '0000deadbeaf0000',\n application_key: '0000abcd0000abcd',\n from: 12340,\n to: 12340,\n query: 'avg:system.load.5{*}'\n }\n };\n let datasourceRequest = sinon.spy(ctx.ds.backendSrv, 'datasourceRequest');\n\n ctx.ds.query(ctx.options);\n expect(datasourceRequest).to.have.been.calledWith(expected_params);\n done();\n });\n });\n});\n"]}
@@ -0,0 +1,84 @@
'use strict';
var _dfunc = require('../dfunc');
var _dfunc2 = _interopRequireDefault(_dfunc);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
describe('when creating func instance from func names', function () {
it('should return func instance', function () {
var func = _dfunc2.default.createFuncInstance('top');
expect(func).to.be.ok;
expect(func.def.name).to.equal('top');
expect(func.def.params.length).to.equal(3);
expect(func.def.defaultParams.length).to.equal(3);
});
it('should return func instance from funcDef', function () {
var func = _dfunc2.default.createFuncInstance('top');
var func2 = _dfunc2.default.createFuncInstance(func.def);
expect(func2).to.be.ok;
});
it('func instance should have text representation', function () {
var func = _dfunc2.default.createFuncInstance('top');
func.params[0] = 5;
func.params[1] = 'mean';
func.params[2] = 'dir';
func.updateText();
expect(func.text).to.equal("top(5, mean, dir)");
});
});
describe('when rendering func instance', function () {
it('should handle single metric param', function () {
var func = _dfunc2.default.createFuncInstance('abs');
expect(func.render('a')).to.equal("abs(a)");
});
it('should include default params if options enable it', function () {
var func = _dfunc2.default.createFuncInstance('top', { withDefaultParams: true });
expect(func.render('a')).to.equal("top(a, 5, mean, dir)");
});
it('should handle int or interval params with number', function () {
var func = _dfunc2.default.createFuncInstance('anomalies');
func.params[0] = 'basic';
func.params[1] = '5';
expect(func.render('hello')).to.equal("anomalies(hello, basic, 5)");
});
});
describe('when requesting function categories', function () {
it('should return function categories', function () {
var catIndex = _dfunc2.default.getCategories();
expect(catIndex.Arithmatic.length).to.be.greaterThan(3);
});
});
describe('when updating func param', function () {
it('should update param value and update text representation', function () {
var func = _dfunc2.default.createFuncInstance('top', { withDefaultParams: true });
func.updateParam('10', 0);
expect(func.params[0]).to.be.equal('10');
func.updateText();
expect(func.text).to.be.equal('top(10, mean, dir)');
});
it('should parse numbers as float', function () {
var func = _dfunc2.default.createFuncInstance('outliers');
func.updateParam('0.5', 1);
expect(func.params[1]).to.be.equal('0.5');
});
});
describe('when updating func param with optional second parameter', function () {
it('should update value and text', function () {
var func = _dfunc2.default.createFuncInstance('fill');
func.updateParam('null', 0);
expect(func.params[0]).to.be.equal('null');
});
});
//# sourceMappingURL=dfunc_spec.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,194 @@
'use strict';
var _module = require('../module');
require('app/core/services/segment_srv');
var _sinon = require('sinon');
var _sinon2 = _interopRequireDefault(_sinon);
var _dfunc = require('../dfunc');
var _dfunc2 = _interopRequireDefault(_dfunc);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
describe('DataDogQueryCtrl', function () {
var ctx = {};
beforeEach(angularMocks.module('grafana.core'));
beforeEach(angularMocks.module('grafana.controllers'));
beforeEach(angularMocks.module('grafana.services'));
beforeEach(ctx.providePhase());
beforeEach(angularMocks.inject(function ($rootScope, $controller, $q) {
ctx.$q = $q;
ctx.scope = $rootScope.$new();
ctx.target = {};
ctx.datasource.metricFindQuery = _sinon2.default.stub().returns(ctx.$q.when([]));
ctx.panelCtrl = { panel: {} };
ctx.panelCtrl.refresh = _sinon2.default.spy();
ctx.ctrl = $controller(_module.QueryCtrl, { $scope: ctx.scope }, {
panelCtrl: ctx.panelCtrl,
datasource: ctx.datasource,
target: ctx.target
});
ctx.scope.$digest();
}));
describe('init', function () {
it('should validate metric key exists', function () {
expect(ctx.datasource.metricFindQuery.getCall(0).args[0]).to.be('test.prod.*');
});
it('should delete last segment if no metrics are found', function () {
expect(ctx.ctrl.segments[2].value).to.be('select metric');
});
it('should parse expression and build function model', function () {
expect(ctx.ctrl.functions.length).to.be(2);
});
});
describe('when adding function', function () {
beforeEach(function () {
ctx.ctrl.target.target = 'test.prod.*.count';
ctx.ctrl.datasource.metricFindQuery = _sinon2.default.stub().returns(ctx.$q.when([{ expandable: false }]));
ctx.ctrl.parseTarget();
ctx.ctrl.addFunction(gfunc.getFuncDef('aliasByNode'));
});
it('should add function with correct node number', function () {
expect(ctx.ctrl.functions[0].params[0]).to.be(2);
});
it('should update target', function () {
expect(ctx.ctrl.target.target).to.be('aliasByNode(test.prod.*.count, 2)');
});
it('should call refresh', function () {
expect(ctx.panelCtrl.refresh.called).to.be(true);
});
});
describe('when adding function before any metric segment', function () {
beforeEach(function () {
ctx.ctrl.target.target = '';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([{ expandable: true }]));
ctx.ctrl.parseTarget();
ctx.ctrl.addFunction(gfunc.getFuncDef('asPercent'));
});
it('should add function and remove select metric link', function () {
expect(ctx.ctrl.segments.length).to.be(0);
});
});
describe('when initalizing target without metric expression and only function', function () {
beforeEach(function () {
ctx.ctrl.target.target = 'asPercent(#A, #B)';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.ctrl.parseTarget();
ctx.scope.$digest();
});
it('should not add select metric segment', function () {
expect(ctx.ctrl.segments.length).to.be(0);
});
it('should add both series refs as params', function () {
expect(ctx.ctrl.functions[0].params.length).to.be(2);
});
});
describe('when initializing a target with single param func using variable', function () {
beforeEach(function () {
ctx.ctrl.target.target = 'movingAverage(prod.count, $var)';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.ctrl.parseTarget();
});
it('should add 2 segments', function () {
expect(ctx.ctrl.segments.length).to.be(2);
});
it('should add function param', function () {
expect(ctx.ctrl.functions[0].params.length).to.be(1);
});
});
describe('when initalizing target without metric expression and function with series-ref', function () {
beforeEach(function () {
ctx.ctrl.target.target = 'asPercent(metric.node.count, #A)';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.ctrl.parseTarget();
});
it('should add segments', function () {
expect(ctx.ctrl.segments.length).to.be(3);
});
it('should have correct func params', function () {
expect(ctx.ctrl.functions[0].params.length).to.be(1);
});
});
describe('when getting altSegments and metricFindQuery retuns empty array', function () {
beforeEach(function () {
ctx.ctrl.target.target = 'test.count';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.ctrl.parseTarget();
ctx.ctrl.getAltSegments(1).then(function (results) {
ctx.altSegments = results;
});
ctx.scope.$digest();
});
it('should have no segments', function () {
expect(ctx.altSegments.length).to.be(0);
});
});
describe('targetChanged', function () {
beforeEach(function () {
ctx.ctrl.datasource.metricFindQuery = _sinon2.default.stub().returns(ctx.$q.when([{ expandable: false }]));
ctx.ctrl.parseTarget();
ctx.ctrl.target.target = '';
ctx.ctrl.targetChanged();
});
it('should rebuld target after expression model', function () {
expect(ctx.ctrl.target.target).to.be('aliasByNode(scaleToSeconds(test.prod.*, 1), 2)');
});
it('should call panelCtrl.refresh', function () {
expect(ctx.panelCtrl.refresh.called).to.be(true);
});
});
describe('when updating targets with nested query', function () {
beforeEach(function () {
ctx.ctrl.target.target = 'scaleToSeconds(#A)';
ctx.ctrl.datasource.metricFindQuery = _sinon2.default.stub().returns(ctx.$q.when([{ expandable: false }]));
ctx.ctrl.parseTarget();
ctx.ctrl.panelCtrl.panel.targets = [{
target: 'nested.query.count',
refId: 'A'
}];
ctx.ctrl.updateModelTarget();
});
it('target should remain the same', function () {
expect(ctx.ctrl.target.target).to.be('scaleToSeconds(#A)');
});
it('targetFull should include nexted queries', function () {
expect(ctx.ctrl.target.targetFull).to.be('scaleToSeconds(nested.query.count)');
});
});
});
//# sourceMappingURL=query_ctrl_soon.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,64 @@
'use strict';
var _prunk = require('prunk');
var _prunk2 = _interopRequireDefault(_prunk);
var _jsdom = require('jsdom');
var _chai = require('chai');
var _chai2 = _interopRequireDefault(_chai);
var _sinonChai = require('sinon-chai');
var _sinonChai2 = _interopRequireDefault(_sinonChai);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// Mock angular module
// JSHint options
/* globals global: false */
var angularMocks = {
module: function module() {
return {
directive: function directive() {}
};
}
};
var datemathMock = {
parse: function parse() {}
};
var momentMock = {
duration: function duration(num, str) {
return 60;
}
};
// Mock Grafana modules that are not available outside of the core project
// Required for loading module.js
_prunk2.default.mock('./css/query-editor.css!', 'no css, dude.');
_prunk2.default.mock('app/plugins/sdk', {
QueryCtrl: null
});
_prunk2.default.mock('app/core/utils/datemath', datemathMock);
_prunk2.default.mock('moment', momentMock);
_prunk2.default.mock('angular', angularMocks);
_prunk2.default.mock('jquery', 'module not found');
// Setup jsdom
// Required for loading angularjs
global.document = (0, _jsdom.jsdom)('<html><head><script></script></head><body></body></html>');
global.window = global.document.parentWindow;
global.navigator = window.navigator = {};
global.Node = window.Node;
// Setup Chai
_chai2.default.should();
_chai2.default.use(_sinonChai2.default);
global.assert = _chai2.default.assert;
global.expect = _chai2.default.expect;
//# sourceMappingURL=test-main.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../../../spec/test-main.js"],"names":["angularMocks","module","directive","datemathMock","parse","momentMock","duration","num","str","mock","QueryCtrl","global","document","window","parentWindow","navigator","Node","should","use","assert","expect"],"mappings":";;AAGA;;;;AACA;;AACA;;;;AACA;;;;;;AAEA;AARA;AACA;;AAQA,IAAIA,eAAe;AACjBC,UAAQ,kBAAW;AACjB,WAAO;AACLC,iBAAW,qBAAW,CAAE;AADnB,KAAP;AAGD;AALgB,CAAnB;;AAQA,IAAIC,eAAe;AACjBC,SAAO,iBAAW,CAAE;AADH,CAAnB;;AAIA,IAAIC,aAAa;AACfC,YAAU,kBAASC,GAAT,EAAcC,GAAd,EAAmB;AAC3B,WAAO,EAAP;AACD;AAHc,CAAjB;;AAMA;AACA;AACA,gBAAMC,IAAN,CAAW,yBAAX,EAAsC,eAAtC;AACA,gBAAMA,IAAN,CAAW,iBAAX,EAA8B;AAC5BC,aAAW;AADiB,CAA9B;AAGA,gBAAMD,IAAN,CAAW,yBAAX,EAAsCN,YAAtC;AACA,gBAAMM,IAAN,CAAW,QAAX,EAAqBJ,UAArB;AACA,gBAAMI,IAAN,CAAW,SAAX,EAAsBT,YAAtB;AACA,gBAAMS,IAAN,CAAW,QAAX,EAAqB,kBAArB;;AAEA;AACA;AACAE,OAAOC,QAAP,GAAkB,kBAAM,0DAAN,CAAlB;AACAD,OAAOE,MAAP,GAAgBF,OAAOC,QAAP,CAAgBE,YAAhC;AACAH,OAAOI,SAAP,GAAmBF,OAAOE,SAAP,GAAmB,EAAtC;AACAJ,OAAOK,IAAP,GAAcH,OAAOG,IAArB;;AAEA;AACA,eAAKC,MAAL;AACA,eAAKC,GAAL;AACAP,OAAOQ,MAAP,GAAgB,eAAKA,MAArB;AACAR,OAAOS,MAAP,GAAgB,eAAKA,MAArB","file":"test-main.js","sourcesContent":["// JSHint options\n/* globals global: false */\n\nimport prunk from 'prunk';\nimport {jsdom} from 'jsdom';\nimport chai from 'chai';\nimport sinonChai from 'sinon-chai';\n\n// Mock angular module\nvar angularMocks = {\n module: function() {\n return {\n directive: function() {}\n };\n }\n};\n\nvar datemathMock = {\n parse: function() {}\n};\n\nvar momentMock = {\n duration: function(num, str) {\n return 60;\n }\n};\n\n// Mock Grafana modules that are not available outside of the core project\n// Required for loading module.js\nprunk.mock('./css/query-editor.css!', 'no css, dude.');\nprunk.mock('app/plugins/sdk', {\n QueryCtrl: null\n});\nprunk.mock('app/core/utils/datemath', datemathMock);\nprunk.mock('moment', momentMock);\nprunk.mock('angular', angularMocks);\nprunk.mock('jquery', 'module not found');\n\n// Setup jsdom\n// Required for loading angularjs\nglobal.document = jsdom('<html><head><script></script></head><body></body></html>');\nglobal.window = global.document.parentWindow;\nglobal.navigator = window.navigator = {};\nglobal.Node = window.Node;\n\n// Setup Chai\nchai.should();\nchai.use(sinonChai);\nglobal.assert = chai.assert;\nglobal.expect = chai.expect;\n"]}
@@ -0,0 +1,7 @@
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- ./dist:/var/lib/grafana/plugins/datadog-datasource
@@ -0,0 +1,49 @@
{
"name": "grafana-datadog-datasource",
"private": true,
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/voidfiles/datadog-datasource.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/voidfiles/grafana-datasource-datagog/issues"
},
"devDependencies": {
"babel": "~6.5.1",
"babel-plugin-transform-es2015-for-of": "^6.6.0",
"babel-plugin-transform-es2015-modules-systemjs": "^6.5.0",
"babel-preset-es2015": "^6.5.0",
"chai": "~3.5.0",
"grunt": "~0.4.5",
"grunt-babel": "~6.0.0",
"grunt-contrib-clean": "~0.6.0",
"grunt-contrib-copy": "~0.8.2",
"grunt-contrib-jshint": "^1.0.0",
"grunt-contrib-uglify": "~0.11.0",
"grunt-contrib-watch": "^0.6.1",
"grunt-execute": "~0.2.2",
"grunt-jscs": "^2.8.0",
"grunt-mocha-test": "~0.12.7",
"grunt-systemjs-builder": "^0.2.5",
"jsdom": "~3.1.2",
"jshint-stylish": "^2.1.0",
"load-grunt-tasks": "~3.2.0",
"mocha": "^2.4.5",
"prunk": "~1.2.1",
"q": "~1.4.1",
"sinon": "~1.16.1",
"sinon-chai": "~2.8.0"
},
"dependencies": {
"lodash": "~4.0.0"
},
"homepage": "https://github.com/voidfiles/grafana-datasource-datagog/issues"
}
@@ -0,0 +1,66 @@
import {Datasource} from "../module";
import Q from "q";
import sinon from 'sinon';
describe('DataDogDatasource', () => {
let ctx = {};
beforeEach(function() {
ctx.$q = Q;
ctx.backendSrv = {
datasourceRequest: () => {
return ctx.$q.when({
status: 200
});
}
};
ctx.templateSrv = {
replace: (str) => str,
getAdhocFilters: () => []
};
let instanceSettings = {
url: 'https://app.datadoghq.com/api/v1',
jsonData: {
api_key: '0000deadbeaf0000',
app_key: '0000abcd0000abcd'
}
};
ctx.ds = new Datasource(instanceSettings, ctx.backendSrv, ctx.templateSrv);
});
describe('When doing DataDog API request', () => {
beforeEach(function() {
let targets = [
{ query: 'avg:system.load.5{*}', rawQuery: true }
];
ctx.options = {
range: {
from: 12340000,
to: 12340000
},
targets: targets
};
});
it('should send request with proper params', (done) => {
let expected_params = {
method: 'GET',
url: 'https://app.datadoghq.com/api/v1/query',
params: {
api_key: '0000deadbeaf0000',
application_key: '0000abcd0000abcd',
from: 12340,
to: 12340,
query: 'avg:system.load.5{*}'
}
};
let datasourceRequest = sinon.spy(ctx.ds.backendSrv, 'datasourceRequest');
ctx.ds.query(ctx.options);
expect(datasourceRequest).to.have.been.calledWith(expected_params);
done();
});
});
});
@@ -0,0 +1,79 @@
import dfunc from '../dfunc';
describe('when creating func instance from func names', function() {
it('should return func instance', function() {
var func = dfunc.createFuncInstance('top');
expect(func).to.be.ok;
expect(func.def.name).to.equal('top');
expect(func.def.params.length).to.equal(3);
expect(func.def.defaultParams.length).to.equal(3);
});
it('should return func instance from funcDef', function() {
var func = dfunc.createFuncInstance('top');
var func2 = dfunc.createFuncInstance(func.def);
expect(func2).to.be.ok;
});
it('func instance should have text representation', function() {
var func = dfunc.createFuncInstance('top');
func.params[0] = 5;
func.params[1] = 'mean';
func.params[2] = 'dir';
func.updateText();
expect(func.text).to.equal("top(5, mean, dir)");
});
});
describe('when rendering func instance', function() {
it('should handle single metric param', function() {
var func = dfunc.createFuncInstance('abs');
expect(func.render('a')).to.equal("abs(a)");
});
it('should include default params if options enable it', function() {
var func = dfunc.createFuncInstance('top', { withDefaultParams: true });
expect(func.render('a')).to.equal("top(a, 5, mean, dir)");
});
it('should handle int or interval params with number', function() {
var func = dfunc.createFuncInstance('anomalies');
func.params[0] = 'basic';
func.params[1] = '5';
expect(func.render('hello')).to.equal("anomalies(hello, basic, 5)");
});
});
describe('when requesting function categories', function() {
it('should return function categories', function() {
var catIndex = dfunc.getCategories();
expect(catIndex.Arithmatic.length).to.be.greaterThan(3);
});
});
describe('when updating func param', function() {
it('should update param value and update text representation', function() {
var func = dfunc.createFuncInstance('top', { withDefaultParams: true });
func.updateParam('10', 0);
expect(func.params[0]).to.be.equal('10');
func.updateText();
expect(func.text).to.be.equal('top(10, mean, dir)');
});
it('should parse numbers as float', function() {
var func = dfunc.createFuncInstance('outliers');
func.updateParam('0.5', 1);
expect(func.params[1]).to.be.equal('0.5');
});
});
describe('when updating func param with optional second parameter', function() {
it('should update value and text', function() {
var func = dfunc.createFuncInstance('fill');
func.updateParam('null', 0);
expect(func.params[0]).to.be.equal('null');
});
});

Some files were not shown because too many files have changed in this diff Show More