availcheck.checker

  1# -*- coding: utf-8 -*-
  2# Copyright (C) 2024 TU Dresden
  3# ralf.klammer@tu-dresden.de
  4
  5import logging
  6
  7from json import dumps
  8from time import time
  9
 10from . import (
 11    BROWSER_DIR,
 12    O4A_ENTITIES_COUNT,
 13)
 14from .selenium import SeleniumAgent
 15from .configs import CheckerConfigs
 16
 17log = logging.getLogger(__name__)
 18
 19
 20def resolve_url(func):
 21    """
 22    Decorator to resolve the URL before running any check.
 23    """
 24
 25    def wrap(
 26        self,
 27        subdomain=None,
 28        path=None,
 29        ignore_shadow_root=False,
 30        *args,
 31        **kwargs,
 32    ):
 33        current_url = self.selenium.get_url()
 34        self.url = self.create_var_url(subdomain=subdomain, path=path)
 35        if current_url != self.url:
 36            try:
 37                self.selenium.get(self.url)
 38                if (
 39                    getattr(self, "shadow_root", None)
 40                    and ignore_shadow_root is not True
 41                ):
 42                    self.shadow_host = self.selenium.get_shadow_root(
 43                        self.shadow_root
 44                    )
 45            except Exception as e:
 46                log.error(f"Unable to open URL: {self.url}")
 47                log.error(e)
 48                return False
 49
 50            return func(self, *args, **kwargs)
 51        else:
 52            return func(self, *args, **kwargs)
 53
 54    return wrap
 55
 56
 57class CheckerBase(object):
 58    """
 59    Base class for all the availability checker
 60    """
 61
 62    def __init__(self, url=None, instance="prod", path_of_screenshots=None):
 63        self.instance = instance
 64        self.selenium = SeleniumAgent(
 65            path_of_screenshots=path_of_screenshots,
 66            implicit_wait=10,
 67            browser_dir=(
 68                f"{BROWSER_DIR}_{self.__class__.__name__}_{self.instance}"
 69                if BROWSER_DIR
 70                else None
 71            ),
 72        )
 73
 74        self.url = url
 75        self._checker_config = None
 76
 77    @property
 78    def checker_config(self):
 79        """
 80        Get the checker configs for the given instance.
 81        """
 82        return self._checker_config
 83
 84    @checker_config.setter
 85    def checker_config(self, value):
 86        """
 87        Set the checker configs for the given instance.
 88        """
 89        self._checker_config = value
 90
 91    @property
 92    def checks(self):
 93        """
 94        Get the relevant checks for the given instance from the checker configs.
 95        """
 96        if self.checker_config is not None:
 97            return self.checker_config.get_checks()
 98
 99    def get_base_url(self):
