Nicole Dresselhaus ba3fd2d09e wrong URL -.-
2025-05-09 21:51:23 +02:00

1251 lines
81 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
<meta charset="utf-8">
<meta name="generator" content="quarto-1.7.23">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<meta name="dcterms.date" content="2020-04-01">
<title>Webapp-Example: Main.hs Nicole Dresselhaus</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
vertical-align: middle;
}
/* CSS for syntax highlighting */
html { -webkit-text-size-adjust: 100%; }
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
}
pre.numberSource { margin-left: 3em; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
</style>
<script src="../../../site_libs/quarto-nav/quarto-nav.js"></script>
<script src="../../../site_libs/quarto-nav/headroom.min.js"></script>
<script src="../../../site_libs/clipboard/clipboard.min.js"></script>
<script src="../../../site_libs/quarto-search/autocomplete.umd.js"></script>
<script src="../../../site_libs/quarto-search/fuse.min.js"></script>
<script src="../../../site_libs/quarto-search/quarto-search.js"></script>
<meta name="quarto:offset" content="../../../">
<script src="../../../site_libs/quarto-html/quarto.js" type="module"></script>
<script src="../../../site_libs/quarto-html/tabsets/tabsets.js" type="module"></script>
<script src="../../../site_libs/quarto-html/popper.min.js"></script>
<script src="../../../site_libs/quarto-html/tippy.umd.min.js"></script>
<script src="../../../site_libs/quarto-html/anchor.min.js"></script>
<link href="../../../site_libs/quarto-html/tippy.css" rel="stylesheet">
<link href="../../../site_libs/quarto-html/quarto-syntax-highlighting-dark-2c84ecb840a13f4c7993f9e5648f0c14.css" rel="stylesheet" class="quarto-color-scheme quarto-color-alternate" id="quarto-text-highlighting-styles">
<link href="../../../site_libs/quarto-html/quarto-syntax-highlighting-6cf5824034cebd0380a5b9c74c43f006.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-text-highlighting-styles">
<script src="../../../site_libs/bootstrap/bootstrap.min.js"></script>
<link href="../../../site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
<link href="../../../site_libs/bootstrap/bootstrap-ec71cb1e120c0dd41819aca960e74e38.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme" id="quarto-bootstrap" data-mode="light">
<link href="../../../site_libs/bootstrap/bootstrap-dark-6ed95ce66646ab2447a87e45f81c21f3.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme quarto-color-alternate" id="quarto-bootstrap" data-mode="dark">
<link href="../../../site_libs/bootstrap/bootstrap-ec71cb1e120c0dd41819aca960e74e38.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme-extra" id="quarto-bootstrap" data-mode="light">
<script id="quarto-search-options" type="application/json">{
"location": "navbar",
"copy-button": false,
"collapse-after": 3,
"panel-placement": "end",
"type": "overlay",
"limit": 50,
"keyboard-shortcut": [
"f",
"/",
"s"
],
"show-item-context": false,
"language": {
"search-no-results-text": "No results",
"search-matching-documents-text": "matching documents",
"search-copy-link-title": "Copy link to search",
"search-hide-matches-text": "Hide additional matches",
"search-more-match-text": "more match in this document",
"search-more-matches-text": "more matches in this document",
"search-clear-button-title": "Clear",
"search-text-placeholder": "",
"search-detached-cancel-button-title": "Cancel",
"search-submit-button-title": "Submit",
"search-label": "Search"
}
}</script>
<meta property="og:title" content="Webapp-Example: Main.hs Nicole Dresselhaus">
<meta property="og:description" content="Ramblings of a madwoman">
<meta property="og:site_name" content="Nicole Dresselhaus">
</head>
<body class="nav-sidebar docked nav-fixed quarto-light"><script id="quarto-html-before-body" type="application/javascript">
const toggleBodyColorMode = (bsSheetEl) => {
const mode = bsSheetEl.getAttribute("data-mode");
const bodyEl = window.document.querySelector("body");
if (mode === "dark") {
bodyEl.classList.add("quarto-dark");
bodyEl.classList.remove("quarto-light");
} else {
bodyEl.classList.add("quarto-light");
bodyEl.classList.remove("quarto-dark");
}
}
const toggleBodyColorPrimary = () => {
const bsSheetEl = window.document.querySelector("link#quarto-bootstrap:not([rel=disabled-stylesheet])");
if (bsSheetEl) {
toggleBodyColorMode(bsSheetEl);
}
}
window.setColorSchemeToggle = (alternate) => {
const toggles = window.document.querySelectorAll('.quarto-color-scheme-toggle');
for (let i=0; i < toggles.length; i++) {
const toggle = toggles[i];
if (toggle) {
if (alternate) {
toggle.classList.add("alternate");
} else {
toggle.classList.remove("alternate");
}
}
}
};
const toggleColorMode = (alternate) => {
// Switch the stylesheets
const primaryStylesheets = window.document.querySelectorAll('link.quarto-color-scheme:not(.quarto-color-alternate)');
const alternateStylesheets = window.document.querySelectorAll('link.quarto-color-scheme.quarto-color-alternate');
manageTransitions('#quarto-margin-sidebar .nav-link', false);
if (alternate) {
// note: dark is layered on light, we don't disable primary!
enableStylesheet(alternateStylesheets);
for (const sheetNode of alternateStylesheets) {
if (sheetNode.id === "quarto-bootstrap") {
toggleBodyColorMode(sheetNode);
}
}
} else {
disableStylesheet(alternateStylesheets);
enableStylesheet(primaryStylesheets)
toggleBodyColorPrimary();
}
manageTransitions('#quarto-margin-sidebar .nav-link', true);
// Switch the toggles
window.setColorSchemeToggle(alternate)
// Hack to workaround the fact that safari doesn't
// properly recolor the scrollbar when toggling (#1455)
if (navigator.userAgent.indexOf('Safari') > 0 && navigator.userAgent.indexOf('Chrome') == -1) {
manageTransitions("body", false);
window.scrollTo(0, 1);
setTimeout(() => {
window.scrollTo(0, 0);
manageTransitions("body", true);
}, 40);
}
}
const disableStylesheet = (stylesheets) => {
for (let i=0; i < stylesheets.length; i++) {
const stylesheet = stylesheets[i];
stylesheet.rel = 'disabled-stylesheet';
}
}
const enableStylesheet = (stylesheets) => {
for (let i=0; i < stylesheets.length; i++) {
const stylesheet = stylesheets[i];
if(stylesheet.rel !== 'stylesheet') { // for Chrome, which will still FOUC without this check
stylesheet.rel = 'stylesheet';
}
}
}
const manageTransitions = (selector, allowTransitions) => {
const els = window.document.querySelectorAll(selector);
for (let i=0; i < els.length; i++) {
const el = els[i];
if (allowTransitions) {
el.classList.remove('notransition');
} else {
el.classList.add('notransition');
}
}
}
const isFileUrl = () => {
return window.location.protocol === 'file:';
}
window.hasAlternateSentinel = () => {
let styleSentinel = getColorSchemeSentinel();
if (styleSentinel !== null) {
return styleSentinel === "alternate";
} else {
return false;
}
}
const setStyleSentinel = (alternate) => {
const value = alternate ? "alternate" : "default";
if (!isFileUrl()) {
window.localStorage.setItem("quarto-color-scheme", value);
} else {
localAlternateSentinel = value;
}
}
const getColorSchemeSentinel = () => {
if (!isFileUrl()) {
const storageValue = window.localStorage.getItem("quarto-color-scheme");
return storageValue != null ? storageValue : localAlternateSentinel;
} else {
return localAlternateSentinel;
}
}
const toggleGiscusIfUsed = (isAlternate, darkModeDefault) => {
const baseTheme = document.querySelector('#giscus-base-theme')?.value ?? 'light';
const alternateTheme = document.querySelector('#giscus-alt-theme')?.value ?? 'dark';
let newTheme = '';
if(darkModeDefault) {
newTheme = isAlternate ? baseTheme : alternateTheme;
} else {
newTheme = isAlternate ? alternateTheme : baseTheme;
}
const changeGiscusTheme = () => {
// From: https://github.com/giscus/giscus/issues/336
const sendMessage = (message) => {
const iframe = document.querySelector('iframe.giscus-frame');
if (!iframe) return;
iframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app');
}
sendMessage({
setConfig: {
theme: newTheme
}
});
}
const isGiscussLoaded = window.document.querySelector('iframe.giscus-frame') !== null;
if (isGiscussLoaded) {
changeGiscusTheme();
}
};
const queryPrefersDark = window.matchMedia('(prefers-color-scheme: dark)');
const darkModeDefault = queryPrefersDark.matches;
document.querySelector('link.quarto-color-scheme-extra').rel = 'disabled-stylesheet';
let localAlternateSentinel = darkModeDefault ? 'alternate' : 'default';
// Dark / light mode switch
window.quartoToggleColorScheme = () => {
// Read the current dark / light value
let toAlternate = !window.hasAlternateSentinel();
toggleColorMode(toAlternate);
setStyleSentinel(toAlternate);
toggleGiscusIfUsed(toAlternate, darkModeDefault);
};
queryPrefersDark.addEventListener("change", e => {
if(window.localStorage.getItem("quarto-color-scheme") !== null)
return;
const alternate = e.matches
toggleColorMode(alternate);
localAlternateSentinel = e.matches ? 'alternate' : 'default'; // this is used alongside local storage!
toggleGiscusIfUsed(alternate, darkModeDefault);
});
// Switch to dark mode if need be
if (window.hasAlternateSentinel()) {
toggleColorMode(true);
} else {
toggleColorMode(false);
}
</script>
<div id="quarto-search-results"></div>
<header id="quarto-header" class="headroom fixed-top">
<nav class="navbar navbar-expand-lg " data-bs-theme="dark">
<div class="navbar-container container-fluid">
<div class="navbar-brand-container mx-auto">
<a class="navbar-brand" href="../../../index.html">
<span class="navbar-title">Nicole Dresselhaus</span>
</a>
</div>
<div id="quarto-search" class="" title="Search"></div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" role="menu" aria-expanded="false" aria-label="Toggle navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav navbar-nav-scroll me-auto">
<li class="nav-item">
<a class="nav-link" href="../../../index.html"> <i class="bi bi-house" role="img">
</i>
<span class="menu-text">Home</span></a>
</li>
<li class="nav-item">
<a class="nav-link active" href="../../../About/index.html" aria-current="page"> <i class="bi bi-file-person" role="img">
</i>
<span class="menu-text">About</span></a>
</li>
</ul>
<ul class="navbar-nav navbar-nav-scroll ms-auto">
<li class="nav-item compact">
<a class="nav-link" href="../../../index.xml"> <i class="bi bi-rss" role="img">
</i>
<span class="menu-text"></span></a>
</li>
</ul>
</div> <!-- /navcollapse -->
<div class="quarto-navbar-tools">
<a href="" class="quarto-color-scheme-toggle quarto-navigation-tool px-1" onclick="window.quartoToggleColorScheme(); return false;" title="Toggle dark mode"><i class="bi"></i></a>
<a href="" class="quarto-reader-toggle quarto-navigation-tool px-1" onclick="window.quartoToggleReader(); return false;" title="Toggle reader mode">
<div class="quarto-reader-toggle-btn">
<i class="bi"></i>
</div>
</a>
</div>
</div> <!-- /container-fluid -->
</nav>
<nav class="quarto-secondary-nav">
<div class="container-fluid d-flex">
<button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" role="button" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<i class="bi bi-layout-text-sidebar-reverse"></i>
</button>
<nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item">Serious</li><li class="breadcrumb-item">Coding</li><li class="breadcrumb-item"><a href="../../../Coding/Haskell/Advantages.html">Haskell</a></li><li class="breadcrumb-item"><a href="../../../Coding/Haskell/Webapp-Example/index.html">Webapp-Development in Haskell</a></li><li class="breadcrumb-item"><a href="../../../Coding/Haskell/Webapp-Example/Main.hs.html">Webapp-Example: Main.hs</a></li></ol></nav>
<a class="flex-grow-1" role="navigation" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
</a>
</div>
</nav>
</header>
<!-- content -->
<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article page-navbar">
<!-- sidebar -->
<nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal quarto-sidebar-collapse-item sidebar-navigation docked overflow-auto">
<div class="sidebar-menu-container">
<ul class="list-unstyled mt-1">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" role="navigation" aria-expanded="true">
<span class="menu-text">Serious</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-1" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" role="navigation" aria-expanded="false">
<span class="menu-text">Writing</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-2" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Writing/documentation.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Writing/ner4all-case-study.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Case Study: Local LLM-Based NER with n8n and Ollama</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Writing/Obsidian-RAG.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" role="navigation" aria-expanded="true">
<span class="menu-text">Coding</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-3" class="collapse list-unstyled sidebar-section depth2 show">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-4" role="navigation" aria-expanded="true">
<span class="menu-text">Haskell</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-4" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-4" class="collapse list-unstyled sidebar-section depth3 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Coding/Haskell/Advantages.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Talks und Posts zu Haskell</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Coding/Haskell/FFPiH.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Fortgeschrittene funktionale Programmierung in Haskell</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Coding/Haskell/Lenses.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Lenses</span></a>
</div>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-5" role="navigation" aria-expanded="false">
<span class="menu-text">Code Snippets</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-5" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-5" class="collapse list-unstyled sidebar-section depth4 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Coding/Haskell/Code Snippets/Monoid.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Monoid? Da war doch was…</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Coding/Haskell/Code Snippets/Morphisms.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">*-Morpisms</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a href="../../../Coding/Haskell/Webapp-Example/index.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Webapp-Development in Haskell</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-6" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-6" class="collapse list-unstyled sidebar-section depth4 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Coding/Haskell/Webapp-Example/Main.hs.html" class="sidebar-item-text sidebar-link active">
<span class="menu-text">Webapp-Example: Main.hs</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Coding/Haskell/Webapp-Example/MyService_Types.hs.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Webapp-Example: MyService/Types.hs</span></a>
</div>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-7" role="navigation" aria-expanded="false">
<span class="menu-text">Health</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-7" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-7" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Health/Issues.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Mental Health</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-8" role="navigation" aria-expanded="false">
<span class="menu-text">Uni</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-8" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-8" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Uni/Lernerfolg_an_der_Uni.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Wie lerne ich richtig an der Uni?</span></a>
</div>
</li>
</ul>
</li>
</ul>
</li>
<li class="px-0"><hr class="sidebar-divider hi "></li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-9" role="navigation" aria-expanded="true">
<span class="menu-text">Fun</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-9" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-9" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-10" role="navigation" aria-expanded="false">
<span class="menu-text">Opinions</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-10" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-10" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Opinions/Editors.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Editors</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Opinions/Keyboard-Layout.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Keyboard-Layout</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-11" role="navigation" aria-expanded="false">
<span class="menu-text">Stuff</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-11" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-11" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../Stuff/Bielefeldverschwoerung.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Die Bielefeld-Verschwörung</span></a>
</div>
</li>
</ul>
</li>
</ul>
</li>
<li class="px-0"><hr class="sidebar-divider hi "></li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-12" role="navigation" aria-expanded="true">
<span class="menu-text">Info</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-12" role="navigation" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-12" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a href="../../../About/index.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">About me</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-13" role="navigation" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-13" class="collapse list-unstyled sidebar-section depth2 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../About/Experience.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Highlights of my experiences in the programming world</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../About/Extracurricular.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Studium generale / University-Life</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="../../../About/Work.html" class="sidebar-item-text sidebar-link">
<span class="menu-text">Work-Experience</span></a>
</div>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</nav>
<div id="quarto-sidebar-glass" class="quarto-sidebar-collapse-item" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item"></div>
<!-- margin-sidebar -->
<div id="quarto-margin-sidebar" class="sidebar margin-sidebar zindex-bottom">
</div>
<!-- main -->
<main class="content" id="quarto-document-content">
<header id="title-block-header" class="quarto-title-block default"><nav class="quarto-page-breadcrumbs quarto-title-breadcrumbs d-none d-lg-block" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item">Serious</li><li class="breadcrumb-item">Coding</li><li class="breadcrumb-item"><a href="../../../Coding/Haskell/Advantages.html">Haskell</a></li><li class="breadcrumb-item"><a href="../../../Coding/Haskell/Webapp-Example/index.html">Webapp-Development in Haskell</a></li><li class="breadcrumb-item"><a href="../../../Coding/Haskell/Webapp-Example/Main.hs.html">Webapp-Example: Main.hs</a></li></ol></nav>
<div class="quarto-title">
<h1 class="title">Webapp-Example: Main.hs</h1>
<div class="quarto-categories">
<div class="quarto-category">Haskell</div>
<div class="quarto-category">Code</div>
</div>
</div>
<div class="quarto-title-meta">
<div>
<div class="quarto-title-meta-heading">Published</div>
<div class="quarto-title-meta-contents">
<p class="date">April 1, 2020</p>
</div>
</div>
</div>
</header>
<p>Wie man das verwendet, siehe <a href="../../../Coding/Haskell/Webapp-Example/index.html">Webapp-Example</a>.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell code-with-copy"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# OPTIONS_GHC -Wno-name-shadowing #-}</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE FlexibleContexts #-}</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE LambdaCase #-}</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE OverloadedStrings #-}</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE RankNTypes #-}</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE RecordWildCards #-}</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE ScopedTypeVariables #-}</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="kw">module</span> <span class="dt">MyService</span> <span class="kw">where</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="co">-- generische imports aus den dependencies/base, nicht in der prelude</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Codec.MIME.Type</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Configuration.Dotenv</span> <span class="kw">as</span> <span class="dt">Dotenv</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Concurrent</span> (forkIO, threadDelay)</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Concurrent.Async</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Concurrent.STM</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Monad</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Monad.Catch</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Monad.Except</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Conversion</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Conversion.Text</span> ()</span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Binary.Builder</span></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.String</span> (<span class="dt">IsString</span> (..))</span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Time</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Time.Clock</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Time.Format</span></span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Default</span></span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Network.HostName</span></span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Network.HTTP.Client</span> <span class="kw">as</span> <span class="dt">HTTP</span> <span class="kw">hiding</span></span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a> (withConnection)</span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Network.HTTP.Types</span> (<span class="dt">Status</span>, statusCode)</span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Network.Mom.Stompl.Client.Queue</span></span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Network.Wai</span> (<span class="dt">Middleware</span>)</span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Network.Wai.Logger</span></span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Network.Wai.Middleware.Cors</span></span>
<span id="cb1-35"><a href="#cb1-35" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Network.Wai.Middleware.RequestLogger</span> (<span class="dt">OutputFormat</span> (..),</span>
<span id="cb1-36"><a href="#cb1-36" aria-hidden="true" tabindex="-1"></a> logStdout,</span>
<span id="cb1-37"><a href="#cb1-37" aria-hidden="true" tabindex="-1"></a> mkRequestLogger,</span>
<span id="cb1-38"><a href="#cb1-38" aria-hidden="true" tabindex="-1"></a> outputFormat)</span>
<span id="cb1-39"><a href="#cb1-39" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Servant.Client</span> (mkClientEnv,</span>
<span id="cb1-40"><a href="#cb1-40" aria-hidden="true" tabindex="-1"></a> parseBaseUrl)</span>
<span id="cb1-41"><a href="#cb1-41" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">System.Directory</span></span>
<span id="cb1-42"><a href="#cb1-42" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">System.Envy</span></span>
<span id="cb1-43"><a href="#cb1-43" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">System.IO</span></span>
<span id="cb1-44"><a href="#cb1-44" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">System.Log.FastLogger</span></span>
<span id="cb1-45"><a href="#cb1-45" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Text.PrettyPrint.GenericPretty</span></span>
<span id="cb1-46"><a href="#cb1-46" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-47"><a href="#cb1-47" aria-hidden="true" tabindex="-1"></a><span class="co">-- generische imports, aber qualified, weil es sonst zu name-clashes kommt</span></span>
<span id="cb1-48"><a href="#cb1-48" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-49"><a href="#cb1-49" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.ByteString</span> <span class="kw">as</span> <span class="dt">BS</span></span>
<span id="cb1-50"><a href="#cb1-50" aria-hidden="true" tabindex="-1"></a><span class="co">-- import qualified Data.ByteString.Char8 as BS8</span></span>
<span id="cb1-51"><a href="#cb1-51" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.ByteString.Lazy</span> <span class="kw">as</span> <span class="dt">LBS</span></span>
<span id="cb1-52"><a href="#cb1-52" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Network.HTTP.Client.TLS</span> <span class="kw">as</span> <span class="dt">UseDefaultHTTPSSettings</span> (tlsManagerSettings)</span>
<span id="cb1-53"><a href="#cb1-53" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Network.Mom.Stompl.Client.Queue</span> <span class="kw">as</span> <span class="dt">AMQ</span></span>
<span id="cb1-54"><a href="#cb1-54" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Network.Wai</span> <span class="kw">as</span> <span class="dt">WAI</span></span>
<span id="cb1-55"><a href="#cb1-55" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-56"><a href="#cb1-56" aria-hidden="true" tabindex="-1"></a><span class="co">-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries</span></span>
<span id="cb1-57"><a href="#cb1-57" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">MyService.Handler</span> <span class="kw">as</span> <span class="dt">H</span> <span class="co">-- handler der H.myApiEndpointV1Post implementiert</span></span>
<span id="cb1-58"><a href="#cb1-58" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">MyService.Types</span> <span class="co">-- weitere Type (s. nächste box)</span></span>
<span id="cb1-59"><a href="#cb1-59" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">MyServiceGen.API</span> <span class="kw">as</span> <span class="dt">MS</span> <span class="co">-- aus der generierten library</span></span>
<span id="cb1-60"><a href="#cb1-60" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-61"><a href="#cb1-61" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-62"><a href="#cb1-62" aria-hidden="true" tabindex="-1"></a><span class="ot">myServicemain ::</span> <span class="dt">IO</span> ()</span>
<span id="cb1-63"><a href="#cb1-63" aria-hidden="true" tabindex="-1"></a>myServicemain <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb1-64"><a href="#cb1-64" aria-hidden="true" tabindex="-1"></a> <span class="co">-- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt</span></span>
<span id="cb1-65"><a href="#cb1-65" aria-hidden="true" tabindex="-1"></a> void <span class="op">$</span> loadFile <span class="op">$</span> <span class="dt">Dotenv.Config</span> [<span class="st">".env"</span>] [] <span class="dt">False</span></span>
<span id="cb1-66"><a href="#cb1-66" aria-hidden="true" tabindex="-1"></a> <span class="co">-- Config holen (defaults + overrides aus dem Environment)</span></span>
<span id="cb1-67"><a href="#cb1-67" aria-hidden="true" tabindex="-1"></a> sc<span class="op">@</span><span class="dt">ServerConfig</span>{<span class="op">..</span>} <span class="ot">&lt;-</span> decodeWithDefaults defConfig</span>
<span id="cb1-68"><a href="#cb1-68" aria-hidden="true" tabindex="-1"></a> <span class="co">-- Backend-Setup</span></span>
<span id="cb1-69"><a href="#cb1-69" aria-hidden="true" tabindex="-1"></a> <span class="co">-- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices</span></span>
<span id="cb1-70"><a href="#cb1-70" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> defaultHTTPSSettings <span class="ot">=</span> UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout <span class="ot">=</span> responseTimeoutMicro <span class="op">$</span> <span class="dv">1000</span> <span class="op">*</span> <span class="dv">1000</span> <span class="op">*</span> myserviceMaxTimeout }</span>
<span id="cb1-71"><a href="#cb1-71" aria-hidden="true" tabindex="-1"></a> createBackend url proxy <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb1-72"><a href="#cb1-72" aria-hidden="true" tabindex="-1"></a> manager <span class="ot">&lt;-</span> newManager <span class="op">.</span> managerSetProxy proxy</span>
<span id="cb1-73"><a href="#cb1-73" aria-hidden="true" tabindex="-1"></a> <span class="op">$</span> defaultHTTPSSettings</span>
<span id="cb1-74"><a href="#cb1-74" aria-hidden="true" tabindex="-1"></a> url' <span class="ot">&lt;-</span> parseBaseUrl url</span>
<span id="cb1-75"><a href="#cb1-75" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> (mkClientEnv manager url')</span>
<span id="cb1-76"><a href="#cb1-76" aria-hidden="true" tabindex="-1"></a> internalProxy <span class="ot">=</span> <span class="kw">case</span> myserviceInternalProxyUrl <span class="kw">of</span></span>
<span id="cb1-77"><a href="#cb1-77" aria-hidden="true" tabindex="-1"></a> <span class="st">""</span> <span class="ot">-&gt;</span> noProxy</span>
<span id="cb1-78"><a href="#cb1-78" aria-hidden="true" tabindex="-1"></a> url <span class="ot">-&gt;</span> useProxy <span class="op">$</span> <span class="dt">HTTP.Proxy</span> (fromString url) myserviceInternalProxyPort</span>
<span id="cb1-79"><a href="#cb1-79" aria-hidden="true" tabindex="-1"></a> <span class="co">-- externalProxy = case myserviceExternalProxyUrl of</span></span>
<span id="cb1-80"><a href="#cb1-80" aria-hidden="true" tabindex="-1"></a> <span class="co">-- "" -&gt; noProxy</span></span>
<span id="cb1-81"><a href="#cb1-81" aria-hidden="true" tabindex="-1"></a> <span class="co">-- url -&gt; useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort</span></span>
<span id="cb1-82"><a href="#cb1-82" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-83"><a href="#cb1-83" aria-hidden="true" tabindex="-1"></a> <span class="co">-- Definieren &amp; Erzeugen der Funktionen um die anderen Services anzusprechen.</span></span>
<span id="cb1-84"><a href="#cb1-84" aria-hidden="true" tabindex="-1"></a> calls <span class="ot">&lt;-</span> (,)</span>
<span id="cb1-85"><a href="#cb1-85" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;$&gt;</span> createBackend myserviceAUri internalProxy</span>
<span id="cb1-86"><a href="#cb1-86" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;*&gt;</span> createBackend myserviceBUri internalProxy</span>
<span id="cb1-87"><a href="#cb1-87" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-88"><a href="#cb1-88" aria-hidden="true" tabindex="-1"></a> <span class="co">-- Logging-Setup</span></span>
<span id="cb1-89"><a href="#cb1-89" aria-hidden="true" tabindex="-1"></a> hSetBuffering stdout <span class="dt">LineBuffering</span></span>
<span id="cb1-90"><a href="#cb1-90" aria-hidden="true" tabindex="-1"></a> hSetBuffering stderr <span class="dt">LineBuffering</span></span>
<span id="cb1-91"><a href="#cb1-91" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-92"><a href="#cb1-92" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-93"><a href="#cb1-93" aria-hidden="true" tabindex="-1"></a> <span class="co">-- Infos holen, brauchen wir später</span></span>
<span id="cb1-94"><a href="#cb1-94" aria-hidden="true" tabindex="-1"></a> myName <span class="ot">&lt;-</span> getHostName</span>
<span id="cb1-95"><a href="#cb1-95" aria-hidden="true" tabindex="-1"></a> today <span class="ot">&lt;-</span> formatTime defaultTimeLocale <span class="st">"%F"</span> <span class="op">.</span> utctDay <span class="op">&lt;$&gt;</span> getCurrentTime</span>
<span id="cb1-96"><a href="#cb1-96" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-97"><a href="#cb1-97" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-98"><a href="#cb1-98" aria-hidden="true" tabindex="-1"></a> <span class="co">-- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten</span></span>
<span id="cb1-99"><a href="#cb1-99" aria-hidden="true" tabindex="-1"></a> amqPost <span class="ot">&lt;-</span> newTQueueIO</span>
<span id="cb1-100"><a href="#cb1-100" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-101"><a href="#cb1-101" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-102"><a href="#cb1-102" aria-hidden="true" tabindex="-1"></a> <span class="co">-- bracket a b c == erst a machen, ergebnis an c als variablen übergeben. Schmeisst c ne exception/wird gekillt/..., werden die variablen an b übergeben.</span></span>
<span id="cb1-103"><a href="#cb1-103" aria-hidden="true" tabindex="-1"></a> bracket</span>
<span id="cb1-104"><a href="#cb1-104" aria-hidden="true" tabindex="-1"></a> <span class="co">-- logfiles öffnen</span></span>
<span id="cb1-105"><a href="#cb1-105" aria-hidden="true" tabindex="-1"></a> (<span class="dt">LogFiles</span> <span class="op">&lt;$&gt;</span> openFile (<span class="st">"/logs/myservice-"</span><span class="op">&lt;&gt;</span>myName<span class="op">&lt;&gt;</span><span class="st">"-"</span><span class="op">&lt;&gt;</span>today<span class="op">&lt;&gt;</span><span class="st">".info"</span>) <span class="dt">AppendMode</span></span>
<span id="cb1-106"><a href="#cb1-106" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;*&gt;</span> openFile (<span class="kw">if</span> myserviceDebug <span class="kw">then</span> <span class="st">"/logs/myservice-"</span><span class="op">&lt;&gt;</span>myName<span class="op">&lt;&gt;</span><span class="st">"-"</span><span class="op">&lt;&gt;</span>today<span class="op">&lt;&gt;</span><span class="st">".debug"</span> <span class="kw">else</span> <span class="st">"/dev/null"</span>) <span class="dt">AppendMode</span></span>
<span id="cb1-107"><a href="#cb1-107" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;*&gt;</span> openFile (<span class="st">"/logs/myservice-"</span><span class="op">&lt;&gt;</span>myName<span class="op">&lt;&gt;</span><span class="st">"-"</span><span class="op">&lt;&gt;</span>today<span class="op">&lt;&gt;</span><span class="st">".error"</span>) <span class="dt">AppendMode</span></span>
<span id="cb1-108"><a href="#cb1-108" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;*&gt;</span> openFile (<span class="st">"/logs/myservice-"</span><span class="op">&lt;&gt;</span>myName<span class="op">&lt;&gt;</span><span class="st">"-"</span><span class="op">&lt;&gt;</span>today<span class="op">&lt;&gt;</span><span class="st">".timings"</span>) <span class="dt">AppendMode</span></span>
<span id="cb1-109"><a href="#cb1-109" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb1-110"><a href="#cb1-110" aria-hidden="true" tabindex="-1"></a> <span class="co">-- und bei exception/beendigung schlißen.h</span></span>
<span id="cb1-111"><a href="#cb1-111" aria-hidden="true" tabindex="-1"></a> (\(<span class="dt">LogFiles</span> a b c d) <span class="ot">-&gt;</span> <span class="fu">mapM_</span> hClose [a,b,c,d])</span>
<span id="cb1-112"><a href="#cb1-112" aria-hidden="true" tabindex="-1"></a> <span class="op">$</span> \logfiles <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb1-113"><a href="#cb1-113" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-114"><a href="#cb1-114" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-115"><a href="#cb1-115" aria-hidden="true" tabindex="-1"></a> <span class="co">-- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.</span></span>
<span id="cb1-116"><a href="#cb1-116" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> <span class="fu">log</span> <span class="ot">=</span> printLogFiles<span class="ot"> logfiles ::</span> <span class="dt">MonadIO</span> m <span class="ot">=&gt;</span> [<span class="dt">LogItem</span>] <span class="ot">-&gt;</span> m ()</span>
<span id="cb1-117"><a href="#cb1-117" aria-hidden="true" tabindex="-1"></a> iolog <span class="ot">=</span> printLogFilesIO<span class="ot"> logfiles ::</span> [<span class="dt">LogItem</span>] <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb1-118"><a href="#cb1-118" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-119"><a href="#cb1-119" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-120"><a href="#cb1-120" aria-hidden="true" tabindex="-1"></a> <span class="co">-- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei</span></span>
<span id="cb1-121"><a href="#cb1-121" aria-hidden="true" tabindex="-1"></a> <span class="co">-- Per Default bekommen Handler sowas wie die server-config, die Funktionen um mit anderen Services zu reden, die AMQ-Queue um ins Kibana zu loggen und eine Datei-Logging-Funktion</span></span>
<span id="cb1-122"><a href="#cb1-122" aria-hidden="true" tabindex="-1"></a> <span class="co">-- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.</span></span>
<span id="cb1-123"><a href="#cb1-123" aria-hidden="true" tabindex="-1"></a> server <span class="ot">=</span> <span class="dt">MyServiceBackend</span>{ myApiEndpointV1Post <span class="ot">=</span> H.myApiEndpointV1Post sc calls amqPost <span class="fu">log</span></span>
<span id="cb1-124"><a href="#cb1-124" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb1-125"><a href="#cb1-125" aria-hidden="true" tabindex="-1"></a> config <span class="ot">=</span> <span class="dt">MS.Config</span> <span class="op">$</span> <span class="st">"http://"</span> <span class="op">++</span> myserviceHost <span class="op">++</span> <span class="st">":"</span> <span class="op">++</span> <span class="fu">show</span> myservicePort <span class="op">++</span> <span class="st">"/"</span></span>
<span id="cb1-126"><a href="#cb1-126" aria-hidden="true" tabindex="-1"></a> iolog <span class="op">.</span> <span class="fu">pure</span> <span class="op">.</span> <span class="dt">Info</span> <span class="op">$</span> <span class="st">"Using Server configuration:"</span></span>
<span id="cb1-127"><a href="#cb1-127" aria-hidden="true" tabindex="-1"></a> iolog <span class="op">.</span> <span class="fu">pure</span> <span class="op">.</span> <span class="dt">Info</span> <span class="op">$</span> pretty sc { myserviceActivemqPassword <span class="ot">=</span> <span class="st">"******"</span> <span class="co">-- Do NOT log the password ;)</span></span>
<span id="cb1-128"><a href="#cb1-128" aria-hidden="true" tabindex="-1"></a> , myserviceMongoPassword <span class="ot">=</span> <span class="st">"******"</span></span>
<span id="cb1-129"><a href="#cb1-129" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb1-130"><a href="#cb1-130" aria-hidden="true" tabindex="-1"></a> <span class="co">-- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ</span></span>
<span id="cb1-131"><a href="#cb1-131" aria-hidden="true" tabindex="-1"></a> void <span class="op">$</span> forkIO <span class="op">$</span> keepActiveMQConnected sc iolog amqPost</span>
<span id="cb1-132"><a href="#cb1-132" aria-hidden="true" tabindex="-1"></a> <span class="co">-- logging-Framework erzeugen</span></span>
<span id="cb1-133"><a href="#cb1-133" aria-hidden="true" tabindex="-1"></a> loggingMW <span class="ot">&lt;-</span> loggingMiddleware</span>
<span id="cb1-134"><a href="#cb1-134" aria-hidden="true" tabindex="-1"></a> <span class="co">-- server starten</span></span>
<span id="cb1-135"><a href="#cb1-135" aria-hidden="true" tabindex="-1"></a> <span class="kw">if</span> myserviceDebug</span>
<span id="cb1-136"><a href="#cb1-136" aria-hidden="true" tabindex="-1"></a> <span class="kw">then</span> runMyServiceMiddlewareServer config (cors (\_ <span class="ot">-&gt;</span> <span class="dt">Just</span> (simpleCorsResourcePolicy {corsRequestHeaders <span class="ot">=</span> [<span class="st">"Content-Type"</span>]})) <span class="op">.</span> loggingMW <span class="op">.</span> logStdout) server</span>
<span id="cb1-137"><a href="#cb1-137" aria-hidden="true" tabindex="-1"></a> <span class="kw">else</span> runMyServiceMiddlewareServer config (cors (\_ <span class="ot">-&gt;</span> <span class="dt">Just</span> (simpleCorsResourcePolicy {corsRequestHeaders <span class="ot">=</span> [<span class="st">"Content-Type"</span>]}))) server</span>
<span id="cb1-138"><a href="#cb1-138" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-139"><a href="#cb1-139" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-140"><a href="#cb1-140" aria-hidden="true" tabindex="-1"></a><span class="co">-- Sollte bald in die Library hs-stomp ausgelagert werden</span></span>
<span id="cb1-141"><a href="#cb1-141" aria-hidden="true" tabindex="-1"></a><span class="co">-- ist ein Beispiel für einen ActiveMQ-Dumper</span></span>
<span id="cb1-142"><a href="#cb1-142" aria-hidden="true" tabindex="-1"></a><span class="ot">keepActiveMQConnected ::</span> <span class="dt">ServerConfig</span> <span class="ot">-&gt;</span> ([<span class="dt">LogItem</span>] <span class="ot">-&gt;</span> <span class="dt">IO</span> ()) <span class="ot">-&gt;</span> <span class="dt">TQueue</span> <span class="dt">BS.ByteString</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb1-143"><a href="#cb1-143" aria-hidden="true" tabindex="-1"></a>keepActiveMQConnected sc<span class="op">@</span><span class="dt">ServerConfig</span>{<span class="op">..</span>} printLog var <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb1-144"><a href="#cb1-144" aria-hidden="true" tabindex="-1"></a> res <span class="ot">&lt;-</span> handle (\(<span class="ot">e ::</span> <span class="dt">SomeException</span>) <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb1-145"><a href="#cb1-145" aria-hidden="true" tabindex="-1"></a> printLog <span class="op">.</span> <span class="fu">pure</span> <span class="op">.</span> <span class="dt">Error</span> <span class="op">$</span> <span class="st">"Exception in AMQ-Thread: "</span><span class="op">&lt;&gt;</span><span class="fu">show</span> e</span>
<span id="cb1-146"><a href="#cb1-146" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> <span class="op">$</span> <span class="dt">Right</span> ()</span>
<span id="cb1-147"><a href="#cb1-147" aria-hidden="true" tabindex="-1"></a> ) <span class="op">$</span> AMQ.try <span class="op">$</span> <span class="kw">do</span> <span class="co">-- catches all AMQ-Exception that we can handle. All others bubble up.</span></span>
<span id="cb1-148"><a href="#cb1-148" aria-hidden="true" tabindex="-1"></a> printLog <span class="op">.</span> <span class="fu">pure</span> <span class="op">.</span> <span class="dt">Info</span> <span class="op">$</span> <span class="st">"AMQ: connecting..."</span></span>
<span id="cb1-149"><a href="#cb1-149" aria-hidden="true" tabindex="-1"></a> withConnection myserviceActivemqHost myserviceActivemqPort [ <span class="dt">OAuth</span> myserviceActivemqUsername myserviceActivemqPassword</span>
<span id="cb1-150"><a href="#cb1-150" aria-hidden="true" tabindex="-1"></a> , <span class="dt">OTmo</span> (<span class="dv">30</span><span class="op">*</span><span class="dv">1000</span>) <span class="co">{- 30 sec timeout -}</span></span>
<span id="cb1-151"><a href="#cb1-151" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb1-152"><a href="#cb1-152" aria-hidden="true" tabindex="-1"></a> [] <span class="op">$</span> \c <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb1-153"><a href="#cb1-153" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> oconv <span class="ot">=</span> <span class="fu">return</span></span>
<span id="cb1-154"><a href="#cb1-154" aria-hidden="true" tabindex="-1"></a> printLog <span class="op">.</span> <span class="fu">pure</span> <span class="op">.</span> <span class="dt">Info</span> <span class="op">$</span> <span class="st">"AMQ: connected"</span></span>
<span id="cb1-155"><a href="#cb1-155" aria-hidden="true" tabindex="-1"></a> withWriter c <span class="st">"Chaos-Logger for Kibana"</span> <span class="st">"chaos.logs"</span> [] [] oconv <span class="op">$</span> \writer <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb1-156"><a href="#cb1-156" aria-hidden="true" tabindex="-1"></a> printLog <span class="op">.</span> <span class="fu">pure</span> <span class="op">.</span> <span class="dt">Info</span> <span class="op">$</span> <span class="st">"AMQ: queue created"</span></span>
<span id="cb1-157"><a href="#cb1-157" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> postfun <span class="ot">=</span> writeQ writer (<span class="dt">Type</span> (<span class="dt">Application</span> <span class="st">"json"</span>) []) []</span>
<span id="cb1-158"><a href="#cb1-158" aria-hidden="true" tabindex="-1"></a> void <span class="op">$</span> race</span>
<span id="cb1-159"><a href="#cb1-159" aria-hidden="true" tabindex="-1"></a> (forever <span class="op">$</span> atomically (readTQueue var) <span class="op">&gt;&gt;=</span> postfun)</span>
<span id="cb1-160"><a href="#cb1-160" aria-hidden="true" tabindex="-1"></a> (threadDelay (<span class="dv">600</span><span class="op">*</span><span class="dv">1000</span><span class="op">*</span><span class="dv">1000</span>)) <span class="co">-- wait 10 Minutes</span></span>
<span id="cb1-161"><a href="#cb1-161" aria-hidden="true" tabindex="-1"></a> <span class="co">-- close writer</span></span>
<span id="cb1-162"><a href="#cb1-162" aria-hidden="true" tabindex="-1"></a> <span class="co">-- close connection</span></span>
<span id="cb1-163"><a href="#cb1-163" aria-hidden="true" tabindex="-1"></a> <span class="co">-- get outside of all try/handle/...-constructions befor recursing.</span></span>
<span id="cb1-164"><a href="#cb1-164" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> res <span class="kw">of</span></span>
<span id="cb1-165"><a href="#cb1-165" aria-hidden="true" tabindex="-1"></a> <span class="dt">Left</span> ex <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb1-166"><a href="#cb1-166" aria-hidden="true" tabindex="-1"></a> printLog <span class="op">.</span> <span class="fu">pure</span> <span class="op">.</span> <span class="dt">Error</span> <span class="op">$</span> <span class="st">"AMQ: "</span><span class="op">&lt;&gt;</span><span class="fu">show</span> ex</span>
<span id="cb1-167"><a href="#cb1-167" aria-hidden="true" tabindex="-1"></a> keepActiveMQConnected sc printLog var</span>
<span id="cb1-168"><a href="#cb1-168" aria-hidden="true" tabindex="-1"></a> <span class="dt">Right</span> _ <span class="ot">-&gt;</span> keepActiveMQConnected sc printLog var</span>
<span id="cb1-169"><a href="#cb1-169" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-170"><a href="#cb1-170" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-171"><a href="#cb1-171" aria-hidden="true" tabindex="-1"></a><span class="co">-- Beispiel für eine Custom-Logging-Middleware.</span></span>
<span id="cb1-172"><a href="#cb1-172" aria-hidden="true" tabindex="-1"></a><span class="co">-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.</span></span>
<span id="cb1-173"><a href="#cb1-173" aria-hidden="true" tabindex="-1"></a><span class="co">-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)</span></span>
<span id="cb1-174"><a href="#cb1-174" aria-hidden="true" tabindex="-1"></a><span class="ot">loggingMiddleware ::</span> <span class="dt">IO</span> <span class="dt">Middleware</span></span>
<span id="cb1-175"><a href="#cb1-175" aria-hidden="true" tabindex="-1"></a>loggingMiddleware <span class="ot">=</span> liftIO <span class="op">$</span> mkRequestLogger <span class="op">$</span> def { outputFormat <span class="ot">=</span> <span class="dt">CustomOutputFormatWithDetails</span> out }</span>
<span id="cb1-176"><a href="#cb1-176" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb1-177"><a href="#cb1-177" aria-hidden="true" tabindex="-1"></a><span class="ot"> out ::</span> <span class="dt">ZonedDate</span> <span class="ot">-&gt;</span> <span class="dt">WAI.Request</span> <span class="ot">-&gt;</span> <span class="dt">Status</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Integer</span> <span class="ot">-&gt;</span> <span class="dt">NominalDiffTime</span> <span class="ot">-&gt;</span> [<span class="dt">BS.ByteString</span>] <span class="ot">-&gt;</span> <span class="dt">Builder</span> <span class="ot">-&gt;</span> <span class="dt">LogStr</span></span>
<span id="cb1-178"><a href="#cb1-178" aria-hidden="true" tabindex="-1"></a> out _ r status _ _ payload _</span>
<span id="cb1-179"><a href="#cb1-179" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> statusCode status <span class="op">&lt;</span> <span class="dv">300</span> <span class="ot">=</span> <span class="st">""</span></span>
<span id="cb1-180"><a href="#cb1-180" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> statusCode status <span class="op">&gt;</span> <span class="dv">399</span> <span class="op">&amp;&amp;</span> statusCode status <span class="op">&lt;</span> <span class="dv">500</span> <span class="ot">=</span> <span class="st">"Error code "</span><span class="op">&lt;&gt;</span>toLogStr (statusCode status) <span class="op">&lt;&gt;</span><span class="st">" sent. Request-Payload was: "</span><span class="op">&lt;&gt;</span> <span class="fu">mconcat</span> (toLogStr <span class="op">&lt;$&gt;</span> payload) <span class="op">&lt;&gt;</span> <span class="st">"\n"</span></span>
<span id="cb1-181"><a href="#cb1-181" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> toLogStr (<span class="fu">show</span> r) <span class="op">&lt;&gt;</span> <span class="st">"\n"</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
</main> <!-- /main -->
<script id="quarto-html-after-body" type="application/javascript">
window.document.addEventListener("DOMContentLoaded", function (event) {
// Ensure there is a toggle, if there isn't float one in the top right
if (window.document.querySelector('.quarto-color-scheme-toggle') === null) {
const a = window.document.createElement('a');
a.classList.add('top-right');
a.classList.add('quarto-color-scheme-toggle');
a.href = "";
a.onclick = function() { try { window.quartoToggleColorScheme(); } catch {} return false; };
const i = window.document.createElement("i");
i.classList.add('bi');
a.appendChild(i);
window.document.body.appendChild(a);
}
window.setColorSchemeToggle(window.hasAlternateSentinel())
const icon = "";
const anchorJS = new window.AnchorJS();
anchorJS.options = {
placement: 'right',
icon: icon
};
anchorJS.add('.anchored');
const isCodeAnnotation = (el) => {
for (const clz of el.classList) {
if (clz.startsWith('code-annotation-')) {
return true;
}
}
return false;
}
const onCopySuccess = function(e) {
// button target
const button = e.trigger;
// don't keep focus
button.blur();
// flash "checked"
button.classList.add('code-copy-button-checked');
var currentTitle = button.getAttribute("title");
button.setAttribute("title", "Copied!");
let tooltip;
if (window.bootstrap) {
button.setAttribute("data-bs-toggle", "tooltip");
button.setAttribute("data-bs-placement", "left");
button.setAttribute("data-bs-title", "Copied!");
tooltip = new bootstrap.Tooltip(button,
{ trigger: "manual",
customClass: "code-copy-button-tooltip",
offset: [0, -8]});
tooltip.show();
}
setTimeout(function() {
if (tooltip) {
tooltip.hide();
button.removeAttribute("data-bs-title");
button.removeAttribute("data-bs-toggle");
button.removeAttribute("data-bs-placement");
}
button.setAttribute("title", currentTitle);
button.classList.remove('code-copy-button-checked');
}, 1000);
// clear code selection
e.clearSelection();
}
const getTextToCopy = function(trigger) {
const codeEl = trigger.previousElementSibling.cloneNode(true);
for (const childEl of codeEl.children) {
if (isCodeAnnotation(childEl)) {
childEl.remove();
}
}
return codeEl.innerText;
}
const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', {
text: getTextToCopy
});
clipboard.on('success', onCopySuccess);
if (window.document.getElementById('quarto-embedded-source-code-modal')) {
const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', {
text: getTextToCopy,
container: window.document.getElementById('quarto-embedded-source-code-modal')
});
clipboardModal.on('success', onCopySuccess);
}
var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//);
var mailtoRegex = new RegExp(/^mailto:/);
var filterRegex = new RegExp("https:\/\/drezil\.de");
var isInternal = (href) => {
return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href);
}
// Inspect non-navigation links and adorn them if external
var links = window.document.querySelectorAll('a[href]:not(.nav-link):not(.navbar-brand):not(.toc-action):not(.sidebar-link):not(.sidebar-item-toggle):not(.pagination-link):not(.no-external):not([aria-hidden]):not(.dropdown-item):not(.quarto-navigation-tool):not(.about-link)');
for (var i=0; i<links.length; i++) {
const link = links[i];
if (!isInternal(link.href)) {
// undo the damage that might have been done by quarto-nav.js in the case of
// links that we want to consider external
if (link.dataset.originalHref !== undefined) {
link.href = link.dataset.originalHref;
}
// target, if specified
link.setAttribute("target", "_blank");
if (link.getAttribute("rel") === null) {
link.setAttribute("rel", "noopener");
}
// default icon
link.classList.add("external");
}
}
function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
const config = {
allowHTML: true,
maxWidth: 500,
delay: 100,
arrow: false,
appendTo: function(el) {
return el.parentElement;
},
interactive: true,
interactiveBorder: 10,
theme: 'quarto',
placement: 'bottom-start',
};
if (contentFn) {
config.content = contentFn;
}
if (onTriggerFn) {
config.onTrigger = onTriggerFn;
}
if (onUntriggerFn) {
config.onUntrigger = onUntriggerFn;
}
window.tippy(el, config);
}
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
for (var i=0; i<noterefs.length; i++) {
const ref = noterefs[i];
tippyHover(ref, function() {
// use id or data attribute instead here
let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
try { href = new URL(href).hash; } catch {}
const id = href.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
if (note) {
return note.innerHTML;
} else {
return "";
}
});
}
const xrefs = window.document.querySelectorAll('a.quarto-xref');
const processXRef = (id, note) => {
// Strip column container classes
const stripColumnClz = (el) => {
el.classList.remove("page-full", "page-columns");
if (el.children) {
for (const child of el.children) {
stripColumnClz(child);
}
}
}
stripColumnClz(note)
if (id === null || id.startsWith('sec-')) {
// Special case sections, only their first couple elements
const container = document.createElement("div");
if (note.children && note.children.length > 2) {
container.appendChild(note.children[0].cloneNode(true));
for (let i = 1; i < note.children.length; i++) {
const child = note.children[i];
if (child.tagName === "P" && child.innerText === "") {
continue;
} else {
container.appendChild(child.cloneNode(true));
break;
}
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(container);
}
return container.innerHTML
} else {
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
return note.innerHTML;
}
} else {
// Remove any anchor links if they are present
const anchorLink = note.querySelector('a.anchorjs-link');
if (anchorLink) {
anchorLink.remove();
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
if (note.classList.contains("callout")) {
return note.outerHTML;
} else {
return note.innerHTML;
}
}
}
for (var i=0; i<xrefs.length; i++) {
const xref = xrefs[i];
tippyHover(xref, undefined, function(instance) {
instance.disable();
let url = xref.getAttribute('href');
let hash = undefined;
if (url.startsWith('#')) {
hash = url;
} else {
try { hash = new URL(url).hash; } catch {}
}
if (hash) {
const id = hash.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
if (note !== null) {
try {
const html = processXRef(id, note.cloneNode(true));
instance.setContent(html);
} finally {
instance.enable();
instance.show();
}
} else {
// See if we can fetch this
fetch(url.split('#')[0])
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.getElementById(id);
if (note !== null) {
const html = processXRef(id, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
} else {
// See if we can fetch a full url (with no hash to target)
// This is a special case and we should probably do some content thinning / targeting
fetch(url)
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.querySelector('main.content');
if (note !== null) {
// This should only happen for chapter cross references
// (since there is no id in the URL)
// remove the first header
if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
note.children[0].remove();
}
const html = processXRef(null, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
}, function(instance) {
});
}
let selectedAnnoteEl;
const selectorForAnnotation = ( cell, annotation) => {
let cellAttr = 'data-code-cell="' + cell + '"';
let lineAttr = 'data-code-annotation="' + annotation + '"';
const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
return selector;
}
const selectCodeLines = (annoteEl) => {
const doc = window.document;
const targetCell = annoteEl.getAttribute("data-target-cell");
const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
const lines = annoteSpan.getAttribute("data-code-lines").split(",");
const lineIds = lines.map((line) => {
return targetCell + "-" + line;
})
let top = null;
let height = null;
let parent = null;
if (lineIds.length > 0) {
//compute the position of the single el (top and bottom and make a div)
const el = window.document.getElementById(lineIds[0]);
top = el.offsetTop;
height = el.offsetHeight;
parent = el.parentElement.parentElement;
if (lineIds.length > 1) {
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
const bottom = lastEl.offsetTop + lastEl.offsetHeight;
height = bottom - top;
}
if (top !== null && height !== null && parent !== null) {
// cook up a div (if necessary) and position it
let div = window.document.getElementById("code-annotation-line-highlight");
if (div === null) {
div = window.document.createElement("div");
div.setAttribute("id", "code-annotation-line-highlight");
div.style.position = 'absolute';
parent.appendChild(div);
}
div.style.top = top - 2 + "px";
div.style.height = height + 4 + "px";
div.style.left = 0;
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
if (gutterDiv === null) {
gutterDiv = window.document.createElement("div");
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
gutterDiv.style.position = 'absolute';
const codeCell = window.document.getElementById(targetCell);
const gutter = codeCell.querySelector('.code-annotation-gutter');
gutter.appendChild(gutterDiv);
}
gutterDiv.style.top = top - 2 + "px";
gutterDiv.style.height = height + 4 + "px";
}
selectedAnnoteEl = annoteEl;
}
};
const unselectCodeLines = () => {
const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
elementsIds.forEach((elId) => {
const div = window.document.getElementById(elId);
if (div) {
div.remove();
}
});
selectedAnnoteEl = undefined;
};
// Handle positioning of the toggle
window.addEventListener(
"resize",
throttle(() => {
elRect = undefined;
if (selectedAnnoteEl) {
selectCodeLines(selectedAnnoteEl);
}
}, 10)
);
function throttle(fn, ms) {
let throttle = false;
let timer;
return (...args) => {
if(!throttle) { // first call gets through
fn.apply(this, args);
throttle = true;
} else { // all the others get throttled
if(timer) clearTimeout(timer); // cancel #2
timer = setTimeout(() => {
fn.apply(this, args);
timer = throttle = false;
}, ms);
}
};
}
// Attach click handler to the DT
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
for (const annoteDlNode of annoteDls) {
annoteDlNode.addEventListener('click', (event) => {
const clickedEl = event.target;
if (clickedEl !== selectedAnnoteEl) {
unselectCodeLines();
const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
if (activeEl) {
activeEl.classList.remove('code-annotation-active');
}
selectCodeLines(clickedEl);
clickedEl.classList.add('code-annotation-active');
} else {
// Unselect the line
unselectCodeLines();
clickedEl.classList.remove('code-annotation-active');
}
});
}
const findCites = (el) => {
const parentEl = el.parentElement;
if (parentEl) {
const cites = parentEl.dataset.cites;
if (cites) {
return {
el,
cites: cites.split(' ')
};
} else {
return findCites(el.parentElement)
}
} else {
return undefined;
}
};
var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
for (var i=0; i<bibliorefs.length; i++) {
const ref = bibliorefs[i];
const citeInfo = findCites(ref);
if (citeInfo) {
tippyHover(citeInfo.el, function() {
var popup = window.document.createElement('div');
citeInfo.cites.forEach(function(cite) {
var citeDiv = window.document.createElement('div');
citeDiv.classList.add('hanging-indent');
citeDiv.classList.add('csl-entry');
var biblioDiv = window.document.getElementById('ref-' + cite);
if (biblioDiv) {
citeDiv.innerHTML = biblioDiv.innerHTML;
}
popup.appendChild(citeDiv);
});
return popup.innerHTML;
});
}
}
});
</script>
</div> <!-- /content -->
</body></html>