├── .babelrc ├── .coveralls.yml ├── .dockerignore ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .gitmodules ├── .npmrc ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── Release.md ├── build ├── docker │ ├── Dockerfile │ └── publish-to-docker-hub.sh └── zipkin │ ├── README.md │ ├── base.json │ └── zipkin-quickstart.sh ├── deployment └── terraform │ ├── main.tf │ ├── outputs.tf │ ├── templates │ ├── deployment_yaml.tpl │ └── haystack-ui_json.tpl │ └── variables.tf ├── package-lock.json ├── package.json ├── public ├── assets.json ├── favicon.ico ├── fonts │ ├── LICENSE_THEMIFY │ ├── LICENSE_TITILLIUM_WEB │ ├── themify.ttf │ ├── themify.woff │ ├── titillium-web-v5-latin-200.ttf │ ├── titillium-web-v5-latin-200.woff │ ├── titillium-web-v5-latin-200.woff2 │ ├── titillium-web-v5-latin-300.ttf │ ├── titillium-web-v5-latin-300.woff │ ├── titillium-web-v5-latin-300.woff2 │ ├── titillium-web-v5-latin-600.ttf │ ├── titillium-web-v5-latin-600.woff │ ├── titillium-web-v5-latin-600.woff2 │ ├── titillium-web-v5-latin-700.ttf │ ├── titillium-web-v5-latin-700.woff │ ├── titillium-web-v5-latin-700.woff2 │ ├── titillium-web-v5-latin-regular.ttf │ ├── titillium-web-v5-latin-regular.woff │ └── titillium-web-v5-latin-regular.woff2 ├── images │ ├── assets │ │ ├── alerts.png │ │ ├── demo.gif │ │ ├── logo_lighter.png │ │ ├── logo_with_title.png │ │ ├── logo_with_title_dark.png │ │ ├── logo_with_title_transparent.png │ │ ├── service_graph.png │ │ ├── trace_timeline.png │ │ ├── trends.png │ │ └── universal_search.png │ ├── error.svg │ ├── loading.gif │ ├── logo-white.png │ ├── logo.png │ ├── slack.png │ ├── success.svg │ └── zipkin-logo.jpg └── scripts │ ├── particles.json │ └── particles.min.js ├── server ├── app.js ├── config │ ├── base.js │ ├── config.js │ └── override.js ├── connectors │ ├── alerts │ │ ├── haystack │ │ │ ├── alertsConnector.js │ │ │ ├── expressionTreeBuilder.js │ │ │ └── subscriptionsConnector.js │ │ └── stub │ │ │ ├── alertsConnector.js │ │ │ └── subscriptionsConnector.js │ ├── operations │ │ ├── grpcDeleter.js │ │ ├── grpcFetcher.js │ │ ├── grpcPoster.js │ │ ├── grpcPutter.js │ │ └── restFetcher.js │ ├── serviceGraph │ │ ├── haystack │ │ │ ├── graphDataExtractor.js │ │ │ └── serviceGraphConnector.js │ │ ├── stub │ │ │ └── serviceGraphConnector.js │ │ └── zipkin │ │ │ ├── converter.js │ │ │ └── serviceGraphConnector.js │ ├── serviceInsights │ │ ├── detectCycles.js │ │ ├── fetcher.js │ │ ├── graphDataExtractor.js │ │ └── serviceInsightsConnector.js │ ├── services │ │ └── servicesConnector.js │ ├── traces │ │ ├── haystack │ │ │ ├── expressionTreeBuilder.js │ │ │ ├── protobufConverters │ │ │ │ ├── callGraphConverter.js │ │ │ │ ├── traceConverter.js │ │ │ │ └── traceCountsConverter.js │ │ │ ├── search │ │ │ │ ├── searchRequestBuilder.js │ │ │ │ └── searchResultsTransformer.js │ │ │ ├── timeline │ │ │ │ └── traceCountsRequestBuilder.js │ │ │ └── tracesConnector.js │ │ ├── mock │ │ │ ├── mock-web-ui.js │ │ │ ├── spanTypes.js │ │ │ ├── tracesConnector.js │ │ │ └── tracesGenerator.js │ │ ├── stub │ │ │ └── tracesConnector.js │ │ └── zipkin │ │ │ ├── converter.js │ │ │ └── tracesConnector.js │ ├── trends │ │ ├── haystack │ │ │ └── trendsConnector.js │ │ └── stub │ │ │ └── trendsConnector.js │ └── utils │ │ ├── LoaderBackedCache.js │ │ ├── encoders │ │ ├── Base64Encoder.js │ │ ├── MetricpointNameEncoder.js │ │ ├── NoopEncoder.js │ │ └── PeriodReplacementEncoder.js │ │ ├── errorConverter.js │ │ └── objectUtils.js ├── routes │ ├── alertsApi.js │ ├── auth.js │ ├── index.js │ ├── login.js │ ├── serviceGraphApi.js │ ├── serviceInsightsApi.js │ ├── servicesApi.js │ ├── servicesPerfApi.js │ ├── sso.js │ ├── tracesApi.js │ ├── trendsApi.js │ ├── user.js │ └── utils │ │ └── apiResponseHandler.js ├── sso │ ├── authChecker.js │ └── samlSsoAuthenticator.js ├── start.js ├── utils │ ├── logger.js │ ├── metrics.js │ ├── metricsMiddleware.js │ ├── metricsReporter.js │ └── server.js └── views │ └── index.pug ├── src ├── app.jsx ├── app.less ├── bootstrap │ ├── LICENSE_BOOTSTRAP │ ├── LICENSE_BOOTSWATCH │ ├── alerts.less │ ├── badges.less │ ├── bootstrap.less │ ├── bootswatch.less │ ├── breadcrumbs.less │ ├── button-groups.less │ ├── buttons.less │ ├── carousel.less │ ├── close.less │ ├── code.less │ ├── component-animations.less │ ├── dropdowns.less │ ├── forms.less │ ├── grid.less │ ├── input-groups.less │ ├── jumbotron.less │ ├── labels.less │ ├── list-group.less │ ├── media.less │ ├── mixins.less │ ├── mixins │ │ ├── alerts.less │ │ ├── background-variant.less │ │ ├── border-radius.less │ │ ├── buttons.less │ │ ├── center-block.less │ │ ├── clearfix.less │ │ ├── forms.less │ │ ├── gradients.less │ │ ├── grid-framework.less │ │ ├── grid.less │ │ ├── hide-text.less │ │ ├── image.less │ │ ├── labels.less │ │ ├── list-group.less │ │ ├── nav-divider.less │ │ ├── nav-vertical-align.less │ │ ├── opacity.less │ │ ├── pagination.less │ │ ├── panels.less │ │ ├── progress-bar.less │ │ ├── reset-filter.less │ │ ├── reset-text.less │ │ ├── resize.less │ │ ├── responsive-visibility.less │ │ ├── size.less │ │ ├── tab-focus.less │ │ ├── table-row.less │ │ ├── text-emphasis.less │ │ ├── text-overflow.less │ │ └── vendor-prefixes.less │ ├── modals.less │ ├── navbar.less │ ├── navs.less │ ├── normalize.less │ ├── pager.less │ ├── pagination.less │ ├── panels.less │ ├── popovers.less │ ├── print.less │ ├── progress-bars.less │ ├── responsive-embed.less │ ├── responsive-utilities.less │ ├── scaffolding.less │ ├── svc-colors.less │ ├── tables.less │ ├── theme.less │ ├── themify-icons.less │ ├── thumbnails.less │ ├── titillium-web.less │ ├── tooltip.less │ ├── type.less │ ├── utilities.less │ ├── variables.less │ └── wells.less ├── components │ ├── alerts │ │ ├── alertCounter.jsx │ │ ├── alertTabs.jsx │ │ ├── alerts.jsx │ │ ├── alerts.less │ │ ├── alertsTable.jsx │ │ ├── alertsTableSparkline.jsx │ │ ├── alertsToolbar.jsx │ │ ├── details │ │ │ ├── alertDetails.jsx │ │ │ ├── alertDetailsToolbar.jsx │ │ │ ├── alertHistory.jsx │ │ │ ├── alertSubscriptions.jsx │ │ │ └── subscriptionRow.jsx │ │ ├── stores │ │ │ ├── alertDetailsStore.js │ │ │ ├── alertTrendFetcher.js │ │ │ └── serviceAlertsStore.js │ │ └── utils │ │ │ └── subscriptionConstructor.js │ ├── common │ │ ├── error.jsx │ │ ├── error.less │ │ ├── loading.jsx │ │ ├── loading.less │ │ ├── login.jsx │ │ ├── login.less │ │ ├── modal.jsx │ │ ├── modal.less │ │ ├── noMatch.jsx │ │ ├── resultsTable.less │ │ ├── timeRangePicker.jsx │ │ ├── timeRangePicker.less │ │ ├── withTracker.jsx │ │ └── workInProgress.jsx │ ├── docs │ │ └── help.jsx │ ├── layout │ │ ├── authenticationTimeoutModal.jsx │ │ ├── footer.jsx │ │ ├── footer.less │ │ └── slimHeader.jsx │ ├── serviceGraph │ │ ├── connectionDetails.jsx │ │ ├── graphSearch.jsx │ │ ├── nodeDetails.jsx │ │ ├── serviceGraph.jsx │ │ ├── serviceGraph.less │ │ ├── serviceGraphContainer.jsx │ │ ├── serviceGraphResults.jsx │ │ ├── stores │ │ │ └── serviceGraphStore.js │ │ ├── trafficTable.jsx │ │ ├── util │ │ │ └── graph.js │ │ ├── vizceralConfig.js │ │ └── vizceralExt.jsx │ ├── serviceInsights │ │ ├── serviceInsights.jsx │ │ ├── serviceInsights.less │ │ ├── serviceInsightsGraph │ │ │ ├── dataLayout.js │ │ │ ├── dragGroup.jsx │ │ │ ├── icons │ │ │ │ ├── db.svg │ │ │ │ ├── gateway.svg │ │ │ │ ├── outbound.svg │ │ │ │ └── uninstrumented.svg │ │ │ ├── label.jsx │ │ │ ├── labels.jsx │ │ │ ├── lines.jsx │ │ │ ├── nodes.jsx │ │ │ ├── serviceInsightsGraph.jsx │ │ │ ├── serviceInsightsGraph.less │ │ │ ├── tooltip.jsx │ │ │ └── tooltip.less │ │ ├── stores │ │ │ └── serviceInsightsStore.js │ │ └── summary.jsx │ ├── servicePerf │ │ ├── servicePerformance.jsx │ │ ├── servicePerformance.less │ │ └── stores │ │ │ └── servicePerfStore.js │ ├── traces │ │ ├── details │ │ │ ├── latency │ │ │ │ ├── latencyCostTab.jsx │ │ │ │ └── latencyCostTabContainer.jsx │ │ │ ├── rawTraceModal.jsx │ │ │ ├── relatedTraces │ │ │ │ ├── relatedTracesRow.jsx │ │ │ │ ├── relatedTracesTab.jsx │ │ │ │ └── relatedTracesTabContainer.jsx │ │ │ ├── timeline │ │ │ │ ├── logsTable.jsx │ │ │ │ ├── rawSpan.jsx │ │ │ │ ├── span.jsx │ │ │ │ ├── spanDetailsModal.jsx │ │ │ │ ├── tagsTable.jsx │ │ │ │ ├── timelineTab.jsx │ │ │ │ └── timelineTabContainer.jsx │ │ │ ├── traceDetails.jsx │ │ │ ├── traceDetails.less │ │ │ └── trends │ │ │ │ ├── serviceOperationTrendRow.jsx │ │ │ │ ├── trendsTab.jsx │ │ │ │ └── trendsTabContainer.jsx │ │ ├── results │ │ │ ├── noSearch.jsx │ │ │ ├── noSearch.less │ │ │ ├── spanResultsTable.jsx │ │ │ ├── spansView.jsx │ │ │ ├── tagsFilter.jsx │ │ │ ├── traceResults.jsx │ │ │ ├── traceResultsTable.jsx │ │ │ ├── traceTimeline.jsx │ │ │ ├── traceTimeline.less │ │ │ └── tracesContainer.jsx │ │ ├── stores │ │ │ ├── latencyCostStore.js │ │ │ ├── rawSpanStore.js │ │ │ ├── rawTraceStore.js │ │ │ ├── searchableKeysStore.js │ │ │ ├── spansSearchStore.js │ │ │ ├── traceDetailsStore.js │ │ │ ├── traceTrendFetcher.js │ │ │ └── tracesSearchStore.js │ │ ├── traces.less │ │ ├── upload │ │ │ ├── uploadContainer.jsx │ │ │ └── uploadHeader.jsx │ │ └── utils │ │ │ ├── auxiliaryTags.js │ │ │ ├── presets.js │ │ │ └── traceQueryParser.js │ ├── trends │ │ ├── details │ │ │ ├── graphs │ │ │ │ ├── countGraph.jsx │ │ │ │ ├── durationGraph.jsx │ │ │ │ ├── graphContainer.jsx │ │ │ │ ├── graphContainer.less │ │ │ │ ├── missingTrend.jsx │ │ │ │ ├── missingTrend.less │ │ │ │ ├── options.js │ │ │ │ └── successGraph.jsx │ │ │ ├── trendDetails.jsx │ │ │ ├── trendDetails.less │ │ │ ├── trendDetailsToolbar.jsx │ │ │ └── trendDetailsToolbar.less │ │ ├── operation │ │ │ ├── operationResults.jsx │ │ │ ├── operationResults.less │ │ │ ├── operationResultsHeatmap.jsx │ │ │ ├── operationResultsHeatmap.less │ │ │ ├── operationResultsTable.jsx │ │ │ └── operationResultsTable.less │ │ ├── stores │ │ │ └── operationStore.js │ │ └── utils │ │ │ ├── metricGranularity.js │ │ │ ├── trendSparklines.jsx │ │ │ └── trendsTableFormatters.jsx │ └── universalSearch │ │ ├── searchBar │ │ ├── autosuggest.jsx │ │ ├── autosuggest.less │ │ ├── chips.jsx │ │ ├── guide.jsx │ │ ├── queryBank.jsx │ │ ├── searchBar.jsx │ │ ├── searchSubmit.jsx │ │ ├── stores │ │ │ ├── searchBarUiStateStore.js │ │ │ └── searchableKeysStore.js │ │ ├── suggestions.jsx │ │ ├── timeRangePicker.jsx │ │ ├── timeRangePicker.less │ │ └── timeWindowPicker.jsx │ │ ├── tabs │ │ ├── emptyTabPlaceholder.jsx │ │ ├── externalLinksList.jsx │ │ ├── serviceGraph.jsx │ │ ├── serviceInsights.jsx │ │ ├── servicePerformance.jsx │ │ ├── tabStores │ │ │ ├── alertsTabStateStore.js │ │ │ ├── serviceGraphStateStore.js │ │ │ ├── serviceInsightsTabStateStore.js │ │ │ ├── servicePerformanceStateStore.js │ │ │ ├── tracesTabStateStore.js │ │ │ └── trendsTabStateStore.js │ │ └── tabs.jsx │ │ ├── universalSearch.jsx │ │ ├── universalSearch.less │ │ └── utils │ │ └── urlUtils.js ├── main.jsx ├── stores │ ├── authenticationStore.js │ ├── errorHandlingStore.js │ ├── operationStore.js │ ├── serviceStore.js │ └── storesInitializer.js └── utils │ ├── blobUtil.js │ ├── externalLinkFormatter.jsx │ ├── formatters.js │ ├── hashUtil.js │ ├── linkBuilder.js │ ├── loginRenewer.js │ ├── queryParser.js │ ├── serviceColorMapper.js │ ├── timeWindow.js │ └── validUrl.js ├── test ├── server │ ├── config │ │ └── override.spec.js │ ├── connectors │ │ ├── serviceGraph │ │ │ ├── haystack │ │ │ │ └── serviceGraphConnector.spec.js │ │ │ └── zipkin │ │ │ │ └── converter.spec.js │ │ ├── serviceInsights │ │ │ ├── detectCycles.spec.js │ │ │ ├── fetcher.spec.js │ │ │ ├── graphDataExtractor.spec.js │ │ │ └── serviceInsightsConnector.spec.js │ │ ├── traces │ │ │ ├── haystack │ │ │ │ ├── search │ │ │ │ │ └── searchResultsTransformer.spec.js │ │ │ │ └── tracesConnector.spec.js │ │ │ └── zipkin │ │ │ │ ├── converter.spec.js │ │ │ │ └── tracesConnector.spec.js │ │ ├── trends │ │ │ └── haystack │ │ │ │ └── trendsConnector.spec.js │ │ └── utils │ │ │ └── encoders │ │ │ ├── Base64Encoder.spec.js │ │ │ ├── MetricpointNameEncoder.spec.js │ │ │ ├── NoopEncoder.spec.js │ │ │ └── PeriodReplacementEncoder.spec.js │ └── routes │ │ ├── alertsApi.spec.js │ │ ├── index.spec.js │ │ ├── serviceInsightsApi.spec.js │ │ ├── servicesApi.spec.js │ │ ├── servicesPerfApi.spec.js │ │ ├── tracesApi.spec.js │ │ └── trendsApi.spec.js └── src │ ├── components │ ├── alerts.spec.jsx │ ├── common │ │ ├── help.spec.jsx │ │ ├── noMatch.spec.jsx │ │ └── timeRangePicker.spec.jsx │ ├── layout.spec.jsx │ ├── serviceGraph │ │ ├── graph.spec.js │ │ ├── serviceGraph.spec.jsx │ │ └── util │ │ │ └── edges.js │ ├── serviceInsights │ │ ├── serviceInsights.spec.jsx │ │ ├── serviceInsightsGraph │ │ │ ├── dataLayout.spec.js │ │ │ ├── dragGroup.spec.jsx │ │ │ ├── label.spec.jsx │ │ │ ├── labels.spec.jsx │ │ │ ├── lines.spec.jsx │ │ │ ├── nodes.spec.jsx │ │ │ ├── serviceInsightsGraph.spec.jsx │ │ │ └── tooltip.spec.jsx │ │ ├── stores │ │ │ └── serviceInsightsStore.spec.js │ │ └── summary.spec.jsx │ ├── servicePerf.spec.jsx │ ├── traces │ │ ├── latencyCost.spec.jsx │ │ ├── rawSpan.spec.jsx │ │ ├── rawTrace.spec.jsx │ │ ├── spansView.spec.jsx │ │ └── traces.spec.jsx │ ├── trends │ │ ├── details │ │ │ └── graphs │ │ │ │ └── durationGraph.spec.jsx │ │ └── trends.spec.jsx │ └── universalSearch │ │ ├── searchBar │ │ ├── stores │ │ │ └── searchBarUiStateStore.spec.js │ │ └── timeRangePicker.spec.jsx │ │ ├── tabs │ │ ├── serviceInsights.spec.jsx │ │ └── tabStores │ │ │ ├── serviceInsightsTabStateStore.spec.js │ │ │ └── tracesTabStateStore.spec.js │ │ └── universalSearch.spec.jsx │ ├── main.spec.jsx │ ├── stores │ ├── alertStore.spec.js │ ├── operationStore.spec.js │ ├── serviceGraphStore.spec.js │ ├── serviceStore.spec.js │ ├── tracesStore.spec.js │ └── trendsStore.spec.js │ ├── test_helper.js │ └── utils │ └── timeWindow.spec.js ├── universal └── enums.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "lodash", 4 | [ 5 | "@babel/plugin-proposal-decorators", 6 | { 7 | "legacy": true 8 | } 9 | ], 10 | [ 11 | "@babel/plugin-proposal-class-properties", 12 | { 13 | "loose": true 14 | } 15 | ], 16 | "dynamic-import-node" 17 | ], 18 | "presets": [ 19 | ["@babel/preset-env", { 20 | "targets": { 21 | "node": "current" 22 | } 23 | }], 24 | "@babel/preset-react" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | repo_token: rmv39G8JKUPr9W8whITKKlHLl3x4CEi50 -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | public/* 2 | haystack-idl/* 3 | static_codegen/* 4 | coverage/*' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | sample 4 | *.log 5 | 6 | # Mac Trash directories 7 | *.DS_Store 8 | 9 | # Dependency directory 10 | node_modules 11 | 12 | #intellij 13 | .idea/ 14 | *.iml 15 | *.ipr 16 | *.iws 17 | 18 | # generated scripts/css in public should not be commited 19 | public/bundles 20 | static_codegen 21 | 22 | # Code coverage reports 23 | .nyc_output/ 24 | coverage 25 | 26 | # HTTPS Certs 27 | server.cert 28 | server.key 29 | 30 | # Zipkin runner 31 | zipkin-workspace -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "haystack-idl"] 2 | path = haystack-idl 3 | url = https://github.com/ExpediaDotCom/haystack-idl 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | package-lock.json -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "printWidth": 150, 4 | "singleQuote": true, 5 | "bracketSpacing": false, 6 | "arrowParens": "always" 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "12" 5 | 6 | services: 7 | - docker 8 | 9 | dist: xenial 10 | 11 | env: 12 | global: 13 | - BRANCH=${TRAVIS_BRANCH} 14 | - TAG=${TRAVIS_TAG} 15 | - SHA=${TRAVIS_COMMIT} 16 | 17 | before_install: 18 | - sudo apt-get update 19 | - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce 20 | - sudo apt-get install libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ 21 | 22 | script: 23 | # build, create docker image 24 | # upload to dockerhub only for master(non PR) and tag scenario 25 | - if ([ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]) || [ -n "$TRAVIS_TAG" ]; then make release; else make all; fi 26 | 27 | notifications: 28 | email: 29 | - haystack-notifications@expedia.com 30 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | // Prettier 4 | "editor.formatOnSave": true, 5 | "[less]": { 6 | "editor.formatOnSave": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ##Bugs 2 | We use Github Issues for our bug reporting. Please make sure the bug isn't already listed before opening a new issue. 3 | 4 | ##Development 5 | All work on Haystack happens directly on Github. Core Haystack team members will review opened pull requests. 6 | 7 | ##Requests 8 | If you see a feature that you would like to be added, please open an issue in the respective repository or in the general Haystack repo. 9 | 10 | ##Contributing to Documentation 11 | To contribute to documentation, you can directly modify the corresponding .md files in the docs directory under the base haystack repository, and submit a pull request. Once your PR is merged, the documentation is automatically built and deployed to https://expediadotcom.github.io/haystack. 12 | 13 | ##License 14 | By contributing to Haystack, you agree that your contributions will be licensed under its Apache License. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean build docker_build all release 2 | 3 | # docker namespace 4 | export DOCKER_ORG := expediadotcom 5 | export DOCKER_IMAGE_NAME := haystack-ui 6 | 7 | clean: 8 | npm run clean 9 | 10 | install: 11 | npm install 12 | 13 | build: clean install 14 | npm run build 15 | 16 | docker_build: 17 | docker build -t $(DOCKER_IMAGE_NAME) -f build/docker/Dockerfile . 18 | 19 | all: build docker_build 20 | 21 | # build all and release 22 | release: all 23 | ./build/docker/publish-to-docker-hub.sh 24 | -------------------------------------------------------------------------------- /Release.md: -------------------------------------------------------------------------------- 1 | #Releasing 2 | Currently we publish the repo to only docker hub. We don't publish it to nexus central repository since its a npm module. 3 | 4 | #How to release and publish 5 | 6 | * Git tagging: 7 | 8 | ```git tag -a 1.x.x -m "Release description..."``` 9 | 10 | Or you can also tag using UI: https://github.com/ExpediaDotCom/haystack-ui/releases -------------------------------------------------------------------------------- /build/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 AS base 2 | 3 | ENV APP_HOME /app 4 | ENV PUBLIC_PATH /${APP_HOME}/public 5 | ENV SERVER_PATH /${APP_HOME}/server 6 | ENV UNIVERSAL_PATH /${APP_HOME}/universal 7 | ENV NODE_MODULES_PATH /${APP_HOME}/node_modules 8 | ENV STATIC_CODEGEN_PATH /${APP_HOME}/static_codegen 9 | ENV PACKAGE_JSON_PATH /${APP_HOME}/package.json 10 | WORKDIR ${APP_HOME} 11 | 12 | # generating proto code, building bundles and running tests 13 | FROM base AS builder 14 | COPY . . 15 | RUN npm -q install 16 | RUN npm -q run build 17 | 18 | # creating release image 19 | FROM base AS release 20 | COPY --from=builder ${PUBLIC_PATH} ${PUBLIC_PATH} 21 | COPY --from=builder ${SERVER_PATH} ${SERVER_PATH} 22 | COPY --from=builder ${UNIVERSAL_PATH} ${UNIVERSAL_PATH} 23 | COPY --from=builder ${STATIC_CODEGEN_PATH} ${STATIC_CODEGEN_PATH} 24 | COPY --from=builder ${PACKAGE_JSON_PATH} ${PACKAGE_JSON_PATH} 25 | RUN npm -q install --only=prod 26 | 27 | EXPOSE 8080 28 | CMD node server/start.js 29 | -------------------------------------------------------------------------------- /build/docker/publish-to-docker-hub.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | QUALIFIED_DOCKER_IMAGE_NAME=$DOCKER_ORG/$DOCKER_IMAGE_NAME 6 | echo "DOCKER_ORG=$DOCKER_ORG, DOCKER_IMAGE_NAME=$DOCKER_IMAGE_NAME, QUALIFIED_DOCKER_IMAGE_NAME=$QUALIFIED_DOCKER_IMAGE_NAME" 7 | echo "BRANCH=$BRANCH, TAG=$TAG, SHA=$SHA" 8 | 9 | # login 10 | docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD 11 | 12 | # Add tags 13 | if [[ $TAG =~ ([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then 14 | echo "releasing semantic versions" 15 | 16 | unset MAJOR MINOR PATCH 17 | MAJOR="${BASH_REMATCH[1]}" 18 | MINOR="${BASH_REMATCH[2]}" 19 | PATCH="${BASH_REMATCH[3]}" 20 | 21 | # for tag, add MAJOR, MAJOR.MINOR, MAJOR.MINOR.PATCH and latest as tag 22 | docker tag $DOCKER_IMAGE_NAME $QUALIFIED_DOCKER_IMAGE_NAME:$MAJOR 23 | docker tag $DOCKER_IMAGE_NAME $QUALIFIED_DOCKER_IMAGE_NAME:$MAJOR.$MINOR 24 | docker tag $DOCKER_IMAGE_NAME $QUALIFIED_DOCKER_IMAGE_NAME:$MAJOR.$MINOR.$PATCH 25 | docker tag $DOCKER_IMAGE_NAME $QUALIFIED_DOCKER_IMAGE_NAME:latest 26 | 27 | # publish image with tags 28 | docker push $QUALIFIED_DOCKER_IMAGE_NAME 29 | 30 | elif [[ "$BRANCH" == "master" ]]; then 31 | echo "releasing master branch" 32 | 33 | # for 'master' branch, add SHA as tags 34 | docker tag $DOCKER_IMAGE_NAME $QUALIFIED_DOCKER_IMAGE_NAME:$SHA 35 | 36 | # publish image with tags 37 | docker push $QUALIFIED_DOCKER_IMAGE_NAME 38 | fi 39 | -------------------------------------------------------------------------------- /build/zipkin/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Zipkin haystack-ui Quickstart Utility 3 | 4 | Utility script to run haystack-ui with a zipkin instance as backend for traces. It spins sleuth-webmvc-example services for feeding traces in Zipkin cluster and generates some examples. It configures haystack-ui point to Zipkin V2 api and has only traces subsystem. 5 | 6 | 7 | ### PREREQUISITES 8 | 9 | - Assumes that you have mvn and git available on your machine. 10 | - haystack-ui must be already installed (npm install) and built (npm build), if not please install and build before running this script 11 | 12 | 13 | ### USAGE 14 | 15 | ```> ./zipkin-quickstart``` 16 | 17 | Wait for couple of minutes till you see `Express server listening : 8080` message. Then you can hit [http://localhost:8080/search?serviceName=backend](http://localhost:8080/search?serviceName=backend) to use haystack-ui. Search for `serviceName=backend` to see pre-feeded traces coming from Zipkin backend. 18 | 19 | 20 | ### OPTIONS 21 | 22 | ``` 23 | -h help 24 | -d debug mode, will emit out all logs from zipkin and sleuth-webmvc-example 25 | ``` 26 | 27 | -------------------------------------------------------------------------------- /build/zipkin/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 8080, 3 | "cluster": false, 4 | "upstreamTimeout": 30000, 5 | "connectors": { 6 | "traces": { 7 | "connectorName": "zipkin", 8 | "zipkinUrl": "http://localhost:9411/api/v2" 9 | }, 10 | "serviceGraph": { 11 | "connectorName": "zipkin", 12 | "zipkinUrl": "http://localhost:9411/api/v2" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /deployment/terraform/outputs.tf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/deployment/terraform/outputs.tf -------------------------------------------------------------------------------- /deployment/terraform/templates/deployment_yaml.tpl: -------------------------------------------------------------------------------- 1 | # ------------------- Deployment ------------------- # 2 | 3 | kind: Deployment 4 | apiVersion: apps/v1beta2 5 | metadata: 6 | labels: 7 | k8s-app: ${app_name} 8 | name: ${app_name} 9 | namespace: ${namespace} 10 | spec: 11 | replicas: ${replicas} 12 | revisionHistoryLimit: 10 13 | selector: 14 | matchLabels: 15 | k8s-app: ${app_name} 16 | template: 17 | metadata: 18 | labels: 19 | k8s-app: ${app_name} 20 | spec: 21 | containers: 22 | - name: ${app_name} 23 | image: ${image} 24 | volumeMounts: 25 | # Create on-disk volume to store exec logs 26 | - mountPath: /config 27 | name: config-volume 28 | resources: 29 | limits: 30 | cpu: ${cpu_limit} 31 | memory: ${memory_limit}Mi 32 | requests: 33 | cpu: ${cpu_request} 34 | memory: ${memory_limit}Mi 35 | env: 36 | - name: "HAYSTACK_OVERRIDES_CONFIG_PATH" 37 | value: "/config/haystack-ui.json" 38 | nodeSelector: 39 | ${node_selecter_label} 40 | volumes: 41 | - name: config-volume 42 | configMap: 43 | name: ${configmap_name} 44 | 45 | # ------------------- Service ------------------- # 46 | --- 47 | apiVersion: v1 48 | kind: Service 49 | metadata: 50 | labels: 51 | k8s-app: ${app_name} 52 | name: ${app_name} 53 | namespace: ${namespace} 54 | spec: 55 | ports: 56 | - port: ${service_port} 57 | targetPort: ${container_port} 58 | selector: 59 | k8s-app: ${app_name} 60 | -------------------------------------------------------------------------------- /deployment/terraform/templates/haystack-ui_json.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "port": 8080, 3 | "cluster": true, 4 | "upstreamTimeout": 30000, 5 | "encoder": "${encoder_type}", 6 | "enableServicePerformance": false, 7 | "enableServiceLevelTrends": false, 8 | "enableLatencyCostViewer": true, 9 | "graphite": { 10 | "host": "${graphite_hostname}", 11 | "port": ${graphite_port} 12 | }, 13 | "grpcOptions": { 14 | "grpc.max_receive_message_length": 52428800 15 | }, 16 | "connectors": { 17 | "traces": { 18 | "connectorName": "haystack", 19 | "haystackHost": "${trace_reader_hostname}", 20 | "haystackPort": ${trace_reader_service_port}, 21 | "serviceRefreshIntervalInSecs": 60, 22 | "fieldKeys": [${whitelisted_fields}] 23 | }, 24 | "trends": { 25 | "connectorName": "haystack", 26 | "metricTankUrl": "http://${metrictank_hostname}:${metrictank_port}" 27 | }, 28 | "alerts": { 29 | "connectorName": "haystack", 30 | "metricTankUrl": "http://${metrictank_hostname}:${metrictank_port}", 31 | "alertFreqInSec": 300, 32 | "alertMergeBufferTimeInSec": 60, 33 | "subscriptions": { 34 | "connectorName": "stub", 35 | "enabled": false 36 | } 37 | } 38 | }, 39 | "enableSSO": ${ui_enable_sso}, 40 | "saml": { 41 | "callbackUrl": "${ui_saml_callback_url}", 42 | "entry_point": "${ui_saml_entry_point}", 43 | "issuer": "${ui_saml_issuer}" 44 | }, 45 | "sessionTimeout": 3600000, 46 | "sessionSecret": "${ui_session_secret}" 47 | } 48 | -------------------------------------------------------------------------------- /deployment/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "enabled" { 2 | default = true 3 | } 4 | 5 | variable "image" {} 6 | variable "replicas" {} 7 | variable "namespace" {} 8 | variable "kubectl_executable_name" {} 9 | variable "kubectl_context_name" {} 10 | variable "node_selecter_label"{} 11 | variable "memory_request"{} 12 | variable "memory_limit"{} 13 | variable "cpu_request"{} 14 | variable "cpu_limit"{} 15 | variable "graphite_hostname" {} 16 | variable "graphite_port" {} 17 | variable "encoder_type" {} 18 | 19 | variable "termination_grace_period" { 20 | default = 30 21 | } 22 | variable "service_port" { 23 | default = 80 24 | } 25 | variable "container_port" { 26 | default = 8080 27 | } 28 | 29 | variable "k8s_cluster_name" {} 30 | 31 | variable "trace_reader_hostname" {} 32 | 33 | variable "trace_reader_service_port" {} 34 | 35 | variable "metrictank_hostname" {} 36 | 37 | variable "metrictank_port" {} 38 | 39 | variable "whitelisted_fields" {} 40 | 41 | variable "ui_enable_sso" { 42 | default = false 43 | } 44 | 45 | variable "ui_saml_callback_url" {} 46 | 47 | variable "ui_saml_entry_point" {} 48 | 49 | variable "ui_saml_issuer" {} 50 | 51 | variable "ui_session_secret" {} 52 | -------------------------------------------------------------------------------- /public/assets.json: -------------------------------------------------------------------------------- 1 | {"ServiceInsights":{"css":"/bundles/style/ServiceInsights.css","js":"/bundles/js/ServiceInsights.js"},"app":{"css":"/bundles/style/app.css","js":"/bundles/js/app.js"},"commons":{"js":"/bundles/js/commons.js"},"serviceGraphContainer":{"js":"/bundles/js/serviceGraphContainer.js"},"servicePerformance":{"css":"/bundles/style/servicePerformance.css","js":"/bundles/js/servicePerformance.js"},"vendors~ServiceInsights":{"js":"/bundles/js/vendors~ServiceInsights.js"},"vendors~ServiceInsights~serviceGraphContainer":{"js":"/bundles/js/vendors~ServiceInsights~serviceGraphContainer.js"},"vendors~serviceGraphContainer":{"js":"/bundles/js/vendors~serviceGraphContainer.js"},"vendors~servicePerformance":{"js":"/bundles/js/vendors~servicePerformance.js"},"vendors~vis":{"js":"/bundles/js/vendors~vis.js"}} -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/LICENSE_THEMIFY: -------------------------------------------------------------------------------- 1 | LICENSE 2 | 3 | - Themify Icons font licensed under: http://scripts.sil.org/OFL 4 | - Code licensed under: http://opensource.org/licenses/mit-license.html 5 | - All brand icons are copyright/trademarks of their respective owners. 6 | -------------------------------------------------------------------------------- /public/fonts/themify.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/themify.ttf -------------------------------------------------------------------------------- /public/fonts/themify.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/themify.woff -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-200.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-200.ttf -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-200.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-200.woff -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-200.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-200.woff2 -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-300.ttf -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-300.woff -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-300.woff2 -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-600.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-600.ttf -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-600.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-600.woff -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-600.woff2 -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-700.ttf -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-700.woff -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-700.woff2 -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-regular.ttf -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-regular.woff -------------------------------------------------------------------------------- /public/fonts/titillium-web-v5-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/fonts/titillium-web-v5-latin-regular.woff2 -------------------------------------------------------------------------------- /public/images/assets/alerts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/assets/alerts.png -------------------------------------------------------------------------------- /public/images/assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/assets/demo.gif -------------------------------------------------------------------------------- /public/images/assets/logo_lighter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/assets/logo_lighter.png -------------------------------------------------------------------------------- /public/images/assets/logo_with_title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/assets/logo_with_title.png -------------------------------------------------------------------------------- /public/images/assets/logo_with_title_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/assets/logo_with_title_dark.png -------------------------------------------------------------------------------- /public/images/assets/logo_with_title_transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/assets/logo_with_title_transparent.png -------------------------------------------------------------------------------- /public/images/assets/service_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/assets/service_graph.png -------------------------------------------------------------------------------- /public/images/assets/trace_timeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/assets/trace_timeline.png -------------------------------------------------------------------------------- /public/images/assets/trends.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/assets/trends.png -------------------------------------------------------------------------------- /public/images/assets/universal_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/assets/universal_search.png -------------------------------------------------------------------------------- /public/images/error.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /public/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/loading.gif -------------------------------------------------------------------------------- /public/images/logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/logo-white.png -------------------------------------------------------------------------------- /public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/logo.png -------------------------------------------------------------------------------- /public/images/slack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/slack.png -------------------------------------------------------------------------------- /public/images/success.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /public/images/zipkin-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExpediaDotCom/haystack-ui/08370d17a5316378519322ee7e9b15a720f5b33d/public/images/zipkin-logo.jpg -------------------------------------------------------------------------------- /server/config/config.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | * Copyright 2018 Expedia Group 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | 18 | */ 19 | 20 | const _ = require('lodash'); 21 | const baseConfiguration = require('../config/base'); 22 | const override = require('./override'); 23 | 24 | let finalConfiguration = _.merge({}, baseConfiguration); 25 | 26 | // if an override configuration file is provided, extend the base config with 27 | // the provided one. This is not a recursive merge, just a top level extend with 28 | // the overriden config 29 | if (process.env.HAYSTACK_OVERRIDES_CONFIG_PATH) { 30 | let overridesConfigration = process.env.HAYSTACK_OVERRIDES_CONFIG_PATH; 31 | if (!overridesConfigration.startsWith('/')) { 32 | overridesConfigration = `${process.cwd()}/${overridesConfigration}`; 33 | } 34 | // eslint-disable-next-line global-require, import/no-dynamic-require 35 | const environmentSpecificConfiguration = require(overridesConfigration); 36 | finalConfiguration = _.extend({}, finalConfiguration, environmentSpecificConfiguration); 37 | } 38 | 39 | // if there are environment variables, read them as objects and merge them 40 | // into the current configuration 41 | const overrideObject = override.readOverrides(process.env); 42 | module.exports = _.merge({}, finalConfiguration, overrideObject); 43 | -------------------------------------------------------------------------------- /server/connectors/alerts/haystack/expressionTreeBuilder.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const requestBuilder = {}; 18 | const messages = require('../../../../static_codegen/subscription/subscriptionManagement_pb'); 19 | 20 | 21 | requestBuilder.createSubscriptionExpressionTree = (subscription) => { 22 | const expressionTree = new messages.ExpressionTree(); 23 | expressionTree.setOperator(messages.ExpressionTree.Operator.AND); 24 | const uiExpressionTree = subscription.expressionTree; 25 | 26 | const operands = Object.keys(uiExpressionTree).map((key) => { 27 | const op = new messages.Operand(); 28 | 29 | const field = new messages.Field(); 30 | field.setName(key); 31 | field.setValue(uiExpressionTree[key]); 32 | 33 | op.setField(field); 34 | 35 | return op; 36 | }); 37 | 38 | expressionTree.setOperandsList(operands); 39 | return expressionTree; 40 | }; 41 | 42 | module.exports = requestBuilder; 43 | -------------------------------------------------------------------------------- /server/connectors/operations/restFetcher.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const axios = require('axios'); 18 | const Q = require('q'); 19 | const errorConverter = require('../utils/errorConverter'); 20 | const logger = require('../../utils/logger').withIdentifier('fetcher.rest'); 21 | const metrics = require('../../utils/metrics'); 22 | 23 | const fetcher = (fetcherName) => ({ 24 | fetch: (url, headers = {}) => { 25 | const deferred = Q.defer(); 26 | const timer = metrics.timer(`fetcher_rest_${fetcherName}`).start(); 27 | 28 | axios.get(url, {headers}).then( 29 | (response) => { 30 | timer.end(); 31 | logger.info(`fetch successful: ${url}`); 32 | 33 | deferred.resolve(response.data); 34 | }, 35 | (error) => { 36 | timer.end(); 37 | metrics.meter(`fetcher_rest_failure_${fetcherName}`).mark(); 38 | logger.error(`fetch failed: ${url}`); 39 | 40 | deferred.reject(errorConverter.fromAxiosError(error)); 41 | } 42 | ); 43 | 44 | return deferred.promise; 45 | } 46 | }); 47 | 48 | module.exports = fetcher; 49 | -------------------------------------------------------------------------------- /server/connectors/serviceGraph/haystack/serviceGraphConnector.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const Q = require('q'); 18 | 19 | const fetcher = require('../../operations/restFetcher'); 20 | const config = require('../../../config/config'); 21 | const extractor = require('./graphDataExtractor'); 22 | 23 | const trendsFetcher = fetcher('serviceGraph'); 24 | 25 | const connector = {}; 26 | const serviceGraphUrl = config.connectors.serviceGraph && config.connectors.serviceGraph.serviceGraphUrl; 27 | 28 | function fetchServiceGraph(from, to) { 29 | return trendsFetcher 30 | .fetch(`${serviceGraphUrl}?from=${from}&to=${to}`) 31 | .then(data => extractor.extractGraphs(data)); 32 | } 33 | 34 | connector.getServiceGraphForTimeLine = (from, to) => Q.fcall(() => fetchServiceGraph(from, to)); 35 | 36 | module.exports = connector; 37 | -------------------------------------------------------------------------------- /server/connectors/serviceGraph/zipkin/serviceGraphConnector.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const Q = require('q'); 18 | 19 | const fetcher = require('../../operations/restFetcher'); 20 | const config = require('../../../config/config'); 21 | const converter = require('./converter'); 22 | const extractor = require('../haystack/graphDataExtractor'); 23 | 24 | const dependenciesFetcher = fetcher('getDependencies'); 25 | 26 | const connector = {}; 27 | const baseZipkinUrl = config.connectors.serviceGraph.zipkinUrl; 28 | 29 | function fetchServiceGraph(from, to) { 30 | const endTs = parseInt(to, 10); 31 | const lookback = endTs - parseInt(from, 10); 32 | 33 | return dependenciesFetcher 34 | .fetch(`${baseZipkinUrl}/dependencies?endTs=${endTs}&lookback=${lookback}`) 35 | .then(data => extractor.extractGraphFromEdges(converter.toHaystackServiceEdges(data))); 36 | } 37 | 38 | connector.getServiceGraphForTimeLine = (from, to) => Q.fcall(() => fetchServiceGraph(from, to)); 39 | 40 | module.exports = connector; 41 | -------------------------------------------------------------------------------- /server/connectors/services/servicesConnector.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | const config = require('../../config/config'); 17 | const LoaderBackedCache = require('../utils/LoaderBackedCache'); 18 | 19 | const tracesConnector = config.connectors.traces && require(`../traces/${config.connectors.traces.connectorName}/tracesConnector`); // eslint-disable-line import/no-dynamic-require, global-require 20 | const refreshIntervalInSecs = config.connectors.traces.serviceRefreshIntervalInSecs; 21 | const connector = {}; 22 | 23 | const serviceCache = new LoaderBackedCache(() => tracesConnector.getServices(), refreshIntervalInSecs * 1000); 24 | connector.getServices = () => serviceCache.get(); 25 | 26 | const operationsCache = new LoaderBackedCache((serviceName) => tracesConnector.getOperations(serviceName), refreshIntervalInSecs * 1000); 27 | connector.getOperations = (serviceName) => operationsCache.get(serviceName); 28 | 29 | module.exports = connector; 30 | -------------------------------------------------------------------------------- /server/connectors/traces/haystack/protobufConverters/traceCountsConverter.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const converter = {}; 18 | 19 | converter.toTraceCountsJson = pbTraceCounts => pbTraceCounts.tracecountList.map( 20 | pbTraceCount => ({ 21 | x: pbTraceCount.timestamp, 22 | y: pbTraceCount.count 23 | } 24 | )); 25 | 26 | module.exports = converter; 27 | -------------------------------------------------------------------------------- /server/connectors/traces/haystack/search/searchRequestBuilder.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const expressionTreeBuilder = require('../expressionTreeBuilder'); 18 | 19 | const requestBuilder = {}; 20 | const messages = require('../../../../../static_codegen/traceReader_pb'); 21 | 22 | const DEFAULT_RESULTS_LIMIT = 25; 23 | 24 | requestBuilder.buildRequest = (query) => { 25 | const request = new messages.TracesSearchRequest(); 26 | 27 | request.setFilterexpression(expressionTreeBuilder.createFilterExpression(query)); 28 | request.setStarttime(parseInt(query.startTime, 10)); 29 | request.setEndtime(parseInt(query.endTime, 10)); 30 | request.setLimit(parseInt(query.limit, 10) || DEFAULT_RESULTS_LIMIT); 31 | 32 | return request; 33 | }; 34 | 35 | module.exports = requestBuilder; 36 | -------------------------------------------------------------------------------- /server/connectors/utils/encoders/Base64Encoder.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | class Base64Encoder { 18 | static encode(name) { 19 | return Buffer.from(name).toString('base64').replace(new RegExp('=', 'g'), '_'); 20 | } 21 | 22 | static decode(name) { 23 | return Buffer.from(name.replace(new RegExp('_', 'g'), '='), 'base64').toString('utf8'); 24 | } 25 | } 26 | 27 | module.exports = Base64Encoder; 28 | -------------------------------------------------------------------------------- /server/connectors/utils/encoders/MetricpointNameEncoder.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const PeriodReplacementEncoder = require('../encoders/PeriodReplacementEncoder'); 18 | const Base64Encoder = require('../encoders/Base64Encoder'); 19 | const NoopEncoder = require('../encoders/NoopEncoder'); 20 | 21 | class MetricpointNameEncoder { 22 | constructor(encoderType) { 23 | if (encoderType === 'periodreplacement') { 24 | this.encoder = PeriodReplacementEncoder; 25 | } else if (encoderType === 'base64') { 26 | this.encoder = Base64Encoder; 27 | } else { 28 | this.encoder = NoopEncoder; 29 | } 30 | } 31 | 32 | encodeMetricpointName(operationName) { 33 | return this.encoder.encode(decodeURIComponent(operationName)); 34 | } 35 | 36 | decodeMetricpointName(operationName) { 37 | return this.encoder.decode(operationName); 38 | } 39 | } 40 | 41 | module.exports = MetricpointNameEncoder; 42 | -------------------------------------------------------------------------------- /server/connectors/utils/encoders/NoopEncoder.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | class NoopEncoder { 18 | static encode(name) { 19 | return name; 20 | } 21 | 22 | static decode(name) { 23 | return name; 24 | } 25 | } 26 | 27 | module.exports = NoopEncoder; 28 | -------------------------------------------------------------------------------- /server/connectors/utils/encoders/PeriodReplacementEncoder.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | class PeriodReplacementEncoder { 18 | static encode(name) { 19 | return name.replace(/\./gi, '___'); 20 | } 21 | 22 | static decode(name) { 23 | return name.replace(/___/gi, '.'); 24 | } 25 | } 26 | 27 | module.exports = PeriodReplacementEncoder; 28 | -------------------------------------------------------------------------------- /server/connectors/utils/errorConverter.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const converter = {}; 18 | 19 | converter.fromAxiosError = (error) => new Error(`${error} ${JSON.stringify(error.config)}`); 20 | 21 | converter.fromGrpcError = (error) => new Error(`${JSON.stringify(error)}`); 22 | 23 | module.exports = converter; 24 | -------------------------------------------------------------------------------- /server/connectors/utils/objectUtils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | const _ = require('lodash'); 17 | 18 | const utils = {}; 19 | 20 | utils.getPropIgnoringCase = (array, name) => { 21 | const prop = _.compact(array.map(query => JSON.parse(query.toLowerCase())[name.toLowerCase()])); 22 | return prop && prop.length > 0 && prop[0]; 23 | }; 24 | 25 | module.exports = utils; 26 | 27 | -------------------------------------------------------------------------------- /server/routes/auth.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const express = require('express'); 18 | const authenticatorWithRedirect = require('../sso/samlSsoAuthenticator').authenticatorWithRedirect; 19 | const authChecker = require('../sso/authChecker'); 20 | 21 | const router = express.Router(); 22 | 23 | const loggedOutHome = '/login'; 24 | 25 | const authenticate = req => authenticatorWithRedirect(req); 26 | 27 | const extractFullRedirectUrl = req => req.originalUrl.split('/auth/login?redirectUrl=').pop(); 28 | 29 | router.get('/login', (req, res, next) => { 30 | const redirectUrl = extractFullRedirectUrl(req); 31 | return authenticate(redirectUrl)(redirectUrl, res, next); 32 | }); 33 | 34 | // check for active login session and then renew user 35 | router.get('/renewlogin', authChecker.forApi, (req, res, next) => { 36 | req.login({...req.user, timestamp: Date.now()}, (err) => { 37 | if (err) { 38 | next(err); 39 | } else { 40 | res.send(200); 41 | } 42 | }); 43 | }); 44 | 45 | router.get('/logout', (req, res) => { 46 | req.logout(); 47 | req.session = null; 48 | res.redirect(loggedOutHome); 49 | }); 50 | 51 | module.exports = router; 52 | -------------------------------------------------------------------------------- /server/routes/login.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2018 Expedia Group 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | const express = require('express'); 19 | const config = require('../config/config'); 20 | const assets = require('../../public/assets.json'); 21 | 22 | const router = express.Router(); 23 | 24 | router.get('/', (req, res) => { 25 | res.render('index', { 26 | bundleAppJsPath: assets.app.js, 27 | bundleAppCssPath: assets.app.css, 28 | bundleCommonsJsPath: assets.commons.js, 29 | ssoAdfsDomain: config.saml && config.saml.adfsDomain 30 | }); 31 | }); 32 | 33 | module.exports = router; 34 | -------------------------------------------------------------------------------- /server/routes/serviceGraphApi.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const express = require('express'); 18 | const config = require('../config/config'); 19 | const handleResponsePromise = require('./utils/apiResponseHandler').handleResponsePromise; 20 | 21 | const serviceGraphConnector = require(`../connectors/serviceGraph/${config.connectors.serviceGraph.connectorName}/serviceGraphConnector`); // eslint-disable-line import/no-dynamic-require 22 | 23 | const router = express.Router(); 24 | 25 | router.get('/serviceGraph', (req, res, next) => { 26 | handleResponsePromise(res, next, 'svc_graph_SVC')( 27 | () => serviceGraphConnector.getServiceGraphForTimeLine(req.query.from, req.query.to) 28 | 29 | ); 30 | }); 31 | 32 | module.exports = router; 33 | -------------------------------------------------------------------------------- /server/routes/serviceInsightsApi.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const express = require('express'); 18 | const {handleResponsePromise} = require('./utils/apiResponseHandler'); 19 | 20 | const serviceInsightsConnector = require('../connectors/serviceInsights/serviceInsightsConnector'); 21 | 22 | const router = express.Router(); 23 | 24 | router.get('/serviceInsights', (req, res, next) => { 25 | handleResponsePromise(res, next, 'svc_insights_SVC')(() => { 26 | const {serviceName, operationName, traceId, startTime, endTime, limit, relationship} = req.query; 27 | return serviceInsightsConnector.getServiceInsightsForService({ 28 | serviceName, 29 | operationName, 30 | traceId, 31 | startTime, 32 | endTime, 33 | limit, 34 | relationship 35 | }); 36 | }); 37 | }); 38 | 39 | module.exports = router; 40 | -------------------------------------------------------------------------------- /server/routes/servicesApi.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const express = require('express'); 18 | 19 | const handleResponsePromise = require('./utils/apiResponseHandler').handleResponsePromise; 20 | const servicesConnector = require('../connectors/services/servicesConnector'); 21 | 22 | const router = express.Router(); 23 | 24 | router.get('/services', (req, res, next) => { 25 | handleResponsePromise(res, next, 'services')( 26 | () => servicesConnector.getServices() 27 | ); 28 | }); 29 | 30 | router.get('/operations', (req, res, next) => { 31 | handleResponsePromise(res, next, 'operations')( 32 | () => servicesConnector.getOperations(req.query.serviceName) 33 | ); 34 | }); 35 | 36 | module.exports = router; 37 | -------------------------------------------------------------------------------- /server/routes/servicesPerfApi.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const express = require('express'); 18 | 19 | const config = require('../config/config'); 20 | const handleResponsePromise = require('./utils/apiResponseHandler').handleResponsePromise; 21 | 22 | const trendsConnector = require(`../connectors/trends/${config.connectors.trends.connectorName}/trendsConnector`); // eslint-disable-line import/no-dynamic-require 23 | 24 | const router = express.Router(); 25 | 26 | router.get('/servicePerf', (req, res, next) => { 27 | const { 28 | granularity, 29 | from, 30 | until 31 | } = req.query; 32 | handleResponsePromise(res, next, 'servicePerf')( 33 | () => trendsConnector.getServicePerfStats(granularity, from, until) 34 | ); 35 | }); 36 | 37 | module.exports = router; 38 | -------------------------------------------------------------------------------- /server/routes/sso.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const express = require('express'); 18 | const authenticator = require('../sso/samlSsoAuthenticator').authenticator; 19 | const logger = require('../utils/logger').withIdentifier('sso'); 20 | 21 | const router = express.Router(); 22 | 23 | const extractFullRedirectUrl = req => req.originalUrl.split('/sso/saml/consume?redirectUrl=').pop(); 24 | 25 | router.use('/saml/consume', authenticator.authenticate('saml'), 26 | (req, res) => { 27 | const redirectUrl = extractFullRedirectUrl(req); 28 | logger.info(`action=authentication, status=success, redirectUrl=${redirectUrl}`); 29 | res.redirect(redirectUrl); 30 | }); 31 | 32 | module.exports = router; 33 | -------------------------------------------------------------------------------- /server/routes/user.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const express = require('express'); 18 | 19 | const router = express.Router(); 20 | 21 | router.get('/details', (req, res) => { 22 | res.type('application/json').send(req.user); 23 | }); 24 | 25 | module.exports = router; 26 | -------------------------------------------------------------------------------- /server/routes/utils/apiResponseHandler.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const metrics = require('../../utils/metrics'); 18 | 19 | const responseHandler = {}; 20 | 21 | responseHandler.handleResponsePromise = (response, next, pathName) => (operation) => { 22 | const timer = metrics.timer(`http_rq_${pathName}`).start(); 23 | 24 | operation() 25 | .then( 26 | (result) => response.json(result), 27 | (err) => { 28 | metrics.meter(`http_rq_${pathName}_failed`).mark(); 29 | next(err); 30 | } 31 | ) 32 | .fin(() => timer.end()) 33 | .done(); 34 | }; 35 | 36 | module.exports = responseHandler; 37 | -------------------------------------------------------------------------------- /server/sso/authChecker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | function authChecker(errorHandler) { 18 | return (req, res, next) => { 19 | if (req.user) { 20 | next(); 21 | } else { 22 | errorHandler(res, req.originalUrl); 23 | } 24 | }; 25 | } 26 | 27 | module.exports = { 28 | forPage: authChecker((res, redirectUrl) => { res.redirect(`/login?redirectUrl=${redirectUrl}`); }), 29 | forApi: authChecker((res) => { res.status(401).send('UNAUTHORIZED'); }) 30 | }; 31 | -------------------------------------------------------------------------------- /server/start.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | const app = require('./app'); 19 | const config = require('./config/config'); 20 | const Server = require('./utils/server'); 21 | 22 | const server = new Server(app); 23 | 24 | if (config.cluster) { 25 | server.startInClusterMode(); 26 | } else { 27 | server.startInStandaloneMode(); 28 | } 29 | -------------------------------------------------------------------------------- /server/utils/metrics.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const measured = require('measured'); 18 | 19 | module.exports = measured.createCollection(); 20 | -------------------------------------------------------------------------------- /server/utils/metricsMiddleware.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const onFinished = require('finished'); 18 | const metrics = require('./metrics'); 19 | 20 | const middleware = {}; 21 | 22 | middleware.httpMetrics = (req, res, next) => { 23 | onFinished(res, () => { 24 | metrics.meter(`http_${res.statusCode}`).mark(); 25 | }); 26 | 27 | next(); 28 | }; 29 | 30 | module.exports = middleware; 31 | -------------------------------------------------------------------------------- /server/utils/metricsReporter.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const flatten = require('flat'); 18 | const metrics = require('./metrics'); 19 | const logger = require('./logger').withIdentifier('metrics'); 20 | 21 | const reporter = {}; 22 | 23 | reporter.start = (host, port, prefix, interval) => { 24 | setInterval(() => { 25 | const flattenedMetrics = flatten(metrics.toJSON()); 26 | 27 | // report to logs 28 | logger.info(prefix); 29 | logger.info(JSON.stringify(flattenedMetrics)); 30 | }, interval); 31 | }; 32 | 33 | module.exports = reporter; 34 | -------------------------------------------------------------------------------- /src/app.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import ReactDOM from 'react-dom'; 20 | import {Route, BrowserRouter as Router} from 'react-router-dom'; 21 | import Main from './main'; 22 | import storesInitializer from './stores/storesInitializer'; 23 | import withTracker from './components/common/withTracker'; 24 | 25 | // app initializers 26 | storesInitializer.init(); 27 | 28 | // mount react components 29 | ReactDOM.render( 30 | 31 | 32 | 33 | , document.getElementById('root') 34 | ); 35 | -------------------------------------------------------------------------------- /src/bootstrap/LICENSE_BOOTSTRAP: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/bootstrap/LICENSE_BOOTSWATCH: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Thomas Park 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/bootstrap/alerts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Alerts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // ------------------------- 8 | 9 | .alert { 10 | padding: @alert-padding; 11 | margin-bottom: @line-height-computed; 12 | border: 1px solid transparent; 13 | border-radius: @alert-border-radius; 14 | 15 | // Headings for larger alerts 16 | h4 { 17 | margin-top: 0; 18 | // Specified for the h4 to prevent conflicts of changing @headings-color 19 | color: inherit; 20 | } 21 | 22 | // Provide class for links that match alerts 23 | .alert-link { 24 | font-weight: @alert-link-font-weight; 25 | } 26 | 27 | // Improve alignment and spacing of inner content 28 | > p, 29 | > ul { 30 | margin-bottom: 0; 31 | } 32 | 33 | > p + p { 34 | margin-top: 5px; 35 | } 36 | } 37 | 38 | // Dismissible alerts 39 | // 40 | // Expand the right padding and account for the close button's positioning. 41 | 42 | .alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0. 43 | .alert-dismissible { 44 | padding-right: (@alert-padding + 20); 45 | 46 | // Adjust close link position 47 | .close { 48 | position: relative; 49 | top: -2px; 50 | right: -21px; 51 | color: inherit; 52 | } 53 | } 54 | 55 | // Alternate styles 56 | // 57 | // Generate contextual modifier classes for colorizing the alert. 58 | 59 | .alert-success { 60 | .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text); 61 | } 62 | 63 | .alert-info { 64 | .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text); 65 | } 66 | 67 | .alert-warning { 68 | .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text); 69 | } 70 | 71 | .alert-danger { 72 | .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text); 73 | } 74 | -------------------------------------------------------------------------------- /src/bootstrap/badges.less: -------------------------------------------------------------------------------- 1 | // 2 | // Badges 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .badge { 8 | display: inline-block; 9 | min-width: 10px; 10 | padding: 3px 7px; 11 | font-size: @font-size-small; 12 | font-weight: @badge-font-weight; 13 | color: @badge-color; 14 | line-height: @badge-line-height; 15 | vertical-align: middle; 16 | white-space: nowrap; 17 | text-align: center; 18 | background-color: @badge-bg; 19 | border-radius: @badge-border-radius; 20 | 21 | // Empty badges collapse automatically (not available in IE8) 22 | &:empty { 23 | display: none; 24 | } 25 | 26 | // Quick fix for badges in buttons 27 | .btn & { 28 | position: relative; 29 | top: -1px; 30 | } 31 | 32 | .btn-xs &, 33 | .btn-group-xs > .btn & { 34 | top: 0; 35 | padding: 1px 5px; 36 | } 37 | 38 | // Hover state, but only for links 39 | a& { 40 | &:hover, 41 | &:focus { 42 | color: @badge-link-hover-color; 43 | text-decoration: none; 44 | cursor: pointer; 45 | } 46 | } 47 | 48 | // Account for badges in navs 49 | .list-group-item.active > &, 50 | .nav-pills > .active > a > & { 51 | color: @badge-active-color; 52 | background-color: @badge-active-bg; 53 | } 54 | 55 | .list-group-item > & { 56 | float: right; 57 | } 58 | 59 | .list-group-item > & + & { 60 | margin-right: 5px; 61 | } 62 | 63 | .nav-pills > li > a > & { 64 | margin-left: 3px; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/bootstrap/bootstrap.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.7 (http://getbootstrap.com) 3 | * Copyright 2011-2016 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | // Core variables and mixins 8 | @import 'variables.less'; 9 | @import 'mixins.less'; 10 | 11 | // Reset and dependencies 12 | @import 'normalize.less'; 13 | @import 'print.less'; 14 | @import 'themify-icons.less'; 15 | @import 'titillium-web.less'; 16 | 17 | // Core CSS 18 | @import 'scaffolding.less'; 19 | @import 'type.less'; 20 | @import 'code.less'; 21 | @import 'grid.less'; 22 | @import 'tables.less'; 23 | @import 'forms.less'; 24 | @import 'buttons.less'; 25 | 26 | // Components 27 | @import 'component-animations.less'; 28 | @import 'dropdowns.less'; 29 | @import 'button-groups.less'; 30 | @import 'input-groups.less'; 31 | @import 'navs.less'; 32 | @import 'navbar.less'; 33 | //@import "breadcrumbs.less"; 34 | @import 'pagination.less'; 35 | @import 'pager.less'; 36 | @import 'labels.less'; 37 | @import 'badges.less'; 38 | @import 'jumbotron.less'; 39 | //@import "thumbnails.less"; 40 | //@import "alerts.less"; 41 | //@import "progress-bars.less"; 42 | //@import "media.less"; 43 | @import 'list-group.less'; 44 | //@import "panels.less"; 45 | //@import "responsive-embed.less"; 46 | @import 'wells.less'; 47 | @import 'close.less'; 48 | 49 | // Components w/ JavaScript 50 | @import 'modals.less'; 51 | @import 'tooltip.less'; 52 | //@import "popovers.less"; 53 | //@import "carousel.less"; 54 | 55 | // Utility classes 56 | @import 'utilities.less'; 57 | @import 'responsive-utilities.less'; 58 | 59 | // custom colors 60 | @import 'svc-colors.less'; 61 | 62 | // Theme override 63 | @import 'bootswatch.less'; 64 | -------------------------------------------------------------------------------- /src/bootstrap/breadcrumbs.less: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal; 8 | margin-bottom: @line-height-computed; 9 | list-style: none; 10 | background-color: @breadcrumb-bg; 11 | border-radius: @border-radius-base; 12 | 13 | > li { 14 | display: inline-block; 15 | 16 | + li:before { 17 | content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space 18 | padding: 0 5px; 19 | color: @breadcrumb-color; 20 | } 21 | } 22 | 23 | > .active { 24 | color: @breadcrumb-active-color; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/bootstrap/close.less: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: (@font-size-base * 1.5); 9 | font-weight: @close-font-weight; 10 | line-height: 1; 11 | color: @close-color; 12 | text-shadow: @close-text-shadow; 13 | .opacity(.2); 14 | 15 | &:hover, 16 | &:focus { 17 | color: @close-color; 18 | text-decoration: none; 19 | cursor: pointer; 20 | .opacity(.5); 21 | } 22 | 23 | // Additional properties for button version 24 | // iOS requires the button element instead of an anchor tag. 25 | // If you want the anchor version, it requires `href="#"`. 26 | // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile 27 | button& { 28 | padding: 0; 29 | cursor: pointer; 30 | background: transparent; 31 | border: 0; 32 | -webkit-appearance: none; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/bootstrap/code.less: -------------------------------------------------------------------------------- 1 | // 2 | // Code (inline and block) 3 | // -------------------------------------------------- 4 | 5 | 6 | // Inline and block code styles 7 | code, 8 | kbd, 9 | pre, 10 | samp { 11 | font-family: @font-family-monospace; 12 | } 13 | 14 | // Inline code 15 | code { 16 | padding: 2px 4px; 17 | font-size: 90%; 18 | color: @code-color; 19 | background-color: @code-bg; 20 | border-radius: @border-radius-base; 21 | } 22 | 23 | // User input typically entered via keyboard 24 | kbd { 25 | padding: 2px 4px; 26 | font-size: 90%; 27 | color: @kbd-color; 28 | background-color: @kbd-bg; 29 | border-radius: @border-radius-small; 30 | box-shadow: inset 0 -1px 0 rgba(0,0,0,.25); 31 | 32 | kbd { 33 | padding: 0; 34 | font-size: 100%; 35 | font-weight: bold; 36 | box-shadow: none; 37 | } 38 | } 39 | 40 | // Blocks of code 41 | pre { 42 | display: block; 43 | padding: ((@line-height-computed - 1) / 2); 44 | margin: 0 0 (@line-height-computed / 2); 45 | font-size: (@font-size-base - 1); // 14px to 13px 46 | line-height: @line-height-base; 47 | word-break: break-all; 48 | word-wrap: break-word; 49 | color: @pre-color; 50 | background-color: @pre-bg; 51 | border: 1px solid @pre-border-color; 52 | border-radius: @border-radius-base; 53 | 54 | // Account for some code outputs that place code tags in pre tags 55 | code { 56 | padding: 0; 57 | font-size: inherit; 58 | color: inherit; 59 | white-space: pre-wrap; 60 | background-color: transparent; 61 | border-radius: 0; 62 | } 63 | } 64 | 65 | // Enable scrollable blocks of code 66 | .pre-scrollable { 67 | max-height: @pre-scrollable-max-height; 68 | overflow-y: scroll; 69 | } 70 | -------------------------------------------------------------------------------- /src/bootstrap/component-animations.less: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | // Heads up! 6 | // 7 | // We don't use the `.opacity()` mixin here since it causes a bug with text 8 | // fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552. 9 | 10 | .fade { 11 | opacity: 0; 12 | .transition(opacity .15s linear); 13 | &.in { 14 | opacity: 1; 15 | } 16 | } 17 | 18 | .collapse { 19 | display: none; 20 | 21 | &.in { display: block; } 22 | tr&.in { display: table-row; } 23 | tbody&.in { display: table-row-group; } 24 | } 25 | 26 | .collapsing { 27 | position: relative; 28 | height: 0; 29 | overflow: hidden; 30 | .transition-property(~"height, visibility"); 31 | .transition-duration(.35s); 32 | .transition-timing-function(ease); 33 | } 34 | -------------------------------------------------------------------------------- /src/bootstrap/grid.less: -------------------------------------------------------------------------------- 1 | // 2 | // Grid system 3 | // -------------------------------------------------- 4 | 5 | 6 | // Container widths 7 | // 8 | // Set the container width, and override it for fixed navbars in media queries. 9 | 10 | .container { 11 | .container-fixed(); 12 | 13 | @media (min-width: @screen-sm-min) { 14 | width: @container-sm; 15 | } 16 | @media (min-width: @screen-md-min) { 17 | width: @container-md; 18 | } 19 | @media (min-width: @screen-lg-min) { 20 | width: @container-lg; 21 | } 22 | } 23 | 24 | 25 | // Fluid container 26 | // 27 | // Utilizes the mixin meant for fixed width containers, but without any defined 28 | // width for fluid, full width layouts. 29 | 30 | .container-fluid { 31 | .container-fixed(); 32 | } 33 | 34 | 35 | // Row 36 | // 37 | // Rows contain and clear the floats of your columns. 38 | 39 | .row { 40 | .make-row(); 41 | } 42 | 43 | 44 | // Columns 45 | // 46 | // Common styles for small and large grid columns 47 | 48 | .make-grid-columns(); 49 | 50 | 51 | // Extra small grid 52 | // 53 | // Columns, offsets, pushes, and pulls for extra small devices like 54 | // smartphones. 55 | 56 | .make-grid(xs); 57 | 58 | 59 | // Small grid 60 | // 61 | // Columns, offsets, pushes, and pulls for the small device range, from phones 62 | // to tablets. 63 | 64 | @media (min-width: @screen-sm-min) { 65 | .make-grid(sm); 66 | } 67 | 68 | 69 | // Medium grid 70 | // 71 | // Columns, offsets, pushes, and pulls for the desktop device range. 72 | 73 | @media (min-width: @screen-md-min) { 74 | .make-grid(md); 75 | } 76 | 77 | 78 | // Large grid 79 | // 80 | // Columns, offsets, pushes, and pulls for the large desktop device range. 81 | 82 | @media (min-width: @screen-lg-min) { 83 | .make-grid(lg); 84 | } 85 | -------------------------------------------------------------------------------- /src/bootstrap/jumbotron.less: -------------------------------------------------------------------------------- 1 | // 2 | // Jumbotron 3 | // -------------------------------------------------- 4 | 5 | 6 | .jumbotron { 7 | padding-top: @jumbotron-padding; 8 | padding-bottom: @jumbotron-padding; 9 | margin-bottom: @jumbotron-padding; 10 | color: @jumbotron-color; 11 | background-color: @jumbotron-bg; 12 | 13 | h1, 14 | .h1 { 15 | color: @jumbotron-heading-color; 16 | } 17 | 18 | p { 19 | margin-bottom: (@jumbotron-padding / 2); 20 | font-size: @jumbotron-font-size; 21 | color: #fff; 22 | } 23 | 24 | > hr { 25 | border-top-color: darken(@jumbotron-bg, 10%); 26 | } 27 | 28 | .container &, 29 | .container-fluid & { 30 | border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container 31 | padding-left: (@grid-gutter-width / 2); 32 | padding-right: (@grid-gutter-width / 2); 33 | } 34 | 35 | .container { 36 | max-width: 100%; 37 | } 38 | 39 | @media screen and (min-width: @screen-sm-min) { 40 | padding-top: (@jumbotron-padding * 1.6); 41 | padding-bottom: (@jumbotron-padding * 1.6); 42 | 43 | .container &, 44 | .container-fluid & { 45 | padding-left: (@jumbotron-padding * 2); 46 | padding-right: (@jumbotron-padding * 2); 47 | } 48 | 49 | h1, 50 | .h1 { 51 | font-size: @jumbotron-heading-font-size; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/bootstrap/labels.less: -------------------------------------------------------------------------------- 1 | // 2 | // Labels 3 | // -------------------------------------------------- 4 | 5 | .label { 6 | display: inline; 7 | padding: .2em .6em .3em; 8 | font-size: 75%; 9 | font-weight: bold; 10 | line-height: 1; 11 | color: @label-color; 12 | text-align: center; 13 | white-space: nowrap; 14 | vertical-align: baseline; 15 | border-radius: .25em; 16 | 17 | // Add hover effects, but only for links 18 | a& { 19 | &:hover, 20 | &:focus { 21 | color: @label-link-hover-color; 22 | text-decoration: none; 23 | cursor: pointer; 24 | } 25 | } 26 | 27 | // Empty labels collapse automatically (not available in IE8) 28 | &:empty { 29 | display: none; 30 | } 31 | 32 | // Quick fix for labels in buttons 33 | .btn & { 34 | position: relative; 35 | top: -1px; 36 | } 37 | } 38 | 39 | // Colors 40 | // Contextual variations (linked labels get darker on :hover) 41 | 42 | .label-default { 43 | .label-variant(@label-default-bg); 44 | } 45 | 46 | .label-primary { 47 | .label-variant(@label-primary-bg); 48 | } 49 | 50 | .label-success { 51 | .label-variant(@label-success-bg); 52 | } 53 | 54 | .label-info { 55 | .label-variant(@label-info-bg); 56 | } 57 | 58 | .label-warning { 59 | .label-variant(@label-warning-bg); 60 | } 61 | 62 | .label-danger { 63 | .label-variant(@label-danger-bg); 64 | } 65 | -------------------------------------------------------------------------------- /src/bootstrap/media.less: -------------------------------------------------------------------------------- 1 | .media { 2 | // Proper spacing between instances of .media 3 | margin-top: 15px; 4 | 5 | &:first-child { 6 | margin-top: 0; 7 | } 8 | } 9 | 10 | .media, 11 | .media-body { 12 | zoom: 1; 13 | overflow: hidden; 14 | } 15 | 16 | .media-body { 17 | width: 10000px; 18 | } 19 | 20 | .media-object { 21 | display: block; 22 | 23 | // Fix collapse in webkit from max-width: 100% and display: table-cell. 24 | &.img-thumbnail { 25 | max-width: none; 26 | } 27 | } 28 | 29 | .media-right, 30 | .media > .pull-right { 31 | padding-left: 10px; 32 | } 33 | 34 | .media-left, 35 | .media > .pull-left { 36 | padding-right: 10px; 37 | } 38 | 39 | .media-left, 40 | .media-right, 41 | .media-body { 42 | display: table-cell; 43 | vertical-align: top; 44 | } 45 | 46 | .media-middle { 47 | vertical-align: middle; 48 | } 49 | 50 | .media-bottom { 51 | vertical-align: bottom; 52 | } 53 | 54 | // Reset margins on headings for tighter default spacing 55 | .media-heading { 56 | margin-top: 0; 57 | margin-bottom: 5px; 58 | } 59 | 60 | // Media list variation 61 | // 62 | // Undo default ul/ol styles 63 | .media-list { 64 | padding-left: 0; 65 | list-style: none; 66 | } 67 | -------------------------------------------------------------------------------- /src/bootstrap/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------------------------------- 3 | 4 | // Utilities 5 | @import "mixins/hide-text.less"; 6 | @import "mixins/opacity.less"; 7 | @import "mixins/image.less"; 8 | @import "mixins/labels.less"; 9 | @import "mixins/reset-filter.less"; 10 | @import "mixins/resize.less"; 11 | @import "mixins/responsive-visibility.less"; 12 | @import "mixins/size.less"; 13 | @import "mixins/tab-focus.less"; 14 | @import "mixins/reset-text.less"; 15 | @import "mixins/text-emphasis.less"; 16 | @import "mixins/text-overflow.less"; 17 | @import "mixins/vendor-prefixes.less"; 18 | 19 | // Components 20 | @import "mixins/alerts.less"; 21 | @import "mixins/buttons.less"; 22 | @import "mixins/panels.less"; 23 | @import "mixins/pagination.less"; 24 | @import "mixins/list-group.less"; 25 | @import "mixins/nav-divider.less"; 26 | @import "mixins/forms.less"; 27 | @import "mixins/progress-bar.less"; 28 | @import "mixins/table-row.less"; 29 | 30 | // Skins 31 | @import "mixins/background-variant.less"; 32 | @import "mixins/border-radius.less"; 33 | @import "mixins/gradients.less"; 34 | 35 | // PageLayout 36 | @import "mixins/clearfix.less"; 37 | @import "mixins/center-block.less"; 38 | @import "mixins/nav-vertical-align.less"; 39 | @import "mixins/grid-framework.less"; 40 | @import "mixins/grid.less"; 41 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/alerts.less: -------------------------------------------------------------------------------- 1 | // Alerts 2 | 3 | .alert-variant(@background; @border; @text-color) { 4 | background-color: @background; 5 | border-color: @border; 6 | color: @text-color; 7 | 8 | hr { 9 | border-top-color: darken(@border, 5%); 10 | } 11 | .alert-link { 12 | color: darken(@text-color, 10%); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/background-variant.less: -------------------------------------------------------------------------------- 1 | // Contextual backgrounds 2 | 3 | .bg-variant(@color) { 4 | background-color: @color; 5 | a&:hover, 6 | a&:focus { 7 | background-color: darken(@color, 10%); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/border-radius.less: -------------------------------------------------------------------------------- 1 | // Single side border-radius 2 | 3 | .border-top-radius(@radius) { 4 | border-top-right-radius: @radius; 5 | border-top-left-radius: @radius; 6 | } 7 | .border-right-radius(@radius) { 8 | border-bottom-right-radius: @radius; 9 | border-top-right-radius: @radius; 10 | } 11 | .border-bottom-radius(@radius) { 12 | border-bottom-right-radius: @radius; 13 | border-bottom-left-radius: @radius; 14 | } 15 | .border-left-radius(@radius) { 16 | border-bottom-left-radius: @radius; 17 | border-top-left-radius: @radius; 18 | } 19 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/buttons.less: -------------------------------------------------------------------------------- 1 | // Button variants 2 | // 3 | // Easily pump out default styles, as well as :hover, :focus, :active, 4 | // and disabled options for all buttons 5 | 6 | .button-variant(@color; @background; @border) { 7 | color: @color; 8 | background-color: @background; 9 | border-color: @border; 10 | 11 | &:focus, 12 | &.focus { 13 | color: @color; 14 | background-color: darken(@background, 10%); 15 | border-color: darken(@border, 25%); 16 | } 17 | &:hover { 18 | color: @color; 19 | background-color: darken(@background, 10%); 20 | border-color: darken(@border, 12%); 21 | } 22 | &:active, 23 | &.active, 24 | .open > .dropdown-toggle& { 25 | color: @color; 26 | background-color: darken(@background, 10%); 27 | border-color: darken(@border, 12%); 28 | 29 | &:hover, 30 | &:focus, 31 | &.focus { 32 | color: @color; 33 | background-color: darken(@background, 17%); 34 | border-color: darken(@border, 25%); 35 | } 36 | } 37 | &:active, 38 | &.active, 39 | .open > .dropdown-toggle& { 40 | background-image: none; 41 | } 42 | &.disabled, 43 | &[disabled], 44 | fieldset[disabled] & { 45 | &:hover, 46 | &:focus, 47 | &.focus { 48 | background-color: @background; 49 | border-color: @border; 50 | } 51 | } 52 | 53 | .badge { 54 | color: @background; 55 | background-color: @color; 56 | } 57 | } 58 | 59 | // Button sizes 60 | .button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) { 61 | padding: @padding-vertical @padding-horizontal; 62 | font-size: @font-size; 63 | line-height: @line-height; 64 | border-radius: @border-radius; 65 | } 66 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/center-block.less: -------------------------------------------------------------------------------- 1 | // Center-align a block level element 2 | 3 | .center-block() { 4 | display: block; 5 | margin-left: auto; 6 | margin-right: auto; 7 | } 8 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/clearfix.less: -------------------------------------------------------------------------------- 1 | // Clearfix 2 | // 3 | // For modern browsers 4 | // 1. The space content is one way to avoid an Opera bug when the 5 | // contenteditable attribute is included anywhere else in the document. 6 | // Otherwise it causes space to appear at the top and bottom of elements 7 | // that are clearfixed. 8 | // 2. The use of `table` rather than `block` is only necessary if using 9 | // `:before` to contain the top-margins of child elements. 10 | // 11 | // Source: http://nicolasgallagher.com/micro-clearfix-hack/ 12 | 13 | .clearfix() { 14 | &:before, 15 | &:after { 16 | content: ' '; // 1 17 | display: table; // 2 18 | } 19 | &:after { 20 | clear: both; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/hide-text.less: -------------------------------------------------------------------------------- 1 | // CSS image replacement 2 | // 3 | // Heads up! v3 launched with only `.hide-text()`, but per our pattern for 4 | // mixins being reused as classes with the same name, this doesn't hold up. As 5 | // of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`. 6 | // 7 | // Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757 8 | 9 | // Deprecated as of v3.0.1 (has been removed in v4) 10 | .hide-text() { 11 | font: ~'0/0' a; 12 | color: transparent; 13 | text-shadow: none; 14 | background-color: transparent; 15 | border: 0; 16 | } 17 | 18 | // New mixin to use as of v3.0.1 19 | .text-hide() { 20 | .hide-text(); 21 | } 22 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/image.less: -------------------------------------------------------------------------------- 1 | // Image Mixins 2 | // - Responsive image 3 | // - Retina image 4 | 5 | // Responsive image 6 | // 7 | // Keep images from scaling beyond the width of their parents. 8 | .img-responsive(@display: block) { 9 | display: @display; 10 | max-width: 100%; // Part 1: Set a maximum relative to the parent 11 | height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching 12 | } 13 | 14 | // Retina image 15 | // 16 | // Short retina mixin for setting background-image and -size. Note that the 17 | // spelling of `min--moz-device-pixel-ratio` is intentional. 18 | .img-retina(@file-1x; @file-2x; @width-1x; @height-1x) { 19 | background-image: url('@{file-1x}'); 20 | 21 | @media only screen and (-webkit-min-device-pixel-ratio: 2), 22 | only screen and (min--moz-device-pixel-ratio: 2), 23 | only screen and (-o-min-device-pixel-ratio: 2/1), 24 | only screen and (min-device-pixel-ratio: 2), 25 | only screen and (min-resolution: 192dpi), 26 | only screen and (min-resolution: 2dppx) { 27 | background-image: url('@{file-2x}'); 28 | background-size: @width-1x @height-1x; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/labels.less: -------------------------------------------------------------------------------- 1 | // Labels 2 | 3 | .label-variant(@color) { 4 | background-color: @color; 5 | 6 | &[href] { 7 | &:hover, 8 | &:focus { 9 | background-color: darken(@color, 10%); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/list-group.less: -------------------------------------------------------------------------------- 1 | // List Groups 2 | 3 | .list-group-item-variant(@state; @background; @color) { 4 | .list-group-item-@{state} { 5 | color: @color; 6 | background-color: @background; 7 | 8 | a&, 9 | button& { 10 | color: @color; 11 | 12 | .list-group-item-heading { 13 | color: inherit; 14 | } 15 | 16 | &:hover, 17 | &:focus { 18 | color: @color; 19 | background-color: darken(@background, 5%); 20 | } 21 | &.active, 22 | &.active:hover, 23 | &.active:focus { 24 | color: #fff; 25 | background-color: @color; 26 | border-color: @color; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/nav-divider.less: -------------------------------------------------------------------------------- 1 | // Horizontal dividers 2 | // 3 | // Dividers (basically an hr) within dropdowns and nav lists 4 | 5 | .nav-divider(@color: #e5e5e5) { 6 | height: 1px; 7 | margin: ((@line-height-computed / 2) - 1) 0; 8 | overflow: hidden; 9 | background-color: @color; 10 | } 11 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/nav-vertical-align.less: -------------------------------------------------------------------------------- 1 | // Navbar vertical align 2 | // 3 | // Vertically center elements in the navbar. 4 | // Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin. 5 | 6 | .navbar-vertical-align(@element-height) { 7 | margin-top: ((@navbar-height - @element-height) / 2); 8 | margin-bottom: ((@navbar-height - @element-height) / 2); 9 | } 10 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/opacity.less: -------------------------------------------------------------------------------- 1 | // Opacity 2 | 3 | .opacity(@opacity) { 4 | opacity: @opacity; 5 | // IE8 filter 6 | @opacity-ie: (@opacity * 100); 7 | filter: ~'alpha(opacity=@{opacity-ie})'; 8 | } 9 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/pagination.less: -------------------------------------------------------------------------------- 1 | // Pagination 2 | 3 | .pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) { 4 | > li { 5 | > a, 6 | > span { 7 | padding: @padding-vertical @padding-horizontal; 8 | font-size: @font-size; 9 | line-height: @line-height; 10 | } 11 | &:first-child { 12 | > a, 13 | > span { 14 | .border-left-radius(@border-radius); 15 | } 16 | } 17 | &:last-child { 18 | > a, 19 | > span { 20 | .border-right-radius(@border-radius); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/panels.less: -------------------------------------------------------------------------------- 1 | // Panels 2 | 3 | .panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) { 4 | border-color: @border; 5 | 6 | & > .panel-heading { 7 | color: @heading-text-color; 8 | background-color: @heading-bg-color; 9 | border-color: @heading-border; 10 | 11 | + .panel-collapse > .panel-body { 12 | border-top-color: @border; 13 | } 14 | .badge { 15 | color: @heading-bg-color; 16 | background-color: @heading-text-color; 17 | } 18 | } 19 | & > .panel-footer { 20 | + .panel-collapse > .panel-body { 21 | border-bottom-color: @border; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/progress-bar.less: -------------------------------------------------------------------------------- 1 | // Progress bars 2 | 3 | .progress-bar-variant(@color) { 4 | background-color: @color; 5 | 6 | // Deprecated parent class requirement as of v3.2.0 7 | .progress-striped & { 8 | #gradient > .striped(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/reset-filter.less: -------------------------------------------------------------------------------- 1 | // Reset filters for IE 2 | // 3 | // When you need to remove a gradient background, do not forget to use this to reset 4 | // the IE filter for IE9 and below. 5 | 6 | .reset-filter() { 7 | filter: e(%('progid:DXImageTransform.Microsoft.gradient(enabled = false)')); 8 | } 9 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/reset-text.less: -------------------------------------------------------------------------------- 1 | .reset-text() { 2 | font-family: @font-family-base; 3 | // We deliberately do NOT reset font-size. 4 | font-style: normal; 5 | font-weight: normal; 6 | letter-spacing: normal; 7 | line-break: auto; 8 | line-height: @line-height-base; 9 | text-align: left; // Fallback for where `start` is not supported 10 | text-align: start; 11 | text-decoration: none; 12 | text-shadow: none; 13 | text-transform: none; 14 | white-space: normal; 15 | word-break: normal; 16 | word-spacing: normal; 17 | word-wrap: normal; 18 | } 19 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/resize.less: -------------------------------------------------------------------------------- 1 | // Resize anything 2 | 3 | .resizable(@direction) { 4 | resize: @direction; // Options: horizontal, vertical, both 5 | overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible` 6 | } 7 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/responsive-visibility.less: -------------------------------------------------------------------------------- 1 | // Responsive utilities 2 | 3 | // 4 | // More easily include all the states for responsive-utilities.less. 5 | .responsive-visibility() { 6 | display: block !important; 7 | table& { 8 | display: table !important; 9 | } 10 | tr& { 11 | display: table-row !important; 12 | } 13 | th&, 14 | td& { 15 | display: table-cell !important; 16 | } 17 | } 18 | 19 | .responsive-invisibility() { 20 | display: none !important; 21 | } 22 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/size.less: -------------------------------------------------------------------------------- 1 | // Sizing shortcuts 2 | 3 | .size(@width; @height) { 4 | width: @width; 5 | height: @height; 6 | } 7 | 8 | .square(@size) { 9 | .size(@size; @size); 10 | } 11 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/tab-focus.less: -------------------------------------------------------------------------------- 1 | // WebKit-style focus 2 | 3 | .tab-focus() { 4 | // WebKit-specific. Other browsers will keep their default outline style. 5 | // (Initially tried to also force default via `outline: initial`, 6 | // but that seems to erroneously remove the outline in Firefox altogether.) 7 | outline: 5px auto -webkit-focus-ring-color; 8 | outline-offset: -2px; 9 | } 10 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/table-row.less: -------------------------------------------------------------------------------- 1 | // Tables 2 | 3 | .table-row-variant(@state; @background) { 4 | // Exact selectors below required to override `.table-striped` and prevent 5 | // inheritance to nested tables. 6 | .table > thead > tr, 7 | .table > tbody > tr, 8 | .table > tfoot > tr { 9 | > td.@{state}, 10 | > th.@{state}, 11 | &.@{state} > td, 12 | &.@{state} > th { 13 | background-color: @background; 14 | } 15 | } 16 | 17 | // Hover states for `.table-hover` 18 | // Note: this is not available for cells or rows within `thead` or `tfoot`. 19 | .table-hover > tbody > tr { 20 | > td.@{state}:hover, 21 | > th.@{state}:hover, 22 | &.@{state}:hover > td, 23 | &:hover > .@{state}, 24 | &.@{state}:hover > th { 25 | background-color: darken(@background, 5%); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/text-emphasis.less: -------------------------------------------------------------------------------- 1 | // Typography 2 | 3 | .text-emphasis-variant(@color) { 4 | color: @color; 5 | a&:hover, 6 | a&:focus { 7 | color: darken(@color, 10%); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/bootstrap/mixins/text-overflow.less: -------------------------------------------------------------------------------- 1 | // Text overflow 2 | // Requires inline-block or block for proper styling 3 | 4 | .text-overflow() { 5 | overflow: hidden; 6 | text-overflow: ellipsis; 7 | white-space: nowrap; 8 | } 9 | -------------------------------------------------------------------------------- /src/bootstrap/pager.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pager pagination 3 | // -------------------------------------------------- 4 | 5 | 6 | .pager { 7 | padding-left: 0; 8 | margin: @line-height-computed 0; 9 | list-style: none; 10 | text-align: center; 11 | &:extend(.clearfix all); 12 | li { 13 | display: inline; 14 | > a, 15 | > span { 16 | display: inline-block; 17 | padding: 5px 14px; 18 | background-color: @pager-bg; 19 | border: 1px solid @pager-border; 20 | border-radius: @pager-border-radius; 21 | } 22 | 23 | > a:hover, 24 | > a:focus { 25 | text-decoration: none; 26 | background-color: @pager-hover-bg; 27 | } 28 | } 29 | 30 | .next { 31 | > a, 32 | > span { 33 | float: right; 34 | } 35 | } 36 | 37 | .previous { 38 | > a, 39 | > span { 40 | float: left; 41 | } 42 | } 43 | 44 | .disabled { 45 | > a, 46 | > a:hover, 47 | > a:focus, 48 | > span { 49 | color: @pager-disabled-color; 50 | background-color: @pager-bg; 51 | cursor: @cursor-disabled; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/bootstrap/responsive-embed.less: -------------------------------------------------------------------------------- 1 | // Embeds responsive 2 | // 3 | // Credit: Nicolas Gallagher and SUIT CSS. 4 | 5 | .embed-responsive { 6 | position: relative; 7 | display: block; 8 | height: 0; 9 | padding: 0; 10 | overflow: hidden; 11 | 12 | .embed-responsive-item, 13 | iframe, 14 | embed, 15 | object, 16 | video { 17 | position: absolute; 18 | top: 0; 19 | left: 0; 20 | bottom: 0; 21 | height: 100%; 22 | width: 100%; 23 | border: 0; 24 | } 25 | } 26 | 27 | // Modifier class for 16:9 aspect ratio 28 | .embed-responsive-16by9 { 29 | padding-bottom: 56.25%; 30 | } 31 | 32 | // Modifier class for 4:3 aspect ratio 33 | .embed-responsive-4by3 { 34 | padding-bottom: 75%; 35 | } 36 | -------------------------------------------------------------------------------- /src/bootstrap/thumbnails.less: -------------------------------------------------------------------------------- 1 | // 2 | // Thumbnails 3 | // -------------------------------------------------- 4 | 5 | 6 | // Mixin and adjust the regular image class 7 | .thumbnail { 8 | display: block; 9 | padding: @thumbnail-padding; 10 | margin-bottom: @line-height-computed; 11 | line-height: @line-height-base; 12 | background-color: @thumbnail-bg; 13 | border: 1px solid @thumbnail-border; 14 | border-radius: @thumbnail-border-radius; 15 | .transition(border .2s ease-in-out); 16 | 17 | > img, 18 | a > img { 19 | &:extend(.img-responsive); 20 | margin-left: auto; 21 | margin-right: auto; 22 | } 23 | 24 | // Add a hover state for linked versions only 25 | a&:hover, 26 | a&:focus, 27 | a&.active { 28 | border-color: @link-color; 29 | } 30 | 31 | // Image captions 32 | .caption { 33 | padding: @thumbnail-caption-padding; 34 | color: @thumbnail-caption-color; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/bootstrap/utilities.less: -------------------------------------------------------------------------------- 1 | // 2 | // Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Floats 7 | // ------------------------- 8 | 9 | .clearfix { 10 | .clearfix(); 11 | } 12 | .center-block { 13 | .center-block(); 14 | } 15 | .pull-right { 16 | float: right !important; 17 | } 18 | .pull-left { 19 | float: left !important; 20 | } 21 | 22 | 23 | // Toggling content 24 | // ------------------------- 25 | 26 | // Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1 27 | .hide { 28 | display: none !important; 29 | } 30 | .show { 31 | display: block !important; 32 | } 33 | .invisible { 34 | visibility: hidden; 35 | } 36 | .text-hide { 37 | .text-hide(); 38 | } 39 | 40 | 41 | // Hide from screenreaders and browsers 42 | // 43 | // Credit: HTML5 Boilerplate 44 | 45 | .hidden { 46 | display: none !important; 47 | } 48 | 49 | 50 | // For Affix plugin 51 | // ------------------------- 52 | 53 | .affix { 54 | position: fixed; 55 | } 56 | -------------------------------------------------------------------------------- /src/bootstrap/wells.less: -------------------------------------------------------------------------------- 1 | // 2 | // Wells 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .well { 8 | min-height: 20px; 9 | padding: 19px; 10 | margin-bottom: 20px; 11 | background-color: @well-bg; 12 | border: 1px solid @well-border; 13 | border-radius: @border-radius-base; 14 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 15 | blockquote { 16 | border-color: #ddd; 17 | border-color: rgba(0,0,0,.15); 18 | } 19 | } 20 | 21 | // Sizes 22 | .well-lg { 23 | padding: 24px; 24 | border-radius: @border-radius-large; 25 | } 26 | .well-sm { 27 | padding: 9px; 28 | border-radius: @border-radius-small; 29 | } 30 | -------------------------------------------------------------------------------- /src/components/alerts/stores/alertTrendFetcher.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import Q from 'q'; 17 | import axios from 'axios'; 18 | import ErrorHandlingStore from '../../../stores/errorHandlingStore'; 19 | 20 | const fetcher = {}; 21 | 22 | fetcher.fetchOperationTrends = (serviceName, operationName, granularity, from, until) => { 23 | const deferred = Q.defer(); 24 | 25 | axios 26 | .get(`/api/trends/operation/${encodeURIComponent(serviceName)}/${encodeURIComponent(operationName)}?granularity=${granularity}&from=${from}&until=${until}`) 27 | .then((result) => { 28 | deferred.resolve(result.data); 29 | }) 30 | .catch((result) => { 31 | ErrorHandlingStore.handleError(result); 32 | }); 33 | 34 | return deferred.promise; 35 | }; 36 | 37 | export default fetcher; 38 | -------------------------------------------------------------------------------- /src/components/alerts/utils/subscriptionConstructor.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | export default function newSubscriptionConstructor(serviceName, operationName, type, interval, dispatchers) { 19 | return { 20 | dispatchers, 21 | expressionTree: { 22 | serviceName, 23 | operationName, 24 | metric_key: type, 25 | stat: type === 'failure-span' ? 'count' : '*_99', 26 | interval, 27 | mtype: 'gauge', 28 | product: 'haystack' 29 | } 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src/components/common/error.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import PropTypes from 'prop-types'; 20 | 21 | import './error.less'; 22 | 23 | const Error = ({errorMessage}) => ( 24 |
25 |
26 |
{"Oops! Couldn't find the needle"}
27 |

{errorMessage}

28 |
29 |
30 | ); 31 | 32 | Error.defaultProps = { 33 | errorMessage: 'Please try again later' 34 | }; 35 | 36 | Error.propTypes = { 37 | errorMessage: PropTypes.string 38 | }; 39 | 40 | export default Error; 41 | -------------------------------------------------------------------------------- /src/components/common/error.less: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * Copyright 2018 Expedia Group 4 | * * 5 | * * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * * you may not use this file except in compliance with the License. 7 | * * You may obtain a copy of the License at 8 | * * 9 | * * http://www.apache.org/licenses/LICENSE-2.0 10 | * * 11 | * * Unless required by applicable law or agreed to in writing, software 12 | * * distributed under the License is distributed on an "AS IS" BASIS, 13 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * * See the License for the specific language governing permissions and 15 | * * limitations under the License. 16 | * * 17 | * 18 | */ 19 | 20 | @import (reference) '../../app'; 21 | 22 | .error-message_text { 23 | display: inline-block; 24 | margin: @spacing-xl; 25 | padding: @spacing-s @spacing-l @spacing-s @spacing-l; 26 | border: 1px solid @gray-mid-lighter; 27 | background-color: white; 28 | } 29 | -------------------------------------------------------------------------------- /src/components/common/loading.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import './loading.less'; 20 | 21 | export default () => ( 22 |
23 |
24 | loading... 25 |
Loading...
26 |
27 |
28 | ); 29 | -------------------------------------------------------------------------------- /src/components/common/loading.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | @import (reference) '../../app'; 19 | 20 | .loading_text { 21 | display: inline-block; 22 | margin: @spacing-xl; 23 | padding: @spacing-s @spacing-xl @spacing-s @spacing-xl; 24 | border: 2px solid @brand-primary; 25 | background-color: white; 26 | } 27 | -------------------------------------------------------------------------------- /src/components/common/login.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @import (reference) '../../app'; 18 | 19 | .login-cover { 20 | position: relative; 21 | height: 100%; 22 | background-color: @gray-darker; 23 | } 24 | 25 | .login-box { 26 | position: absolute; 27 | top: 150px; 28 | width: 100%; 29 | } 30 | 31 | .login-box_jumbotron { 32 | .jumbotron; 33 | display: inline-block; 34 | width: 600px; 35 | margin-right: 0; 36 | margin-left: 0; 37 | border-radius: 6px; 38 | background-color: @gray-lighter; 39 | } 40 | 41 | .login-box_title.h1 { 42 | color: @gray-dark; 43 | } 44 | 45 | .login-box_logo { 46 | width: 70px; 47 | margin-right: @spacing-s; 48 | padding-bottom: @spacing-l; 49 | } 50 | 51 | .login-box_btn { 52 | padding-left: @spacing-xl !important; 53 | padding-right: @spacing-xl !important; 54 | } 55 | -------------------------------------------------------------------------------- /src/components/common/modal.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @import (reference) '../../app'; 18 | 19 | .ReactModalPortal > * { 20 | // When the modal is closed, overlay div has no css class 21 | // This selector should be overridden by the `&--after-open` class below 22 | opacity: 0; 23 | } 24 | 25 | .ReactModal__Overlay { 26 | transition: opacity 200ms ease-in-out; 27 | background: rgba(0, 0, 0, 0.15); 28 | &--after-open { 29 | opacity: 1; 30 | } 31 | &--before-close { 32 | opacity: 0; 33 | } 34 | } 35 | 36 | /* Remove scroll on the body when react-modal is open */ 37 | .ReactModal__Body--open { 38 | overflow: hidden; 39 | } 40 | -------------------------------------------------------------------------------- /src/components/common/noMatch.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import {Link} from 'react-router-dom'; 20 | 21 | export default () => ( 22 |
23 |
24 |

Sorry!

25 |

Page not found

26 |

27 | Return to home 28 |

29 |
30 |
31 | ); 32 | -------------------------------------------------------------------------------- /src/components/common/timeRangePicker.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @import (reference) '../../app'; 18 | 19 | .custom-timerange-picker__datetime-from { 20 | margin-bottom: @spacing-s; 21 | } 22 | -------------------------------------------------------------------------------- /src/components/docs/help.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | 20 | export default () => ( 21 |
22 |
23 |

Help

24 |

Coming Soon!

25 |
26 |
27 | ); 28 | -------------------------------------------------------------------------------- /src/components/layout/authenticationTimeoutModal.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import Modal from 'react-modal'; 20 | 21 | const modalStyles = { 22 | overlay: { 23 | zIndex: 10, 24 | backgroundColor: 'rgba(0, 0, 0, 0.5)' 25 | }, 26 | content: { 27 | width: '45%', 28 | maxWidth: '1240px', 29 | top: '30%', 30 | bottom: 'auto', 31 | left: '0', 32 | right: '0', 33 | marginLeft: 'auto', 34 | marginRight: 'auto' 35 | } 36 | }; 37 | 38 | export default () => ( 39 | 40 |
41 |

Session Expired

42 |
43 | Please login to continue where you left 44 | off. 45 |
46 |
47 |
48 | ); 49 | -------------------------------------------------------------------------------- /src/components/layout/footer.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | @import (reference) '../../app'; 19 | 20 | .primary-footer { 21 | padding-top: @spacing-m; 22 | padding-bottom: @spacing-m; 23 | border-top: 1px solid @gray-mid-lighter; 24 | } 25 | 26 | .footer-links { 27 | display: block; 28 | padding: 0; 29 | } 30 | 31 | .footer-links__item { 32 | display: inline-block; 33 | margin-right: @spacing-l; 34 | } 35 | 36 | .footer-links__link { 37 | color: @text-color; 38 | } 39 | -------------------------------------------------------------------------------- /src/components/serviceGraph/graphSearch.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import PropTypes from 'prop-types'; 20 | 21 | const ServiceGraphSearch = ({searchString, searchStringChanged}) => ( 22 |
23 | { 27 | searchStringChanged(event.currentTarget.value); 28 | }} 29 | value={searchString} 30 | placeholder="Filter services..." 31 | /> 32 | 33 |
34 | ); 35 | 36 | ServiceGraphSearch.propTypes = { 37 | searchStringChanged: PropTypes.func.isRequired, 38 | searchString: PropTypes.string.isRequired 39 | }; 40 | 41 | export default ServiceGraphSearch; 42 | -------------------------------------------------------------------------------- /src/components/serviceGraph/serviceGraph.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import PropTypes from 'prop-types'; 20 | import ServiceGraphContainer from './serviceGraphContainer'; 21 | import './serviceGraph.less'; 22 | import serviceGraphStore from './stores/serviceGraphStore'; 23 | 24 | const ServiceGraph = ({history}) => { 25 | const search = { 26 | serviceName: undefined, 27 | time: { 28 | preset: '1h' 29 | } 30 | }; 31 | return ( 32 |
33 | 34 |
35 | ); 36 | }; 37 | 38 | ServiceGraph.propTypes = { 39 | history: PropTypes.object.isRequired 40 | }; 41 | export default ServiceGraph; 42 | -------------------------------------------------------------------------------- /src/components/serviceGraph/stores/serviceGraphStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import axios from 'axios'; 18 | import {action, observable} from 'mobx'; 19 | import {fromPromise} from 'mobx-utils'; 20 | import {ErrorHandlingStore} from '../../../stores/errorHandlingStore'; 21 | 22 | export class ServiceGraphStore extends ErrorHandlingStore { 23 | @observable graphs = []; 24 | @observable promiseState = null; 25 | @observable filterQuery = null; 26 | 27 | @action fetchServiceGraph(filterQuery) { 28 | this.promiseState = fromPromise( 29 | axios 30 | .get(`/api/serviceGraph?from=${filterQuery.from}&to=${filterQuery.to}`) 31 | .then((result) => { 32 | this.filterQuery = filterQuery; 33 | this.graphs = result.data; 34 | }) 35 | .catch((result) => { 36 | ServiceGraphStore.handleError(result); 37 | }) 38 | ); 39 | } 40 | } 41 | export default new ServiceGraphStore(); 42 | -------------------------------------------------------------------------------- /src/components/serviceGraph/vizceralConfig.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | renderer: 'region', 3 | layout: 'ltrTree', 4 | name: 'haystack', 5 | maxVolume: 100000, 6 | metadata: { 7 | streaming: 1 8 | }, 9 | updated: new Date().getTime(), 10 | nodes: [], 11 | connections: [] 12 | }; 13 | -------------------------------------------------------------------------------- /src/components/serviceGraph/vizceralExt.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Vizceral from 'vizceral-react'; 18 | 19 | class VizceralExt extends Vizceral { 20 | componentWillUnmount() { 21 | this.vizceral.animate = () => {}; 22 | delete this.vizceral; 23 | } 24 | } 25 | 26 | export default VizceralExt; 27 | -------------------------------------------------------------------------------- /src/components/serviceInsights/serviceInsights.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | .service-insights { 19 | margin-top: -18px; 20 | margin-bottom: -19px; 21 | } 22 | 23 | .service-insights__header { 24 | label { 25 | padding-right: 20px; 26 | } 27 | } 28 | 29 | .service-insights__filter { 30 | position: absolute; 31 | right: 50px; 32 | z-index: 1; 33 | span { 34 | display: inline-block; 35 | margin-right: 5px; 36 | } 37 | select { 38 | padding-right: 20px; 39 | } 40 | } 41 | 42 | .header-summary { 43 | position: absolute; 44 | max-width: 500px; 45 | margin-top: 20px; 46 | padding: 8px 16px; 47 | z-index: 1; 48 | background: #f2f4fbde; 49 | border: solid 1px #cacaca; 50 | pointer-events: none; 51 | 52 | .violation-grid { 53 | display: grid; 54 | grid-template-columns: auto auto; 55 | align-items: baseline; 56 | line-height: 14px; 57 | color: red; 58 | } 59 | 60 | .violation-title { 61 | padding-right: 5px; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/components/serviceInsights/serviceInsightsGraph/icons/db.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/serviceInsights/serviceInsightsGraph/icons/gateway.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/components/serviceInsights/serviceInsightsGraph/icons/outbound.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/components/serviceInsights/serviceInsightsGraph/icons/uninstrumented.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/components/serviceInsights/serviceInsightsGraph/labels.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import PropTypes from 'prop-types'; 20 | 21 | import Label from './label'; 22 | 23 | function Labels({nodes}) { 24 | return ( 25 | 26 | {nodes.map((node) => { 27 | if (node.data.type === 'mesh') { 28 | return null; 29 | } 30 | return 33 | ); 34 | } 35 | 36 | Labels.propTypes = { 37 | nodes: PropTypes.array.isRequired 38 | }; 39 | 40 | export default Labels; 41 | -------------------------------------------------------------------------------- /src/components/serviceInsights/summary.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import PropTypes from 'prop-types'; 20 | 21 | function Summary({data}) { 22 | return ( 23 |
24 |
25 | Traces considered: {data.tracesConsidered} {data.traceLimitReached ? '(limit reached)' : ''} 26 |
27 | {data.hasViolations && ( 28 |
29 |
Violations found:
30 |
31 | {Object.keys(data.violations).map((key) => ( 32 |
{`${key} (${data.violations[key]})`}
33 | ))} 34 |
35 |
36 | )} 37 |
38 | ); 39 | } 40 | 41 | Summary.propTypes = { 42 | data: PropTypes.object.isRequired 43 | }; 44 | 45 | export default Summary; 46 | -------------------------------------------------------------------------------- /src/components/servicePerf/stores/servicePerfStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | import axios from 'axios'; 18 | import {action, observable} from 'mobx'; 19 | import {fromPromise} from 'mobx-utils'; 20 | 21 | import { ErrorHandlingStore } from '../../../stores/errorHandlingStore'; 22 | 23 | export class ServicePerfStore extends ErrorHandlingStore { 24 | @observable servicePerfStats = []; 25 | @observable promiseState = { case: ({empty}) => empty() }; 26 | 27 | @action fetchServicePerf(timeWindow, from, until) { 28 | this.promiseState = fromPromise( 29 | axios({ 30 | method: 'get', 31 | url: `/api/servicePerf?timeWindow=${timeWindow}&from=${from}&until=${until}` 32 | }) 33 | .then((response) => { 34 | this.servicePerfStats = response.data; 35 | }) 36 | .catch((result) => { 37 | ServicePerfStore.handleError(result); 38 | }) 39 | ); 40 | } 41 | } 42 | 43 | export default new ServicePerfStore(); 44 | -------------------------------------------------------------------------------- /src/components/traces/results/noSearch.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import './noSearch.less'; 20 | 21 | export default () => ( 22 |
23 |
24 |
{'Please select a service and search'}
25 |
26 |
27 | ); 28 | -------------------------------------------------------------------------------- /src/components/traces/results/noSearch.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @import (reference) '../../../app'; 18 | 19 | .no-search_text { 20 | display: inline-block; 21 | margin: @spacing-xl; 22 | padding: @spacing-s @spacing-l @spacing-s @spacing-l; 23 | border: 1px solid @gray-mid-lighter; 24 | background-color: white; 25 | } 26 | -------------------------------------------------------------------------------- /src/components/traces/stores/latencyCostStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import axios from 'axios'; 18 | import {observable, action} from 'mobx'; 19 | import { fromPromise } from 'mobx-utils'; 20 | import { ErrorHandlingStore } from '../../../stores/errorHandlingStore'; 21 | 22 | export class LatencyCostStore extends ErrorHandlingStore { 23 | @observable latencyCost = {}; 24 | @observable promiseState = null; 25 | traceId = null; 26 | 27 | @action fetchLatencyCost(traceId) { 28 | if (this.traceId === traceId) return; 29 | this.traceId = traceId; 30 | 31 | this.promiseState = fromPromise( 32 | axios 33 | .get(`/api/trace/${traceId}/latencyCost`) 34 | .then((result) => { 35 | this.latencyCost = result.data; 36 | }) 37 | .catch((result) => { 38 | LatencyCostStore.handleError(result); 39 | }) 40 | ); 41 | } 42 | } 43 | 44 | export default new LatencyCostStore(); 45 | -------------------------------------------------------------------------------- /src/components/traces/stores/rawSpanStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import axios from 'axios'; 17 | import {observable, action} from 'mobx'; 18 | import { fromPromise } from 'mobx-utils'; 19 | import { ErrorHandlingStore } from '../../../stores/errorHandlingStore'; 20 | 21 | export class RawSpanStore extends ErrorHandlingStore { 22 | @observable rawSpan = []; 23 | 24 | @observable promiseState = null; 25 | 26 | @action fetchRawSpan(traceId, spanId, serviceName) { 27 | this.promiseState = fromPromise( 28 | axios 29 | .get(`/api/trace/raw/${traceId}/${spanId}?serviceName=${serviceName}`) 30 | .then((result) => { 31 | this.rawSpan = result.data; 32 | }) 33 | .catch((result) => { 34 | RawSpanStore.handleError(result); 35 | }) 36 | ); 37 | } 38 | } 39 | 40 | export default new RawSpanStore(); 41 | -------------------------------------------------------------------------------- /src/components/traces/stores/rawTraceStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import axios from 'axios'; 17 | import {observable, action} from 'mobx'; 18 | import { fromPromise } from 'mobx-utils'; 19 | import { ErrorHandlingStore } from '../../../stores/errorHandlingStore'; 20 | 21 | 22 | export class RawTraceStore extends ErrorHandlingStore { 23 | @observable rawTrace = []; 24 | 25 | @observable promiseState = null; 26 | 27 | @action fetchRawTrace(traceId) { 28 | this.promiseState = fromPromise( 29 | axios 30 | .get(`/api/trace/raw/${traceId}`) 31 | .then((result) => { 32 | this.rawTrace = result.data; 33 | }) 34 | .catch((result) => { 35 | RawTraceStore.handleError(result); 36 | }) 37 | ); 38 | } 39 | } 40 | 41 | export default new RawTraceStore(); 42 | -------------------------------------------------------------------------------- /src/components/traces/stores/searchableKeysStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | import axios from 'axios'; 18 | import {observable, action} from 'mobx'; 19 | 20 | export class SearchableKeysStore { 21 | @observable keys = []; 22 | 23 | @action fetchKeys() { 24 | if (this.keys.length) { 25 | return; // services already available, don't retrigger 26 | } 27 | axios({ 28 | method: 'get', 29 | url: '/api/traces/searchableKeys' 30 | }) 31 | .then((response) => { 32 | this.keys = response.data.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); 33 | }); 34 | } 35 | } 36 | 37 | export default new SearchableKeysStore(); 38 | -------------------------------------------------------------------------------- /src/components/traces/stores/spansSearchStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | import axios from 'axios'; 18 | import {observable, action} from 'mobx'; 19 | import { fromPromise } from 'mobx-utils'; 20 | import { ErrorHandlingStore } from '../../../stores/errorHandlingStore'; 21 | 22 | export class SpansSearchStore extends ErrorHandlingStore { 23 | @observable results = []; 24 | @observable promiseState = null; 25 | 26 | @action fetchSpans(traceIds) { 27 | this.promiseState = fromPromise( 28 | axios 29 | .get(`/api/traces/raw?traceIds=${encodeURIComponent(JSON.stringify(traceIds))}`) 30 | .then((result) => { 31 | this.results = result.data; 32 | }) 33 | .catch((result) => { 34 | SpansSearchStore.handleError(result); 35 | }) 36 | ); 37 | } 38 | } 39 | 40 | export default new SpansSearchStore(); 41 | -------------------------------------------------------------------------------- /src/components/traces/stores/traceTrendFetcher.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import Q from 'q'; 17 | import axios from 'axios'; 18 | import ErrorHandlingStore from '../../../stores/errorHandlingStore'; 19 | 20 | const fetcher = {}; 21 | 22 | fetcher.fetchOperationTrends = (serviceName, operationName, granularity, from, until) => { 23 | const deferred = Q.defer(); 24 | 25 | axios 26 | .get(`/api/trends/operation/${encodeURIComponent(serviceName)}/${encodeURIComponent(operationName)}?granularity=${granularity}&from=${from}&until=${until}`) 27 | .then((result) => { 28 | deferred.resolve(result.data); 29 | }) 30 | .catch((result) => { 31 | ErrorHandlingStore.handleError(result); 32 | }); 33 | 34 | return deferred.promise; 35 | }; 36 | 37 | export default fetcher; 38 | -------------------------------------------------------------------------------- /src/components/traces/utils/auxiliaryTags.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | export default { 18 | IS_MERGED_SPAN: 'X-HAYSTACK-IS-MERGED-SPAN', 19 | CLIENT_SERVICE_NAME: 'X-HAYSTACK-CLIENT-SERVICE-NAME', 20 | CLIENT_OPERATION_NAME: 'X-HAYSTACK-CLIENT-OPERATION-NAME', 21 | CLIENT_START_TIME: 'X-HAYSTACK-CLIENT-START-TIME', 22 | CLIENT_DURATION: 'X-HAYSTACK-CLIENT-DURATION', 23 | CLIENT_SPAN_ID: 'X-HAYSTACK-CLIENT-SPAN-ID', 24 | IS_AUTOGENERATED_SPAN: 'X-HAYSTACK-AUTOGEN' 25 | }; 26 | -------------------------------------------------------------------------------- /src/components/traces/utils/presets.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | function getUnitText(unit) { 19 | if (unit === 'd') return 'day'; 20 | if (unit === 'h') return 'hour'; 21 | return 'minute'; 22 | } 23 | 24 | export function toPresetDisplayText(preset) { 25 | const count = preset.substr(0, preset.length - 1); 26 | const unit = getUnitText(preset[preset.length - 1]); 27 | 28 | return `last ${count} ${unit}${count > 1 ? 's' : ''}`; 29 | } 30 | 31 | export function toDurationMicroseconds(preset) { 32 | if (preset) { 33 | const count = preset.substr(0, preset.length - 1); 34 | const unit = preset[preset.length - 1]; 35 | let multiplier; 36 | 37 | switch (unit) { 38 | case 's': 39 | multiplier = 60; 40 | break; 41 | case 'h': 42 | multiplier = 60 * 60; 43 | break; 44 | case 'd': 45 | multiplier = 60 * 60 * 24; 46 | break; 47 | default: 48 | multiplier = 60; 49 | } 50 | 51 | return parseInt(count, 10) * multiplier * 1000 * 1000; 52 | } 53 | 54 | return null; 55 | } 56 | -------------------------------------------------------------------------------- /src/components/trends/details/graphs/graphContainer.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @import (reference) '../../../../app'; 18 | 19 | .reset-button { 20 | position: relative; 21 | margin-right: 15px; 22 | margin-bottom: -55px; 23 | z-index: 100; 24 | } 25 | -------------------------------------------------------------------------------- /src/components/trends/details/graphs/missingTrend.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React from 'react'; 18 | import PropTypes from 'prop-types'; 19 | 20 | import './missingTrend.less'; 21 | 22 | const MissingTrend = ({title}) => 23 | ( 24 |
25 |
{title}
26 |
27 |
28 | No data points found 29 |
30 |
31 |
32 | ); 33 | 34 | MissingTrend.propTypes = { 35 | title: PropTypes.string.isRequired 36 | }; 37 | 38 | export default MissingTrend; 39 | -------------------------------------------------------------------------------- /src/components/trends/details/graphs/missingTrend.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @import (reference) '../../../../app'; 18 | 19 | .missing-data-point-warn { 20 | display: inline-block; 21 | padding: @spacing-s; 22 | border: 1px solid @gray-mid-light; 23 | margin-bottom: @spacing-l; 24 | } -------------------------------------------------------------------------------- /src/components/trends/details/graphs/options.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import {Chart} from 'react-chartjs-2'; 18 | 19 | Chart.defaults.global.defaultFontFamily = 'monospace'; 20 | 21 | export default { 22 | maintainAspectRatio: false, 23 | legend: { 24 | position: 'right' 25 | }, 26 | scales: { 27 | xAxes: [ 28 | { 29 | type: 'time' 30 | } 31 | ], 32 | yAxes: [ 33 | { 34 | ticks: { 35 | beginAtZero: true 36 | } 37 | } 38 | ] 39 | }, 40 | pan: { 41 | enabled: false, 42 | mode: 'x' 43 | }, 44 | zoom: { 45 | enabled: true, 46 | mode: 'x', 47 | speed: 0.05, 48 | drag: { 49 | borderColor: 'rgba(63,77,113,0.4)', 50 | borderWidth: 0.3, 51 | backgroundColor: 'rgba(63,77,113,0.2)' 52 | } 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /src/components/trends/details/trendDetails.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @import (reference) '../../../app'; 18 | 19 | .chart-container { 20 | height: 200px; 21 | margin-bottom: @spacing-s; 22 | } 23 | 24 | canvas { 25 | user-select: none; 26 | } -------------------------------------------------------------------------------- /src/components/trends/operation/operationResults.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @import (reference) '../../../app'; 17 | 18 | .operation-results .react-bs-table-container { 19 | margin-top: 0; 20 | } 21 | .results-table-heading { 22 | font-size: xx-large; 23 | } 24 | .operation-results-view-selector { 25 | margin-bottom: @spacing-m; 26 | } 27 | -------------------------------------------------------------------------------- /src/components/trends/utils/metricGranularity.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | const granularity = {}; 19 | 20 | granularity.options = [ 21 | { 22 | shortName: '1m', 23 | longName: 'OneMinute', 24 | value: 60 * 1000 25 | }, 26 | { 27 | shortName: '5m', 28 | longName: 'FiveMinute', 29 | value: 5 * 60 * 1000 30 | }, 31 | { 32 | shortName: '15m', 33 | longName: 'FifteenMinute', 34 | value: 15 * 60 * 1000 35 | }, 36 | { 37 | shortName: '1h', 38 | longName: 'OneHour', 39 | value: 60 * 60 * 1000 40 | } 41 | ]; 42 | 43 | granularity.getMinGranularity = timeInMs => granularity.options.find(option => option.value >= timeInMs) || granularity.options[granularity.options.length - 1]; 44 | 45 | const granularityRanges = [60 * 1000, 5 * 60 * 1000, 15 * 60 * 1000, 30 * 60 * 1000, 60 * 60 * 1000, 3 * 60 * 60 * 1000, 6 * 60 * 60 * 1000, 12 * 60 * 60 * 1000, 24 * 60 * 60 * 1000]; 46 | 47 | granularity.getMaxGranularity = timeInMs => granularityRanges.find(option => option >= timeInMs); 48 | 49 | export default granularity; 50 | -------------------------------------------------------------------------------- /src/components/universalSearch/searchBar/searchSubmit.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React from 'react'; 18 | import PropTypes from 'prop-types'; 19 | 20 | const Submit = ({handleSearch}) => ( 21 |
22 | 25 |
26 | ); 27 | 28 | Submit.propTypes = { 29 | handleSearch: PropTypes.func.isRequired 30 | }; 31 | 32 | export default Submit; 33 | -------------------------------------------------------------------------------- /src/components/universalSearch/searchBar/stores/searchableKeysStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | import axios from 'axios'; 18 | import {observable, action} from 'mobx'; 19 | import {ErrorHandlingStore} from '../../../../stores/errorHandlingStore'; 20 | 21 | export class SearchableKeysStore extends ErrorHandlingStore { 22 | @observable keys = {}; 23 | 24 | @action fetchKeys() { 25 | if (Object.keys(this.keys).length > 3) { // traceId, serviceName and operationName are default 26 | return; // don't re-trigger if other keys are available 27 | } 28 | axios 29 | .get('/api/traces/searchableKeys') 30 | .then((response) => { 31 | this.keys = response.data; 32 | if (this.keys.error) { 33 | this.keys.error.values = ['true', 'false']; 34 | } 35 | }) 36 | .catch((result) => { 37 | SearchableKeysStore.handleError(result); 38 | } 39 | ); 40 | } 41 | } 42 | 43 | export default new SearchableKeysStore(); 44 | -------------------------------------------------------------------------------- /src/components/universalSearch/tabs/emptyTabPlaceholder.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React from 'react'; 18 | 19 | export default () => ( 20 |
21 |
22 |
23 |
24 |
25 | Start with a query for serviceName, traceId, or any other 26 | searchable key 27 |
28 |

29 | e.g. serviceName=test-service 30 |

31 |
32 |
33 |
34 |
35 | ); 36 | -------------------------------------------------------------------------------- /src/components/universalSearch/tabs/serviceInsights.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React from 'react'; 18 | import {observer} from 'mobx-react'; 19 | import PropTypes from 'prop-types'; 20 | 21 | @observer 22 | export default class ServiceInsights extends React.Component { 23 | static propTypes = { 24 | history: PropTypes.object.isRequired, 25 | search: PropTypes.object.isRequired, 26 | store: PropTypes.object.isRequired 27 | }; 28 | 29 | state = {}; 30 | 31 | componentDidMount() { 32 | import(/* webpackChunkName: "ServiceInsights", webpackPreload: true */ '../../serviceInsights/serviceInsights').then((mod) => { 33 | this.setState({ServiceInsightsView: mod.default}); 34 | }); 35 | } 36 | 37 | render() { 38 | const ServiceInsightsView = this.state.ServiceInsightsView; 39 | 40 | return ( 41 |
42 | {ServiceInsightsView && } 43 |
44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/components/universalSearch/tabs/tabStores/serviceGraphStateStore.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | /* 3 | * Copyright 2018 Expedia Group 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | import store from '../../../serviceGraph/stores/serviceGraphStore'; 19 | 20 | const subsystems = (window.haystackUiConfig && window.haystackUiConfig.subsystems) || []; 21 | const enabled = subsystems.includes('serviceGraph'); 22 | 23 | export class ServiceGraphStateStore { 24 | search = null; 25 | isAvailable = false; 26 | 27 | init(search, tabProperties) { 28 | // initialize observables using search object 29 | // check if for the given search context tab should be available 30 | this.search = search; 31 | 32 | this.isAvailable = enabled && (tabProperties.onlyService || !tabProperties.queries.length); 33 | } 34 | 35 | fetch() { 36 | return store; 37 | } 38 | } 39 | 40 | export default new ServiceGraphStateStore(); 41 | -------------------------------------------------------------------------------- /src/components/universalSearch/tabs/tabStores/servicePerformanceStateStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* eslint-disable class-methods-use-this */ 18 | 19 | const enabled = window.haystackUiConfig && window.haystackUiConfig.subsystems.includes('trends') && window.haystackUiConfig.enableServicePerformance; 20 | 21 | export class ServicePerformanceStateStore { 22 | isAvailable = false; 23 | 24 | init(search, tabProperties) { 25 | this.isAvailable = enabled && !tabProperties.queries.length; 26 | } 27 | 28 | fetch() {} 29 | } 30 | 31 | export default new ServicePerformanceStateStore(); 32 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import {Route, Switch} from 'react-router-dom'; 20 | 21 | import Login from './components/common/login'; 22 | import UploadContainer from './components/traces/upload/uploadContainer'; 23 | import UniversalSearch from './components/universalSearch/universalSearch'; 24 | import './app.less'; 25 | 26 | export default () => ( 27 | 28 | 29 | }/> 30 | 31 | 32 | 33 | 34 | ); 35 | -------------------------------------------------------------------------------- /src/stores/authenticationStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import {observable} from 'mobx'; 18 | 19 | export class AuthenticationStore { 20 | @observable timedOut = false; 21 | } 22 | 23 | export default new AuthenticationStore(); 24 | -------------------------------------------------------------------------------- /src/stores/errorHandlingStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import authenticationStore from './authenticationStore'; 18 | 19 | function HaystackApiException(data) { 20 | this.message = 'Unable to resolve promise'; 21 | this.data = data; 22 | } 23 | 24 | export class ErrorHandlingStore { 25 | static handleError(result) { 26 | if (result.response.status === 401) { 27 | authenticationStore.timedOut = true; 28 | } 29 | throw new HaystackApiException(result); 30 | } 31 | } 32 | 33 | export default new ErrorHandlingStore(); 34 | -------------------------------------------------------------------------------- /src/stores/operationStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import axios from 'axios'; 18 | import {observable, action} from 'mobx'; 19 | 20 | import { ErrorHandlingStore } from './errorHandlingStore'; 21 | 22 | export class OperationStore extends ErrorHandlingStore { 23 | @observable operations = []; 24 | 25 | @action fetchOperations(serviceName, callback) { 26 | this.operations = []; 27 | axios({ 28 | method: 'get', 29 | url: `/api/operations?serviceName=${serviceName}` 30 | }) 31 | .then((response) => { 32 | this.operations = ['all', ...response.data.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))]; 33 | callback(); 34 | }) 35 | .catch((result) => { 36 | OperationStore.handleError(result); 37 | }); 38 | } 39 | } 40 | 41 | export default new OperationStore(); 42 | -------------------------------------------------------------------------------- /src/stores/serviceStore.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | import axios from 'axios'; 18 | import {observable, action} from 'mobx'; 19 | 20 | import { ErrorHandlingStore } from './errorHandlingStore'; 21 | 22 | export class ServiceStore extends ErrorHandlingStore { 23 | @observable services = []; 24 | 25 | @action fetchServices() { 26 | if (this.services.length) { 27 | return; // services already available, don't retrigger 28 | } 29 | if (window.haystackUiConfig.services) { 30 | this.services = window.haystackUiConfig.services; 31 | return; // services found in the html, don't retrigger 32 | } 33 | axios({ 34 | method: 'get', 35 | url: '/api/services' 36 | }) 37 | .then((response) => { 38 | this.services = response.data.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); 39 | }) 40 | .catch((result) => { 41 | ServiceStore.handleError(result); 42 | }); 43 | } 44 | } 45 | 46 | export default new ServiceStore(); 47 | -------------------------------------------------------------------------------- /src/stores/storesInitializer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import axios from 'axios'; 19 | 20 | export default { 21 | init: () => { 22 | // axios init 23 | axios.defaults.timeout = 60000; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/utils/blobUtil.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Expedia, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const blobUtil = {}; 18 | 19 | blobUtil.isBlobUrlTag = (key) => { 20 | const lowercaseKey = key.toLowerCase(); 21 | return lowercaseKey.endsWith('-blob') || lowercaseKey === 'request' || lowercaseKey === 'response'; 22 | }; 23 | 24 | blobUtil.formatBlobTagValue = (tagValue, blobsUrl) => { 25 | if (tagValue.startsWith('/getBlob/')) { 26 | return blobsUrl ? `${blobsUrl}${tagValue}` : `${window.location.protocol}//${window.location.host}${tagValue}`; 27 | } 28 | return blobsUrl ? `${blobsUrl}/getBlob/${tagValue}` : `${window.location.protocol}//${window.location.host}/getBlob/${tagValue}`; 29 | }; 30 | 31 | export default blobUtil; 32 | -------------------------------------------------------------------------------- /src/utils/hashUtil.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-bitwise */ 2 | /* 3 | * Copyright 2018 Expedia Group 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | const hashUtil = {}; 19 | 20 | hashUtil.calculateHash = svc => 21 | svc.split('').reduce((a, b) => { 22 | let running = a; 23 | running = ((running << 5) - running) + b.charCodeAt(0); 24 | return running & running; 25 | }, 0); 26 | 27 | export default hashUtil; 28 | -------------------------------------------------------------------------------- /src/utils/linkBuilder.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { convertSearchToUrlQuery } from '../components/universalSearch/utils/urlUtils'; 18 | 19 | const linkBuilder = {}; 20 | 21 | linkBuilder.withAbsoluteUrl = relativeUrl => `${window.location.protocol}//${window.location.host}${relativeUrl}`; 22 | 23 | linkBuilder.universalSearchLink = search => `/search?${convertSearchToUrlQuery(search)}`; 24 | 25 | linkBuilder.universalSearchTracesLink = search => `/search?${convertSearchToUrlQuery(search)}&tabId=traces`; 26 | 27 | linkBuilder.universalSearchTrendsLink = search => `/search?${convertSearchToUrlQuery(search)}&tabId=trends`; 28 | 29 | linkBuilder.universalSearchAlertsLink = search => `/search?${convertSearchToUrlQuery(search)}&tabId=alerts`; 30 | 31 | export default linkBuilder; 32 | -------------------------------------------------------------------------------- /src/utils/loginRenewer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import axios from 'axios'; 18 | 19 | const renewer = {}; 20 | 21 | renewer.init = () => setInterval(() => axios.get('/auth/renewlogin'), 5 * 60 * 1000); 22 | 23 | export default renewer; 24 | -------------------------------------------------------------------------------- /src/utils/queryParser.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | export const toQueryUrlString = query => Object 19 | .keys(query) 20 | .filter(key => query[key]) 21 | .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(query[key])}`) 22 | .join('&'); 23 | 24 | export const toQuery = (query) => { 25 | const queryDict = {}; 26 | 27 | if (!query || query.length <= 1) return {}; 28 | 29 | query.substr(1) 30 | .split('&') 31 | .forEach((item) => { 32 | queryDict[decodeURIComponent(item.split('=')[0])] = decodeURIComponent(item.split('=')[1]); 33 | }); 34 | return queryDict; 35 | }; 36 | -------------------------------------------------------------------------------- /src/utils/serviceColorMapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* eslint-disable no-bitwise */ 17 | 18 | import hashUtil from './hashUtil'; 19 | 20 | const colorMapper = {}; 21 | 22 | function mapToColorIndex(svc) { 23 | return (Math.abs(hashUtil.calculateHash(svc)) % 25) + 1; 24 | } 25 | 26 | colorMapper.toFillClass = serviceName => `svc-color-${mapToColorIndex(serviceName)}-fill`; 27 | 28 | colorMapper.toBackgroundClass = serviceName => `svc-color-${mapToColorIndex(serviceName)}-bg`; 29 | 30 | export default colorMapper; 31 | -------------------------------------------------------------------------------- /src/utils/validUrl.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const validUrl = {}; 18 | 19 | const urlRegex = new RegExp(/(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/); // eslint-disable-line no-useless-escape 20 | 21 | validUrl.isUrl = str => urlRegex.test(str); 22 | 23 | export default validUrl; 24 | -------------------------------------------------------------------------------- /test/server/connectors/utils/encoders/Base64Encoder.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the License); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an AS IS BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | import {expect} from 'chai'; 20 | 21 | const Base64Encoder = require('../../../../../server/connectors/utils/encoders/Base64Encoder'); 22 | 23 | describe('Base64Encoder', () => { 24 | it('encodes and decodes correctly', () => { 25 | const value = 'some Value. /'; 26 | 27 | const encodedValue = Base64Encoder.encode(value); 28 | expect(encodedValue).equals('c29tZSBWYWx1ZS4gLw__'); 29 | expect(Base64Encoder.decode(encodedValue)).equals(value); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/server/connectors/utils/encoders/NoopEncoder.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the License); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an AS IS BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | import {expect} from 'chai'; 20 | 21 | const NoopEncoder = require('../../../../../server/connectors/utils/encoders/NoopEncoder'); 22 | 23 | describe('NoopEncoder', () => { 24 | it('encodes and decodes correctly', () => { 25 | const value = 'some Value. /'; 26 | 27 | const encodedValue = NoopEncoder.encode(value); 28 | expect(encodedValue).equals(value); 29 | expect(NoopEncoder.decode(encodedValue)).equals(value); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/server/connectors/utils/encoders/PeriodReplacementEncoder.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the License); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an AS IS BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | import {expect} from 'chai'; 20 | 21 | const PeriodReplacementEncoder = require('../../../../../server/connectors/utils/encoders/PeriodReplacementEncoder'); 22 | 23 | describe('PeriodReplacementEncoder', () => { 24 | it('encodes and decodes correctly', () => { 25 | const value = 'some Value. /'; 26 | 27 | const encodedValue = PeriodReplacementEncoder.encode(value); 28 | expect(encodedValue).equals('some Value___ /'); 29 | expect(PeriodReplacementEncoder.decode(encodedValue)).equals(value); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/server/routes/index.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const server = require('../../../server/app.js'); 18 | const request = require('supertest'); 19 | 20 | describe('routes.index', () => { 21 | it('returns http 200', (done) => { 22 | request(server) 23 | .get('/') 24 | .expect(200) 25 | .end((err) => { 26 | if (err) { 27 | return done(err); 28 | } 29 | return done(); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/server/routes/serviceInsightsApi.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const server = require('../../../server/app.js'); 18 | const request = require('supertest'); 19 | 20 | describe('routes.servicesApi', () => { 21 | it('returns http 200 for /api/serviceInsights', (done) => { 22 | request(server) 23 | .get('/api/serviceInsights?serviceName=stark-service&from=1000&to=2000') 24 | .expect(200) 25 | .end((err) => { 26 | if (err) { 27 | return done(err); 28 | } 29 | return done(); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/server/routes/servicesApi.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const server = require('../../../server/app.js'); 18 | const request = require('supertest'); 19 | 20 | describe('routes.servicesApi', () => { 21 | it('returns http 200 for /api/services', (done) => { 22 | request(server) 23 | .get('/api/services') 24 | .expect(200) 25 | .end((err) => { 26 | if (err) { 27 | return done(err); 28 | } 29 | return done(); 30 | }); 31 | }); 32 | 33 | it('returns http 200 for /api/operations', (done) => { 34 | request(server) 35 | .get('/api/operations?serviceName=service') 36 | .expect(200) 37 | .end((err) => { 38 | if (err) { 39 | return done(err); 40 | } 41 | return done(); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/server/routes/servicesPerfApi.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const server = require('../../../server/app.js'); 18 | const request = require('supertest'); 19 | 20 | describe('routes.servicePerfApi', () => { 21 | it('returns http 200 for /api/servicePerf', (done) => { 22 | request(server) 23 | .get('/api/servicePerf') 24 | .expect(200) 25 | .end((err) => { 26 | if (err) { 27 | return done(err); 28 | } 29 | return done(); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/src/components/common/help.spec.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import { mount } from 'enzyme'; 20 | import { expect } from 'chai'; 21 | import Help from '../../../../src/components/docs/help'; 22 | 23 | describe('', () => { 24 | it('should render the help panel`', () => { 25 | const wrapper = mount(); 26 | expect(wrapper.find('.help-panel')).to.have.length(1); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/src/components/common/noMatch.spec.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import { shallow } from 'enzyme'; 20 | import { expect } from 'chai'; 21 | import NoMatch from '../../../../src/components/common/noMatch'; 22 | 23 | describe('', () => { 24 | it('should render the NoMatch panel`', () => { 25 | const wrapper = shallow(); 26 | expect(wrapper.find('.nomatch-panel')).to.have.length(1); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/src/components/common/timeRangePicker.spec.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import {expect} from 'chai'; 19 | import DateTime from 'react-datetime'; 20 | import TimeRangePicker from '../../../../src/components/common/timeRangePicker'; 21 | 22 | describe('TimeRangePicker.isFromValid', () => { 23 | window.haystackUiConfig.trendsTTL = 3 * 24 * 60 * 60 * 1000; 24 | 25 | it('should be true when current is before today and after the ttl', () => { 26 | expect(TimeRangePicker.isFromValid(DateTime.moment().subtract(1, 'day'))).to.equal(true); 27 | }); 28 | 29 | it('should be false when current is after today and after the ttl', () => { 30 | expect(TimeRangePicker.isFromValid(DateTime.moment().add(1, 'day'))).to.equal(false); 31 | }); 32 | 33 | it('should be false when current is before today and before the ttl', () => { 34 | expect(TimeRangePicker.isFromValid(DateTime.moment().subtract(11, 'day'))).to.equal(false); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/src/components/layout.spec.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Expedia Group 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | import React from 'react'; 19 | import {mount} from 'enzyme'; 20 | import {expect} from 'chai'; 21 | import {MemoryRouter} from 'react-router-dom'; 22 | 23 | import Footer from '../../../src/components/layout/footer'; 24 | 25 | describe('