100        """
101        Get the base URL for the given instance from the checker configs.
102        """
103        if self.checker_config is not None:
104            return self.checker_config.get_url(self.instance)
105
106    def run_check(self, check, checkmk_format=True):
107        """
108        Run a single check for the given instance.
109        """
110        # check if the check is supported
111        if check not in self.checks:
112            raise ValueError(f"Requested check '{check}' does not exist.")
113        elif self.instance not in self.checks[check]["instances"]:
114            raise ValueError(
115                f"Requested check '{check}' is not supported for instance "
116                f"'{self.instance}'."
117            )
118        elif checkmk_format:
119            result = getattr(self, check)()
120            start_time = time()
121            result = getattr(self, check)()
122            execution_time = round(time() - start_time, 4)
123            status_code = (
124                0 if result else self.checks[check]["status_on_error"]
125            )
126            return "%i 'AvailabilityCheck | %s-%s | %s' duration=%s %s" % (
127                status_code,
128                self.checker_config.id,
129                self.instance,
130                check,
131                execution_time,
132                f'{self.checks[check]["description"]} (URL: {self.url})',
133            )
134        else:
135            return getattr(self, check)()
136
137    def run_checks(self):
138        """
139        Run all checks defined in `self.checks` for the given instance.
140        """
141        for check in self.checks:
142            if self.instance not in self.checks[check]["instances"]:
143                continue
144            yield self.run_check(check)
145
146    def save_checks(self, path):
147        """
148        Save the results of the checks to a file.
149        """
150        results = self.run_checks()
151        filename = f"{self.__class__.__name__}_{self.instance}.json"
152        with open(f"{path.rstrip('/')}/{filename}", "w") as f:
153            f.write(dumps(results))
154        return filename
155
156    def create_var_url(
157        self,
158        subdomain=None,
159        path=None,
160    ):
161        """
162        Cutting the url and setting subdomains based
163        if production or testing instance set.
164        """
165
166        base_url = self.get_base_url()
167
168        if base_url is None:
169            raise ValueError(
170                f"self.Instance '{self.instance}' is not supported. "
171                "Please provide a valid URL."
172            )
173
174        url = base_url
175        if subdomain is not None:
176            url = f"https://{subdomain}.{base_url.split('://')[1]}"
177        if path is not None:
178            url = f"{url}/{path.lstrip('/')}"
179
180        return url
181
182    def quit(self):
183        self.selenium.quit()
184
185
186class CheckerWebApps(CheckerBase):
187    """
188    Checker for the Webapps Service
189    """
190
191    checker_config = CheckerConfigs("webapps")
192
193    @resolve_url
194    def landing_page_webapps(self):
195        """
196        Checks if the landing Page for Webapps is available
197        """
198
199        result = self.selenium.find_by_css(
200            css="img[alt='Logo des NFDI Konsortiums NFDI4Earth']"
201        )
202
203        return bool(result)
204
205    @resolve_url
206    def _visual_search_engine(self):
207        """
208        Checks if the Graph Based Visual Search Engine is available
209        """
210        result = self.selenium.wait_until(
211            css=".MuiDataGrid-cell--withRenderer", want_bool=True
212        )
213
214        result = self.selenium.amount_of_elements(
215            css=".MuiDataGrid-cell--withRenderer", minimum=2
216        )
217
218        return bool(result)
219
220    def visual_search_engine(self):
221        return self._visual_search_engine(subdomain="vesa")
222
223
224class CheckerKnowledgeHub(CheckerBase):
225    """
226    Checker for the Knowledgehub Service
227    """
228
229    checker_config = CheckerConfigs("knowledgehub")
230
231    @resolve_url
232    def _sparql(self):
233        """
234        Checks if the Knowledgehub-SPARQL Endpoint is availiable
235        This is done by checking if an anchor of class 'navbar-brand' exists
236        and its text is 'SPARQL Query Editor'
237        """
238        result = self.selenium.find_by_css(css="a[class='navbar-brand']")
239        return getattr(result, "text").strip() == "SPARQL Query Editor"
240
241    def sparql(self):
242        return self._sparql(subdomain="sparql")
243
244    @resolve_url
245    def _cordra(self):
246        """
247        Checks if cordra is available,
248        This is done by checking for the searchbar.
249        """
250        result = self.selenium.find_by_css(css="input[placeholder='Search']")
251
252        return bool(result)
253
254    def cordra(self):
255        return self._cordra(subdomain="cordra")
256
257    @resolve_url
258    def landing_page_knowledgehub(self):
259        """
260        Checks if the Landingpage of Knowlegehub is available.
261        """
262        current_url = self.selenium.get_url()
263        result = current_url.startswith("https://knowledgehub")
264
265        return bool(result)
266
267    @resolve_url
268    def more_examples_loaded(self):
269        """
270        Check if more cards appear after button click.
271        """
272        toggle_button = self.selenium.find_by_css(
273            css=("#hidden-card-deck-toggle")
274        )
275        if toggle_button is None:
276            log.warning(f"{toggle_button} not found")
277            return False
278        else:
279            self.selenium.zoom()
280            toggle_button.click()
281            result = self.selenium.wait_until(
282                css="div[class='row mb-3'][id='card-deck']", want_bool=True
283            )
284        return result
285
286    @resolve_url
287    def table_is_displayed(self):
288        """
289        Checks if the "Harvested Ressources table appears
290        with atleast 10 Entries fetched.
291        """
292        self.selenium.zoom()
293        self.selenium.wait_until(css=".col-3.border")
294        result = self.selenium.amount_of_elements(css=".col-3.border")
295        return bool(result)
296
297    @resolve_url
298    def check_button_endpoints(self):
299        """
300        Checks for anker links working
301        """
302        self.selenium.save_screenshot("/tmp/table.png")
303        result = self.selenium.check_button_redirection("a")
304        return bool(result)
305
306
307class CheckerSupportApps(CheckerBase):
308    """
309    Checker for the SupportApps Service
310    """
311
312    checker_config = CheckerConfigs("supportapps")
313
314    @resolve_url
315    def _jupyterhub(self):
316        """
317        Checks if entry level of JupyterHub is restricted / service is running
318        This is done by checking if Logo and corresponding error code is shown.
319        """
320
321        results = []
322
323        #  Check error
324        result = self.selenium.find_by_text(
325            css=".error",
326            text="405 : Method Not Allowed",
327            case_sensitive=False,
328        )
329        results.append(result)
330
331        #  Check logo
332        result = self.selenium.find_by_css(css="img[src='/hub/logo']")
333        results.append(result)
334
335        return all(results)
336
337    def jupyterhub(self):
338        return self._jupyterhub(subdomain="jupyterhub")
339
340
341class CheckerEduTrain(CheckerBase):
342    """
343    Checker for the EduTrain Service
344    """
345
346    checker_config = CheckerConfigs("edutrain")
347
348    @resolve_url
349    def openedx_lms(self):
350        """
351        Check if the main page is loaded correctly.
352        This is done by checking if the DFG logo is present.
353        """
354        result = self.selenium.find_by_css(
355            # Path may change on a regular, check again once more
356            css="a.explore-btn[href='/courses']"
357        )
358
359        return bool(result)
360
361    @resolve_url
362    def _openedx_cms(self):
363        """
364        Check if Educational Portal is Working
365        This is achieved by checking for the appearance of the logo
366        """
367        result = self.selenium.find_by_css(css="a.brand-link img.brand-image")
368        return bool(result)
369
370    def openedx_cms(self):
371        return self._openedx_cms(subdomain="teacher")
372
373    @resolve_url
374    def _authentication(self):
375        """
376        Check if URL is available, if "AcademicID" button
377        is available and leads to login page
378        """
379        results = []
380        academy_button = self.selenium.find_by_css(
381            css='button[id="oa2-academicid-oauth2"]'
382        )
383
384        academy_button.click()
385
386        current_url = self.selenium.get_url()
387        self.selenium.save_screenshot("/tmp/auth.png")
388        target_page_reached = current_url.startswith(
389            "https://sso.academiccloud.de/simplesaml/module.php/"
390        )
391
392        results.append(target_page_reached)
393        return all(results)
394
395    def authentication(self):
396        return self._authentication(
397            subdomain="apps", path="/authn/login?next=%2F"
398        )
399
400
401class CheckerOneStop4All(CheckerBase):
402    """
403    Checker for the OneStop4All Service
404    """
405
406    checker_config = CheckerConfigs("onestop4all")
407
408    def __init__(self, url=None, instance=None, *args, **kwargs):
409        super().__init__(url=url, instance=instance, *args, **kwargs)
410        self.shadow_root = "onestop4all-app"
411
412    @resolve_url
413    def main_page(self):
414        """
415        Check if the main page is loaded correctly.
416        This is done by checking if the DFG logo is present.
417        """
418        result = self.selenium.find_by_css(
419            css='img[src="/dfg-logo.svg"]', elem=self.shadow_host
420        )
421        return bool(result)
422
423    @resolve_url
424    def number_of_entities(self):
425        """
426        Check if the number of entities is correct.
427        By checking if there is the correct amount of buttons on the webpage.
428        """
429        results = self.selenium.find_by_css(
430            css="div.resourceEntries",
431            elem=self.shadow_host,
432            as_list=True,
433        )
434        return len(results) == O4A_ENTITIES_COUNT
435
436    @resolve_url
437    def involvement(self):
438        """
439        Check if the buttons withing involvement section is loaded correctly.
440        This is simply done, by checking if there are any buttons.
441        """
442        results = self.selenium.find_by_css(
443            css="div.get-involved div.overlap > img",
444            elem=self.shadow_host,
445            as_list=True,
446        )
447        return len(results) > 0
448
449    @resolve_url
450    def support_form(self, close_modal=True):
451        """
452        Check if the support form is loaded correctly.
453        This is done by checking if the subject input field is present.
454        """
455        # try to find the support form button
456        support_form_button = self.selenium.find_by_text(
457            css=".chakra-link",
458            text="user Support",
459            elem=self.shadow_host,
460            case_sensitive=False,
461        )
462
463        # stop check if no support form button was found
464        if not support_form_button:
465            return False
466
467        # click on the support form button to open modal window
468        support_form_button.click()
469
470        subject = self.selenium.find_by_css(
471            css="input[name='subject']", elem=self.shadow_host
472        )
473        if subject and close_modal:
474            # close the modal window finally
475            self.selenium.find_by_css(
476                css="button[aria-label='Close']", elem=self.shadow_host
477            ).click()
478        return bool(subject)
479
480    @resolve_url
481    def captcha_in_support_form(self):
482        """
483        Check if the CAPTCHA is loaded correctly in the support form.
484        This is done by:
485            1. checking if the CAPTCHA checkbox is present
486            2. clicking on the CAPTCHA checkbox
487            3. checking if the CAPTCHA input field is present AND
488                its value is not empty
489        """
490        if self.support_form(close_modal=False):
491            altcha_checkbox = self.selenium.wait_until(
492                css="input#altcha_checkbox",
493                elem=self.shadow_host,
494            )
495            if altcha_checkbox:
496                altcha_checkbox.click()
497                altcha_input = self.selenium.wait_until(
498                    css="input[type='hidden'][name='altcha']",
499                    elem=self.shadow_host,
500                )
501                if altcha_input and altcha_input.get_attribute("value") != "":
502                    return True
503        return False
504
505    @resolve_url
506    def _solr(self):
507        """
508        Check if the Solr login page is loaded correctly.
509        This is done by checking if the Solr logo is present.
510        """
511        dashboard = self.selenium.find_by_css(css="a[href='#/']")
512        if dashboard is None:
513            return False
514        dashboard.click()
515        login = self.selenium.find_by_css(css="button.btn-danger")
516        return login is not None and login.text == "Login"
517
518    def solr(self):
519        return self._solr(path="/solr/#/login/", ignore_shadow_root=True)
log = <Logger availcheck.checker (WARNING)>
def resolve_url(func):
21def resolve_url(func):
22    """
23    Decorator to resolve the URL before running any check.
24    """
25
26    def wrap(
27        self,
28        subdomain=None,
29        path=None,
30        ignore_shadow_root=False,
31        *args,
32        **kwargs,
33    ):
34        current_url = self.selenium.get_url()
35        self.url = self.create_var_url(subdomain=subdomain, path=path)
36        if current_url != self.url:
37            try:
38                self.selenium.get(self.url)
39                if (
40                    getattr(self, "shadow_root", None)
41                    and ignore_shadow_root is not True
42                ):
43                    self.shadow_host = self.selenium.get_shadow_root(
44                        self.shadow_root
45                    )
46            except Exception as e:
47                log.error(f"Unable to open URL: {self.url}")
48                log.error(e)
49                return False
50
51            return func(self, *args, **kwargs)
52        else:
53            return func(self, *args, **kwargs)
54
55    return wrap

