main.swift 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. /*
  2. * Copyright IBM Corporation 2018
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Foundation
  17. import Kitura
  18. import LoggerAPI
  19. import HeliumLogger
  20. import KituraStencil
  21. import Stencil
  22. import TechEmpowerCommon
  23. import KueryPostgres
  24. import SwiftKueryORM
  25. import KueryPostgresORM
  26. Log.logger = HeliumLogger(.info)
  27. // Stencil stuff
  28. let ext = Extension()
  29. // Stencil does not yet support automatic HTML escaping:
  30. // https://github.com/kylef/Stencil/pull/80
  31. //
  32. ext.registerFilter("htmlencode") { (value: Any?) in
  33. if let value = value as? String {
  34. return value
  35. .replacingOccurrences(of: "&", with: "&")
  36. .replacingOccurrences(of: "<", with: "&lt;")
  37. .replacingOccurrences(of: ">", with: "&gt;")
  38. .replacingOccurrences(of: "'", with: "&apos;")
  39. .replacingOccurrences(of: "\"", with: "&quot;")
  40. }
  41. return value
  42. }
  43. let router = Router()
  44. router.add(templateEngine: StencilTemplateEngine(extension: ext))
  45. // Configure our ORM Database connection pool as dbConnPool created by KueryPostgres
  46. Database.default = Database(dbConnPool)
  47. // Define the query parameters we can receive
  48. struct TFBParams: QueryParams {
  49. let queries: Int
  50. // Override default decode to cater for the query parameter specification:
  51. // If the parameter is missing, is not an integer, or is an integer less
  52. // than 1, the value should be interpreted as 1.
  53. // This means that rather than failing to decode on a non-integer value, we
  54. // should fall back to a value of 1.
  55. public init(from decoder: Decoder) throws {
  56. let container = try decoder.container(keyedBy: CodingKeys.self)
  57. if let value = try? container.decode(Int.self, forKey: CodingKeys.queries) {
  58. self.queries = value
  59. } else {
  60. self.queries = 1
  61. }
  62. }
  63. }
  64. // Set Server header on all responses as per TechEmpower spec
  65. router.all("/*") {
  66. _, response, next in
  67. response.headers["Server"] = "Kitura"
  68. next()
  69. }
  70. //
  71. // TechEmpower test 2: Single database query (full ORM)
  72. //
  73. router.get("/db") { (respondWith: @escaping (RandomRow?, RequestError?) -> Void) in
  74. // Select random row from database range
  75. RandomRow.find(id: RandomRow.randomId, respondWith)
  76. }
  77. //
  78. // TechEmpower test 3: Multiple database queries (full ORM)
  79. // Get param provides number of queries: /queries?queries=N
  80. //
  81. router.get("/queries") { (params: TFBParams, respondWith: @escaping ([RandomRow]?, RequestError?) -> Void) in
  82. let numQueries = max(1, min(params.queries, 500)) // Snap to range of 1-500 as per test spec
  83. getRandomRows(count: numQueries, completion: respondWith)
  84. }
  85. router.get("/queriesParallel") { (params: TFBParams, respondWith: @escaping ([RandomRow]?, RequestError?) -> Void) in
  86. let numQueries = max(1, min(params.queries, 500)) // Snap to range of 1-500 as per test spec
  87. getRandomRowsParallel(count: numQueries, completion: respondWith)
  88. }
  89. //
  90. // TechEmpower test 4: fortunes (full ORM)
  91. // TODO: convert to Codable once templating support is available
  92. //
  93. router.get("/fortunes") {
  94. request, response, next in
  95. response.headers["Content-Type"] = "text/html; charset=UTF-8"
  96. Fortune.findAll { (fortunes, err) in
  97. if var fortunes = fortunes {
  98. fortunes.append(Fortune(id: 0, message: "Additional fortune added at request time."))
  99. do {
  100. try response.render("fortunes.stencil", context: ["fortunes": fortunes.sorted()]).end()
  101. } catch {
  102. try? response.status(.internalServerError).send("Error: \(error)").end()
  103. }
  104. } else {
  105. try? response.status(.internalServerError).send("Error: \(err ?? .internalServerError)").end()
  106. }
  107. }
  108. }
  109. //
  110. // TechEmpower test 5: updates (full ORM)
  111. //
  112. router.get("/updates") { (params: TFBParams, respondWith: @escaping ([RandomRow]?, RequestError?) -> Void) in
  113. let numQueries = max(1, min(params.queries, 500)) // Snap to range of 1-500 as per test spec
  114. updateRandomRows(count: numQueries, completion: respondWith)
  115. }
  116. router.get("/updatesParallel") { (params: TFBParams, respondWith: @escaping ([RandomRow]?, RequestError?) -> Void) in
  117. let numQueries = max(1, min(params.queries, 500)) // Snap to range of 1-500 as per test spec
  118. updateRandomRowsParallel(count: numQueries, completion: respondWith)
  119. }
  120. Kitura.addHTTPServer(onPort: 8080, with: router)
  121. Kitura.run()