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:
@@ -0,0 +1,10 @@
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
Dockerfile.local
|
||||
.env
|
||||
.gitignore
|
||||
.vs
|
||||
.vscode
|
||||
**/bin
|
||||
**/obj
|
||||
**/.toolstarget
|
||||
+86
-217
@@ -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 they’re 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 #
|
||||
############
|
||||
# it’s 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
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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
|
||||
@@ -1,2 +1,60 @@
|
||||
# analytics
|
||||
# Strata.Analytics
|
||||
|
||||
[](https://github.com/stratadecision/analytics/actions/workflows/build.yaml)
|
||||
|
||||
[](https://octopus.sdt.local/app#/Spaces-1/projects/analytics)
|
||||
|
||||
[](https://sonarqube.ops.stratanetwork.net/dashboard?id=Strata.Analytics)
|
||||
|
||||
[](https://proget.ops.stratanetwork.net/feeds/nuget/Strata.Analytics.Client/versions?HidePrerelease=True)
|
||||
|
||||
[](https://analytics-api.dev.stratanetwork.net)
|
||||
|
||||
[](https://analytics.dev.stratanetwork.net)
|
||||
|
||||
[](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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface IUserData {
|
||||
Title: string;
|
||||
UserName: string;
|
||||
Password: string;
|
||||
Description: string;
|
||||
}
|
||||
@@ -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}` }
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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'));
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
Generated
+9229
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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"]
|
||||
@@ -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"]
|
||||
@@ -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
@@ -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
|
||||
|
||||

|
||||
|
||||
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
|
||||

|
||||
|
||||
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 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,30 @@
|
||||
# Datadog Grafana Datasource Plugin
|
||||
|
||||

|
||||
|
||||
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
|
||||

|
||||
|
||||
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 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
@@ -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 |
@@ -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"]}
|
||||
+29
@@ -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
Reference in New Issue
Block a user