Decorator to resolve the URL before running any check.

class CheckerBase:
 58class CheckerBase(object):
 59    """
 60    Base class for all the availability checker
 61    """
 62
 63    def __init__(self, url=None, instance="prod", path_of_screenshots=None):
 64        self.instance = instance
 65        self.selenium = SeleniumAgent(
 66            path_of_screenshots=path_of_screenshots,
 67            implicit_wait=10,
 68            browser_dir=(
 69                f"{BROWSER_DIR}_{self.__class__.__name__}_{self.instance}"
 70                if BROWSER_DIR
 71                else None
 72            ),
 73        )
 74
 75        self.url = url
 76        self._checker_config = None
 77
 78    @property
 79    def checker_config(self):
 80        """
 81        Get the checker configs for the given instance.
 82        """
 83        return self._checker_config
 84
 85    @checker_config.setter
 86    def checker_config(self, value):
 87        """
 88        Set the checker configs for the given instance.
 89        """
 90        self._checker_config = value
 91
 92    @property
 93    def checks(self):
 94        """
 95        Get the relevant checks for the given instance from the checker configs.
 96        """
 97        if self.checker_config is not None:
 98            return self.checker_config.get_checks()
 99
100    def get_base_url(self):
101        """
102        Get the base URL for the given instance from the checker configs.
103        """
104        if self.checker_config is not None:
105            return self.checker_config.get_url(self.instance)
106
107    def run_check(self, check, checkmk_format=True):
108        """
109        Run a single check for the given instance.
110        """
111        # check if the check is supported
112        if check not in self.checks:
113            raise ValueError(f"Requested check '{check}' does not exist.")
114        elif self.instance not in self.checks[check]["instances"]:
115            raise ValueError(
116                f"Requested check '{check}' is not supported for instance "
117                f"'{self.instance}'."
118            )
119        elif checkmk_format:
120            result = getattr(self, check)()
121            start_time = time()
122            result = getattr(self, check)()
123            execution_time = round(time() - start_time, 4)
124            status_code = (
125                0 if result else self.checks[check]["status_on_error"]
126            )
127            return "%i 'AvailabilityCheck | %s-%s | %s' duration=%s %s" % (
128                status_code,
129                self.checker_config.id,
130                self.instance,
131                check,
132                execution_time,
133                f'{self.checks[check]["description"]} (URL: {self.url})',
134            )
135        else:
136            return getattr(self, check)()
137
138    def run_checks(self):
139        """
140        Run all checks defined in `self.checks` for the given instance.
141        """
142        for check in self.checks:
143            if self.instance not in self.checks[check]["instances"]:
144                continue
145            yield self.run_check(check)
146
147    def save_checks(self, path):
148        """
149        Save the results of the checks to a file.
150        """
151        results = self.run_checks()
152        filename = f"{self.__class__.__name__}_{self.instance}.json"
153        with open(f"{path.rstrip('/')}/{filename}", "w") as f:
154            f.write(dumps(results))
155        return filename
156
157    def create_var_url(
158        self,
159        subdomain=None,
160        path=None,
161    ):
162        """
163        Cutting the url and setting subdomains based
164        if production or testing instance set.
165        """
166
167        base_url = self.get_base_url()
168
169        if base_url is None:
170            raise ValueError(
171                f"self.Instance '{self.instance}' is not supported. "
172                "Please provide a valid URL."
173            )
174
175        url = base_url
176        if subdomain is not None:
177            url = f"https://{subdomain}.{base_url.split('://')[1]}"
178        if path is not None:
179            url = f"{url}/{path.lstrip('/')}"
180
181        return url
182
183    def quit(self):
184        self.selenium.quit()

