├── .swift-version ├── docs ├── img │ ├── dash.png │ ├── gh.png │ ├── carat.png │ └── spinner.gif ├── docsets │ ├── Health.tgz │ └── Health.docset │ │ └── Contents │ │ ├── Resources │ │ ├── docSet.dsidx │ │ └── Documents │ │ │ ├── img │ │ │ ├── gh.png │ │ │ ├── carat.png │ │ │ ├── dash.png │ │ │ └── spinner.gif │ │ │ ├── js │ │ │ ├── jazzy.js │ │ │ └── jazzy.search.js │ │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ │ ├── Structs.html │ │ │ ├── Extensions.html │ │ │ ├── Typealiases.html │ │ │ ├── search.json │ │ │ ├── Enums.html │ │ │ ├── Protocols.html │ │ │ ├── Enums │ │ │ ├── State.html │ │ │ └── InvalidDataError.html │ │ │ └── Extensions │ │ │ └── Date.html │ │ └── Info.plist ├── undocumented.json ├── badge.svg ├── js │ ├── jazzy.js │ └── jazzy.search.js ├── css │ ├── highlight.css │ └── jazzy.css ├── Structs.html ├── Extensions.html ├── Typealiases.html ├── search.json ├── Enums.html ├── Protocols.html ├── Enums │ ├── State.html │ └── InvalidDataError.html └── Extensions │ └── Date.html ├── .gitignore ├── docker-compose.yml ├── .jazzy.yaml ├── Tests ├── LinuxMain.swift └── HealthTests │ └── HealthTests.swift ├── Sources └── Health │ ├── Utils.swift │ ├── Health.swift │ └── Protocols.swift ├── Package.swift ├── Package@swift-4.swift ├── .travis.yml └── README.md /.swift-version: -------------------------------------------------------------------------------- 1 | 5.1 2 | -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Health/HEAD/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Health/HEAD/docs/img/gh.png -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Health/HEAD/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Health/HEAD/docs/img/spinner.gif -------------------------------------------------------------------------------- /docs/docsets/Health.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Health/HEAD/docs/docsets/Health.tgz -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build/* 3 | /.build-ubuntu/* 4 | /*.xcodeproj 5 | build/* 6 | Package.resolved 7 | -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | 4 | ], 5 | "source_directory": "/Users/travis/build/IBM-Swift/Health" 6 | } -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Health/HEAD/docs/docsets/Health.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Health/HEAD/docs/docsets/Health.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Health/HEAD/docs/docsets/Health.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Health/HEAD/docs/docsets/Health.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/Health/HEAD/docs/docsets/Health.docset/Contents/Resources/Documents/img/spinner.gif -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # docker-compose up 2 | app: 3 | image: ibmcom/swift-ubuntu:4.0 4 | volumes: 5 | - .:/Health 6 | command: bash -c "cd /Health && swift package --build-path .build-ubuntu clean && swift build --build-path .build-ubuntu && swift test --build-path .build-ubuntu" 7 | -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | module: Health 2 | author: IBM and the Kitura project authors 3 | github_url: https://github.com/Kitura/Health/ 4 | 5 | theme: fullwidth 6 | clean: true 7 | exclude: [Packages] 8 | 9 | readme: README.md 10 | 11 | skip_undocumented: false 12 | hide_documentation_coverage: false 13 | 14 | xcodebuild_arguments: [-project, Health.xcodeproj, -target, Health, LIBRARY_SEARCH_PATHS=.build/debug] 15 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.health 7 | CFBundleName 8 | Health 9 | DocSetPlatformFamily 10 | health 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2017 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 XCTest 18 | @testable import HealthTests 19 | 20 | XCTMain([ 21 | testCase(HealthTests.allTests), 22 | ]) 23 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 100% 23 | 24 | 25 | 100% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/Health/Utils.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2017 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 Foundation 18 | 19 | /// Extension to the `Date` type. 20 | extension Date { 21 | 22 | /// Returns the current time in milliseconds. 23 | public static func currentTimeMillis() -> UInt64 { 24 | let timeInMillis = UInt64(NSDate().timeIntervalSince1970 * 1000.0) 25 | return timeInMillis 26 | } 27 | 28 | /// Returns the time value of the date in milliseconds (since 1970). 29 | var milliseconds: UInt64 { 30 | get { 31 | return UInt64(self.timeIntervalSince1970 * 1000.0) 32 | } 33 | } 34 | 35 | /// Creates a `Date` instance from milliseconds. 36 | public init(timeInMillis: UInt64) { 37 | self = Date(timeIntervalSince1970: TimeInterval(timeInMillis / 1000)) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | $content = link.parent().parent().next(); 27 | $content.slideToggle(animationDuration); 28 | 29 | // Keeps the document from jumping to the hash. 30 | var href = $(this).attr('href'); 31 | if (history.pushState) { 32 | history.pushState({}, '', href); 33 | } else { 34 | location.hash = href; 35 | } 36 | event.preventDefault(); 37 | }); 38 | 39 | // Dumb down quotes within code blocks that delimit strings instead of quotations 40 | // https://github.com/realm/jazzy/issues/714 41 | $("code q").replaceWith(function () { 42 | return ["\"", $(this).contents(), "\""]; 43 | }); 44 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | $content = link.parent().parent().next(); 27 | $content.slideToggle(animationDuration); 28 | 29 | // Keeps the document from jumping to the hash. 30 | var href = $(this).attr('href'); 31 | if (history.pushState) { 32 | history.pushState({}, '', href); 33 | } else { 34 | location.hash = href; 35 | } 36 | event.preventDefault(); 37 | }); 38 | 39 | // Dumb down quotes within code blocks that delimit strings instead of quotations 40 | // https://github.com/realm/jazzy/issues/714 41 | $("code q").replaceWith(function () { 42 | return ["\"", $(this).contents(), "\""]; 43 | }); 44 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | /* 3 | * Copyright IBM Corporation and the Kitura project authors 2017-2020 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 PackageDescription 19 | 20 | let package = Package( 21 | name: "Health", 22 | products: [ 23 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 24 | .library( 25 | name: "Health", 26 | targets: ["Health"] 27 | ), 28 | ], 29 | dependencies: [ 30 | .package(url: "https://github.com/Kitura/LoggerAPI.git", .upToNextMajor(from: "1.9.200")), 31 | ], 32 | targets: [ 33 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 34 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 35 | // CRUD and CRUD tests removed until the files compile 36 | .target( 37 | name: "Health", 38 | dependencies: ["LoggerAPI"] 39 | ), 40 | .testTarget( 41 | name: "HealthTests", 42 | dependencies: ["Health"] 43 | ), 44 | ] 45 | ) 46 | -------------------------------------------------------------------------------- /Package@swift-4.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | /* 3 | * Copyright IBM Corporation and the Kitura project authors 2017-2020 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 PackageDescription 19 | 20 | let package = Package( 21 | name: "Health", 22 | products: [ 23 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 24 | .library( 25 | name: "Health", 26 | targets: ["Health"] 27 | ), 28 | ], 29 | dependencies: [ 30 | .package(url: "https://github.com/Kitura/LoggerAPI.git", .upToNextMajor(from: "1.9.200")), 31 | ], 32 | targets: [ 33 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 34 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 35 | // CRUD and CRUD tests removed until the files compile 36 | .target( 37 | name: "Health", 38 | dependencies: ["LoggerAPI"] 39 | ), 40 | .testTarget( 41 | name: "HealthTests", 42 | dependencies: ["Health"] 43 | ), 44 | ] 45 | ) 46 | -------------------------------------------------------------------------------- /docs/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var searchIndex = lunr(function() { 3 | this.ref('url'); 4 | this.field('name'); 5 | }); 6 | 7 | var $typeahead = $('[data-typeahead]'); 8 | var $form = $typeahead.parents('form'); 9 | var searchURL = $form.attr('action'); 10 | 11 | function displayTemplate(result) { 12 | return result.name; 13 | } 14 | 15 | function suggestionTemplate(result) { 16 | var t = '
'; 17 | t += '' + result.name + ''; 18 | if (result.parent_name) { 19 | t += '' + result.parent_name + ''; 20 | } 21 | t += '
'; 22 | return t; 23 | } 24 | 25 | $typeahead.one('focus', function() { 26 | $form.addClass('loading'); 27 | 28 | $.getJSON(searchURL).then(function(searchData) { 29 | $.each(searchData, function (url, doc) { 30 | searchIndex.add({url: url, name: doc.name}); 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3 37 | }, 38 | { 39 | limit: 10, 40 | display: displayTemplate, 41 | templates: { suggestion: suggestionTemplate }, 42 | source: function(query, sync) { 43 | var results = searchIndex.search(query).map(function(result) { 44 | var doc = searchData[result.ref]; 45 | doc.url = result.ref; 46 | return doc; 47 | }); 48 | sync(results); 49 | } 50 | } 51 | ); 52 | $form.removeClass('loading'); 53 | $typeahead.trigger('focus'); 54 | }); 55 | }); 56 | 57 | var baseURL = searchURL.slice(0, -"search.json".length); 58 | 59 | $typeahead.on('typeahead:select', function(e, result) { 60 | window.location = baseURL + result.url; 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var searchIndex = lunr(function() { 3 | this.ref('url'); 4 | this.field('name'); 5 | }); 6 | 7 | var $typeahead = $('[data-typeahead]'); 8 | var $form = $typeahead.parents('form'); 9 | var searchURL = $form.attr('action'); 10 | 11 | function displayTemplate(result) { 12 | return result.name; 13 | } 14 | 15 | function suggestionTemplate(result) { 16 | var t = '
'; 17 | t += '' + result.name + ''; 18 | if (result.parent_name) { 19 | t += '' + result.parent_name + ''; 20 | } 21 | t += '
'; 22 | return t; 23 | } 24 | 25 | $typeahead.one('focus', function() { 26 | $form.addClass('loading'); 27 | 28 | $.getJSON(searchURL).then(function(searchData) { 29 | $.each(searchData, function (url, doc) { 30 | searchIndex.add({url: url, name: doc.name}); 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3 37 | }, 38 | { 39 | limit: 10, 40 | display: displayTemplate, 41 | templates: { suggestion: suggestionTemplate }, 42 | source: function(query, sync) { 43 | var results = searchIndex.search(query).map(function(result) { 44 | var doc = searchData[result.ref]; 45 | doc.url = result.ref; 46 | return doc; 47 | }); 48 | sync(results); 49 | } 50 | } 51 | ); 52 | $form.removeClass('loading'); 53 | $typeahead.trigger('focus'); 54 | }); 55 | }); 56 | 57 | var baseURL = searchURL.slice(0, -"search.json".length); 58 | 59 | $typeahead.on('typeahead:select', function(e, result) { 60 | window.location = baseURL + result.url; 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis CI build file. 2 | 3 | # whitelist (branches that should be built) 4 | branches: 5 | only: 6 | - master 7 | - /^issue.*$/ 8 | 9 | # the matrix of builds should cover each combination of Swift version 10 | # and platform that is supported. The version of Swift used is specified 11 | # by .swift-version, unless SWIFT_SNAPSHOT is specified. 12 | matrix: 13 | include: 14 | - os: linux 15 | dist: xenial 16 | sudo: required 17 | services: docker 18 | env: DOCKER_IMAGE=swift:4.0.3 SWIFT_SNAPSHOT=4.0.3 19 | - os: linux 20 | dist: xenial 21 | sudo: required 22 | services: docker 23 | env: DOCKER_IMAGE=swift:4.1.3 SWIFT_SNAPSHOT=4.1.3 24 | - os: linux 25 | dist: xenial 26 | sudo: required 27 | services: docker 28 | env: DOCKER_IMAGE=swift:4.2.4 SWIFT_SNAPSHOT=4.2.4 29 | - os: linux 30 | dist: xenial 31 | sudo: required 32 | services: docker 33 | env: DOCKER_IMAGE=swift:5.0.3-xenial SWIFT_SNAPSHOT=5.0.3 34 | - os: linux 35 | dist: xenial 36 | sudo: required 37 | services: docker 38 | env: DOCKER_IMAGE=swift:5.1 39 | - os: linux 40 | dist: xenial 41 | sudo: required 42 | services: docker 43 | env: DOCKER_IMAGE=swift:5.1 SWIFT_SNAPSHOT=$SWIFT_DEVELOPMENT_SNAPSHOT 44 | - os: osx 45 | osx_image: xcode9.2 46 | sudo: required 47 | env: SWIFT_SNAPSHOT=4.0.3 48 | - os: osx 49 | osx_image: xcode9.4 50 | sudo: required 51 | env: SWIFT_SNAPSHOT=4.1.2 52 | - os: osx 53 | osx_image: xcode10.1 54 | sudo: required 55 | env: SWIFT_SNAPSHOT=4.2.1 56 | - os: osx 57 | osx_image: xcode10.2 58 | sudo: required 59 | env: SWIFT_SNAPSHOT=5.0.1 JAZZY_ELIGIBLE=true 60 | - os: osx 61 | osx_image: xcode11 62 | sudo: required 63 | - os: osx 64 | osx_image: xcode11 65 | sudo: required 66 | env: SWIFT_SNAPSHOT=$SWIFT_DEVELOPMENT_SNAPSHOT 67 | 68 | before_install: 69 | - git clone https://github.com/Kitura/Package-Builder.git 70 | 71 | script: 72 | - ./Package-Builder/build-package.sh -projectDir $TRAVIS_BUILD_DIR 73 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /Sources/Health/Health.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2017 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 Foundation 18 | import Dispatch 19 | 20 | /// The `Health` class provides a concrete implementation of the `HealthProtocol` protocol that 21 | /// applications can instantiate and then register one or more health checks against. Once you 22 | /// have your health checks in place you can ask your `Health` instance for its status. 23 | /// 24 | ///### Usage Example: ### 25 | /// One common use case for this Swift package is to integrate it into a Kitura-based application. In the code sample below, the health of the application is exposed through the /health endpoint. Cloud environments (e.g. Cloud Foundry, Kubernetes, etc.) can then use the status information returned from the /health endpoint to monitor and manage the Swift application instance. 26 | /// 27 | /// For further information and example code see our [README](https://github.com/IBM-Swift/Health/blob/master/README.md) and the [Cloud Foundry documentation for using application health checks](https://docs.cloudfoundry.org/devguide/deploy-apps/healthchecks.html). 28 | ///```swift 29 | /// import Health 30 | /// 31 | /// let health = Health() 32 | /// 33 | /// health.addCheck(check: MyCheck1()) 34 | /// health.addCheck(check: myClosureCheck1) 35 | /// 36 | /// // Define /health endpoint that leverages Health 37 | /// router.get("/health") { request, response, next in 38 | /// let status = health.status 39 | /// if health.status.state == .UP { 40 | /// try response.status(.OK).send(status).end() 41 | /// } else { 42 | /// try response.status(.serviceUnavailable).send(status).end() 43 | /// } 44 | /// } 45 | ///``` 46 | public class Health: HealthProtocol { 47 | private var checks: [HealthCheck] 48 | private var closureChecks: [HealthCheckClosure] 49 | private var lastStatus: Status 50 | private let statusExpirationTime: Int // milliseconds 51 | private let statusSemaphore = DispatchSemaphore(value: 1) 52 | 53 | /// Status instance variable. 54 | public var status: Status { 55 | get { 56 | statusSemaphore.wait() 57 | let current = Date.currentTimeMillis() 58 | let last = self.lastStatus.tsInMillis 59 | 60 | // If elapsed time is bigger than the status expiration window, re-compute status 61 | // Check if current > last to avoid crashs because of negative UInt64 values 62 | // This is possible because of different rounding behaviour of DateFormatter.string() and 63 | // timeIntervalSince1970 > UInt64 convertion 64 | if current > last && (current - last) > UInt64(statusExpirationTime) { 65 | forceUpdateStatus() 66 | } 67 | statusSemaphore.signal() 68 | return lastStatus 69 | } 70 | } 71 | 72 | /// Number of health checks registered. 73 | /// 74 | ///### Usage Example: ### 75 | ///```swift 76 | /// let health = Health() 77 | /// 78 | /// let count = health.numberOfChecks 79 | ///``` 80 | public var numberOfChecks: Int { 81 | get { 82 | return (checks.count + closureChecks.count) 83 | } 84 | } 85 | 86 | /// Creates an instance of the `Health` class. 87 | /// 88 | ///### Usage Example: ### 89 | /// In the example below the `statusExpirationTime` defaults to `30000` milliseconds, in this 90 | /// case 30 seconds must elapse before the `Health` instance computes its status again by 91 | /// querying each health check that has been registered. 92 | ///```swift 93 | ///let health = Health() 94 | ///``` 95 | /// - Parameter statusExpirationTime: Optional. The time window in milliseconds that should 96 | /// elapse before the status cache for this `Health` instance is considered to be expired 97 | /// and should be recomputed. The default value is `30000`. 98 | public init(statusExpirationTime: Int = 30000) { 99 | self.statusExpirationTime = statusExpirationTime 100 | self.lastStatus = Status(state: .UP) 101 | checks = [HealthCheck]() 102 | closureChecks = [HealthCheckClosure]() 103 | } 104 | 105 | /// Registers a health check. 106 | /// 107 | ///### Usage Example: ### 108 | ///```swift 109 | /// let health = Health() 110 | /// 111 | /// // Add custom checks 112 | /// health.addCheck(check: MyCheck1()) 113 | ///``` 114 | /// - Parameter check: An object that extends the `HealthCheck` class. 115 | public func addCheck(check: HealthCheck) { 116 | checks.append(check) 117 | } 118 | 119 | /// Registers a health check. 120 | /// 121 | ///### Usage Example: ### 122 | ///```swift 123 | /// let health = Health() 124 | /// 125 | /// // Add custom checks 126 | /// health.addCheck(check: myClosureCheck1) 127 | ///``` 128 | /// - Parameter check: A closure that conforms to the `HealthCheckClosure` type alias. 129 | public func addCheck(check: @escaping HealthCheckClosure) { 130 | closureChecks.append(check) 131 | } 132 | 133 | /// Forces an update to the status of this instance. 134 | public func forceUpdateStatus() { 135 | let checksDetails = checks.map { $0.evaluate() == State.DOWN ? $0.description : nil } 136 | let closureChecksDetails = closureChecks.map { $0() == State.DOWN ? "A health check closure reported status as DOWN." : nil } 137 | #if swift(>=4.1) 138 | let details = (checksDetails + closureChecksDetails).compactMap { $0 } 139 | #else 140 | let details = (checksDetails + closureChecksDetails).flatMap { $0 } 141 | #endif 142 | let state = (details.isEmpty) ? State.UP : State.DOWN 143 | lastStatus = Status(state: state, details: details) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /docs/Structs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Structures Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | Health Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 111 |
112 | 113 |
114 |
115 |

Structures

116 |

The following structures are available globally.

117 | 118 |
119 |
120 | 121 |
122 |
123 |
124 |
    125 |
  • 126 |
    127 | 128 | 129 | 130 | Status 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    Struct that encapsulates the status of an application.

    139 | 140 | See more 141 |
    142 |
    143 |

    Declaration

    144 |
    145 |

    Swift

    146 |
    public struct Status: Equatable
    147 | 148 |
    149 |
    150 |
    151 |
    152 |
  • 153 |
154 |
155 |
156 |
157 | 158 |
159 |
160 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/Structs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Structures Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | Health Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 111 |
112 | 113 |
114 |
115 |

Structures

116 |

The following structures are available globally.

117 | 118 |
119 |
120 | 121 |
122 |
123 |
124 |
    125 |
  • 126 |
    127 | 128 | 129 | 130 | Status 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    Struct that encapsulates the status of an application.

    139 | 140 | See more 141 |
    142 |
    143 |

    Declaration

    144 |
    145 |

    Swift

    146 |
    public struct Status: Equatable
    147 | 148 |
    149 |
    150 |
    151 |
    152 |
  • 153 |
154 |
155 |
156 |
157 | 158 |
159 |
160 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /docs/Extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extensions Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | Health Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 111 |
112 | 113 |
114 |
115 |

Extensions

116 |

The following extensions are available globally.

117 | 118 |
119 |
120 | 121 |
122 |
123 |
124 |
    125 |
  • 126 |
    127 | 128 | 129 | 130 | Date 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    Extension to the Date type.

    139 | 140 | See more 141 |
    142 |
    143 |

    Declaration

    144 |
    145 |

    Swift

    146 |
    struct Date : ReferenceConvertible, Comparable, Equatable
    147 | 148 |
    149 |
    150 |
    151 |
    152 |
  • 153 |
154 |
155 |
156 |
157 | 158 |
159 |
160 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /docs/Typealiases.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Type Aliases Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | Health Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 111 |
112 | 113 |
114 |
115 |

Type Aliases

116 |

The following type aliases are available globally.

117 | 118 |
119 |
120 | 121 |
122 |
123 |
124 |
    125 |
  • 126 |
    127 | 128 | 129 | 130 | HealthCheckClosure 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    HealthCheckClosure is a type alias for a closure that receives no arguments and 139 | returns a State value.

    140 | 141 |
    142 |
    143 |

    Declaration

    144 |
    145 |

    Swift

    146 |
    public typealias HealthCheckClosure = () -> State
    147 | 148 |
    149 |
    150 |
    151 |
    152 |
  • 153 |
154 |
155 |
156 |
157 | 158 |
159 |
160 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/Extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extensions Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | Health Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 111 |
112 | 113 |
114 |
115 |

Extensions

116 |

The following extensions are available globally.

117 | 118 |
119 |
120 | 121 |
122 |
123 |
124 |
    125 |
  • 126 |
    127 | 128 | 129 | 130 | Date 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    Extension to the Date type.

    139 | 140 | See more 141 |
    142 |
    143 |

    Declaration

    144 |
    145 |

    Swift

    146 |
    struct Date : ReferenceConvertible, Comparable, Equatable
    147 | 148 |
    149 |
    150 |
    151 |
    152 |
  • 153 |
154 |
155 |
156 |
157 | 158 |
159 |
160 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/Typealiases.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Type Aliases Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | Health Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 111 |
112 | 113 |
114 |
115 |

Type Aliases

116 |

The following type aliases are available globally.

117 | 118 |
119 |
120 | 121 |
122 |
123 |
124 |
    125 |
  • 126 |
    127 | 128 | 129 | 130 | HealthCheckClosure 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    HealthCheckClosure is a type alias for a closure that receives no arguments and 139 | returns a State value.

    140 | 141 |
    142 |
    143 |

    Declaration

    144 |
    145 |

    Swift

    146 |
    public typealias HealthCheckClosure = () -> State
    147 | 148 |
    149 |
    150 |
    151 |
    152 |
  • 153 |
154 |
155 |
156 |
157 | 158 |
159 |
160 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /docs/search.json: -------------------------------------------------------------------------------- 1 | {"Typealiases.html#/s:6Health0A12CheckClosurea":{"name":"HealthCheckClosure","abstract":"

HealthCheckClosure is a type alias for a closure that receives no arguments and"},"Structs/Status.html#/s:6Health6StatusV13dateFormatterAA0b4DateD0CvpZ":{"name":"dateFormatter","abstract":"

The date formatter used by the Status struct.

","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV10dateFormatSSvpZ":{"name":"dateFormat","abstract":"

The date format used by the timestamp value in the dictionary.

","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV5stateAA5StateOvp":{"name":"state","abstract":"

The state value contained within this struct.

","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV7detailsSaySSGvp":{"name":"details","abstract":"

List of details describing any failures.

","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV10tsInMilliss6UInt64Vvp":{"name":"tsInMillis","abstract":"

The timestamp value in milliseconds for the status.

","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV9timestampSSvp":{"name":"timestamp","abstract":"

The string timestamp value for the status.

","parent_name":"Status"},"Structs/Status.html#/s:s9EquatableP2eeoiSbx_xtFZ":{"name":"==(_:_:)","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusVAcA5StateO5state_SaySSG7detailsSS9timestamptcfc":{"name":"init(state:details:timestamp:)","abstract":"

Initializes the Status struct, which encapsulates the status of an application.

","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV12toDictionarys0D0VySSypGyF":{"name":"toDictionary()","abstract":"

Returns a dictionary that contains the current status information. This dictionary","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV18toSimpleDictionarys0E0VySSypGyF":{"name":"toSimpleDictionary()","abstract":"

Returns a simple dictionary that contains the current status information. This dictionary","parent_name":"Status"},"Structs/Status.html#/s:s9EncodableP6encodeys7Encoder_p2to_tKF":{"name":"encode(to:)","parent_name":"Status"},"Structs/Status.html#/s:s9DecodablePxs7Decoder_p4from_tKcfc":{"name":"init(from:)","parent_name":"Status"},"Structs/Status.html":{"name":"Status","abstract":"

Struct that encapsulates the status of an application.

"},"Protocols/HealthProtocol.html#/s:6Health0A8ProtocolP6statusAA6StatusVvp":{"name":"status","abstract":"

Status instance variable.

","parent_name":"HealthProtocol"},"Protocols/HealthProtocol.html#/s:6Health0A8ProtocolP8addCheckyAA0aD0_p5check_tF":{"name":"addCheck(check:)","abstract":"

Registers a health check.

","parent_name":"HealthProtocol"},"Protocols/HealthProtocol.html#/s:6Health0A8ProtocolP8addCheckyAA5StateOyc5check_tF":{"name":"addCheck(check:)","abstract":"

Registers a health check.

","parent_name":"HealthProtocol"},"Protocols/HealthCheck.html#/s:6Health0A5CheckP4nameSSvp":{"name":"name","abstract":"

Name for the health check.

","parent_name":"HealthCheck"},"Protocols/HealthCheck.html#/s:6Health0A5CheckP11descriptionSSvp":{"name":"description","abstract":"

Description for the health check.

","parent_name":"HealthCheck"},"Protocols/HealthCheck.html#/s:6Health0A5CheckP8evaluateAA5StateOyF":{"name":"evaluate()","abstract":"

Performs the health check test.

","parent_name":"HealthCheck"},"Protocols/HealthCheck.html":{"name":"HealthCheck","abstract":"

Health check classes should extend this protocol to provide concrete implementations.

"},"Protocols/HealthProtocol.html":{"name":"HealthProtocol","abstract":"

Specifies the blueprint that must be implemented to satisfy the needs of a Health class."},"Extensions/Date.html#/s:10Foundation4DateV6HealthE17currentTimeMilliss6UInt64VyFZ":{"name":"currentTimeMillis()","abstract":"

Returns the current time in milliseconds.

","parent_name":"Date"},"Extensions/Date.html#/s:10Foundation4DateV6HealthEACs6UInt64V12timeInMillis_tcfc":{"name":"init(timeInMillis:)","abstract":"

Creates a Date instance from milliseconds.

","parent_name":"Date"},"Extensions/Date.html":{"name":"Date","abstract":"

Extension to the Date type.

"},"Enums/State.html#/s:6Health5StateO2UPA2CmF":{"name":"UP","abstract":"

Application is running just fine.

","parent_name":"State"},"Enums/State.html#/s:6Health5StateO4DOWNA2CmF":{"name":"DOWN","abstract":"

Application health is not good.

","parent_name":"State"},"Enums/InvalidDataError.html#/s:6Health16InvalidDataErrorO15deserializationACSScACmF":{"name":"deserialization","abstract":"

A deserialization error occurred.

","parent_name":"InvalidDataError"},"Enums/InvalidDataError.html#/s:6Health16InvalidDataErrorO13serializationACSScACmF":{"name":"serialization","abstract":"

A serialization error occurred.

","parent_name":"InvalidDataError"},"Enums/InvalidDataError.html":{"name":"InvalidDataError","abstract":"

InvalidDataError enum

"},"Enums/State.html":{"name":"State","abstract":"

State enum

"},"Classes/StatusDateFormatter.html#/s:6Health19StatusDateFormatterC6stringSS10Foundation0C0V4from_tF":{"name":"string(from:)","abstract":"

Returns a timestamp string representation from the Date parameter.

","parent_name":"StatusDateFormatter"},"Classes/StatusDateFormatter.html#/s:6Health19StatusDateFormatterC4date10Foundation0C0VSgSS4from_tF":{"name":"date(from:)","abstract":"

Returns a Date instance that corresponds to the string parameter.

","parent_name":"StatusDateFormatter"},"Classes/Health.html#/s:6HealthAAC6statusAA6StatusVvp":{"name":"status","abstract":"

Status instance variable.

","parent_name":"Health"},"Classes/Health.html#/s:6HealthAAC14numberOfChecksSivp":{"name":"numberOfChecks","abstract":"

Number of health checks registered.

","parent_name":"Health"},"Classes/Health.html#/s:6HealthAACABSi20statusExpirationTime_tcfc":{"name":"init(statusExpirationTime:)","abstract":"

Creates an instance of the Health class.

","parent_name":"Health"},"Classes/Health.html#/s:6HealthAAC8addCheckyAA0aC0_p5check_tF":{"name":"addCheck(check:)","abstract":"

Registers a health check.

","parent_name":"Health"},"Classes/Health.html#/s:6HealthAAC8addCheckyAA5StateOyc5check_tF":{"name":"addCheck(check:)","abstract":"

Registers a health check.

","parent_name":"Health"},"Classes/Health.html#/s:6HealthAAC17forceUpdateStatusyyF":{"name":"forceUpdateStatus()","abstract":"

Forces an update to the status of this instance.

","parent_name":"Health"},"Classes/Health.html":{"name":"Health","abstract":"

The Health class provides a concrete implementation of the HealthProtocol protocol that"},"Classes/StatusDateFormatter.html":{"name":"StatusDateFormatter","abstract":"

Struct that encapsulates a DateFormatter implementation, specifically used by the Status struct.

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Enums.html":{"name":"Enumerations","abstract":"

The following enumerations are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"},"Typealiases.html":{"name":"Type Aliases","abstract":"

The following type aliases are available globally.

"}} -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/search.json: -------------------------------------------------------------------------------- 1 | {"Typealiases.html#/s:6Health0A12CheckClosurea":{"name":"HealthCheckClosure","abstract":"

HealthCheckClosure is a type alias for a closure that receives no arguments and"},"Structs/Status.html#/s:6Health6StatusV13dateFormatterAA0b4DateD0CvpZ":{"name":"dateFormatter","abstract":"

The date formatter used by the Status struct.

","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV10dateFormatSSvpZ":{"name":"dateFormat","abstract":"

The date format used by the timestamp value in the dictionary.

","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV5stateAA5StateOvp":{"name":"state","abstract":"

The state value contained within this struct.

","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV7detailsSaySSGvp":{"name":"details","abstract":"

List of details describing any failures.

","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV10tsInMilliss6UInt64Vvp":{"name":"tsInMillis","abstract":"

The timestamp value in milliseconds for the status.

","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV9timestampSSvp":{"name":"timestamp","abstract":"

The string timestamp value for the status.

","parent_name":"Status"},"Structs/Status.html#/s:s9EquatableP2eeoiSbx_xtFZ":{"name":"==(_:_:)","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusVAcA5StateO5state_SaySSG7detailsSS9timestamptcfc":{"name":"init(state:details:timestamp:)","abstract":"

Initializes the Status struct, which encapsulates the status of an application.

","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV12toDictionarys0D0VySSypGyF":{"name":"toDictionary()","abstract":"

Returns a dictionary that contains the current status information. This dictionary","parent_name":"Status"},"Structs/Status.html#/s:6Health6StatusV18toSimpleDictionarys0E0VySSypGyF":{"name":"toSimpleDictionary()","abstract":"

Returns a simple dictionary that contains the current status information. This dictionary","parent_name":"Status"},"Structs/Status.html#/s:s9EncodableP6encodeys7Encoder_p2to_tKF":{"name":"encode(to:)","parent_name":"Status"},"Structs/Status.html#/s:s9DecodablePxs7Decoder_p4from_tKcfc":{"name":"init(from:)","parent_name":"Status"},"Structs/Status.html":{"name":"Status","abstract":"

Struct that encapsulates the status of an application.

"},"Protocols/HealthProtocol.html#/s:6Health0A8ProtocolP6statusAA6StatusVvp":{"name":"status","abstract":"

Status instance variable.

","parent_name":"HealthProtocol"},"Protocols/HealthProtocol.html#/s:6Health0A8ProtocolP8addCheckyAA0aD0_p5check_tF":{"name":"addCheck(check:)","abstract":"

Registers a health check.

","parent_name":"HealthProtocol"},"Protocols/HealthProtocol.html#/s:6Health0A8ProtocolP8addCheckyAA5StateOyc5check_tF":{"name":"addCheck(check:)","abstract":"

Registers a health check.

","parent_name":"HealthProtocol"},"Protocols/HealthCheck.html#/s:6Health0A5CheckP4nameSSvp":{"name":"name","abstract":"

Name for the health check.

","parent_name":"HealthCheck"},"Protocols/HealthCheck.html#/s:6Health0A5CheckP11descriptionSSvp":{"name":"description","abstract":"

Description for the health check.

","parent_name":"HealthCheck"},"Protocols/HealthCheck.html#/s:6Health0A5CheckP8evaluateAA5StateOyF":{"name":"evaluate()","abstract":"

Performs the health check test.

","parent_name":"HealthCheck"},"Protocols/HealthCheck.html":{"name":"HealthCheck","abstract":"

Health check classes should extend this protocol to provide concrete implementations.

"},"Protocols/HealthProtocol.html":{"name":"HealthProtocol","abstract":"

Specifies the blueprint that must be implemented to satisfy the needs of a Health class."},"Extensions/Date.html#/s:10Foundation4DateV6HealthE17currentTimeMilliss6UInt64VyFZ":{"name":"currentTimeMillis()","abstract":"

Returns the current time in milliseconds.

","parent_name":"Date"},"Extensions/Date.html#/s:10Foundation4DateV6HealthEACs6UInt64V12timeInMillis_tcfc":{"name":"init(timeInMillis:)","abstract":"

Creates a Date instance from milliseconds.

","parent_name":"Date"},"Extensions/Date.html":{"name":"Date","abstract":"

Extension to the Date type.

"},"Enums/State.html#/s:6Health5StateO2UPA2CmF":{"name":"UP","abstract":"

Application is running just fine.

","parent_name":"State"},"Enums/State.html#/s:6Health5StateO4DOWNA2CmF":{"name":"DOWN","abstract":"

Application health is not good.

","parent_name":"State"},"Enums/InvalidDataError.html#/s:6Health16InvalidDataErrorO15deserializationACSScACmF":{"name":"deserialization","abstract":"

A deserialization error occurred.

","parent_name":"InvalidDataError"},"Enums/InvalidDataError.html#/s:6Health16InvalidDataErrorO13serializationACSScACmF":{"name":"serialization","abstract":"

A serialization error occurred.

","parent_name":"InvalidDataError"},"Enums/InvalidDataError.html":{"name":"InvalidDataError","abstract":"

InvalidDataError enum

"},"Enums/State.html":{"name":"State","abstract":"

State enum

"},"Classes/StatusDateFormatter.html#/s:6Health19StatusDateFormatterC6stringSS10Foundation0C0V4from_tF":{"name":"string(from:)","abstract":"

Returns a timestamp string representation from the Date parameter.

","parent_name":"StatusDateFormatter"},"Classes/StatusDateFormatter.html#/s:6Health19StatusDateFormatterC4date10Foundation0C0VSgSS4from_tF":{"name":"date(from:)","abstract":"

Returns a Date instance that corresponds to the string parameter.

","parent_name":"StatusDateFormatter"},"Classes/Health.html#/s:6HealthAAC6statusAA6StatusVvp":{"name":"status","abstract":"

Status instance variable.

","parent_name":"Health"},"Classes/Health.html#/s:6HealthAAC14numberOfChecksSivp":{"name":"numberOfChecks","abstract":"

Number of health checks registered.

","parent_name":"Health"},"Classes/Health.html#/s:6HealthAACABSi20statusExpirationTime_tcfc":{"name":"init(statusExpirationTime:)","abstract":"

Creates an instance of the Health class.

","parent_name":"Health"},"Classes/Health.html#/s:6HealthAAC8addCheckyAA0aC0_p5check_tF":{"name":"addCheck(check:)","abstract":"

Registers a health check.

","parent_name":"Health"},"Classes/Health.html#/s:6HealthAAC8addCheckyAA5StateOyc5check_tF":{"name":"addCheck(check:)","abstract":"

Registers a health check.

","parent_name":"Health"},"Classes/Health.html#/s:6HealthAAC17forceUpdateStatusyyF":{"name":"forceUpdateStatus()","abstract":"

Forces an update to the status of this instance.

","parent_name":"Health"},"Classes/Health.html":{"name":"Health","abstract":"

The Health class provides a concrete implementation of the HealthProtocol protocol that"},"Classes/StatusDateFormatter.html":{"name":"StatusDateFormatter","abstract":"

Struct that encapsulates a DateFormatter implementation, specifically used by the Status struct.

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Enums.html":{"name":"Enumerations","abstract":"

The following enumerations are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"},"Typealiases.html":{"name":"Type Aliases","abstract":"

The following type aliases are available globally.

"}} -------------------------------------------------------------------------------- /Tests/HealthTests/HealthTests.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2017 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 XCTest 18 | import Foundation 19 | 20 | @testable import Health 21 | 22 | class HealthTests: XCTestCase { 23 | 24 | static var allTests: [(String, (HealthTests) -> () throws -> Void)] { 25 | return [ 26 | ("testBasicConstruction", testBasicConstruction), 27 | ("testAddChecks", testAddChecks), 28 | ("testStatusSerialization", testStatusSerialization), 29 | ("testDateExtensions", testDateExtensions), 30 | ("testDateFormatter", testDateFormatter) 31 | ] 32 | } 33 | 34 | class MyPositiveCheck: HealthCheck { 35 | public var name: String { get { return "MyPositiveCheck"} } 36 | public var description: String { get { return "Description for MyPositiveCheck..."} } 37 | public func evaluate() -> State { 38 | return State.UP 39 | } 40 | } 41 | 42 | class MyNegativeCheck: HealthCheck { 43 | public var name: String { get { return "MyNegativeCheck"} } 44 | public var description: String { get { return "Description for MyNegativeCheck..."} } 45 | public func evaluate() -> State { 46 | return State.DOWN 47 | } 48 | } 49 | 50 | func myPositiveClosureCheck() -> State { 51 | return State.UP 52 | } 53 | 54 | func myNegativeClosureCheck() -> State { 55 | return State.DOWN 56 | } 57 | 58 | override func setUp() { 59 | super.setUp() 60 | } 61 | 62 | // Create Stats, and check that default values are set 63 | func testBasicConstruction() { 64 | 65 | // Create Health instance 66 | let health = Health() 67 | 68 | // Assert initial state 69 | XCTAssertEqual(health.numberOfChecks, 0) 70 | XCTAssertEqual(health.status.state, State.UP) 71 | XCTAssertEqual(health.status.details.count, 0) 72 | 73 | // Assert contents of simple dictionary 74 | let simpleDictionary = health.status.toSimpleDictionary() 75 | print("simpleDictionary: \(simpleDictionary)") 76 | XCTAssertEqual(simpleDictionary.count, 1) 77 | let simpleKeys = simpleDictionary.keys 78 | XCTAssertTrue(simpleKeys.contains("status")) 79 | if let status = simpleDictionary["status"] as? String { 80 | XCTAssertEqual(status, "UP") 81 | } else { 82 | XCTFail("Non-expected status in dictionary.") 83 | } 84 | 85 | // Assert contents of dictionary 86 | let dictionary = health.status.toDictionary() 87 | print("dictionary: \(dictionary)") 88 | // There should only be two keys 89 | XCTAssertEqual(dictionary.count, 3) 90 | let keys = dictionary.keys 91 | XCTAssertTrue(keys.contains("status")) 92 | XCTAssertTrue(keys.contains("details")) 93 | XCTAssertTrue(keys.contains("timestamp")) 94 | 95 | // Validate status 96 | if let status = dictionary["status"] as? String { 97 | XCTAssertEqual(status, "UP") 98 | } else { 99 | XCTFail("'status' field missing in dictionary.") 100 | } 101 | 102 | // Validate details 103 | if let details = dictionary["details"] as? [String] { 104 | XCTAssertEqual(details.count, 0) 105 | } else { 106 | XCTFail("'details' field missing in dictionary.") 107 | } 108 | 109 | // Validate timestamp 110 | if let timestamp = dictionary["timestamp"] as? String { 111 | let dateFormatter = DateFormatter() 112 | dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" 113 | let date = dateFormatter.date(from: timestamp) 114 | XCTAssertNotNil(date, "'timestamp' field in dictionary does not match expected format.") 115 | } else { 116 | XCTFail("'timestamp' field missing in dictionary.") 117 | } 118 | } 119 | 120 | func testAddChecks() { 121 | // Create Health instance 122 | let statusExpirationTime = 3000 123 | let health = Health(statusExpirationTime: statusExpirationTime) 124 | 125 | // Add checks 126 | health.addCheck(check: MyPositiveCheck()) 127 | health.addCheck(check: MyNegativeCheck()) 128 | health.addCheck(check: myPositiveClosureCheck) 129 | health.addCheck(check: myNegativeClosureCheck) 130 | 131 | // Perform assertions 132 | XCTAssertEqual(health.numberOfChecks, 4) 133 | // State should still be up (caching - 30 seconds) 134 | XCTAssertEqual(health.status.state, State.UP) 135 | // Wait for cache to expire 136 | sleep(UInt32((statusExpirationTime + 1000)/1000)) 137 | // State should be down now... 138 | XCTAssertEqual(health.status.state, State.DOWN) 139 | 140 | // Assert contents of dictionary 141 | let dictionary = health.status.toDictionary() 142 | print("dictionary: \(dictionary)") 143 | if let status = dictionary["status"] as? String { 144 | XCTAssertEqual(status, "DOWN") 145 | } else { 146 | XCTFail("Non-expected status in dictionary.") 147 | } 148 | 149 | if let details = dictionary["details"] as? [String] { 150 | //print("details: \(details)") 151 | XCTAssertEqual(details.count, 2) 152 | XCTAssertTrue(details.contains("Description for MyNegativeCheck...")) 153 | XCTAssertTrue(details.contains("A health check closure reported status as DOWN.")) 154 | } else { 155 | XCTFail("Non-expected details in dictionary.") 156 | } 157 | } 158 | 159 | func testDateFormatter() { 160 | // Validate conversion from string timestamp to date and back to string timestamp 161 | let dateStr1 = "2017-10-31T16:15:56+0000" 162 | let dateFormatter = StatusDateFormatter() 163 | guard let date1 = dateFormatter.date(from: dateStr1) else { 164 | XCTFail("Failed to compute date from valid string timestamp!") 165 | return 166 | } 167 | let computedDateStr1 = dateFormatter.string(from: date1) 168 | XCTAssertEqual(dateStr1, computedDateStr1, "Timestamp string values did not match!") 169 | 170 | // Validate conversion to string timestamp from milliseconds 171 | let milliseconds: UInt64 = 1509466556000 172 | let date2 = Date(timeInMillis: milliseconds) 173 | let dateStr2 = dateFormatter.string(from: date2) 174 | XCTAssertEqual(dateStr2, "2017-10-31T16:15:56+0000", "Timestamp string values did not match!") 175 | } 176 | 177 | func testDateExtensions() { 178 | // Compare millis 179 | let milliseconds: UInt64 = 1509466556000 180 | let date1 = Date(timeIntervalSince1970: TimeInterval(milliseconds / 1000)) 181 | let date2 = Date(timeInMillis: milliseconds) 182 | XCTAssertEqual(date1.timeIntervalSince1970, date2.timeIntervalSince1970, "timeIntervalSince1970 values did not match!") 183 | XCTAssertEqual(date1.milliseconds, milliseconds, "milliseconds values did not match!") 184 | XCTAssertEqual(date2.milliseconds, milliseconds, "milliseconds values did not match!") 185 | } 186 | 187 | func testStatusSerialization() { 188 | // Validate serialization and deserialization of status instance 189 | let status: Status = Status(state: .DOWN, details: ["details1", "details2", "details3"], timestamp: "2017-10-31T16:15:56+0000") 190 | 191 | guard let data = try? JSONEncoder().encode(status) else { 192 | XCTFail("Failed to encode Status instance!") 193 | return 194 | } 195 | 196 | guard let decodedStatus = try? JSONDecoder().decode(Status.self, from: data) else { 197 | XCTFail("Failed to decode JSON data into a Status instance!") 198 | return 199 | } 200 | 201 | XCTAssertEqual(status, decodedStatus, "Failed to encode and decode Status instance!") 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /docs/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | Health Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 111 |
112 | 113 |
114 |
115 |

Enumerations

116 |

The following enumerations are available globally.

117 | 118 |
119 |
120 | 121 |
122 |
123 |
124 |
    125 |
  • 126 |
    127 | 128 | 129 | 130 | InvalidDataError 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    InvalidDataError enum

    139 | 140 |

    Enumeration that represents data errors.

    141 | 142 | See more 143 |
    144 |
    145 |

    Declaration

    146 |
    147 |

    Swift

    148 |
    public enum InvalidDataError: Error
    149 | 150 |
    151 |
    152 |
    153 |
    154 |
  • 155 |
  • 156 |
    157 | 158 | 159 | 160 | State 161 | 162 |
    163 |
    164 |
    165 |
    166 |
    167 |
    168 |

    State enum

    169 | 170 |

    Enumeration that encapsulates the two possible states for an application, UP or DOWN.

    171 | 172 | See more 173 |
    174 |
    175 |

    Declaration

    176 |
    177 |

    Swift

    178 |
    public enum State: String
    179 | 180 |
    181 |
    182 |
    183 |
    184 |
  • 185 |
186 |
187 |
188 |
189 | 190 |
191 |
192 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /docs/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | Health Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 111 |
112 | 113 |
114 |
115 |

Protocols

116 |

The following protocols are available globally.

117 | 118 |
119 |
120 | 121 |
122 |
123 |
124 |
    125 |
  • 126 |
    127 | 128 | 129 | 130 | HealthCheck 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    Health check classes should extend this protocol to provide concrete implementations.

    139 | 140 | See more 141 |
    142 |
    143 |

    Declaration

    144 |
    145 |

    Swift

    146 |
    public protocol HealthCheck
    147 | 148 |
    149 |
    150 |
    151 |
    152 |
  • 153 |
  • 154 |
    155 | 156 | 157 | 158 | HealthProtocol 159 | 160 |
    161 |
    162 |
    163 |
    164 |
    165 |
    166 |

    Specifies the blueprint that must be implemented to satisfy the needs of a Health class. 167 | A concrete implementation of this protocol is already provided by this library (Health).

    168 | 169 | See more 170 |
    171 |
    172 |

    Declaration

    173 |
    174 |

    Swift

    175 |
    public protocol HealthProtocol
    176 | 177 |
    178 |
    179 |
    180 |
    181 |
  • 182 |
183 |
184 |
185 |
186 | 187 |
188 |
189 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | Health Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 111 |
112 | 113 |
114 |
115 |

Enumerations

116 |

The following enumerations are available globally.

117 | 118 |
119 |
120 | 121 |
122 |
123 |
124 |
    125 |
  • 126 |
    127 | 128 | 129 | 130 | InvalidDataError 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    InvalidDataError enum

    139 | 140 |

    Enumeration that represents data errors.

    141 | 142 | See more 143 |
    144 |
    145 |

    Declaration

    146 |
    147 |

    Swift

    148 |
    public enum InvalidDataError: Error
    149 | 150 |
    151 |
    152 |
    153 |
    154 |
  • 155 |
  • 156 |
    157 | 158 | 159 | 160 | State 161 | 162 |
    163 |
    164 |
    165 |
    166 |
    167 |
    168 |

    State enum

    169 | 170 |

    Enumeration that encapsulates the two possible states for an application, UP or DOWN.

    171 | 172 | See more 173 |
    174 |
    175 |

    Declaration

    176 |
    177 |

    Swift

    178 |
    public enum State: String
    179 | 180 |
    181 |
    182 |
    183 |
    184 |
  • 185 |
186 |
187 |
188 |
189 | 190 |
191 |
192 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | Health Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 111 |
112 | 113 |
114 |
115 |

Protocols

116 |

The following protocols are available globally.

117 | 118 |
119 |
120 | 121 |
122 |
123 |
124 |
    125 |
  • 126 |
    127 | 128 | 129 | 130 | HealthCheck 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    Health check classes should extend this protocol to provide concrete implementations.

    139 | 140 | See more 141 |
    142 |
    143 |

    Declaration

    144 |
    145 |

    Swift

    146 |
    public protocol HealthCheck
    147 | 148 |
    149 |
    150 |
    151 |
    152 |
  • 153 |
  • 154 |
    155 | 156 | 157 | 158 | HealthProtocol 159 | 160 |
    161 |
    162 |
    163 |
    164 |
    165 |
    166 |

    Specifies the blueprint that must be implemented to satisfy the needs of a Health class. 167 | A concrete implementation of this protocol is already provided by this library (Health).

    168 | 169 | See more 170 |
    171 |
    172 |

    Declaration

    173 |
    174 |

    Swift

    175 |
    public protocol HealthProtocol
    176 | 177 |
    178 |
    179 |
    180 |
    181 |
  • 182 |
183 |
184 |
185 |
186 | 187 |
188 |
189 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /Sources/Health/Protocols.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2017 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 Foundation 18 | import LoggerAPI 19 | 20 | /// InvalidDataError enum 21 | /// 22 | /// Enumeration that represents data errors. 23 | public enum InvalidDataError: Error { 24 | 25 | /// A deserialization error occurred. 26 | case deserialization(String) 27 | 28 | /// A serialization error occurred. 29 | case serialization(String) 30 | } 31 | 32 | /// State enum 33 | /// 34 | /// Enumeration that encapsulates the two possible states for an application, UP or DOWN. 35 | public enum State: String { 36 | 37 | /// Application is running just fine. 38 | case UP 39 | /// Application health is not good. 40 | case DOWN 41 | } 42 | 43 | /// Struct that encapsulates a `DateFormatter` implementation, specifically used by the Status struct. 44 | public class StatusDateFormatter { 45 | private let dateFormatter: DateFormatter 46 | 47 | /// Constructor 48 | /// 49 | /// Wraps a `DateFormatter` instance, sets its timezone to UTC and its date format to 'yyyy-MM-dd'T'HH:mm:ssZ'. 50 | init() { 51 | self.dateFormatter = DateFormatter() 52 | self.dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" 53 | if let timeZone = TimeZone(identifier: "UTC") { 54 | self.dateFormatter.timeZone = timeZone 55 | } else { 56 | // This should never occur... 57 | Log.warning("UTC time zone not found.") 58 | } 59 | } 60 | 61 | /// Returns a timestamp string representation from the `Date` parameter. 62 | /// - Parameter from: A Date instance to obtain the date value from. 63 | public func string(from date: Date) -> String { 64 | return dateFormatter.string(from: date) 65 | } 66 | 67 | /// Returns a `Date` instance that corresponds to the string parameter. 68 | /// - Parameter from: A string in the "yyyy-MM-dd'T'HH:mm:ssZ" format. 69 | public func date(from string: String) -> Date? { 70 | return dateFormatter.date(from: string) 71 | } 72 | 73 | var dateFormat: String { 74 | get { 75 | return self.dateFormatter.dateFormat 76 | } 77 | } 78 | } 79 | 80 | /// Struct that encapsulates the status of an application. 81 | public struct Status: Equatable { 82 | /// The date formatter used by the `Status` struct. 83 | public static let dateFormatter = StatusDateFormatter() 84 | 85 | /// The date format used by the timestamp value in the dictionary. 86 | public static var dateFormat: String { 87 | get { 88 | return dateFormatter.dateFormat 89 | } 90 | } 91 | 92 | /// The state value contained within this struct. 93 | public let state: State 94 | /// List of details describing any failures. 95 | public let details: [String] 96 | /// The timestamp value in milliseconds for the status. 97 | public var tsInMillis: UInt64 { 98 | get { 99 | let date = Status.dateFormatter.date(from: timestamp) 100 | return date!.milliseconds 101 | } 102 | } 103 | /// The string timestamp value for the status. 104 | public let timestamp: String 105 | 106 | enum CodingKeys: String, CodingKey { 107 | case status 108 | case details 109 | case timestamp 110 | } 111 | 112 | public static func ==(lhs: Status, rhs: Status) -> Bool { 113 | return (lhs.state == rhs.state) && (lhs.details == rhs.details) && (lhs.timestamp == rhs.timestamp) 114 | } 115 | 116 | /// Initializes the `Status` struct, which encapsulates the status of an application. 117 | /// 118 | /// - Parameter state: Optional. The state value for this Status instance (default value is 'UP'). 119 | /// - Parameter details: Optional. A list of strings that describes any issues that may have 120 | /// occurred while executing a health check. 121 | /// - Parameter timestamp: Optional. The string timestamp value for the status (default value is current time). 122 | public init(state: State = State.UP, details: [String] = [], timestamp: String = dateFormatter.string(from: Date())) { 123 | self.state = state 124 | self.details = details 125 | if let _ = Status.dateFormatter.date(from: timestamp) { 126 | self.timestamp = timestamp 127 | } else { 128 | self.timestamp = Status.dateFormatter.string(from: Date()) 129 | Log.warning("Provided timestamp value '\(timestamp)' is not valid; using current time value instead.") 130 | } 131 | } 132 | 133 | /// Returns a dictionary that contains the current status information. This dictionary 134 | /// contains three key-pair values, where the keys are 'status', 'timestamp', and 'details'. 135 | public func toDictionary() -> [String : Any] { 136 | // Transform time in milliseconds to readable format 137 | //let timestamp = Status.dateFormatter.string(from: Date(timeInMillis: self.tsInMillis)) 138 | // Add state & details to dictionary 139 | let dict = ["status" : self.state.rawValue, "details" : details, "timestamp" : timestamp] as [String : Any] 140 | return dict 141 | } 142 | 143 | /// Returns a simple dictionary that contains the current status information. This dictionary 144 | /// contains one key-pair value, where the key is 'status' and the value is either 'UP' or 'DOWN'. 145 | public func toSimpleDictionary() -> [String : Any] { 146 | // Add state & details to dictionary 147 | let dict = ["status" : self.state.rawValue] as [String : Any] 148 | return dict 149 | } 150 | } 151 | 152 | /// Extension for the `Status` struct that conforms to the Encodable protocol. 153 | extension Status: Encodable { 154 | public func encode(to encoder: Encoder) throws { 155 | var container = encoder.container(keyedBy: CodingKeys.self) 156 | try container.encode(self.state.rawValue, forKey: .status) 157 | try container.encode(self.details, forKey: .details) 158 | try container.encode(self.timestamp, forKey: .timestamp) 159 | } 160 | } 161 | 162 | /// Extension for the `Status` struct that conforms to the Decodable protocol. 163 | extension Status: Decodable { 164 | public init(from decoder: Decoder) throws { 165 | let values = try decoder.container(keyedBy: CodingKeys.self) 166 | var status = try values.decode(String.self, forKey: .status) 167 | 168 | if status == "" { 169 | status = "DOWN" 170 | } 171 | 172 | guard let state = State(rawValue: status) else { 173 | throw InvalidDataError.deserialization("'\(status)' is not a valid status value.") 174 | } 175 | 176 | let details = try values.decode([String].self, forKey: .details) 177 | var timestamp = try values.decode(String.self, forKey: .timestamp) 178 | 179 | if timestamp == "" { 180 | let dateFormatter = DateFormatter() 181 | dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" 182 | let date = Date() 183 | timestamp = dateFormatter.string(from: date) 184 | } 185 | 186 | guard let _ = Status.dateFormatter.date(from: timestamp) else { 187 | throw InvalidDataError.deserialization("'\(timestamp)' is not a valid timestamp value.") 188 | } 189 | 190 | self.init(state: state, details: details, timestamp: timestamp) 191 | } 192 | } 193 | 194 | /// HealthCheckClosure is a type alias for a closure that receives no arguments and 195 | /// returns a State value. 196 | public typealias HealthCheckClosure = () -> State 197 | 198 | /// Health check classes should extend this protocol to provide concrete implementations. 199 | public protocol HealthCheck { 200 | /// Name for the health check. 201 | var name: String { get } 202 | /// Description for the health check. 203 | var description: String { get } 204 | /// Performs the health check test. 205 | func evaluate() -> State 206 | } 207 | 208 | /// Specifies the blueprint that must be implemented to satisfy the needs of a `Health` class. 209 | /// A concrete implementation of this protocol is already provided by this library (Health). 210 | public protocol HealthProtocol { 211 | /// Status instance variable. 212 | var status: Status { get } 213 | /// Registers a health check. 214 | /// 215 | /// - Parameter check: An object that extends the `HealthCheck` class. 216 | func addCheck(check: HealthCheck) 217 | /// Registers a health check. 218 | /// 219 | /// - Parameter check: A closure that conforms to the `HealthCheckClosure` type alias. 220 | func addCheck(check: @escaping HealthCheckClosure) 221 | } 222 | -------------------------------------------------------------------------------- /docs/Enums/State.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | State Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | Health Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 112 |
113 | 114 |
115 |
116 |

State

117 |
118 |
119 |
public enum State: String
120 | 121 |
122 |
123 |

State enum

124 | 125 |

Enumeration that encapsulates the two possible states for an application, UP or DOWN.

126 | 127 |
128 |
129 | 130 |
131 |
132 |
133 |
    134 |
  • 135 |
    136 | 137 | 138 | 139 | UP 140 | 141 |
    142 |
    143 |
    144 |
    145 |
    146 |
    147 |

    Application is running just fine.

    148 | 149 |
    150 |
    151 |

    Declaration

    152 |
    153 |

    Swift

    154 |
    case UP
    155 | 156 |
    157 |
    158 |
    159 |
    160 |
  • 161 |
162 |
163 |
164 |
    165 |
  • 166 |
    167 | 168 | 169 | 170 | DOWN 171 | 172 |
    173 |
    174 |
    175 |
    176 |
    177 |
    178 |

    Application health is not good.

    179 | 180 |
    181 |
    182 |

    Declaration

    183 |
    184 |

    Swift

    185 |
    case DOWN
    186 | 187 |
    188 |
    189 |
    190 |
    191 |
  • 192 |
193 |
194 |
195 |
196 | 197 |
198 |
199 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/Enums/State.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | State Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | Health Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 112 |
113 | 114 |
115 |
116 |

State

117 |
118 |
119 |
public enum State: String
120 | 121 |
122 |
123 |

State enum

124 | 125 |

Enumeration that encapsulates the two possible states for an application, UP or DOWN.

126 | 127 |
128 |
129 | 130 |
131 |
132 |
133 |
    134 |
  • 135 |
    136 | 137 | 138 | 139 | UP 140 | 141 |
    142 |
    143 |
    144 |
    145 |
    146 |
    147 |

    Application is running just fine.

    148 | 149 |
    150 |
    151 |

    Declaration

    152 |
    153 |

    Swift

    154 |
    case UP
    155 | 156 |
    157 |
    158 |
    159 |
    160 |
  • 161 |
162 |
163 |
164 |
    165 |
  • 166 |
    167 | 168 | 169 | 170 | DOWN 171 | 172 |
    173 |
    174 |
    175 |
    176 |
    177 |
    178 |

    Application health is not good.

    179 | 180 |
    181 |
    182 |

    Declaration

    183 |
    184 |

    Swift

    185 |
    case DOWN
    186 | 187 |
    188 |
    189 |
    190 |
    191 |
  • 192 |
193 |
194 |
195 |
196 | 197 |
198 |
199 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: inherit; } 3 | 4 | body { 5 | margin: 0; 6 | background: #fff; 7 | color: #333; 8 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | letter-spacing: .2px; 10 | -webkit-font-smoothing: antialiased; 11 | box-sizing: border-box; } 12 | 13 | h1 { 14 | font-size: 2rem; 15 | font-weight: 700; 16 | margin: 1.275em 0 0.6em; } 17 | 18 | h2 { 19 | font-size: 1.75rem; 20 | font-weight: 700; 21 | margin: 1.275em 0 0.3em; } 22 | 23 | h3 { 24 | font-size: 1.5rem; 25 | font-weight: 700; 26 | margin: 1em 0 0.3em; } 27 | 28 | h4 { 29 | font-size: 1.25rem; 30 | font-weight: 700; 31 | margin: 1.275em 0 0.85em; } 32 | 33 | h5 { 34 | font-size: 1rem; 35 | font-weight: 700; 36 | margin: 1.275em 0 0.85em; } 37 | 38 | h6 { 39 | font-size: 1rem; 40 | font-weight: 700; 41 | margin: 1.275em 0 0.85em; 42 | color: #777; } 43 | 44 | p { 45 | margin: 0 0 1em; } 46 | 47 | ul, ol { 48 | padding: 0 0 0 2em; 49 | margin: 0 0 0.85em; } 50 | 51 | blockquote { 52 | margin: 0 0 0.85em; 53 | padding: 0 15px; 54 | color: #858585; 55 | border-left: 4px solid #e5e5e5; } 56 | 57 | img { 58 | max-width: 100%; } 59 | 60 | a { 61 | color: #4183c4; 62 | text-decoration: none; } 63 | a:hover, a:focus { 64 | outline: 0; 65 | text-decoration: underline; } 66 | 67 | table { 68 | background: #fff; 69 | width: 100%; 70 | border-collapse: collapse; 71 | border-spacing: 0; 72 | overflow: auto; 73 | margin: 0 0 0.85em; } 74 | 75 | tr:nth-child(2n) { 76 | background-color: #fbfbfb; } 77 | 78 | th, td { 79 | padding: 6px 13px; 80 | border: 1px solid #ddd; } 81 | 82 | pre { 83 | margin: 0 0 1.275em; 84 | padding: .85em 1em; 85 | overflow: auto; 86 | background: #f7f7f7; 87 | font-size: .85em; 88 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 89 | 90 | code { 91 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 92 | 93 | p > code, li > code { 94 | background: #f7f7f7; 95 | padding: .2em; } 96 | p > code:before, p > code:after, li > code:before, li > code:after { 97 | letter-spacing: -.2em; 98 | content: "\00a0"; } 99 | 100 | pre code { 101 | padding: 0; 102 | white-space: pre; } 103 | 104 | .content-wrapper { 105 | display: flex; 106 | flex-direction: column; } 107 | @media (min-width: 768px) { 108 | .content-wrapper { 109 | flex-direction: row; } } 110 | 111 | .header { 112 | display: flex; 113 | padding: 8px; 114 | font-size: 0.875em; 115 | background: #444; 116 | color: #999; } 117 | 118 | .header-col { 119 | margin: 0; 120 | padding: 0 8px; } 121 | 122 | .header-col--primary { 123 | flex: 1; } 124 | 125 | .header-link { 126 | color: #fff; } 127 | 128 | .header-icon { 129 | padding-right: 6px; 130 | vertical-align: -4px; 131 | height: 16px; } 132 | 133 | .breadcrumbs { 134 | font-size: 0.875em; 135 | padding: 8px 16px; 136 | margin: 0; 137 | background: #fbfbfb; 138 | border-bottom: 1px solid #ddd; } 139 | 140 | .carat { 141 | height: 10px; 142 | margin: 0 5px; } 143 | 144 | .navigation { 145 | order: 2; } 146 | @media (min-width: 768px) { 147 | .navigation { 148 | order: 1; 149 | width: 25%; 150 | max-width: 300px; 151 | padding-bottom: 64px; 152 | overflow: hidden; 153 | word-wrap: normal; 154 | background: #fbfbfb; 155 | border-right: 1px solid #ddd; } } 156 | 157 | .nav-groups { 158 | list-style-type: none; 159 | padding-left: 0; } 160 | 161 | .nav-group-name { 162 | border-bottom: 1px solid #ddd; 163 | padding: 8px 0 8px 16px; } 164 | 165 | .nav-group-name-link { 166 | color: #333; } 167 | 168 | .nav-group-tasks { 169 | margin: 8px 0; 170 | padding: 0 0 0 8px; } 171 | 172 | .nav-group-task { 173 | font-size: 1em; 174 | list-style-type: none; 175 | white-space: nowrap; } 176 | 177 | .nav-group-task-link { 178 | color: #808080; } 179 | 180 | .main-content { 181 | order: 1; } 182 | @media (min-width: 768px) { 183 | .main-content { 184 | order: 2; 185 | flex: 1; 186 | padding-bottom: 60px; } } 187 | 188 | .section { 189 | padding: 0 32px; 190 | border-bottom: 1px solid #ddd; } 191 | 192 | .section-content { 193 | max-width: 834px; 194 | margin: 0 auto; 195 | padding: 16px 0; } 196 | 197 | .section-name { 198 | color: #666; 199 | display: block; } 200 | 201 | .declaration .highlight { 202 | overflow-x: initial; 203 | padding: 8px 0; 204 | margin: 0; 205 | background-color: transparent; 206 | border: none; } 207 | 208 | .task-group-section { 209 | border-top: 1px solid #ddd; } 210 | 211 | .task-group { 212 | padding-top: 0px; } 213 | 214 | .task-name-container a[name]:before { 215 | content: ""; 216 | display: block; } 217 | 218 | .item-container { 219 | padding: 0; } 220 | 221 | .item { 222 | padding-top: 8px; 223 | width: 100%; 224 | list-style-type: none; } 225 | .item a[name]:before { 226 | content: ""; 227 | display: block; } 228 | .item .token { 229 | padding-left: 3px; 230 | margin-left: 0px; 231 | font-size: 1rem; } 232 | .item .declaration-note { 233 | font-size: .85em; 234 | color: #808080; 235 | font-style: italic; } 236 | 237 | .pointer-container { 238 | border-bottom: 1px solid #ddd; 239 | left: -23px; 240 | padding-bottom: 13px; 241 | position: relative; 242 | width: 110%; } 243 | 244 | .pointer { 245 | left: 21px; 246 | top: 7px; 247 | display: block; 248 | position: absolute; 249 | width: 12px; 250 | height: 12px; 251 | border-left: 1px solid #ddd; 252 | border-top: 1px solid #ddd; 253 | background: #fff; 254 | transform: rotate(45deg); } 255 | 256 | .height-container { 257 | display: none; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #fff; 263 | border: 1px solid #ddd; 264 | border-top-width: 0; 265 | padding-top: 10px; 266 | padding-bottom: 5px; 267 | padding: 8px 16px; } 268 | 269 | .aside, .language { 270 | padding: 6px 12px; 271 | margin: 12px 0; 272 | border-left: 5px solid #dddddd; 273 | overflow-y: hidden; } 274 | .aside .aside-title, .language .aside-title { 275 | font-size: 9px; 276 | letter-spacing: 2px; 277 | text-transform: uppercase; 278 | padding-bottom: 0; 279 | margin: 0; 280 | color: #aaa; 281 | -webkit-user-select: none; } 282 | .aside p:last-child, .language p:last-child { 283 | margin-bottom: 0; } 284 | 285 | .language { 286 | border-left: 5px solid #cde9f4; } 287 | .language .aside-title { 288 | color: #4183c4; } 289 | 290 | .aside-warning { 291 | border-left: 5px solid #ff6666; } 292 | .aside-warning .aside-title { 293 | color: #ff0000; } 294 | 295 | .graybox { 296 | border-collapse: collapse; 297 | width: 100%; } 298 | .graybox p { 299 | margin: 0; 300 | word-break: break-word; 301 | min-width: 50px; } 302 | .graybox td { 303 | border: 1px solid #ddd; 304 | padding: 5px 25px 5px 10px; 305 | vertical-align: middle; } 306 | .graybox tr td:first-of-type { 307 | text-align: right; 308 | padding: 7px; 309 | vertical-align: top; 310 | word-break: normal; 311 | width: 40px; } 312 | 313 | .slightly-smaller { 314 | font-size: 0.9em; } 315 | 316 | .footer { 317 | padding: 8px 16px; 318 | background: #444; 319 | color: #ddd; 320 | font-size: 0.8em; } 321 | .footer p { 322 | margin: 8px 0; } 323 | .footer a { 324 | color: #fff; } 325 | 326 | html.dash .header, html.dash .breadcrumbs, html.dash .navigation { 327 | display: none; } 328 | html.dash .height-container { 329 | display: block; } 330 | 331 | form[role=search] input { 332 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 333 | font-size: 14px; 334 | line-height: 24px; 335 | padding: 0 10px; 336 | margin: 0; 337 | border: none; 338 | border-radius: 1em; } 339 | .loading form[role=search] input { 340 | background: white url(../img/spinner.gif) center right 4px no-repeat; } 341 | form[role=search] .tt-menu { 342 | margin: 0; 343 | min-width: 300px; 344 | background: #fbfbfb; 345 | color: #333; 346 | border: 1px solid #ddd; } 347 | form[role=search] .tt-highlight { 348 | font-weight: bold; } 349 | form[role=search] .tt-suggestion { 350 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 351 | padding: 0 8px; } 352 | form[role=search] .tt-suggestion span { 353 | display: table-cell; 354 | white-space: nowrap; } 355 | form[role=search] .tt-suggestion .doc-parent-name { 356 | width: 100%; 357 | text-align: right; 358 | font-weight: normal; 359 | font-size: 0.9em; 360 | padding-left: 16px; } 361 | form[role=search] .tt-suggestion:hover, 362 | form[role=search] .tt-suggestion.tt-cursor { 363 | cursor: pointer; 364 | background-color: #4183c4; 365 | color: #fff; } 366 | form[role=search] .tt-suggestion:hover .doc-parent-name, 367 | form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { 368 | color: #fff; } 369 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/css/jazzy.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: inherit; } 3 | 4 | body { 5 | margin: 0; 6 | background: #fff; 7 | color: #333; 8 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | letter-spacing: .2px; 10 | -webkit-font-smoothing: antialiased; 11 | box-sizing: border-box; } 12 | 13 | h1 { 14 | font-size: 2rem; 15 | font-weight: 700; 16 | margin: 1.275em 0 0.6em; } 17 | 18 | h2 { 19 | font-size: 1.75rem; 20 | font-weight: 700; 21 | margin: 1.275em 0 0.3em; } 22 | 23 | h3 { 24 | font-size: 1.5rem; 25 | font-weight: 700; 26 | margin: 1em 0 0.3em; } 27 | 28 | h4 { 29 | font-size: 1.25rem; 30 | font-weight: 700; 31 | margin: 1.275em 0 0.85em; } 32 | 33 | h5 { 34 | font-size: 1rem; 35 | font-weight: 700; 36 | margin: 1.275em 0 0.85em; } 37 | 38 | h6 { 39 | font-size: 1rem; 40 | font-weight: 700; 41 | margin: 1.275em 0 0.85em; 42 | color: #777; } 43 | 44 | p { 45 | margin: 0 0 1em; } 46 | 47 | ul, ol { 48 | padding: 0 0 0 2em; 49 | margin: 0 0 0.85em; } 50 | 51 | blockquote { 52 | margin: 0 0 0.85em; 53 | padding: 0 15px; 54 | color: #858585; 55 | border-left: 4px solid #e5e5e5; } 56 | 57 | img { 58 | max-width: 100%; } 59 | 60 | a { 61 | color: #4183c4; 62 | text-decoration: none; } 63 | a:hover, a:focus { 64 | outline: 0; 65 | text-decoration: underline; } 66 | 67 | table { 68 | background: #fff; 69 | width: 100%; 70 | border-collapse: collapse; 71 | border-spacing: 0; 72 | overflow: auto; 73 | margin: 0 0 0.85em; } 74 | 75 | tr:nth-child(2n) { 76 | background-color: #fbfbfb; } 77 | 78 | th, td { 79 | padding: 6px 13px; 80 | border: 1px solid #ddd; } 81 | 82 | pre { 83 | margin: 0 0 1.275em; 84 | padding: .85em 1em; 85 | overflow: auto; 86 | background: #f7f7f7; 87 | font-size: .85em; 88 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 89 | 90 | code { 91 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 92 | 93 | p > code, li > code { 94 | background: #f7f7f7; 95 | padding: .2em; } 96 | p > code:before, p > code:after, li > code:before, li > code:after { 97 | letter-spacing: -.2em; 98 | content: "\00a0"; } 99 | 100 | pre code { 101 | padding: 0; 102 | white-space: pre; } 103 | 104 | .content-wrapper { 105 | display: flex; 106 | flex-direction: column; } 107 | @media (min-width: 768px) { 108 | .content-wrapper { 109 | flex-direction: row; } } 110 | 111 | .header { 112 | display: flex; 113 | padding: 8px; 114 | font-size: 0.875em; 115 | background: #444; 116 | color: #999; } 117 | 118 | .header-col { 119 | margin: 0; 120 | padding: 0 8px; } 121 | 122 | .header-col--primary { 123 | flex: 1; } 124 | 125 | .header-link { 126 | color: #fff; } 127 | 128 | .header-icon { 129 | padding-right: 6px; 130 | vertical-align: -4px; 131 | height: 16px; } 132 | 133 | .breadcrumbs { 134 | font-size: 0.875em; 135 | padding: 8px 16px; 136 | margin: 0; 137 | background: #fbfbfb; 138 | border-bottom: 1px solid #ddd; } 139 | 140 | .carat { 141 | height: 10px; 142 | margin: 0 5px; } 143 | 144 | .navigation { 145 | order: 2; } 146 | @media (min-width: 768px) { 147 | .navigation { 148 | order: 1; 149 | width: 25%; 150 | max-width: 300px; 151 | padding-bottom: 64px; 152 | overflow: hidden; 153 | word-wrap: normal; 154 | background: #fbfbfb; 155 | border-right: 1px solid #ddd; } } 156 | 157 | .nav-groups { 158 | list-style-type: none; 159 | padding-left: 0; } 160 | 161 | .nav-group-name { 162 | border-bottom: 1px solid #ddd; 163 | padding: 8px 0 8px 16px; } 164 | 165 | .nav-group-name-link { 166 | color: #333; } 167 | 168 | .nav-group-tasks { 169 | margin: 8px 0; 170 | padding: 0 0 0 8px; } 171 | 172 | .nav-group-task { 173 | font-size: 1em; 174 | list-style-type: none; 175 | white-space: nowrap; } 176 | 177 | .nav-group-task-link { 178 | color: #808080; } 179 | 180 | .main-content { 181 | order: 1; } 182 | @media (min-width: 768px) { 183 | .main-content { 184 | order: 2; 185 | flex: 1; 186 | padding-bottom: 60px; } } 187 | 188 | .section { 189 | padding: 0 32px; 190 | border-bottom: 1px solid #ddd; } 191 | 192 | .section-content { 193 | max-width: 834px; 194 | margin: 0 auto; 195 | padding: 16px 0; } 196 | 197 | .section-name { 198 | color: #666; 199 | display: block; } 200 | 201 | .declaration .highlight { 202 | overflow-x: initial; 203 | padding: 8px 0; 204 | margin: 0; 205 | background-color: transparent; 206 | border: none; } 207 | 208 | .task-group-section { 209 | border-top: 1px solid #ddd; } 210 | 211 | .task-group { 212 | padding-top: 0px; } 213 | 214 | .task-name-container a[name]:before { 215 | content: ""; 216 | display: block; } 217 | 218 | .item-container { 219 | padding: 0; } 220 | 221 | .item { 222 | padding-top: 8px; 223 | width: 100%; 224 | list-style-type: none; } 225 | .item a[name]:before { 226 | content: ""; 227 | display: block; } 228 | .item .token { 229 | padding-left: 3px; 230 | margin-left: 0px; 231 | font-size: 1rem; } 232 | .item .declaration-note { 233 | font-size: .85em; 234 | color: #808080; 235 | font-style: italic; } 236 | 237 | .pointer-container { 238 | border-bottom: 1px solid #ddd; 239 | left: -23px; 240 | padding-bottom: 13px; 241 | position: relative; 242 | width: 110%; } 243 | 244 | .pointer { 245 | left: 21px; 246 | top: 7px; 247 | display: block; 248 | position: absolute; 249 | width: 12px; 250 | height: 12px; 251 | border-left: 1px solid #ddd; 252 | border-top: 1px solid #ddd; 253 | background: #fff; 254 | transform: rotate(45deg); } 255 | 256 | .height-container { 257 | display: none; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #fff; 263 | border: 1px solid #ddd; 264 | border-top-width: 0; 265 | padding-top: 10px; 266 | padding-bottom: 5px; 267 | padding: 8px 16px; } 268 | 269 | .aside, .language { 270 | padding: 6px 12px; 271 | margin: 12px 0; 272 | border-left: 5px solid #dddddd; 273 | overflow-y: hidden; } 274 | .aside .aside-title, .language .aside-title { 275 | font-size: 9px; 276 | letter-spacing: 2px; 277 | text-transform: uppercase; 278 | padding-bottom: 0; 279 | margin: 0; 280 | color: #aaa; 281 | -webkit-user-select: none; } 282 | .aside p:last-child, .language p:last-child { 283 | margin-bottom: 0; } 284 | 285 | .language { 286 | border-left: 5px solid #cde9f4; } 287 | .language .aside-title { 288 | color: #4183c4; } 289 | 290 | .aside-warning { 291 | border-left: 5px solid #ff6666; } 292 | .aside-warning .aside-title { 293 | color: #ff0000; } 294 | 295 | .graybox { 296 | border-collapse: collapse; 297 | width: 100%; } 298 | .graybox p { 299 | margin: 0; 300 | word-break: break-word; 301 | min-width: 50px; } 302 | .graybox td { 303 | border: 1px solid #ddd; 304 | padding: 5px 25px 5px 10px; 305 | vertical-align: middle; } 306 | .graybox tr td:first-of-type { 307 | text-align: right; 308 | padding: 7px; 309 | vertical-align: top; 310 | word-break: normal; 311 | width: 40px; } 312 | 313 | .slightly-smaller { 314 | font-size: 0.9em; } 315 | 316 | .footer { 317 | padding: 8px 16px; 318 | background: #444; 319 | color: #ddd; 320 | font-size: 0.8em; } 321 | .footer p { 322 | margin: 8px 0; } 323 | .footer a { 324 | color: #fff; } 325 | 326 | html.dash .header, html.dash .breadcrumbs, html.dash .navigation { 327 | display: none; } 328 | html.dash .height-container { 329 | display: block; } 330 | 331 | form[role=search] input { 332 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 333 | font-size: 14px; 334 | line-height: 24px; 335 | padding: 0 10px; 336 | margin: 0; 337 | border: none; 338 | border-radius: 1em; } 339 | .loading form[role=search] input { 340 | background: white url(../img/spinner.gif) center right 4px no-repeat; } 341 | form[role=search] .tt-menu { 342 | margin: 0; 343 | min-width: 300px; 344 | background: #fbfbfb; 345 | color: #333; 346 | border: 1px solid #ddd; } 347 | form[role=search] .tt-highlight { 348 | font-weight: bold; } 349 | form[role=search] .tt-suggestion { 350 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 351 | padding: 0 8px; } 352 | form[role=search] .tt-suggestion span { 353 | display: table-cell; 354 | white-space: nowrap; } 355 | form[role=search] .tt-suggestion .doc-parent-name { 356 | width: 100%; 357 | text-align: right; 358 | font-weight: normal; 359 | font-size: 0.9em; 360 | padding-left: 16px; } 361 | form[role=search] .tt-suggestion:hover, 362 | form[role=search] .tt-suggestion.tt-cursor { 363 | cursor: pointer; 364 | background-color: #4183c4; 365 | color: #fff; } 366 | form[role=search] .tt-suggestion:hover .doc-parent-name, 367 | form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { 368 | color: #fff; } 369 | -------------------------------------------------------------------------------- /docs/Enums/InvalidDataError.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | InvalidDataError Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | Health Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 112 |
113 | 114 |
115 |
116 |

InvalidDataError

117 |
118 |
119 |
public enum InvalidDataError: Error
120 | 121 |
122 |
123 |

InvalidDataError enum

124 | 125 |

Enumeration that represents data errors.

126 | 127 |
128 |
129 | 130 |
131 |
132 |
133 |
    134 |
  • 135 |
    136 | 137 | 138 | 139 | deserialization 140 | 141 |
    142 |
    143 |
    144 |
    145 |
    146 |
    147 |

    A deserialization error occurred.

    148 | 149 |
    150 |
    151 |

    Declaration

    152 |
    153 |

    Swift

    154 |
    case deserialization(String)
    155 | 156 |
    157 |
    158 |
    159 |
    160 |
  • 161 |
162 |
163 |
164 |
    165 |
  • 166 |
    167 | 168 | 169 | 170 | serialization 171 | 172 |
    173 |
    174 |
    175 |
    176 |
    177 |
    178 |

    A serialization error occurred.

    179 | 180 |
    181 |
    182 |

    Declaration

    183 |
    184 |

    Swift

    185 |
    case serialization(String)
    186 | 187 |
    188 |
    189 |
    190 |
    191 |
  • 192 |
193 |
194 |
195 |
196 | 197 |
198 |
199 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /docs/Extensions/Date.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Date Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | Health Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 112 |
113 | 114 |
115 |
116 |

Date

117 |
118 |
119 |
struct Date : ReferenceConvertible, Comparable, Equatable
120 | 121 |
122 |
123 |

Extension to the Date type.

124 | 125 |
126 |
127 | 128 |
129 |
130 |
131 |
    132 |
  • 133 |
    134 | 135 | 136 | 137 | currentTimeMillis() 138 | 139 |
    140 |
    141 |
    142 |
    143 |
    144 |
    145 |

    Returns the current time in milliseconds.

    146 | 147 |
    148 |
    149 |

    Declaration

    150 |
    151 |

    Swift

    152 |
    public static func currentTimeMillis() -> UInt64
    153 | 154 |
    155 |
    156 |
    157 |
    158 |
  • 159 |
  • 160 |
    161 | 162 | 163 | 164 | init(timeInMillis:) 165 | 166 |
    167 |
    168 |
    169 |
    170 |
    171 |
    172 |

    Creates a Date instance from milliseconds.

    173 | 174 |
    175 |
    176 |

    Declaration

    177 |
    178 |

    Swift

    179 |
    public init(timeInMillis: UInt64)
    180 | 181 |
    182 |
    183 |
    184 |
    185 |
  • 186 |
187 |
188 |
189 |
190 | 191 |
192 |
193 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/Enums/InvalidDataError.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | InvalidDataError Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | Health Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 112 |
113 | 114 |
115 |
116 |

InvalidDataError

117 |
118 |
119 |
public enum InvalidDataError: Error
120 | 121 |
122 |
123 |

InvalidDataError enum

124 | 125 |

Enumeration that represents data errors.

126 | 127 |
128 |
129 | 130 |
131 |
132 |
133 |
    134 |
  • 135 |
    136 | 137 | 138 | 139 | deserialization 140 | 141 |
    142 |
    143 |
    144 |
    145 |
    146 |
    147 |

    A deserialization error occurred.

    148 | 149 |
    150 |
    151 |

    Declaration

    152 |
    153 |

    Swift

    154 |
    case deserialization(String)
    155 | 156 |
    157 |
    158 |
    159 |
    160 |
  • 161 |
162 |
163 |
164 |
    165 |
  • 166 |
    167 | 168 | 169 | 170 | serialization 171 | 172 |
    173 |
    174 |
    175 |
    176 |
    177 |
    178 |

    A serialization error occurred.

    179 | 180 |
    181 |
    182 |

    Declaration

    183 |
    184 |

    Swift

    185 |
    case serialization(String)
    186 | 187 |
    188 |
    189 |
    190 |
    191 |
  • 192 |
193 |
194 |
195 |
196 | 197 |
198 |
199 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /docs/docsets/Health.docset/Contents/Resources/Documents/Extensions/Date.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Date Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | Health Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 112 |
113 | 114 |
115 |
116 |

Date

117 |
118 |
119 |
struct Date : ReferenceConvertible, Comparable, Equatable
120 | 121 |
122 |
123 |

Extension to the Date type.

124 | 125 |
126 |
127 | 128 |
129 |
130 |
131 |
    132 |
  • 133 |
    134 | 135 | 136 | 137 | currentTimeMillis() 138 | 139 |
    140 |
    141 |
    142 |
    143 |
    144 |
    145 |

    Returns the current time in milliseconds.

    146 | 147 |
    148 |
    149 |

    Declaration

    150 |
    151 |

    Swift

    152 |
    public static func currentTimeMillis() -> UInt64
    153 | 154 |
    155 |
    156 |
    157 |
    158 |
  • 159 |
  • 160 |
    161 | 162 | 163 | 164 | init(timeInMillis:) 165 | 166 |
    167 |
    168 |
    169 |
    170 |
    171 |
    172 |

    Creates a Date instance from milliseconds.

    173 | 174 |
    175 |
    176 |

    Declaration

    177 |
    178 |

    Swift

    179 |
    public init(timeInMillis: UInt64)
    180 | 181 |
    182 |
    183 |
    184 |
    185 |
  • 186 |
187 |
188 |
189 |
190 | 191 |
192 |
193 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Kitura 4 | 5 |

6 | 7 |

8 | 9 | APIDoc 10 | 11 | 12 | Build Status - Master 13 | 14 | 15 | codecov 16 | macOS 17 | Linux 18 | Apache 2 19 | 20 | Slack Status 21 | 22 |

23 | 24 | # Health 25 | The Health package provides a basic infrastructure that Swift applications can use for reporting their overall health status. 26 | 27 | As an application developer, you create an instance of the `Health` class and then register one or more health checks. A health check can be either a closure that conforms to the `HealthCheckClosure` typealias or a class that conforms to the `HealthCheck` protocol. Once you have your health checks in place, you can ask your `Health` instance for its status. 28 | 29 | ## Swift version 30 | The latest version of Health works with the `4.1.2` version of the Swift binaries. You can download this version of the Swift binaries by following this [link](https://swift.org/download/#snapshots). 31 | 32 | ## Usage 33 | 34 | ### Add dependencies 35 | 36 | Add `Health` to the dependencies within your application's `Package.swift` file. Substitute `"x.x.x"` with the latest `Health` [release](https://github.com/Kitura/Health/releases). 37 | 38 | ```swift 39 | .package(url: "https://github.com/Kitura/Health.git", from: "x.x.x") 40 | ``` 41 | Add `Health` to your target's dependencies: 42 | 43 | ```Swift 44 | .target(name: "example", dependencies: ["Health"]), 45 | ``` 46 | 47 | ### Initialize Health 48 | 49 | The example code below shows how to create a `Health` instance and register your health checks: 50 | 51 | ```swift 52 | import Health 53 | 54 | let health = Health() 55 | 56 | // Add custom checks 57 | health.addCheck(check: MyCheck1()) 58 | health.addCheck(check: MyCheck2()) 59 | health.addCheck(check: myClosureCheck1) 60 | health.addCheck(check: myClosureCheck1) 61 | 62 | // Get current health status 63 | let count = health.numberOfChecks 64 | let status: Status = health.status 65 | let state: State = status.state 66 | let dictionary = status.toDictionary() 67 | let simpleDictionary = status.toSimpleDictionary() 68 | ``` 69 | 70 | The simple dictionary contains a key-value pair that lets you know whether the application is UP or DOWN: 71 | 72 | ``` 73 | ["status": "UP"] 74 | ``` 75 | 76 | The dictionary contains a key-value pair that lets you know whether the application is UP or DOWN, additional details about the health checks that failed (if any), and a Universal Time Coordinated (UTC) timestamp value: 77 | 78 | ``` 79 | ["status": "DOWN", "timestamp": "2017-06-12T18:04:38+0000", "details": ["Cloudant health check.", "A health check closure reported status as DOWN."]] 80 | ``` 81 | 82 | Swift applications can use either dictionary, depending on the use case, to report the overall status of the application. For instance, an endpoint on the application could be defined that queries the `Health` object to get the overall status and then send it back to a client as a JSON payload. 83 | 84 | The `Status` structure now conforms to the `Codable` protocol, which enables you to serialize an instance of this structure and send it as a response to a client. If you use this mechanism you don't need to invoke either the `toDictionary()` or the `toSimpleDictionary()` methods in order to obtain the status payload for a client: 85 | 86 | ``` 87 | let status: Status = health.status 88 | let payload = try JSONEncoder().encode(status) 89 | // Send payload to client 90 | ``` 91 | 92 | ## Caching 93 | When you create an instance of the `Health` class, you can pass an optional argument (named `statusExpirationTime`) to its initializer as shown next: 94 | 95 | ```swift 96 | let health = Health(statusExpirationTime: 30000) 97 | ``` 98 | 99 | The `statusExpirationTime` parameter specifies the number of milliseconds that a given instance of `Health` should cache its status for before recomputing it. For instance, if the value assigned to `statusExpirationTime` is `30000` (as shown above), then 30 seconds must elapse before the `Health` instance computes its status again by querying each health check that has been registered. The default value for the `statusExpirationTime` parameter is `30000`. 100 | 101 | ## Implementing a health check 102 | You can implement health checks by either extending the `HealthCheck` protocol or creating a closure that returns a `State` value. 103 | 104 | The following snippet of code shows the implementation of a class named `MyCustomCheck`, which implements the `HealthCheck` protocol: 105 | 106 | ```swift 107 | class MyCustomCheck: HealthCheck { 108 | public var name: String { get { return "MyCustomCheck for XYZ"} } 109 | 110 | public var description: String { get { return "Description for MyCustomCheck..."} } 111 | 112 | public func evaluate() -> State { 113 | let state: State = isConnected() ? State.UP : State.DOWN 114 | return state 115 | } 116 | 117 | private func isConnected() -> Bool { 118 | ... 119 | } 120 | } 121 | ``` 122 | 123 | The following snippet of code shows the implementation for a similar health check but using a closure instead: 124 | 125 | ```swift 126 | func myCustomCheck() -> State { 127 | let state: State = isConnected() ? State.UP : State.DOWN 128 | return state 129 | } 130 | 131 | func isConnected() -> Bool { 132 | ... 133 | } 134 | ``` 135 | 136 | ## Using Health in a Kitura application 137 | One common use case for this Swift package is to integrate it into a Kitura-based application, as shown below: 138 | 139 | ```swift 140 | import Kitura 141 | import Foundation 142 | 143 | ... 144 | 145 | // Create main objects... 146 | router = Router() 147 | 148 | health = Health() 149 | 150 | // Register health checks... 151 | health.addCheck(check: Microservice1Check()) 152 | health.addCheck(check: microservice2Check) 153 | 154 | ... 155 | 156 | // Define /health endpoint that leverages Health 157 | router.get("/health") { request, response, next in 158 | // let status = health.status.toDictionary() 159 | let status = health.status.toSimpleDictionary() 160 | if health.status.state == .UP { 161 | try response.send(json: status).end() 162 | } else { 163 | try response.status(.serviceUnavailable).send(json: status).end() 164 | } 165 | } 166 | 167 | ``` 168 | 169 | In the code sample above, the health of the application is exposed through the `/health` endpoint. Cloud environments (e.g. Cloud Foundry, Kubernetes, etc.) can then use the status information returned from the `/health` endpoint to monitor and manage the Swift application instance. 170 | 171 | As an alternative to the implementation shown above for the `/health` endpoint, you can take advantage of the `Codable` protocol available in Swift 4. Since the `Status` struct satisfies the `Codable` protocol, a simpler implementation for the `/health` endpoint can be implemented using the new codable capabilities in Kitura 2.0 as shown below: 172 | 173 | ```swift 174 | ... 175 | 176 | // Define /health endpoint that leverages Health 177 | router.get("/health") { request, response, next in 178 | let status = health.status 179 | if health.status.state == .UP { 180 | try response.status(.OK).send(status).end() 181 | } else { 182 | try response.status(.serviceUnavailable).send(status).end() 183 | } 184 | } 185 | 186 | ... 187 | 188 | ``` 189 | 190 | In addition to sending the dictionary response, a server needs to respond with a non-200 status code, if the health state is considered down. This can be accomplished with a status code such as 503 `.serviceUnavailable`. That way, the cloud environment can recognize the negative health response, destroy that instance of the application, and restart the application. 191 | 192 | ## Using Cloud Foundry Environment 193 | If using a Cloud Foundry environment, make sure to update your `manifest.yml` to support health check. In the example above, you would set the `health-check-type` value to `http` and the `health-check-http-endpoint` to the correct health endpoint path, which is `/health` in this case. Review the [health check documentation](https://docs.cloudfoundry.org/devguide/deploy-apps/healthchecks.html) for more details. 194 | 195 | ## API documentation 196 | 197 | For more information visit our [API reference](http://kitura.github.io/Health/). 198 | 199 | ## Community 200 | 201 | We love to talk server-side Swift and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team! 202 | 203 | ## License 204 | 205 | This library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/Kitura/Health/blob/master/LICENSE.txt). 206 | --------------------------------------------------------------------------------