* API version 1.1.4 * added /health, returns 200 (OK, for status checks) * moved status code to a separate class * added status.mosad.xyz reporting
This commit is contained in:
		@ -48,4 +48,4 @@ compileTestKotlin {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
group 'org.mosad'
 | 
			
		||||
version '1.1.5'
 | 
			
		||||
version '1.1.6'
 | 
			
		||||
 | 
			
		||||
@ -28,14 +28,15 @@ import org.mosad.thecitadelofricks.controller.CacheController.Companion.getLesso
 | 
			
		||||
import org.mosad.thecitadelofricks.controller.CacheController.Companion.getLessonSubjectList
 | 
			
		||||
import org.mosad.thecitadelofricks.controller.CacheController.Companion.getTimetable
 | 
			
		||||
import org.mosad.thecitadelofricks.controller.CacheController.Companion.mensaMenu
 | 
			
		||||
import org.mosad.thecitadelofricks.controller.CacheController.Companion.timetableList
 | 
			
		||||
import org.mosad.thecitadelofricks.controller.StatusController.Companion.getStatus
 | 
			
		||||
import org.mosad.thecitadelofricks.controller.StatusController.Companion.updateMensaMenuRequests
 | 
			
		||||
import org.mosad.thecitadelofricks.controller.StatusController.Companion.updateTimetableRequests
 | 
			
		||||
import org.mosad.thecitadelofricks.controller.StatusController.Companion.updateTotalRequests
 | 
			
		||||
import org.slf4j.Logger
 | 
			
		||||
import org.slf4j.LoggerFactory
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestParam
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController
 | 
			
		||||
import java.net.HttpURLConnection
 | 
			
		||||
import java.net.URL
 | 
			
		||||
import java.time.LocalDateTime
 | 
			
		||||
import java.util.*
 | 
			
		||||
import kotlin.collections.ArrayList
 | 
			
		||||
@ -45,13 +46,11 @@ class APIController {
 | 
			
		||||
 | 
			
		||||
    private val logger: Logger = LoggerFactory.getLogger(APIController::class.java)
 | 
			
		||||
 | 
			
		||||
    private val apiVersion = "1.1.3"
 | 
			
		||||
    private val softwareVersion = "1.1.5"
 | 
			
		||||
    private val startTime = System.currentTimeMillis() / 1000
 | 
			
		||||
 | 
			
		||||
    var totalRequests = 0
 | 
			
		||||
    private var mensaMenuRequests = 0
 | 
			
		||||
    private var timetableRequests = ArrayList<TimetableCounter>()
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val apiVersion = "1.1.4"
 | 
			
		||||
        const val softwareVersion = "1.1.6"
 | 
			
		||||
        val startTime = System.currentTimeMillis() / 1000
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        CacheController() // initialize the CacheController
 | 
			
		||||
@ -67,15 +66,15 @@ class APIController {
 | 
			
		||||
    @RequestMapping("/courseList")
 | 
			
		||||
    fun courseList(): CourseList {
 | 
			
		||||
        logger.info("courseList request at ${LocalDateTime.now()}!")
 | 
			
		||||
        totalRequests++
 | 
			
		||||
        updateTotalRequests()
 | 
			
		||||
        return courseList
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @RequestMapping("/mensamenu")
 | 
			
		||||
    fun mensamenu(): MensaMenu {
 | 
			
		||||
        logger.info("mensamenu request at ${LocalDateTime.now()}!")
 | 
			
		||||
        mensaMenuRequests++
 | 
			
		||||
        totalRequests++
 | 
			
		||||
        updateTotalRequests()
 | 
			
		||||
        updateMensaMenuRequests()
 | 
			
		||||
        return mensaMenu
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -85,8 +84,8 @@ class APIController {
 | 
			
		||||
        @RequestParam(value = "week", defaultValue = "0") week: Int
 | 
			
		||||
    ): TimetableCourseWeek {
 | 
			
		||||
        logger.info("timetable request at ${LocalDateTime.now()}!")
 | 
			
		||||
        updateTotalRequests()
 | 
			
		||||
        updateTimetableRequests(courseName)
 | 
			
		||||
        totalRequests++
 | 
			
		||||
        return getTimetable(courseName, week)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -96,7 +95,7 @@ class APIController {
 | 
			
		||||
        @RequestParam(value = "week", defaultValue = "0") week: Int
 | 
			
		||||
    ): HashSet<String> {
 | 
			
		||||
        logger.info("lessonSubjectList request at ${LocalDateTime.now()}!")
 | 
			
		||||
        totalRequests++
 | 
			
		||||
        updateTotalRequests()
 | 
			
		||||
        return getLessonSubjectList(courseName, week)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -107,75 +106,20 @@ class APIController {
 | 
			
		||||
        @RequestParam(value = "week", defaultValue = "0") week: Int
 | 
			
		||||
    ): ArrayList<Lesson> {
 | 
			
		||||
        logger.info("lesson request at ${LocalDateTime.now()}!")
 | 
			
		||||
        totalRequests++
 | 
			
		||||
        updateTotalRequests()
 | 
			
		||||
        return getLesson(courseName, lessonSubject, week)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @RequestMapping("/status")
 | 
			
		||||
    fun status(): Status {
 | 
			
		||||
        logger.info("status request at ${LocalDateTime.now()}!")
 | 
			
		||||
        return getStatus()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // non direct api functions
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * if a timetable is requested update the request counter
 | 
			
		||||
     */
 | 
			
		||||
    private fun updateTimetableRequests(courseName: String) {
 | 
			
		||||
        when (timetableRequests.stream().filter { x -> x.courseName == courseName }.findAny().orElse(null)) {
 | 
			
		||||
            null -> timetableRequests.add(TimetableCounter(courseName, 0))
 | 
			
		||||
        }
 | 
			
		||||
        timetableRequests.stream().filter { x ->x.courseName == courseName }.findFirst().ifPresent { x -> x.requests++ }
 | 
			
		||||
 | 
			
		||||
        // TODO Java 11
 | 
			
		||||
//        timetableRequests.stream().filter { it.courseName == courseName }.findFirst().ifPresentOrElse({
 | 
			
		||||
//            it.requests++
 | 
			
		||||
//        }, {
 | 
			
		||||
//
 | 
			
		||||
//        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getStatus(): Status {
 | 
			
		||||
        val currentTime = System.currentTimeMillis() / 1000
 | 
			
		||||
        val minutes = (currentTime - startTime) % 3600 / 60
 | 
			
		||||
        val hours = (currentTime - startTime) % 86400 / 3600
 | 
			
		||||
        val days = (currentTime - startTime) / 86400
 | 
			
		||||
 | 
			
		||||
        var hsoCode = 999
 | 
			
		||||
        var swfrCode = 999
 | 
			
		||||
        logger.info("status request at ${LocalDateTime.now()}!")
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            val hsoURL = URL("https://www.hs-offenburg.de/")
 | 
			
		||||
            val swfrURL = URL("https://www.swfr.de/")
 | 
			
		||||
 | 
			
		||||
            var connection = hsoURL.openConnection() as HttpURLConnection
 | 
			
		||||
            connection.requestMethod = "HEAD"
 | 
			
		||||
 | 
			
		||||
            connection.connectTimeout = 15000
 | 
			
		||||
            hsoCode = connection.responseCode
 | 
			
		||||
 | 
			
		||||
            connection = swfrURL.openConnection() as HttpURLConnection
 | 
			
		||||
            connection.connectTimeout = 15000
 | 
			
		||||
            swfrCode = connection.responseCode
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            logger.error("Error while fetching url response codes!", e)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Status(
 | 
			
		||||
            LocalDateTime.now(),
 | 
			
		||||
            "$days days, $hours:$minutes",
 | 
			
		||||
            apiVersion,
 | 
			
		||||
            softwareVersion,
 | 
			
		||||
            totalRequests,
 | 
			
		||||
            mensaMenuRequests,
 | 
			
		||||
            timetableRequests,
 | 
			
		||||
            timetableList.size,
 | 
			
		||||
            Date(courseList.meta.updateTime * 1000),
 | 
			
		||||
            Date(mensaMenu.meta.updateTime * 1000),
 | 
			
		||||
            hsoCode,
 | 
			
		||||
            swfrCode
 | 
			
		||||
        )
 | 
			
		||||
    @RequestMapping("/health")
 | 
			
		||||
    fun health(): Int {
 | 
			
		||||
        logger.info("health request at ${LocalDateTime.now()}!")
 | 
			
		||||
        return 200
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -48,7 +48,6 @@ class CacheController {
 | 
			
		||||
    init {
 | 
			
		||||
        initUpdates()
 | 
			
		||||
        scheduledUpdates()
 | 
			
		||||
        //CachetAPIController.postTotalRequests(5)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // cache objects
 | 
			
		||||
@ -269,7 +268,7 @@ class CacheController {
 | 
			
		||||
 | 
			
		||||
        // post to status.mosad.xyz every hour
 | 
			
		||||
        Timer().scheduleAtFixedRate(initDelay1h, 3600000) {
 | 
			
		||||
            // TODO
 | 
			
		||||
            CachetAPIController.postTotalRequests()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,34 +1,75 @@
 | 
			
		||||
/**
 | 
			
		||||
 * TheCitadelofRicks
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2019  <seil0@mosad.xyz>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | 
			
		||||
 * MA 02110-1301, USA.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.mosad.thecitadelofricks.controller
 | 
			
		||||
 | 
			
		||||
import org.mosad.thecitadelofricks.controller.StatusController.Companion.getTotalRequests
 | 
			
		||||
import org.slf4j.Logger
 | 
			
		||||
import org.slf4j.LoggerFactory
 | 
			
		||||
import java.io.BufferedReader
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import java.io.InputStreamReader
 | 
			
		||||
import java.net.HttpURLConnection
 | 
			
		||||
import java.net.URL
 | 
			
		||||
import java.io.OutputStreamWriter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CachetAPIController {
 | 
			
		||||
 | 
			
		||||
     companion object {
 | 
			
		||||
 | 
			
		||||
         private val logger: Logger = LoggerFactory.getLogger(CachetAPIController::class.java)
 | 
			
		||||
         private const val baseURL = "https://status.mosad.xyz"
 | 
			
		||||
         private const val apiKey = "" //TODO
 | 
			
		||||
 | 
			
		||||
         fun postTotalRequests(totalRequests: Int) {
 | 
			
		||||
             logger.info("sending post request ...")
 | 
			
		||||
         private var oldTotalRequests = 0
 | 
			
		||||
 | 
			
		||||
             val currentTime = System.currentTimeMillis() / 1000
 | 
			
		||||
             val data = mapOf(Pair("value", totalRequests.toString()), Pair("timestamp", currentTime.toString()))
 | 
			
		||||
         fun postTotalRequests() {
 | 
			
		||||
             try {
 | 
			
		||||
                 val url = URL("$baseURL/api/v1/metrics/1/points")
 | 
			
		||||
                 val jsonInputString = "{\"value\": ${getTotalRequests() -oldTotalRequests}, \"timestamp\": \"${(System.currentTimeMillis() / 1000)}\"}"
 | 
			
		||||
                 oldTotalRequests = getTotalRequests()
 | 
			
		||||
 | 
			
		||||
             val url = URL("https://status.mosad.xyz/api/v1/metrics/1/points")
 | 
			
		||||
             val connection = url.openConnection() as HttpURLConnection
 | 
			
		||||
             connection.setRequestProperty("X-Cachet-Token", "h0103Wix5xhhNH5tk5KU")
 | 
			
		||||
             connection.requestMethod = "POST"
 | 
			
		||||
             connection.doOutput = true
 | 
			
		||||
                 val con = url.openConnection() as HttpURLConnection
 | 
			
		||||
                 con.requestMethod = "POST"
 | 
			
		||||
                 con.setRequestProperty("Content-Type", "application/json; utf-8")
 | 
			
		||||
                 con.setRequestProperty("Accept", "application/json")
 | 
			
		||||
                 con.setRequestProperty("X-Cachet-Token", apiKey)
 | 
			
		||||
                 con.doOutput = true
 | 
			
		||||
 | 
			
		||||
             val writer = OutputStreamWriter(connection.outputStream)
 | 
			
		||||
             writer.write(data.toString())
 | 
			
		||||
             writer.flush()
 | 
			
		||||
             writer.close()
 | 
			
		||||
                 val os = con.outputStream
 | 
			
		||||
                 val input = jsonInputString.toByteArray(charset("utf-8"))
 | 
			
		||||
                 os.write(input, 0, input.size)
 | 
			
		||||
 | 
			
		||||
                 val br = BufferedReader(InputStreamReader(con.inputStream, "utf-8"))
 | 
			
		||||
                 val response = StringBuilder()
 | 
			
		||||
                 var responseLine: String?
 | 
			
		||||
 | 
			
		||||
                 while (br.readLine().also { responseLine = it } != null) {
 | 
			
		||||
                     response.append(responseLine!!.trim { it <= ' ' })
 | 
			
		||||
                 }
 | 
			
		||||
                 logger.info(response.toString())
 | 
			
		||||
 | 
			
		||||
             } catch (e1: IOException) {
 | 
			
		||||
                 e1.printStackTrace()
 | 
			
		||||
             }
 | 
			
		||||
         }
 | 
			
		||||
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,127 @@
 | 
			
		||||
/**
 | 
			
		||||
 * TheCitadelofRicks
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2019  <seil0@mosad.xyz>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 | 
			
		||||
 * MA 02110-1301, USA.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.mosad.thecitadelofricks.controller
 | 
			
		||||
 | 
			
		||||
import org.mosad.thecitadelofricks.APIController.Companion.apiVersion
 | 
			
		||||
import org.mosad.thecitadelofricks.APIController.Companion.softwareVersion
 | 
			
		||||
import org.mosad.thecitadelofricks.APIController.Companion.startTime
 | 
			
		||||
import org.mosad.thecitadelofricks.Status
 | 
			
		||||
import org.mosad.thecitadelofricks.TimetableCounter
 | 
			
		||||
import org.slf4j.Logger
 | 
			
		||||
import org.slf4j.LoggerFactory
 | 
			
		||||
import java.net.HttpURLConnection
 | 
			
		||||
import java.net.URL
 | 
			
		||||
import java.time.LocalDateTime
 | 
			
		||||
import java.util.*
 | 
			
		||||
import kotlin.collections.ArrayList
 | 
			
		||||
 | 
			
		||||
class StatusController {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private val logger: Logger = LoggerFactory.getLogger(StatusController::class.java)
 | 
			
		||||
 | 
			
		||||
        private var totalRequests = 0
 | 
			
		||||
        private var mensaMenuRequests = 0
 | 
			
		||||
        private var timetableRequests = ArrayList<TimetableCounter>()
 | 
			
		||||
 | 
			
		||||
        fun updateTotalRequests() {
 | 
			
		||||
            totalRequests++
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun updateMensaMenuRequests() {
 | 
			
		||||
            mensaMenuRequests++
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * if a timetable is requested update the request counter
 | 
			
		||||
         */
 | 
			
		||||
        fun updateTimetableRequests(courseName: String) {
 | 
			
		||||
            when (timetableRequests.stream().filter { x -> x.courseName == courseName }.findAny().orElse(null)) {
 | 
			
		||||
                null -> timetableRequests.add(TimetableCounter(courseName, 0))
 | 
			
		||||
            }
 | 
			
		||||
            timetableRequests.stream().filter { x ->x.courseName == courseName }.findFirst().ifPresent { x -> x.requests++ }
 | 
			
		||||
 | 
			
		||||
            // TODO Java 11
 | 
			
		||||
//        timetableRequests.stream().filter { it.courseName == courseName }.findFirst().ifPresentOrElse({
 | 
			
		||||
//            it.requests++
 | 
			
		||||
//        }, {
 | 
			
		||||
//
 | 
			
		||||
//        })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun getTotalRequests(): Int {
 | 
			
		||||
            return totalRequests
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun getMensaMenuRequests(): Int {
 | 
			
		||||
            return mensaMenuRequests
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun getTimetableRequests(): ArrayList<TimetableCounter> {
 | 
			
		||||
            return timetableRequests
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun getStatus(): Status {
 | 
			
		||||
            val currentTime = System.currentTimeMillis() / 1000
 | 
			
		||||
            val minutes = (currentTime - startTime) % 3600 / 60
 | 
			
		||||
            val hours = (currentTime - startTime) % 86400 / 3600
 | 
			
		||||
            val days = (currentTime - startTime) / 86400
 | 
			
		||||
 | 
			
		||||
            var hsoCode = 999
 | 
			
		||||
            var swfrCode = 999
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                val hsoURL = URL("https://www.hs-offenburg.de/")
 | 
			
		||||
                val swfrURL = URL("https://www.swfr.de/")
 | 
			
		||||
 | 
			
		||||
                var connection = hsoURL.openConnection() as HttpURLConnection
 | 
			
		||||
                connection.requestMethod = "HEAD"
 | 
			
		||||
 | 
			
		||||
                connection.connectTimeout = 15000
 | 
			
		||||
                hsoCode = connection.responseCode
 | 
			
		||||
 | 
			
		||||
                connection = swfrURL.openConnection() as HttpURLConnection
 | 
			
		||||
                connection.connectTimeout = 15000
 | 
			
		||||
                swfrCode = connection.responseCode
 | 
			
		||||
            } catch (e: Exception) {
 | 
			
		||||
                logger.error("Error while fetching url response codes!", e)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return Status(
 | 
			
		||||
                LocalDateTime.now(),
 | 
			
		||||
                "$days days, $hours:$minutes",
 | 
			
		||||
                apiVersion,
 | 
			
		||||
                softwareVersion,
 | 
			
		||||
                getTotalRequests(),
 | 
			
		||||
                getMensaMenuRequests(),
 | 
			
		||||
                getTimetableRequests(),
 | 
			
		||||
                CacheController.timetableList.size,
 | 
			
		||||
                Date(CacheController.courseList.meta.updateTime * 1000),
 | 
			
		||||
                Date(CacheController.mensaMenu.meta.updateTime * 1000),
 | 
			
		||||
                hsoCode,
 | 
			
		||||
                swfrCode
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user