Base class for all the availability checker

CheckerBase(url=None, instance='prod', path_of_screenshots=None)
63    def __init__(self, url=None, instance="prod", path_of_screenshots=None):
64        self.instance = instance
65        self.selenium = SeleniumAgent(
66            path_of_screenshots=path_of_screenshots,
67            implicit_wait=10,
68            browser_dir=(
69                f"{BROWSER_DIR}_{self.__class__.__name__}_{self.instance}"
70                if BROWSER_DIR
71                else None
72            ),
73        )
74
75        self.url = url
76        self._checker_config = None
instance
selenium
url
checker_config
78    @property
79    def checker_config(self):
80        """
81        Get the checker configs for the given instance.
82        """
83        return self._checker_config

Get the checker configs for the given instance.

checks
92    @property
93    def checks(self):
94        """
95        Get the relevant checks for the given instance from the checker configs.
96        """
97        if self.checker_config is not None:
98            return self.checker_config.get_checks()

Get the relevant checks for the given instance from the checker configs.

def get_base_url(self):
100    def get_base_url(self):
101        """
102        Get the base URL for the given instance from the checker configs.
103        """
104        if self.checker_config is not None:
105            return self.checker_config.get_url(self.instance)

Get the base URL for the given instance from the checker configs.

def run_check(self, check, checkmk_format=True):
107    def run_check(self, check, checkmk_format=True):
108        """
109        Run a single check for the given instance.
110        """
111        # check if the check is supported
112        if check not in self.checks:
113            raise ValueError(f"Requested check '{check}' does not exist.")
114        elif self.instance not in self.checks[check]["instances"]:
115            raise ValueError(
116                f"Requested check '{check}' is not supported for instance "
117                f"'{self.instance}'."
118            )
119        elif checkmk_format:
120            result = getattr(self, check)()
121            start_time = time()
122            result = getattr(self, check)()
123            execution_time = round(time() - start_time, 4)
124            status_code = (
125                0 if result else self.checks[check]["status_on_error"]
126            )
127            return "%i 'AvailabilityCheck | %s-%s | %s' duration=%s %s" % (
128                status_code,
129                self.checker_config.id,
130                self.instance,
131                check,
132                execution_time,
133                f'{self.checks[check]["description"]} (URL: {self.url})',
134            )
135        else:
136            return getattr(self, check)()

Run a single check for the given instance.

def run_checks(self):
138    def run_checks(self):
139        """
140        Run all checks defined in `self.checks` for the given instance.
141        """
142        for check in self.checks:
143            if self.instance not in self.checks[check]["instances"]:
144                continue
145            yield self.run_check(check)

Run all checks defined in self.checks for the given instance.

def save_checks(self, path):
147    def save_checks(self, path):
148        """
149        Save the results of the checks to a file.
150        """
151        results = self.run_checks()
152        filename = f"{self.__class__.__name__}_{self.instance}.json"
153        with open(f"{path.rstrip('/')}/{filename}", "w") as f:
154            f.write(dumps(results))
155        return filename

Save the results of the checks to a file.

def create_var_url(self, subdomain=None, path=None):
157    def create_var_url(
158        self,
159        subdomain=None,
160        path=None,
161    ):
162        """
163        Cutting the url and setting subdomains based
164        if production or testing instance set.
165        """
166
167        base_url = self.get_base_url()
168
169        if base_url is None:
170            raise ValueError(
171                f"self.Instance '{self.instance}' is not supported. "
172                "Please provide a valid URL."
173            )
174
175        url = base_url
176        if subdomain is not None:
177            url = f"https://{subdomain}.{base_url.split('://')[1]}"
178        if path is not None:
179            url = f"{url}/{path.lstrip('/')}"
180
181        return url

Cutting the url and setting subdomains based if production or testing instance set.

def quit(self):
183    def quit(self):
184        self.selenium.quit()
class CheckerWebApps(CheckerBase):
187class CheckerWebApps(CheckerBase):
188    """
189    Checker for the Webapps Service
190    """
191
192    checker_config = CheckerConfigs("webapps")
193
194    @resolve_url
195    def landing_page_webapps(self):
196        """
197        Checks if the landing Page for Webapps is available
198        """
199
200        result = self.selenium.find_by_css(
201            css="img[alt='Logo des NFDI Konsortiums NFDI4Earth']"
202        )
203
204        return bool(result)
205
206    @resolve_url
207    def _visual_search_engine(self):
208        """
209        Checks if the Graph Based Visual Search Engine is available
210        """
211        result = self.selenium.wait_until(
212            css=".MuiDataGrid-cell--withRenderer", want_bool=True
213        )
214
215        result = self.selenium.amount_of_elements(
216            css=".MuiDataGrid-cell--withRenderer", minimum=2
217        )
218
219        return bool(result)
220
221    def visual_search_engine(self):
222        return self._visual_search_engine(subdomain="vesa")

Checker for the Webapps Service

checker_config = <availcheck.configs.CheckerConfigs object>

Set the checker configs for the given instance.

def landing_page_webapps( self, subdomain=None, path=None, ignore_shadow_root=False, *args, **kwargs):
26    def wrap(
27        self,
28        subdomain=None,
29        path=None,
30        ignore_shadow_root=False,
31        *args,
32        **kwargs,
33    ):
34        current_url = self.selenium.get_url()
35        self.url = self.create_var_url(subdomain=subdomain, path=path)
36        if current_url != self.url:
37            try:
38                self.selenium.get(self.url)
39                if (
40                    getattr(self, "shadow_root", None)
41                    and ignore_shadow_root is not True
42                ):
43                    self.shadow_host = self.selenium.get_shadow_root(
44                        self.shadow_root
45                    )
46            except Exception as e:
47                log.error(f"Unable to open URL: {self.url}")
48                log.error(e)
49                return False
50
51            return func(self, *args, **kwargs)
52        else:
53            return func(self, *args, **kwargs)

Checks if the landing Page for Webapps is available

def visual_search_engine(self):
221    def visual_search_engine(self):
222        return self._visual_search_engine(subdomain="vesa")
class CheckerKnowledgeHub(CheckerBase):
225class CheckerKnowledgeHub(CheckerBase):
226    """
227    Checker for the Knowledgehub Service
228    """
229
230    checker_config = CheckerConfigs("knowledgehub")
231
232    @resolve_url
233    def _sparql(self):
234        """
235        Checks if the Knowledgehub-SPARQL Endpoint is availiable
236        This is done by checking if an anchor of class 'navbar-brand' exists
237        and its text is 'SPARQL Query Editor'
238        """
239        result = self.selenium.find_by_css(css="a[class='navbar-brand']")
240        return getattr(result, "text").strip() == "SPARQL Query Editor"
241
242    def sparql(self):
243        return self._sparql(subdomain="sparql")
244
245    @resolve_url
246    def _cordra(self):
247        """
248        Checks if cordra is available,
249        This is done by checking for the searchbar.
250        """
251        result = self.selenium.find_by_css(css="input[placeholder='Search']")
252
253        return bool(result)
254
255    def cordra(self):
256        return self._cordra(subdomain="cordra")
257
258    @resolve_url
259    def landing_page_knowledgehub(self):
260        """
261        Checks if the Landingpage of Knowlegehub is available.
262        """
263        current_url = self.selenium.get_url()
264        result = current_url.startswith("https://knowledgehub")
265
266        return bool(result)
267
268    @resolve_url
269    def more_examples_loaded(self):
270        """
271        Check if more cards appear after button click.
272        """
273        toggle_button = self.selenium.find_by_css(
274            css=("#hidden-card-deck-toggle")
275        )
276        if toggle_button is None:
277            log.warning(f"{toggle_button} not found")
278            return False
279        else:
280            self.selenium.zoom()
281            toggle_button.click()
282            result = self.selenium.wait_until(
283                css="div[class='row mb-3'][id='card-deck']", want_bool=True
284            )
285        return result
286
287    @resolve_url
288    def table_is_displayed(self):
289        """
290        Checks if the "Harvested Ressources table appears
291        with atleast 10 Entries fetched.
292        """
293        self.selenium.zoom()
294        self.selenium.wait_until(css=".col-3.border")
295        result = self.selenium.amount_of_elements(css=".col-3.border")
296        return bool(result)
297
298    @resolve_url
299    def check_button_endpoints(self):
300        """
301        Checks for anker links working
302        """
303        self.selenium.save_screenshot("/tmp/table.png")
304        result = self.selenium.check_button_redirection("a")
305        return bool(result)

Checker for the Knowledgehub Service

checker_config = <availcheck.configs.CheckerConfigs object>

Set the checker configs for the given instance.

def sparql(self):
242    def sparql(self):
243        return self._sparql(subdomain="sparql")
def cordra(self):
255    def cordra(self):
256        return self._cordra(subdomain="cordra")
def landing_page_knowledgehub( self, subdomain=None, path=None, ignore_shadow_root=False, *args, **kwargs):
26    def wrap(
27        self,
28        subdomain=None,
29        path=None,
30        ignore_shadow_root=False,
31        *args,
32        **kwargs,
33    ):
34        current_url = self.selenium.get_url()
35        self.url = self.create_var_url(subdomain=subdomain, path=path)
36        if current_url != self.url:
37            try:
38                self.selenium.get(self.url)
39                if (
40                    getattr(self, "shadow_root", None)
41                    and ignore_shadow_root is not True
42                ):
43                    self.shadow_host = self.selenium.get_shadow_root(
44                        self.shadow_root
45                    )
46            except Exception as e:
47                log.error(f"Unable to open URL: {self.url}")
48                log.error(e)
49                return False
50
51            return func(self, *args, **kwargs)
52        else:
53            return func(self, *args, **kwargs)

Checks if the Landingpage of Knowlegehub is available.

def more_examples_loaded( self, subdomain=None, path=None, ignore_shadow_root=False, *args, **kwargs):
26    def wrap(
27        self,
28        subdomain=None,
29        path=None,
30        ignore_shadow_root=False,
31        *args,
32        **kwargs,
33    ):
34        current_url = self.selenium.get_url()
35        self.url = self.create_var_url(subdomain=subdomain, path=path)
36        if current_url != self.url:
37            try:
38                self.selenium.get(self.url)
39                if (
40                    getattr(self, "shadow_root", None)
41                    and ignore_shadow_root is not True
42                ):
43                    self.shadow_host = self.selenium.get_shadow_root(
44                        self.shadow_root
45                    )
46            except Exception as e:
47                log.error(f"Unable to open URL: {self.url}")
48                log.error(e)
49                return False
50
51            return func(self, *args, **kwargs)
52        else:
53            return func(self, *args, **kwargs)

Check if more cards appear after button click.

def table_is_displayed( self, subdomain=None, path=None, ignore_shadow_root=False, *args, **kwargs):
26    def wrap(
27        self,
28        subdomain=None,
29        path=None,
30        ignore_shadow_root=False,
31        *args,
32        **kwargs,
33    ):
34        current_url = self.selenium.get_url()
35        self.url = self.create_var_url(subdomain=subdomain, path=path)
36        if current_url != self.url:
37            try:
38                self.selenium.get(self.url)
39                if (
40                    getattr(self, "shadow_root", None)
41                    and ignore_shadow_root is not True
42                ):
43                    self.shadow_host = self.selenium.get_shadow_root(
44                        self.shadow_root
45                    )
46            except Exception as e:
47                log.error(f"Unable to open URL: {self.url}")
48                log.error(e)
49                return False
50
51            return func(self, *args, **kwargs)
52        else:
53            return func(self, *args, **kwargs)

Checks if the "Harvested Ressources table appears with atleast 10 Entries fetched.

def check_button_endpoints( self, subdomain=None, path=None, ignore_shadow_root=False, *args, **kwargs):
26    def wrap(
27        self,
28        subdomain=None,
29        path=None,
30        ignore_shadow_root=False,
31        *args,
32        **kwargs,
33    ):
34        current_url = self.selenium.get_url()
35        self.url = self.create_var_url(subdomain=subdomain, path=path)
36        if current_url != self.url:
37            try:
38                self.selenium.get(self.url)
39                if (
40                    getattr(self, "shadow_root", None)
41                    and ignore_shadow_root is not True
42                ):
43                    self.shadow_host = self.selenium.get_shadow_root(
44                        self.shadow_root
45                    )
46            except Exception as e:
47                log.error(f"Unable to open URL: {self.url}")
48                log.error(e)
49                return False
50
51            return func(self, *args, **kwargs)
52        else:
53            return func(self, *args, **kwargs)

Checks for anker links working

class CheckerSupportApps(CheckerBase):
308class CheckerSupportApps(CheckerBase):
309    """
310    Checker for the SupportApps Service
311    """
312
313    checker_config = CheckerConfigs("supportapps")
314
315    @resolve_url
316    def _jupyterhub(self):
317        """
318        Checks if entry level of JupyterHub is restricted / service is running
319        This is done by checking if Logo and corresponding error code is shown.
320        """
321
322        results = []
323
324        #  Check error
325        result = self.selenium.find_by_text(
326            css=".error",
327            text="405 : Method Not Allowed",
328            case_sensitive=False,
329        )
330        results.append(result)
331
332        #  Check logo
333        result = self.selenium.find_by_css(css="img[src='/hub/logo']")
334        results.append(result)
335
336        return all(results)
337
338    def jupyterhub(self):
339        return self._jupyterhub(subdomain="jupyterhub")

Checker for the SupportApps Service

checker_config = <availcheck.configs.CheckerConfigs object>

Set the checker configs for the given instance.

def jupyterhub(self):
338    def jupyterhub(self):
339        return self._jupyterhub(subdomain="jupyterhub")
class CheckerEduTrain(CheckerBase):
342class CheckerEduTrain(CheckerBase):
343    """
344    Checker for the EduTrain Service
345    """
346
347    checker_config = CheckerConfigs("edutrain")
348
349    @resolve_url
350    def openedx_lms(self):
351        """
352        Check if the main page is loaded correctly.
353        This is done by checking if the DFG logo is present.
354        """
355        result = self.selenium.find_by_css(
356            # Path may change on a regular, check again once more
357            css="a.explore-btn[href='/courses']"
358        )
359
360        return bool(result)
361
362    @resolve_url
363    def _openedx_cms(self):
364        """
365        Check if Educational Portal is Working
366        This is achieved by checking for the appearance of the logo
367        """
368        result = self.selenium.find_by_css(css="a.brand-link img.brand-image")
369        return bool(result)
370
371    def openedx_cms(self):
372        return self._openedx_cms(subdomain="teacher")
373
374    @resolve_url
375    def _authentication(self):
376        """
377        Check if URL is available, if "AcademicID" button
378        is available and leads to login page
379        """
380        results = []
381        academy_button = self.selenium.find_by_css(
382            css='button[id="oa2-academicid-oauth2"]'
383        )
384
385        academy_button.click()
386
387        current_url = self.selenium.get_url()
388        self.selenium.save_screenshot("/tmp/auth.png")
389        target_page_reached = current_url.startswith(
390            "https://sso.academiccloud.de/simplesaml/module.php/"
391        )
392
393        results.append(target_page_reached)
394        return all(results)
395
396    def authentication(self):
397        return self._authentication(
398            subdomain="apps", path="/authn/login?next=%2F"
399        )

Checker for the EduTrain Service

checker_config = <availcheck.configs.CheckerConfigs object>

Set the checker configs for the given instance.

def openedx_lms( self, subdomain=None, path=None, ignore_shadow_root=False, *args, **kwargs):
26    def wrap(
27        self,
28        subdomain=None,
29        path=None,
30        ignore_shadow_root=False,
31        *args,
32        **kwargs,
33    ):
34        current_url = self.selenium.get_url()
35        self.url = self.create_var_url(subdomain=subdomain, path=path)
36        if current_url != self.url:
37            try:
38                self.selenium.get(self.url)
39                if (
40                    getattr(self, "shadow_root", None)
41                    and ignore_shadow_root is not True
42                ):
43                    self.shadow_host = self.selenium.get_shadow_root(
44                        self.shadow_root
45                    )
46            except Exception as e:
47                log.error(f"Unable to open URL: {self.url}")
48                log.error(e)
49                return False
50
51            return func(self, *args, **kwargs)
52        else:
53            return func(self, *args, **kwargs)

Check if the main page is loaded correctly. This is done by checking if the DFG logo is present.

def openedx_cms(self):
371    def openedx_cms(self):
372        return self._openedx_cms(subdomain="teacher")
def authentication(self):
396    def authentication(self):
397        return self._authentication(
398            subdomain="apps", path="/authn/login?next=%2F"
399        )
class CheckerOneStop4All(CheckerBase):
402class CheckerOneStop4All(CheckerBase):
403    """
404    Checker for the OneStop4All Service
405    """
406
407    checker_config = CheckerConfigs("onestop4all")
408
409    def __init__(self, url=None, instance=None, *args, **kwargs):
410        super().__init__(url=url, instance=instance, *args, **kwargs)
411        self.shadow_root = "onestop4all-app"
412
413    @resolve_url
414    def main_page(self):
415        """
416        Check if the main page is loaded correctly.
417        This is done by checking if the DFG logo is present.
418        """
419        result = self.selenium.find_by_css(
420            css='img[src="/dfg-logo.svg"]', elem=self.shadow_host
421        )
422        return bool(result)
423
424    @resolve_url
425    def number_of_entities(self):
426        """
427        Check if the number of entities is correct.
428        By checking if there is the correct amount of buttons on the webpage.
429        """
430        results = self.selenium.find_by_css(
431            css="div.resourceEntries",
432            elem=self.shadow_host,
433            as_list=True,
434        )
435        return len(results) == O4A_ENTITIES_COUNT
436
437    @resolve_url
438    def involvement(self):
439        """
440        Check if the buttons withing involvement section is loaded correctly.
441        This is simply done, by checking if there are any buttons.
442        """
443        results = self.selenium.find_by_css(
444            css="div.get-involved div.overlap > img",
445            elem=self.shadow_host,
446            as_list=True,
447        )
448        return len(results) > 0
449
450    @resolve_url
451    def support_form(self, close_modal=True):
452        """
453        Check if the support form is loaded correctly.
454        This is done by checking if the subject input field is present.
455        """
456        # try to find the support form button
457        support_form_button = self.selenium.find_by_text(
458            css=".chakra-link",
459            text="user Support",
460            elem=self.shadow_host,
461            case_sensitive=False,
462        )
463
464        # stop check if no support form button was found
465        if not support_form_button:
466            return False
467
468        # click on the support form button to open modal window
469        support_form_button.click()
470
471        subject = self.selenium.find_by_css(
472            css="input[name='subject']", elem=self.shadow_host
473        )
474        if subject and close_modal:
475            # close the modal window finally
476            self.selenium.find_by_css(
477                css="button[aria-label='Close']", elem=self.shadow_host
478            ).click()
479        return bool(subject)
480
481    @resolve_url
482    def captcha_in_support_form(self):
483        """
484        Check if the CAPTCHA is loaded correctly in the support form.
485        This is done by:
486            1. checking if the CAPTCHA checkbox is present
487            2. clicking on the CAPTCHA checkbox
488            3. checking if the CAPTCHA input field is present AND
489                its value is not empty
490        """
491        if self.support_form(close_modal=False):
492            altcha_checkbox = self.selenium.wait_until(
493                css="input#altcha_checkbox",
494                elem=self.shadow_host,
495            )
496            if altcha_checkbox:
497                altcha_checkbox.click()
498                altcha_input = self.selenium.wait_until(
499                    css="input[type='hidden'][name='altcha']",
500                    elem=self.shadow_host,
501                )
502                if altcha_input and altcha_input.get_attribute("value") != "":
503                    return True
504        return False
505
506    @resolve_url
507    def _solr(self):
508        """
509        Check if the Solr login page is loaded correctly.
510        This is done by checking if the Solr logo is present.
511        """
512        dashboard = self.selenium.find_by_css(css="a[href='#/']")
513        if dashboard is None:
514            return False
515        dashboard.click()
516        login = self.selenium.find_by_css(css="button.btn-danger")
517        return login is not None and login.text == "Login"
518
519    def solr(self):
520        return self._solr(path="/solr/#/login/", ignore_shadow_root=True)

Checker for the OneStop4All Service

CheckerOneStop4All(url=None, instance=None, *args, **kwargs)
409    def __init__(self, url=None, instance=None, *args, **kwargs):
410        super().__init__(url=url, instance=instance, *args, **kwargs)
411        self.shadow_root = "onestop4all-app"
checker_config = <availcheck.configs.CheckerConfigs object>

Set the checker configs for the given instance.

shadow_root
def main_page( self, subdomain=None, path=None, ignore_shadow_root=False, *args, **kwargs):
26    def wrap(
27        self,
28        subdomain=None,
29        path=None,
30        ignore_shadow_root=False,
31        *args,
32        **kwargs,
33    ):
34        current_url = self.selenium.get_url()
35        self.url = self.create_var_url(subdomain=subdomain, path=path)
36        if current_url != self.url:
37            try:
38                self.selenium.get(self.url)
39                if (
40                    getattr(self, "shadow_root", None)
41                    and ignore_shadow_root is not True
42                ):
43                    self.shadow_host = self.selenium.get_shadow_root(
44                        self.shadow_root
45                    )
46            except Exception as e:
47                log.error(f"Unable to open URL: {self.url}")
48                log.error(e)
49                return False
50
51            return func(self, *args, **kwargs)
52        else:
53            return func(self, *args, **kwargs)

Check if the main page is loaded correctly. This is done by checking if the DFG logo is present.

def number_of_entities( self, subdomain=None, path=None, ignore_shadow_root=False, *args, **kwargs):
26    def wrap(
27        self,
28        subdomain=None,
29        path=None,
30        ignore_shadow_root=False,
31        *args,
32        **kwargs,
33    ):
34        current_url = self.selenium.get_url()
35        self.url = self.create_var_url(subdomain=subdomain, path=path)
36        if current_url != self.url:
37            try:
38                self.selenium.get(self.url)
39                if (
40                    getattr(self, "shadow_root", None)
41                    and ignore_shadow_root is not True
42                ):
43                    self.shadow_host = self.selenium.get_shadow_root(
44                        self.shadow_root
45                    )
46            except Exception as e:
47                log.error(f"Unable to open URL: {self.url}")
48                log.error(e)
49                return False
50
51            return func(self, *args, **kwargs)
52        else:
53            return func(self, *args, **kwargs)

Check if the number of entities is correct. By checking if there is the correct amount of buttons on the webpage.

def involvement( self, subdomain=None, path=None, ignore_shadow_root=False, *args, **kwargs):
26    def wrap(
27        self,
28        subdomain=None,
29        path=None,
30        ignore_shadow_root=False,
31        *args,
32        **kwargs,
33    ):
34        current_url = self.selenium.get_url()
35        self.url = self.create_var_url(subdomain=subdomain, path=path)
36        if current_url != self.url:
37            try:
38                self.selenium.get(self.url)
39                if (
40                    getattr(self, "shadow_root", None)
41                    and ignore_shadow_root is not True
42                ):
43                    self.shadow_host = self.selenium.get_shadow_root(
44                        self.shadow_root
45                    )
46            except Exception as e:
47                log.error(f"Unable to open URL: {self.url}")
48                log.error(e)
49                return False
50
51            return func(self, *args, **kwargs)
52        else:
53            return func(self, *args, **kwargs)

Check if the buttons withing involvement section is loaded correctly. This is simply done, by checking if there are any buttons.

def support_form( self, subdomain=None, path=None, ignore_shadow_root=False, *args, **kwargs):
26    def wrap(
27        self,
28        subdomain=None,
29        path=None,
30        ignore_shadow_root=False,
31        *args,
32        **kwargs,
33    ):
34        current_url = self.selenium.get_url()
35        self.url = self.create_var_url(subdomain=subdomain, path=path)
36        if current_url != self.url:
37            try:
38                self.selenium.get(self.url)
39                if (
40                    getattr(self, "shadow_root", None)
41                    and ignore_shadow_root is not True
42                ):
43                    self.shadow_host = self.selenium.get_shadow_root(
44                        self.shadow_root
45                    )
46            except Exception as e:
47                log.error(f"Unable to open URL: {self.url}")
48                log.error(e)
49                return False
50
51            return func(self, *args, **kwargs)
52        else:
53            return func(self, *args, **kwargs)

Check if the support form is loaded correctly. This is done by checking if the subject input field is present.

def captcha_in_support_form( self, subdomain=None, path=None, ignore_shadow_root=False, *args, **kwargs):
26    def wrap(
27        self,
28        subdomain=None,
29        path=None,
30        ignore_shadow_root=False,
31        *args,
32        **kwargs,
33    ):
34        current_url = self.selenium.get_url()
35        self.url = self.create_var_url(subdomain=subdomain, path=path)
36        if current_url != self.url:
37            try:
38                self.selenium.get(self.url)
39                if (
40                    getattr(self, "shadow_root", None)
41                    and ignore_shadow_root is not True
42                ):
43                    self.shadow_host = self.selenium.get_shadow_root(
44                        self.shadow_root
45                    )
46            except Exception as e:
47                log.error(f"Unable to open URL: {self.url}")
48                log.error(e)
49                return False
50
51            return func(self, *args, **kwargs)
52        else:
53            return func(self, *args, **kwargs)

Check if the CAPTCHA is loaded correctly in the support form. This is done by: 1. checking if the CAPTCHA checkbox is present 2. clicking on the CAPTCHA checkbox 3. checking if the CAPTCHA input field is present AND its value is not empty

def solr(self):
519    def solr(self):
520        return self._solr(path="/solr/#/login/", ignore_shadow_root=True)